diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 99bea676bfb..e38277877bb 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -105,7 +105,8 @@ apps/desktop/macos/autofill-extension @bitwarden/team-autofill-dev # DuckDuckGo integration apps/desktop/native-messaging-test-runner @bitwarden/team-autofill-dev apps/desktop/src/services/duckduckgo-message-handler.service.ts @bitwarden/team-autofill-dev - +# SSH Agent +apps/desktop/desktop_native/core/src/ssh_agent @bitwarden/team-autofill-dev @bitwarden/wg-ssh-keys ## Component Library ## .storybook @bitwarden/team-design-system @@ -138,9 +139,6 @@ apps/cli/src/locales/en/messages.json apps/desktop/src/locales/en/messages.json apps/web/src/locales/en/messages.json -## Ssh agent temporary co-codeowner -apps/desktop/desktop_native/core/src/ssh_agent @bitwarden/team-platform-dev @bitwarden/wg-ssh-keys - ## BRE team owns these workflows ## .github/workflows/brew-bump-desktop.yml @bitwarden/dept-bre .github/workflows/deploy-web.yml @bitwarden/dept-bre diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index a320149281b..73d323851e5 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -37,7 +37,6 @@ ./apps/browser/store/windows/AppxManifest.xml ./apps/browser/src/background/nativeMessaging.background.ts ./apps/browser/src/models/browserComponentState.ts -./apps/browser/src/models/browserSendComponentState.ts ./apps/browser/src/models/browserGroupingsComponentState.ts ./apps/browser/src/models/biometricErrors.ts ./apps/browser/src/browser/safariApp.ts diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index 7740e418e7b..aa62d602ad8 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -114,8 +114,8 @@ jobs: fi - build: - name: Build + build-source: + name: Build browser source runs-on: ubuntu-22.04 needs: - setup @@ -127,7 +127,7 @@ jobs: - name: Check out repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ github.event.pull_request.head.sha }} + ref: ${{ github.event.pull_request.head.sha }} - name: Set up Node uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 @@ -169,21 +169,91 @@ jobs: zip -r browser-source.zip browser-source - name: Upload browser source - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: browser-source-${{ env._BUILD_NUMBER }}.zip path: browser-source.zip if-no-files-found: error + + build: + name: Build + runs-on: ubuntu-22.04 + needs: + - setup + - locales-test + - build-source + env: + _BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }} + _NODE_VERSION: ${{ needs.setup.outputs.node_version }} + strategy: + matrix: + include: + - name: "chrome" + npm_command: "dist:chrome" + archive_name: "dist-chrome.zip" + artifact_name: "dist-chrome-MV3" + - name: "edge" + npm_command: "dist:edge" + archive_name: "dist-edge.zip" + artifact_name: "dist-edge" + - name: "edge-mv3" + npm_command: "dist:edge:mv3" + archive_name: "dist-edge.zip" + artifact_name: "DO-NOT-USE-FOR-PROD-dist-edge-MV3" + - name: "firefox" + npm_command: "dist:firefox" + archive_name: "dist-firefox.zip" + artifact_name: "dist-firefox" + - name: "firefox-mv3" + npm_command: "dist:firefox:mv3" + archive_name: "dist-firefox.zip" + artifact_name: "DO-NOT-USE-FOR-PROD-dist-firefox-MV3" + - name: "opera" + npm_command: "dist:opera" + archive_name: "dist-opera.zip" + artifact_name: "dist-opera" + - name: "opera-mv3" + npm_command: "dist:opera:mv3" + archive_name: "dist-opera.zip" + artifact_name: "DO-NOT-USE-FOR-PROD-dist-opera-MV3" + steps: + - name: Check out repo + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - 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: Print environment + run: | + node --version + npm --version + + - name: Download browser source + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + name: browser-source-${{ env._BUILD_NUMBER }}.zip + + - name: Unzip browser source artifact + run: | + unzip browser-source.zip + rm browser-source.zip + - name: NPM setup run: npm ci working-directory: browser-source/ - - name: Download SDK Artifacts + - name: Download SDK artifacts if: ${{ inputs.sdk_branch != '' }} uses: bitwarden/gh-actions/download-artifacts@main with: - github_token: ${{secrets.GITHUB_TOKEN}} + github_token: ${{ secrets.GITHUB_TOKEN }} workflow: build-wasm-internal.yml workflow_conclusion: success branch: ${{ inputs.sdk_branch }} @@ -195,85 +265,19 @@ jobs: - name: Override SDK if: ${{ inputs.sdk_branch != '' }} working-directory: browser-source/ - run: | - npm link ../sdk-internal - - - name: Build Chrome - run: npm run dist:chrome - working-directory: browser-source/apps/browser - - - name: Upload Chrome MV3 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip - path: browser-source/apps/browser/dist/dist-chrome.zip - if-no-files-found: error + run: npm link ../sdk-internal - - name: Build Edge - run: npm run dist:edge + - name: Build extension + run: npm run ${{ matrix.npm_command }} working-directory: browser-source/apps/browser - - name: Upload Edge artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + - name: Upload extension artifact + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: - name: dist-edge-${{ env._BUILD_NUMBER }}.zip - path: browser-source/apps/browser/dist/dist-edge.zip + name: ${{ matrix.artifact_name }}-${{ env._BUILD_NUMBER }}.zip + path: browser-source/apps/browser/dist/${{ matrix.archive_name }} if-no-files-found: error - - name: Build Edge (MV3) - run: npm run dist:edge:mv3 - working-directory: browser-source/apps/browser - - - name: Upload Edge MV3 artifact (DO NOT USE FOR PROD) - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: DO-NOT-USE-FOR-PROD-dist-edge-MV3-${{ env._BUILD_NUMBER }}.zip - path: browser-source/apps/browser/dist/dist-edge.zip - if-no-files-found: error - - - name: Build Firefox - run: npm run dist:firefox - working-directory: browser-source/apps/browser - - - name: Upload Firefox artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: dist-firefox-${{ env._BUILD_NUMBER }}.zip - path: browser-source/apps/browser/dist/dist-firefox.zip - if-no-files-found: error - - - name: Build Firefox (MV3) - run: npm run dist:firefox:mv3 - working-directory: browser-source/apps/browser - - - name: Upload Firefox MV3 artifact (DO NOT USE FOR PROD) - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: DO-NOT-USE-FOR-PROD-dist-firefox-MV3-${{ env._BUILD_NUMBER }}.zip - path: browser-source/apps/browser/dist/dist-firefox.zip - if-no-files-found: error - - - name: Build Opera - run: npm run dist:opera - working-directory: browser-source/apps/browser - - - name: Upload Opera artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: dist-opera-${{ env._BUILD_NUMBER }}.zip - path: browser-source/apps/browser/dist/dist-opera.zip - if-no-files-found: error - - - name: Build Opera (MV3) - run: npm run dist:opera:mv3 - working-directory: browser-source/apps/browser - - - name: Upload Opera MV3 artifact (DO NOT USE FOR PROD) - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 - with: - name: DO-NOT-USE-FOR-PROD-dist-opera-MV3-${{ env._BUILD_NUMBER }}.zip - path: browser-source/apps/browser/dist/dist-opera.zip - if-no-files-found: error build-safari: name: Build Safari @@ -405,7 +409,7 @@ jobs: ls -la - name: Upload Safari artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: dist-safari-${{ env._BUILD_NUMBER }}.zip path: apps/browser/dist/dist-safari.zip @@ -448,6 +452,7 @@ jobs: upload_sources: true upload_translations: false + check-failures: name: Check for failures if: always() @@ -455,6 +460,7 @@ jobs: needs: - setup - locales-test + - build-source - build - build-safari - crowdin-push diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index d480879fb15..02432e0a5f4 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -163,14 +163,14 @@ jobs: matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}-sha256-${{ env._PACKAGE_VERSION }}.txt - name: Upload unix zip asset - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}-${{ env._PACKAGE_VERSION }}.zip if-no-files-found: error - name: Upload unix checksum asset - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}-sha256-${{ env._PACKAGE_VERSION }}.txt path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}-sha256-${{ env._PACKAGE_VERSION }}.txt @@ -324,14 +324,14 @@ jobs: -t sha256 | Out-File -Encoding ASCII ./dist/bw${{ matrix.license_type.artifact_prefix }}-windows-sha256-${env:_PACKAGE_VERSION}.txt - name: Upload windows zip asset - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bw${{ matrix.license_type.artifact_prefix }}-windows-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-windows-${{ env._PACKAGE_VERSION }}.zip if-no-files-found: error - name: Upload windows checksum asset - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bw${{ matrix.license_type.artifact_prefix }}-windows-sha256-${{ env._PACKAGE_VERSION }}.txt path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-windows-sha256-${{ env._PACKAGE_VERSION }}.txt @@ -339,7 +339,7 @@ jobs: - name: Upload Chocolatey asset if: matrix.license_type.build_prefix == 'bit' - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg path: apps/cli/dist/chocolatey/bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg @@ -350,7 +350,7 @@ jobs: - name: Upload NPM Build Directory asset if: matrix.license_type.build_prefix == 'bit' - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip path: apps/cli/bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip @@ -421,14 +421,14 @@ jobs: run: sudo snap remove bw - name: Upload snap asset - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bw_${{ env._PACKAGE_VERSION }}_amd64.snap path: apps/cli/dist/snap/bw_${{ env._PACKAGE_VERSION }}_amd64.snap if-no-files-found: error - name: Upload snap checksum asset - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bw-snap-sha256-${{ env._PACKAGE_VERSION }}.txt path: apps/cli/dist/snap/bw-snap-sha256-${{ env._PACKAGE_VERSION }}.txt diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index bc9bdec396a..63453b93838 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -207,7 +207,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 id: cache with: path: | @@ -232,42 +232,42 @@ jobs: run: npm run dist:lin - name: Upload .deb artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb if-no-files-found: error - name: Upload .rpm artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm if-no-files-found: error - name: Upload .freebsd artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd if-no-files-found: error - name: Upload .snap artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap path: apps/desktop/dist/bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap if-no-files-found: error - name: Upload .AppImage artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ needs.setup.outputs.release_channel }}-linux.yml path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-linux.yml @@ -280,7 +280,7 @@ jobs: sudo npm run pack:lin:flatpak - name: Upload flatpak artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: com.bitwarden.desktop.flatpak path: apps/desktop/dist/com.bitwarden.desktop.flatpak @@ -373,7 +373,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 id: cache with: path: | @@ -428,91 +428,91 @@ jobs: -NewName bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z - name: Upload portable exe artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error - name: Upload installer exe artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/nsis-web/Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error - name: Upload appx ia32 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx if-no-files-found: error - name: Upload store appx ia32 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx if-no-files-found: error - name: Upload NSIS ia32 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z if-no-files-found: error - name: Upload appx x64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx if-no-files-found: error - name: Upload store appx x64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx if-no-files-found: error - name: Upload NSIS x64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z if-no-files-found: error - name: Upload appx ARM64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx if-no-files-found: error - name: Upload store appx ARM64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx if-no-files-found: error - name: Upload NSIS ARM64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z if-no-files-found: error - name: Upload nupkg artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden.${{ env._PACKAGE_VERSION }}.nupkg path: apps/desktop/dist/chocolatey/bitwarden.${{ env._PACKAGE_VERSION }}.nupkg if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ needs.setup.outputs.release_channel }}.yml path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release_channel }}.yml @@ -561,14 +561,14 @@ jobs: - name: Cache Build id: build-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Cache Safari id: safari-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -681,7 +681,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 id: cache with: path: | @@ -749,14 +749,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -869,7 +869,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 id: cache with: path: | @@ -918,28 +918,28 @@ jobs: run: npm run pack:mac - name: Upload .zip artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip if-no-files-found: error - name: Upload .dmg artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg if-no-files-found: error - name: Upload .dmg blockmap artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ needs.setup.outputs.release_channel }}-mac.yml path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-mac.yml @@ -990,14 +990,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -1117,7 +1117,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 id: cache with: path: | @@ -1166,7 +1166,7 @@ jobs: run: npm run pack:mac:mas - name: Upload .pkg artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg path: apps/desktop/dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg @@ -1193,7 +1193,7 @@ jobs: if: | github.event_name != 'pull_request_target' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc-desktop') - uses: slackapi/slack-github-action@37ebaef184d7626c5f204ab8d3baff4262dd30f0 # v1.27.0 + uses: slackapi/slack-github-action@fcfb566f8b0aab22203f066d80ca1d7e4b5d05b3 # v1.27.1 with: channel-id: C074F5UESQ0 payload: | @@ -1252,14 +1252,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -1372,7 +1372,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 id: cache with: path: | @@ -1424,7 +1424,7 @@ jobs: zip -r Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip Bitwarden.app - name: Upload masdev artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip path: apps/desktop/dist/mas-dev-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-masdev-universal.zip @@ -1433,7 +1433,7 @@ jobs: crowdin-push: name: Crowdin Push - if: github.ref == 'refs/heads/main' + if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main' needs: - linux - windows diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 6e5e11c3361..73ae0e14962 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -164,7 +164,7 @@ jobs: run: zip -r web-${{ env._VERSION }}-${{ matrix.name }}.zip build - name: Upload ${{ matrix.name }} artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: web-${{ env._VERSION }}-${{ matrix.name }}.zip path: apps/web/web-${{ env._VERSION }}-${{ matrix.name }}.zip @@ -174,6 +174,9 @@ jobs: build-containers: name: Build Docker images runs-on: ubuntu-22.04 + permissions: + security-events: write + id-token: write needs: - setup - build-artifacts @@ -270,7 +273,8 @@ jobs: run: echo "name=$_AZ_REGISTRY/${PROJECT_NAME}:${IMAGE_TAG}" >> $GITHUB_OUTPUT - name: Build Docker image - uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6.9.0 + id: build-docker + uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6.10.0 with: context: apps/web file: apps/web/Dockerfile @@ -279,11 +283,40 @@ jobs: tags: ${{ steps.image-name.outputs.name }} secrets: | "GH_PAT=${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }}" + + - name: Install Cosign + if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main' + uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0 + + - name: Sign image with Cosign + if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main' + env: + DIGEST: ${{ steps.build-docker.outputs.digest }} + TAGS: ${{ steps.image-name.outputs.name }} + run: | + IFS="," read -a tags <<< "${TAGS}" + images="" + for tag in "${tags[@]}"; do + images+="${tag}@${DIGEST} " + done + cosign sign --yes ${images} + + - name: Scan Docker image + id: container-scan + uses: anchore/scan-action@869c549e657a088dc0441b08ce4fc0ecdac2bb65 # v5.3.0 + with: + image: ${{ steps.image-name.outputs.name }} + fail-build: false + output-format: sarif + + - name: Upload Grype results to GitHub + uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0 + with: + sarif_file: ${{ steps.container-scan.outputs.sarif }} - name: Log out of Docker run: docker logout - crowdin-push: name: Crowdin Push if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main' diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index 0efd9d22f17..a5ebd363f63 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -43,7 +43,7 @@ jobs: - name: Cache NPM id: npm-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: "~/.npm" key: ${{ runner.os }}-npm-chromatic-${{ hashFiles('**/package-lock.json') }} @@ -56,7 +56,7 @@ jobs: run: npm run build-storybook:ci - name: Publish to Chromatic - uses: chromaui/action@dd2eecb9bef44f54774581f4163b0327fd8cf607 # v11.16.3 + uses: chromaui/action@64a9c0ca3bfb724389b0d536e544f56b7b5ff5b3 # v11.20.2 with: token: ${{ secrets.GITHUB_TOKEN }} projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} diff --git a/.github/workflows/crowdin-pull.yml b/.github/workflows/crowdin-pull.yml index f99cecf91d6..027a2f11e55 100644 --- a/.github/workflows/crowdin-pull.yml +++ b/.github/workflows/crowdin-pull.yml @@ -21,8 +21,17 @@ jobs: - app_name: web crowdin_project_id: "308189" steps: + - name: Generate GH App token + uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1 + id: app-token + with: + app-id: ${{ secrets.BW_GHAPP_ID }} + private-key: ${{ secrets.BW_GHAPP_KEY }} + - name: Checkout repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + token: ${{ steps.app-token.outputs.token }} - name: Login to Azure uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 @@ -35,13 +44,6 @@ jobs: with: keyvault: "bitwarden-ci" secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase" - - - name: Generate GH App token - uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0 - id: app-token - with: - app-id: ${{ secrets.BW_GHAPP_ID }} - private-key: ${{ secrets.BW_GHAPP_KEY }} - name: Download translations uses: bitwarden/gh-actions/crowdin@main diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9dc72c7fdda..a907618bd36 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -58,3 +58,31 @@ jobs: run: | npm ci npm run lint + + rust: + name: Run Rust lint on ${{ matrix.os }} + runs-on: ${{ matrix.os || 'ubuntu-latest' }} + + strategy: + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + + steps: + - name: Checkout repo + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Check Rust version + run: rustup --version + + - name: Run cargo fmt + working-directory: ./apps/desktop/desktop_native + run: cargo fmt --check + + - name: Run Clippy + working-directory: ./apps/desktop/desktop_native + run: cargo clippy --all-features --tests + env: + RUSTFLAGS: "-D warnings" diff --git a/.github/workflows/release-desktop-beta.yml b/.github/workflows/release-desktop-beta.yml index a940ce289ff..3ec11c77852 100644 --- a/.github/workflows/release-desktop-beta.yml +++ b/.github/workflows/release-desktop-beta.yml @@ -158,42 +158,42 @@ jobs: run: npm run dist:lin - name: Upload .deb artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb if-no-files-found: error - name: Upload .rpm artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm if-no-files-found: error - name: Upload .freebsd artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd if-no-files-found: error - name: Upload .snap artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap path: apps/desktop/dist/bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap if-no-files-found: error - name: Upload .AppImage artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ needs.setup.outputs.release-channel }}-linux.yml path: apps/desktop/dist/${{ needs.setup.outputs.release-channel }}-linux.yml @@ -299,91 +299,91 @@ jobs: -NewName bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z - name: Upload portable exe artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error - name: Upload installer exe artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/nsis-web/Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error - name: Upload appx ia32 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx if-no-files-found: error - name: Upload store appx ia32 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx if-no-files-found: error - name: Upload NSIS ia32 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z if-no-files-found: error - name: Upload appx x64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx if-no-files-found: error - name: Upload store appx x64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx if-no-files-found: error - name: Upload NSIS x64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z if-no-files-found: error - name: Upload appx ARM64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx if-no-files-found: error - name: Upload store appx ARM64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx if-no-files-found: error - name: Upload NSIS ARM64 artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z if-no-files-found: error - name: Upload nupkg artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: bitwarden.${{ env._PACKAGE_VERSION }}.nupkg path: apps/desktop/dist/chocolatey/bitwarden.${{ env._PACKAGE_VERSION }}.nupkg if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ needs.setup.outputs.release-channel }}.yml path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release-channel }}.yml @@ -426,14 +426,14 @@ jobs: - name: Cache Build id: build-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Cache Safari id: safari-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -560,14 +560,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -707,28 +707,28 @@ jobs: run: npm run pack:mac - name: Upload .zip artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip if-no-files-found: error - name: Upload .dmg artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg if-no-files-found: error - name: Upload .dmg blockmap artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: ${{ needs.setup.outputs.release-channel }}-mac.yml path: apps/desktop/dist/${{ needs.setup.outputs.release-channel }}-mac.yml @@ -773,14 +773,14 @@ jobs: - name: Get Build Cache id: build-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -915,7 +915,7 @@ jobs: APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} - name: Upload .pkg artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg path: apps/desktop/dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg diff --git a/.github/workflows/repository-management.yml b/.github/workflows/repository-management.yml index 9935ef7674e..a914a2c4a7a 100644 --- a/.github/workflows/repository-management.yml +++ b/.github/workflows/repository-management.yml @@ -66,7 +66,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Generate GH App token - uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0 + uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1 id: app-token with: app-id: ${{ secrets.BW_GHAPP_ID }} @@ -115,7 +115,7 @@ jobs: version: ${{ inputs.version_number_override }} - name: Generate GH App token - uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0 + uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1 id: app-token with: app-id: ${{ secrets.BW_GHAPP_ID }} @@ -452,7 +452,7 @@ jobs: - setup steps: - name: Generate GH App token - uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0 + uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1 id: app-token with: app-id: ${{ secrets.BW_GHAPP_ID }} @@ -490,6 +490,7 @@ jobs: git cherry-pick --strategy-option=theirs -x $SOURCE_COMMIT git push -u origin $destination_branch fi + } # Cherry-pick from 'main' into 'rc' cherry_pick browser main rc diff --git a/.github/workflows/scan.yml b/.github/workflows/scan.yml index 6166ac79b1a..b0874b38cbf 100644 --- a/.github/workflows/scan.yml +++ b/.github/workflows/scan.yml @@ -31,7 +31,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Scan with Checkmarx - uses: checkmarx/ast-github-action@f0869bd1a37fddc06499a096101e6c900e815d81 # 2.0.36 + uses: checkmarx/ast-github-action@184bf2f64f55d1c93fd6636d539edf274703e434 # 2.0.41 env: INCREMENTAL: "${{ contains(github.event_name, 'pull_request') && '--sast-incremental' || '' }}" with: @@ -46,7 +46,7 @@ jobs: --output-path . ${{ env.INCREMENTAL }} - name: Upload Checkmarx results to GitHub - uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0 + uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0 with: sarif_file: cx_result.sarif diff --git a/.github/workflows/version-auto-bump.yml b/.github/workflows/version-auto-bump.yml index f41261cb39a..ef46dbc867d 100644 --- a/.github/workflows/version-auto-bump.yml +++ b/.github/workflows/version-auto-bump.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Generate GH App token - uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0 + uses: actions/create-github-app-token@c1a285145b9d317df6ced56c09f525b5c2b6f755 # v1.11.1 id: app-token with: app-id: ${{ secrets.BW_GHAPP_ID }} diff --git a/apps/browser/package.json b/apps/browser/package.json index 647847db457..3adeb292b6d 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/browser", - "version": "2024.12.0", + "version": "2025.1.0", "scripts": { "build": "npm run build:chrome", "build:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 webpack", @@ -14,11 +14,11 @@ "build:watch:firefox": "npm run build:firefox -- --watch", "build:watch:opera": "npm run build:opera -- --watch", "build:watch:safari": "npm run build:safari -- --watch", - "build:prod:chrome": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" npm run build:chrome", - "build:prod:edge": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" npm run build:edge", - "build:prod:firefox": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" npm run build:firefox", - "build:prod:opera": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" npm run build:opera", - "build:prod:safari": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=4096\" npm run build:safari", + "build:prod:chrome": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=8192\" npm run build:chrome", + "build:prod:edge": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=8192\" npm run build:edge", + "build:prod:firefox": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=8192\" npm run build:firefox", + "build:prod:opera": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=8192\" npm run build:opera", + "build:prod:safari": "cross-env NODE_ENV=production NODE_OPTIONS=\"--max-old-space-size=8192\" npm run build:safari", "dist:chrome": "npm run build:prod:chrome && mkdir -p dist && ./scripts/compress.ps1 dist-chrome.zip", "dist:edge": "npm run build:prod:edge && mkdir -p dist && ./scripts/compress.ps1 dist-edge.zip", "dist:firefox": "npm run build:prod:firefox && mkdir -p dist && ./scripts/compress.ps1 dist-firefox.zip", diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index a71021e7ded..46f119bdafe 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -84,7 +84,7 @@ "message": "انضم إلى المنظمة" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "انضم إلى $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -93,7 +93,7 @@ } }, "finishJoiningThisOrganizationBySettingAMasterPassword": { - "message": "Finish joining this organization by setting a master password." + "message": "أنهي الانضمام إلى هذه المؤسسة عن طريق تعيين كلمة مرور رئيسية." }, "tab": { "message": "علامة تبويب" @@ -120,7 +120,7 @@ "message": "نسخ كلمة المرور" }, "copyPassphrase": { - "message": "Copy passphrase" + "message": "انسخ عبارة المرور" }, "copyNote": { "message": "نسخ الملاحظة" @@ -138,31 +138,31 @@ "message": "نسخ رمز الأمان" }, "copyName": { - "message": "Copy name" + "message": "انسخ الاسم" }, "copyCompany": { - "message": "Copy company" + "message": "انسخ الشركة" }, "copySSN": { - "message": "Copy Social Security number" + "message": "انسخ رَقْم الضمان الاجتماعي" }, "copyPassportNumber": { - "message": "Copy passport number" + "message": "نسخ رَقْم جواز السفر" }, "copyLicenseNumber": { - "message": "Copy license number" + "message": "نسخ رَقْم الترخيص" }, "copyPrivateKey": { - "message": "Copy private key" + "message": "نسخ المفتاح الخاص" }, "copyPublicKey": { - "message": "Copy public key" + "message": "نسخ المفتاح العام" }, "copyFingerprint": { - "message": "Copy fingerprint" + "message": "نسخ البصمة" }, "copyCustomField": { - "message": "Copy $FIELD$", + "message": "نسخ $FIELD$", "placeholders": { "field": { "content": "$1", @@ -171,13 +171,13 @@ } }, "copyWebsite": { - "message": "Copy website" + "message": "نسخ الموقع الإلكتروني" }, "copyNotes": { - "message": "Copy notes" + "message": "نسخ الملاحظات" }, "fill": { - "message": "Fill", + "message": "ملء", "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "هوية التعبئة التلقائية" }, + "fillVerificationCode": { + "message": "ملء رمز التحقق" + }, + "fillVerificationCodeAria": { + "message": "ملء رمز التحقق", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "إنشاء كلمة مرور (تم النسخ)" }, @@ -232,16 +239,16 @@ "message": "إضافة عنصر" }, "accountEmail": { - "message": "Account email" + "message": "البريد الإلكتروني للحساب" }, "requestHint": { - "message": "Request hint" + "message": "طلب تلميح" }, "requestPasswordHint": { - "message": "Request password hint" + "message": "طلب تلميح كلمة المرور" }, "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { - "message": "Enter your account email address and your password hint will be sent to you" + "message": "أدخل عنوان البريد الإلكتروني لحسابك وسيُرسل تلميح كلمة المرور الخاصة بك إليك" }, "passwordHint": { "message": "تلميح كلمة المرور" @@ -274,25 +281,25 @@ "message": "تغيير كلمة المرور الرئيسية" }, "continueToWebApp": { - "message": "Continue to web app?" + "message": "هل تريد المتابعة إلى تطبيق الويب؟" }, "continueToWebAppDesc": { - "message": "Explore more features of your Bitwarden account on the web app." + "message": "استكشف المزيد من الميزات لحساب Bitwarden الخاص بك على تطبيق الويب." }, "continueToHelpCenter": { - "message": "Continue to Help Center?" + "message": "هل تريد المتابعة إلى مركز المساعدة؟" }, "continueToHelpCenterDesc": { - "message": "Learn more about how to use Bitwarden on the Help Center." + "message": "تعرف على المزيد حول كيفية استخدام Bitwarden في مركز المساعدة." }, "continueToBrowserExtensionStore": { - "message": "Continue to browser extension store?" + "message": "هل تريد المتابعة إلى متجر إضافات المتصفح؟" }, "continueToBrowserExtensionStoreDesc": { - "message": "Help others find out if Bitwarden is right for them. Visit your browser's extension store and leave a rating now." + "message": "ساعد الآخرين في معرفة ما إذا كان Bitwarden مناسب لهم. قم بزيارة متجر إضافات المتصفح الخاص بك واترك تقييمًا الآن." }, "changeMasterPasswordOnWebConfirmation": { - "message": "You can change your master password on the Bitwarden web app." + "message": "يمكنك تغيير كلمة المرور الرئيسية الخاصة بك على تطبيق ويب الخاص ب Bitwarden." }, "fingerprintPhrase": { "message": "عبارة بصمة الإصبع", @@ -309,43 +316,43 @@ "message": "تسجيل الخروج" }, "aboutBitwarden": { - "message": "About Bitwarden" + "message": "حول Bitwarden" }, "about": { "message": "عن التطبيق" }, "moreFromBitwarden": { - "message": "More from Bitwarden" + "message": "المزيد من Bitwarden" }, "continueToBitwardenDotCom": { - "message": "Continue to bitwarden.com?" + "message": "هل تريد المتابعة إلى bitwarden.com؟" }, "bitwardenForBusiness": { - "message": "Bitwarden for Business" + "message": "Bitwarden للأعمال التجارية" }, "bitwardenAuthenticator": { - "message": "Bitwarden Authenticator" + "message": "مصادق Bitwarden" }, "continueToAuthenticatorPageDesc": { - "message": "Bitwarden Authenticator allows you to store authenticator keys and generate TOTP codes for 2-step verification flows. Learn more on the bitwarden.com website" + "message": "مصادق Bitwarden يسمح لك بتخزين مفاتيح المصادقة وإنشاء رموز لمرة واحدة المستندة إلى الوقت لعمليات المصادقة الثنائية. تعرف على المزيد على موقع bitwarden.com" }, "bitwardenSecretsManager": { - "message": "Bitwarden Secrets Manager" + "message": "مدير الأسرار من Bitwarden" }, "continueToSecretsManagerPageDesc": { - "message": "Securely store, manage, and share developer secrets with Bitwarden Secrets Manager. Learn more on the bitwarden.com website." + "message": "قم بتخزين وإدارة ومشاركة أسرار التطوير مع مدير الأسرار من Bitwarden. تعرف على المزيد في موقع bitwarden.com." }, "passwordlessDotDev": { "message": "Passwordless.dev" }, "continueToPasswordlessDotDevPageDesc": { - "message": "Create smooth and secure login experiences free from traditional passwords with Passwordless.dev. Learn more on the bitwarden.com website." + "message": "أنشئ تجارِب تسجيل دخول سلسة وآمنة خالية من كلمات المرور التقليدية مع Passwordless.dev. تعلم المزيد على موقع bitwarden.com." }, "freeBitwardenFamilies": { - "message": "Free Bitwarden Families" + "message": "Bitwarden للعائلات المجاني" }, "freeBitwardenFamiliesPageDesc": { - "message": "You are eligible for Free Bitwarden Families. Redeem this offer today in the web app." + "message": "أنت مؤهل للحصول على Bitwarden للعائلات المجاني. أحصل على هذا العرض اليوم عبر تطبيق الويب." }, "version": { "message": "الإصدار" @@ -366,22 +373,22 @@ "message": "تحرير المجلّد" }, "newFolder": { - "message": "New folder" + "message": "مجلد جديد" }, "folderName": { - "message": "Folder name" + "message": "أسم المجلد" }, "folderHintText": { "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums" }, "noFoldersAdded": { - "message": "No folders added" + "message": "لا توجد مجلدات مضافة" }, "createFoldersToOrganize": { - "message": "Create folders to organize your vault items" + "message": "أنشئ مجلدات لتنظيم عناصر المخزن الخاصة بك" }, "deleteFolderPermanently": { - "message": "Are you sure you want to permanently delete this folder?" + "message": "هل أنت متأكد أنك تريد حذف هذا المجلد نهائيًا؟" }, "deleteFolder": { "message": "حذف المجلّد" @@ -424,7 +431,7 @@ "message": "قم بإنشاء كلمات مرور قوية وفريدة لتسجيلات الدخول الخاصة بك." }, "bitWebVaultApp": { - "message": "Bitwarden web app" + "message": "تطبيق ويب Bitwarden" }, "importItems": { "message": "استيراد العناصر" @@ -436,7 +443,7 @@ "message": "توليد كلمة مرور" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "توليد عبارة المرور" }, "regeneratePassword": { "message": "إعادة توليد كلمة المرور" @@ -447,9 +454,6 @@ "length": { "message": "الطول" }, - "passwordMinLength": { - "message": "الحد الأدنى لطول كلمة السر" - }, "uppercase": { "message": "أحرف كبيرة (من A إلى Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -467,11 +471,11 @@ "description": "deprecated. Use specialCharactersLabel instead." }, "include": { - "message": "Include", + "message": "تضمين", "description": "Card header for password generator include block" }, "uppercaseDescription": { - "message": "Include uppercase characters", + "message": "تضمين أحرف ذات نسق كبير", "description": "Tooltip for the password generator uppercase character checkbox" }, "uppercaseLabel": { @@ -479,7 +483,7 @@ "description": "Label for the password generator uppercase character checkbox" }, "lowercaseDescription": { - "message": "Include lowercase characters", + "message": "تضمين أحرف ذات نسق صغير", "description": "Full description for the password generator lowercase character checkbox" }, "lowercaseLabel": { @@ -487,7 +491,7 @@ "description": "Label for the password generator lowercase character checkbox" }, "numbersDescription": { - "message": "Include numbers", + "message": "تضمين أرقام", "description": "Full description for the password generator numbers checkbox" }, "numbersLabel": { @@ -495,7 +499,7 @@ "description": "Label for the password generator numbers checkbox" }, "specialCharactersDescription": { - "message": "Include special characters", + "message": "تضمين أحرف خاصة", "description": "Full description for the password generator special characters checkbox" }, "specialCharactersLabel": { @@ -521,16 +525,12 @@ "minSpecial": { "message": "الحد الأدنى من الأحرف الخاصة" }, - "avoidAmbChar": { - "message": "تجنب الأحرف الغامضة", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { - "message": "Avoid ambiguous characters", + "message": "تجنب الأحرف المبهمة", "description": "Label for the avoid ambiguous characters checkbox." }, "generatorPolicyInEffect": { - "message": "Enterprise policy requirements have been applied to your generator options.", + "message": "طُبقت متطلبات سياسة المؤسسة على خيارات المولد الخاصة بك.", "description": "Indicates that a policy limits the credential generator screen." }, "searchVault": { @@ -564,19 +564,19 @@ "message": "المفضلات" }, "unfavorite": { - "message": "Unfavorite" + "message": "إزالة من المفضلة" }, "itemAddedToFavorites": { - "message": "Item added to favorites" + "message": "تم إضافة العنصر إلى المفضلات" }, "itemRemovedFromFavorites": { - "message": "Item removed from favorites" + "message": "تم إزالة العنصر من المفضلات" }, "notes": { "message": "الملاحظات" }, "privateNote": { - "message": "Private note" + "message": "ملاحظة سرية" }, "note": { "message": "الملاحظة" @@ -597,10 +597,10 @@ "message": "بدء" }, "launchWebsite": { - "message": "Launch website" + "message": "تشغيل الموقع" }, "launchWebsiteName": { - "message": "Launch website $ITEMNAME$", + "message": "تشغيل الموقع $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -621,7 +621,7 @@ "message": "الأخرى" }, "unlockMethods": { - "message": "Unlock options" + "message": "فتح الخيارات" }, "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "أعدنّ طريقة إلغاء القُفْل لتغيير إجراء مهلة المخزن الخاص بك." @@ -630,20 +630,17 @@ "message": "إعداد طريقة إلغاء القفل في الإعدادات" }, "sessionTimeoutHeader": { - "message": "Session timeout" + "message": "مهلة الجَلسة" }, "vaultTimeoutHeader": { - "message": "Vault timeout" + "message": "مهلة الخزنة" }, "otherOptions": { - "message": "Other options" + "message": "خيارات أخرى" }, "rateExtension": { "message": "قيِّم هذه الإضافة" }, - "rateExtensionDesc": { - "message": "يرجى النظر في مساعدتنا بكتابة تعليق إيجابي!" - }, "browserNotSupportClipboard": { "message": "متصفح الويب الخاص بك لا يدعم خاصية النسخ السهل. يرجى استخدام النسخ اليدوي." }, @@ -654,13 +651,13 @@ "message": "خزانتك مقفلة. قم بتأكيد هويتك للمتابعة." }, "yourVaultIsLockedV2": { - "message": "Your vault is locked" + "message": "المخزن الخاص بك مقفل" }, "yourAccountIsLocked": { - "message": "Your account is locked" + "message": "حسابك مقفل" }, "or": { - "message": "or" + "message": "أو" }, "unlock": { "message": "إلغاء القفل" @@ -685,7 +682,7 @@ "message": "نفذ وقت الخزانة" }, "vaultTimeout1": { - "message": "Timeout" + "message": "المهلة" }, "lockNow": { "message": "إقفل الآن" @@ -739,16 +736,16 @@ "message": "الأمان" }, "confirmMasterPassword": { - "message": "Confirm master password" + "message": "تأكيد كلمة المرور الرئيسية" }, "masterPassword": { - "message": "Master password" + "message": "كلمة المرور الرئيسية" }, "masterPassImportant": { - "message": "Your master password cannot be recovered if you forget it!" + "message": "لا يمكن استعادة كلمة المرور الرئيسية إذا نسيتها!" }, "masterPassHintLabel": { - "message": "Master password hint" + "message": "تلميح كلمة المرور الرئيسية" }, "errorOccurred": { "message": "لقد حدث خطأ ما" @@ -782,10 +779,10 @@ "message": "تم إنشاء حسابك الجديد! يمكنك الآن تسجيل الدخول." }, "newAccountCreated2": { - "message": "Your new account has been created!" + "message": "تم إنشاء حسابك الجديد!" }, "youHaveBeenLoggedIn": { - "message": "You have been logged in!" + "message": "لقد قمت بتسجيل الدخول!" }, "youSuccessfullyLoggedIn": { "message": "سجلتَ الدخول بنجاح" @@ -800,7 +797,7 @@ "message": "رمز التحقق مطلوب." }, "webauthnCancelOrTimeout": { - "message": "The authentication was cancelled or took too long. Please try again." + "message": "تم إلغاء المصادقة أو استغرقت وقتا طويلا. الرجاء المحاولة مرة أخرى." }, "invalidVerificationCode": { "message": "رمز التحقق غير صالح" @@ -828,16 +825,16 @@ "message": "مسح رمز QR للمصادقة من صفحة الويب الحالية" }, "totpHelperTitle": { - "message": "Make 2-step verification seamless" + "message": "أجعل التحقق بخطوتين سلس" }, "totpHelper": { - "message": "Bitwarden can store and fill 2-step verification codes. Copy and paste the key into this field." + "message": "Bitwarden يمكنه تخزين وملء رموز التحقق بخطوتين. أنسخ وألصق المفتاح في هذا الحقل." }, "totpHelperWithCapture": { - "message": "Bitwarden can store and fill 2-step verification codes. Select the camera icon to take a screenshot of this website's authenticator QR code, or copy and paste the key into this field." + "message": "Bitwarden يمكنه تخزين وملء رموز التحقق من خطوتين. اختر رمز الكاميرا لأخذ لقطة شاشة لرمز QR المصادق لهذا الموقع، أو أنسخ وألصق المفتاح في هذا الحقل." }, "learnMoreAboutAuthenticators": { - "message": "Learn more about authenticators" + "message": "تعرف على المزيد عن المصادقين" }, "copyTOTP": { "message": "نسخ مفتاح المصادقة (TOTP)" @@ -846,28 +843,28 @@ "message": "تم تسجيل الخروج" }, "loggedOutDesc": { - "message": "You have been logged out of your account." + "message": "لقد تم تسجيل خروجك من حسابك." }, "loginExpired": { "message": "انتهت صلاحية جلسة تسجيل الدخول الخاصة بك." }, "logIn": { - "message": "Log in" + "message": "تسجيل الدخول" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "تسجيل الدخول إلى Bitwarden" }, "restartRegistration": { - "message": "Restart registration" + "message": "إعادة التسجيل" }, "expiredLink": { - "message": "Expired link" + "message": "رابط منتهي الصَّلاحِيَة" }, "pleaseRestartRegistrationOrTryLoggingIn": { - "message": "Please restart registration or try logging in." + "message": "الرجاء إعادة التسجيل أو حاول تسجيل الدخول." }, "youMayAlreadyHaveAnAccount": { - "message": "You may already have an account" + "message": "قد يكون لديك حساب بالفعل" }, "logOutConfirmation": { "message": "هل أنت متأكد من أنك تريد تسجيل الخروج؟" @@ -891,10 +888,10 @@ "message": "تسجيل الدخول بخطوتين يجعل حسابك أكثر أمنا من خلال مطالبتك بالتحقق من تسجيل الدخول باستخدام جهاز آخر مثل مفتاح الأمان، تطبيق المصادقة، الرسائل القصيرة، المكالمة الهاتفية، أو البريد الإلكتروني. يمكن تمكين تسجيل الدخول بخطوتين على خزانة الويب bitwarden.com. هل تريد زيارة الموقع الآن؟" }, "twoStepLoginConfirmationContent": { - "message": "Make your account more secure by setting up two-step login in the Bitwarden web app." + "message": "اجعل حسابك أكثر أمنا من خلال إعداد تسجيل الدخول بخطوتين في تطبيق Bitwarden على الويب." }, "twoStepLoginConfirmationTitle": { - "message": "Continue to web app?" + "message": "هل تريد المتابعة إلى تطبيق الويب؟" }, "editedFolder": { "message": "حُفظ المجلد" @@ -937,7 +934,7 @@ "message": "رابط جديد" }, "addDomain": { - "message": "Add domain", + "message": "أضف نطاق", "description": "'Domain' here refers to an internet domain name (e.g. 'bitwarden.com') and the message in whole described the act of putting a domain value into the context." }, "addedItem": { @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "قائمة عناصر الهوية في صفحة التبويب لسهولة الملء التلقائي." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "مسح الحافظة", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1049,7 +1049,7 @@ "message": "إلغاء القفل" }, "additionalOptions": { - "message": "Additional options" + "message": "خيارات إضافية" }, "enableContextMenuItem": { "message": "إظهار خيارات قائمة السياق" @@ -1089,7 +1089,7 @@ "description": "'Solarized' is a noun and the name of a color scheme. It should not be translated." }, "exportFrom": { - "message": "Export from" + "message": "التصدير من" }, "exportVault": { "message": "تصدير الخزانة" @@ -1098,13 +1098,13 @@ "message": "صيغة الملف" }, "fileEncryptedExportWarningDesc": { - "message": "This file export will be password protected and require the file password to decrypt." + "message": "سيكون المِلَفّ المُصدر محميًا بكلمة مرور وسيتطلب كلمة مرور المِلَفّ لفك تشفيره." }, "filePassword": { - "message": "File password" + "message": "كلمة مرور الملف" }, "exportPasswordDescription": { - "message": "This password will be used to export and import this file" + "message": "ستُستخدم كلمة المرور هذه لتصدير واستيراد هذا المِلَفّ" }, "accountRestrictedOptionDescription": { "message": "Use your account encryption key, derived from your account's username and Master Password, to encrypt the export and restrict import to only the current Bitwarden account." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "الانتقال إلى مؤسسة" }, - "share": { - "message": "مشاركة" - }, "movedItemToOrg": { "message": "$ITEMNAME$ انتقل إلى $ORGNAME$", "placeholders": { @@ -1932,10 +1929,10 @@ "message": "Clear history" }, "nothingToShow": { - "message": "Nothing to show" + "message": "لا يوجد شيء لعرضه" }, "nothingGeneratedRecently": { - "message": "You haven't generated anything recently" + "message": "لم تقم بتوليد أي شيء مؤخرًا" }, "remove": { "message": "إزالة" @@ -1996,16 +1993,16 @@ "message": "فتح باستخدام رمز PIN" }, "setYourPinTitle": { - "message": "Set PIN" + "message": "تعيين رَقَم التعريف الشخصي" }, "setYourPinButton": { - "message": "Set PIN" + "message": "تعيين رَقَم التعريف الشخصي" }, "setYourPinCode": { "message": "تعيين رمز PIN الخاص بك لإلغاء قفل Bitwarden. سيتم إعادة تعيين إعدادات PIN الخاصة بك إذا قمت بتسجيل الخروج بالكامل من التطبيق." }, "setYourPinCode1": { - "message": "Your PIN will be used to unlock Bitwarden instead of your master password. Your PIN will reset if you ever fully log out of Bitwarden." + "message": "سيتم استخدام رَقَم التعريف الشخصي الخاص بك لفتح Bitwarden بدلاً من كلمة المرور الرئيسية. سيتم حذف رَقَم التعريف الشخصي الخاص بك إذا قمت بتسجيل الخروج بالكامل من Bitwarden." }, "pinRequired": { "message": "رمز PIN مطلوب." @@ -2020,7 +2017,7 @@ "message": "فتح باستخدام القياسات الحيوية" }, "unlockWithMasterPassword": { - "message": "Unlock with master password" + "message": "فتح بكلمة المرور الرئيسية" }, "awaitDesktop": { "message": "في انتظار التأكيد من سطح المكتب" @@ -2032,7 +2029,7 @@ "message": "قفل مع كلمة المرور الرئيسية عند إعادة تشغيل المتصفح" }, "lockWithMasterPassOnRestart1": { - "message": "Require master password on browser restart" + "message": "أطلب كلمة المرور الرئيسية عند إعادة تشغيل المتصفح" }, "selectOneCollection": { "message": "يجب عليك تحديد مجموعة واحدة على الأقل." @@ -2043,30 +2040,27 @@ "clone": { "message": "استنساخ" }, - "passwordGeneratorPolicyInEffect": { - "message": "واحدة أو أكثر من سياسات المؤسسة تؤثر على إعدادات المولدات الخاصة بك." - }, "passwordGenerator": { - "message": "Password generator" + "message": "مولد كلمة المرور" }, "usernameGenerator": { - "message": "Username generator" + "message": "مولد اسم المستخدم" }, "useThisPassword": { - "message": "Use this password" + "message": "استخدم كلمة المرور هذه" }, "useThisUsername": { - "message": "Use this username" + "message": "استخدم اسم المستخدم هذا" }, "securePasswordGenerated": { - "message": "Secure password generated! Don't forget to also update your password on the website." + "message": "تم توليد كلمة مرور آمنة! لا تنس أن تقوم أيضا بتحديث كلمة المرور الخاصة بك على الموقع." }, "useGeneratorHelpTextPartOne": { - "message": "Use the generator", + "message": "استخدام المولد", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "useGeneratorHelpTextPartTwo": { - "message": "to create a strong unique password", + "message": "لإنشاء كلمة مرور فريدة قوية", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "vaultTimeoutAction": { @@ -2102,7 +2096,7 @@ "message": "تم استعادة العنصر" }, "alreadyHaveAccount": { - "message": "Already have an account?" + "message": "لديك حساب بالفعل؟" }, "vaultTimeoutLogOutConfirmation": { "message": "سيؤدي تسجيل الخروج إلى إزالة جميع إمكانية الوصول إلى خزانتك ويتطلب المصادقة عبر الإنترنت بعد انتهاء المهلة. هل أنت متأكد من أنك تريد استخدام هذا الإعداد؟" @@ -2114,7 +2108,7 @@ "message": "التعبئة التلقائية والحفظ" }, "fillAndSave": { - "message": "Fill and save" + "message": "عبء ثم احفظ" }, "autoFillSuccessAndSavedUri": { "message": "تم تعبئة العنصر تلقائياً وحفظ عنوان URI" @@ -2195,19 +2189,19 @@ "message": "كلمة المرور الرئيسية الجديدة لا تفي بمتطلبات السياسة العامة." }, "receiveMarketingEmailsV2": { - "message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox." + "message": "احصل على النصائح والإعلانات وفرص البحوث من Bitwarden في صندوق الوارد الخاص بك." }, "unsubscribe": { - "message": "Unsubscribe" + "message": "إلغاء الاشتراك" }, "atAnyTime": { - "message": "at any time." + "message": "في أي وقت." }, "byContinuingYouAgreeToThe": { - "message": "By continuing, you agree to the" + "message": "عن طريق المتابعة، أنت توافق على" }, "and": { - "message": "and" + "message": "و" }, "acceptPolicies": { "message": "من خلال تحديد هذا المربع فإنك توافق على ما يلي:" @@ -2270,7 +2264,7 @@ "message": "عدم تطابق الحساب" }, "nativeMessagingWrongUserKeyTitle": { - "message": "Biometric key missmatch" + "message": "عدم تطابق المفتاح الحيوي" }, "nativeMessagingWrongUserKeyDesc": { "message": "Biometric unlock failed. The biometric secret key failed to unlock the vault. Please try to set up biometrics again." @@ -2327,7 +2321,7 @@ "message": "An organization policy has blocked importing items into your individual vault." }, "domainsTitle": { - "message": "Domains", + "message": "النطاقات", "description": "A category title describing the concept of web domains" }, "excludedDomains": { @@ -2337,10 +2331,10 @@ "message": "Bitwarden لن يطلب حفظ تفاصيل تسجيل الدخول لهذه النطاقات. يجب عليك تحديث الصفحة حتى تصبح التغييرات سارية المفعول." }, "excludedDomainsDescAlt": { - "message": "Bitwarden will not ask to save login details for these domains for all logged in accounts. You must refresh the page for changes to take effect." + "message": "Bitwarden لن يطلب حفظ تفاصيل تسجيل الدخول لهذه النطافات لجميع الحسابات مسجلة الدخول. يجب عليك تحديث الصفحة لكي تصبح التغييرات نافذة المفعول." }, "websiteItemLabel": { - "message": "Website $number$ (URI)", + "message": "الموقع $number$ (URI)", "placeholders": { "number": { "content": "$1", @@ -2358,17 +2352,17 @@ } }, "excludedDomainsSavedSuccess": { - "message": "Excluded domain changes saved" + "message": "تم حفظ تغييرات استبعاد النطاقات" }, "limitSendViews": { "message": "Limit views" }, "limitSendViewsHint": { - "message": "No one can view this Send after the limit is reached.", + "message": "لا يمكن لأحد عرض هذا الإرسال بعد الوصول إلى الحد الأقصى.", "description": "Displayed under the limit views field on Send" }, "limitSendViewsCount": { - "message": "$ACCESSCOUNT$ views left", + "message": "$ACCESSCOUNT$ مشاهدات متبقية", "description": "Displayed under the limit views field on Send", "placeholders": { "accessCount": { @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "نص" }, @@ -2403,22 +2389,15 @@ "message": "ملف" }, "allSends": { - "message": "All Sends", + "message": "كل الإرسالات", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "hideTextByDefault": { - "message": "Hide text by default" - }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." + "message": "إخفاء النص بشكل افتراضي" }, "expired": { "message": "Expired" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "تاريخ الحذف" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "تاريخ انتهاء الصلاحية" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "يوم واحد" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "مُخصّص" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "قبل أن تبدأ" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "لاستخدام منتقي التاريخ على نمط التقويم", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "انقر هنا", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "أن يخرج من النافذة.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "صلاحية تاريخ الانتهاء المقدّم غير صحيح." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "خطأ" }, - "regenerateUsername": { - "message": "إعادة إنشاء اسم المستخدم" - }, "generateUsername": { "message": "إنشاء اسم المستخدم" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "نوع اسم المستخدم" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "اسم الموقع الإلكتروني" }, - "whatWouldYouLikeToGenerate": { - "message": "ما الذي ترغب في توليده؟" - }, - "passwordType": { - "message": "نوع كلمة المرور" - }, "service": { "message": "الخدمة" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "ملء بيانات الاعتماد لـ", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index a1783a91ef8..51bfe95a48a 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Kimliyi avto-doldur" }, + "fillVerificationCode": { + "message": "Doğrulama kodunu doldur" + }, + "fillVerificationCodeAria": { + "message": "Doğrulama Kodunu Doldur", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Parol yarat (kopyalandı)" }, @@ -447,9 +454,6 @@ "length": { "message": "Uzunluq" }, - "passwordMinLength": { - "message": "Minimal parol uzunluğu" - }, "uppercase": { "message": "Böyük hərf (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum simvol" }, - "avoidAmbChar": { - "message": "Anlaşılmaz simvollardan çəkinin", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Anlaşılmaz xarakterlərdən çəkin", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Uzantını qiymətləndir" }, - "rateExtensionDesc": { - "message": "Gözəl bir rəy ilə bizə dəstək ola bilərsiniz!" - }, "browserNotSupportClipboard": { "message": "Veb brauzeriniz lövhəyə kopyalamağı dəstəkləmir. Əvəzində əllə kopyalayın." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Asan avto-doldurma üçün Vərəq səhifəsində kimlik elementlərini sadalayın." }, + "clickToAutofillOnVault": { + "message": "Seyf görünüşündə avto-doldurmaq üçün elementlərə klikləyin" + }, "clearClipboard": { "message": "Lövhəni təmizlə", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Təşkilata daşı" }, - "share": { - "message": "Paylaş" - }, "movedItemToOrg": { "message": "$ITEMNAME$, $ORGNAME$ şirkətinə daşındı", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Klonla" }, - "passwordGeneratorPolicyInEffect": { - "message": "Bir və ya daha çox təşkilat siyasəti yaradıcı ayarlarınıza təsir edir." - }, "passwordGenerator": { "message": "Parol yaradıcı" }, @@ -2385,14 +2379,6 @@ "message": "Send detalları", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "\"Send\"ləri axtar", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "\"Send\" əlavə et", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Mətn" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Mətni ilkin olaraq gizlət" }, - "maxAccessCountReached": { - "message": "Maksimal müraciət sayına çatıldı", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Müddəti bitib" }, - "pendingDeletion": { - "message": "Silinməsi gözlənilir" - }, "passwordProtected": { "message": "Parolla qorunan" }, @@ -2468,24 +2447,9 @@ "message": "\"Send\"ə düzəliş et", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "\"Send\"in növü nədir?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Bu \"Send\"i açıqlayan bir ad.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Göndərmək istədiyiniz fayl." - }, "deletionDate": { "message": "Silinmə tarixi" }, - "deletionDateDesc": { - "message": "\"Send\" göstərilən tarix və saatda birdəfəlik silinəcək.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Send, bu tarixdə həmişəlik silinəcək.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Bitmə tarixi" }, - "expirationDateDesc": { - "message": "Əgər ayarlanıbsa, göstərilən tarix və vaxtda \"Send\"ə müraciət başa çatacaq.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 gün" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Özəl" }, - "maximumAccessCount": { - "message": "Maksimal müraciət sayı" - }, - "maximumAccessCountDesc": { - "message": "Əgər ayarlanıbsa, istifadəçilər maksimal müraciət sayına çatdıqdan sonra bu \"Send\"ə müraciət edə bilməyəcək.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "İstəyinizə görə istifadəçilərdən bu \"Send\"ə müraciət edərkən parol tələb edə bilərsiniz.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Alıcıların bu \"Send\"ə müraciət etməsi üçün ixtiyari bir parol əlavə edin.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Bu \"Send\" ilə bağlı gizli notlar.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Heç kimin müraciət edə bilməməsi üçün bu \"Send\"i deaktiv et.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Saxladıqdan sonra \"Send\"in keçidini lövhəyə kopyala.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Göndərmək istədiyiniz mətn" - }, - "sendHideText": { - "message": "Bu \"Send\"in mətnini ilkin olaraq gizlət", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Hazırkı müraciət sayı" - }, "createSend": { "message": "Yeni \"Send\"", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Başlamazdan əvvəl" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Təqvim stilində tarix seçici istifadə etmək üçün", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "bura klikləyin", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "yeni bir pəncərə açın.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Göstərilən son istifadə tarixi yararsızdır." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Silinmə və son istifadə tarixlərini saxlayarkən xəta baş verdi." }, - "hideEmail": { - "message": "E-poçt ünvanımı alıcılardan gizlət." - }, "hideYourEmail": { "message": "E-poçt ünvanınız baxanlardan gizlədilsin." }, - "sendOptionsPolicyInEffect": { - "message": "Bir və ya daha çox təşkilat siyasətləri \"Send\" seçimlərinizə təsir edir." - }, "passwordPrompt": { "message": "Ana parolu təkrar soruş" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Xəta" }, - "regenerateUsername": { - "message": "İstifadəçi adını yenidən yarat" - }, "generateUsername": { "message": "İstifadəçi adı yarat" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "İstifadəçi adı növü" - }, "plusAddressedEmail": { "message": "Üstəgəl ünvanlı e-poçt", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Veb sayt adı" }, - "whatWouldYouLikeToGenerate": { - "message": "Nə yaratmaq istəyirsiniz?" - }, - "passwordType": { - "message": "Parol növü" - }, "service": { "message": "Xidmət" }, @@ -3174,7 +3071,7 @@ "message": "Bütün giriş seçimlərinə bax" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Bütün giriş seçimlərinə bax" }, "notificationSentDevice": { "message": "Cihazınıza bir bildiriş göndərildi." @@ -3580,6 +3477,14 @@ "message": "Hesabınızın kilidini açın, yeni bir pəncərədə açılır", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Vaxt əsaslı Təkistifadəlik Parol Doğrulama Kodu", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Hazırkı TOTP-nin bitməsinə qalan vaxt", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Kimlik məlumatlarını doldur", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Uzantı ikonunda giriş üçün avto-doldurma təklif sayını göstər" }, + "showQuickCopyActions": { + "message": "Seyfdə cəld kopyalama fəaliyyətlərini göstər" + }, "systemDefault": { "message": "İlkin sistem" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Vacib bildiriş" + }, + "setupTwoStepLogin": { + "message": "İki addımlı girişi qur" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden, 2025-ci ilin Fevral ayından etibarən yeni cihazlardan gələn girişləri doğrulamaq üçün hesabınızın e-poçtuna bir kod göndərəcək." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Hesabınızı qorumaq üçün alternativ bir yol kimi iki addımlı girişi qura və ya e-poçtunuzu müraciət edə biləcəyiniz e-poçtla dəyişdirə bilərsiniz." + }, + "remindMeLater": { + "message": "Daha sonra xatırlat" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "$EMAIL$ e-poçtunuza güvənli şəkildə müraciət edə bilirsiniz?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Xeyr, edə bilmirəm" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Bəli, e-poçtuma güvənli şəkildə müraciət edə bilirəm" + }, + "turnOnTwoStepLogin": { + "message": "İki addımlı girişi işə sal" + }, + "changeAcctEmail": { + "message": "Hesabın e-poçtunu dəyişdir" + }, "extensionWidth": { "message": "Uzantı eni" }, diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 17bcdec4560..46c804d27d9 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Аўтазапаўненне асабістых даных" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Генерыраваць пароль (з капіяваннем)" }, @@ -447,9 +454,6 @@ "length": { "message": "Даўжыня" }, - "passwordMinLength": { - "message": "Мінімальная даўжыня пароля" - }, "uppercase": { "message": "Вялікія літары (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Мінімум спецыяльных сімвалаў" }, - "avoidAmbChar": { - "message": "Пазбягаць неадназначных сімвалаў", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Пазбягаць неадназначных сімвалаў", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Ацаніць пашырэнне" }, - "rateExtensionDesc": { - "message": "Падумайце пра тое, каб дапамагчы нам добрым водгукам!" - }, "browserNotSupportClipboard": { "message": "Ваш вэб-браўзер не падтрымлівае капіяванне даных у буфер абмену. Скапіюйце іх уручную." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Спіс элементаў пасведчання на старонцы з укладкамі для лёгкага аўтазапаўнення." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Ачыстка буфера абмену", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Перамясціць у арганізацыю" }, - "share": { - "message": "Абагуліць" - }, "movedItemToOrg": { "message": "$ITEMNAME$ перамешчана ў $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Кланіраваць" }, - "passwordGeneratorPolicyInEffect": { - "message": "Адна або больш палітык арганізацыі ўплывае на налады генератара." - }, "passwordGenerator": { "message": "Генератар пароляў" }, @@ -2385,14 +2379,6 @@ "message": "Падрабязнасці Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Пошук у Send'ах", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Дадаць Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Тэкст" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Дасягнута максімальная колькасць доступаў", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Пратэрмінавана" }, - "pendingDeletion": { - "message": "Чакаецца выдаленне" - }, "passwordProtected": { "message": "Абаронена паролем" }, @@ -2468,24 +2447,9 @@ "message": "Рэдагаваць Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Які гэта тып Send'a?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Зразумелая назва для апісання гэтага Send'a.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Файл, які вы хочаце адправіць." - }, "deletionDate": { "message": "Дата выдалення" }, - "deletionDateDesc": { - "message": "Send будзе незваротна выдалены ў азначаныя дату і час.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Дата завяршэння" }, - "expirationDateDesc": { - "message": "Калі зададзена, то доступ да гэтага Send міне ў азначаную дату і час.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 дзень" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Карыстальніцкі" }, - "maximumAccessCount": { - "message": "Максімальная колькасць доступаў" - }, - "maximumAccessCountDesc": { - "message": "Калі прызначана, то карыстальнікі больш не змогуць атрымаць доступ да гэтага Send пасля таго, як будзе дасягнута максімальная колькасць зваротаў.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Па магчымасці запытваць у карыстальнікаў пароль для доступу да гэтага Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Прыватныя нататкі пра гэты Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Адключыць гэты Send, каб ніхто не змог атрымаць да яго доступ.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Скапіяваць спасылку на гэты Send у буфер абмену пасля захавання.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Тэкст, які вы хочаце адправіць." - }, - "sendHideText": { - "message": "Прадвызначана хаваць тэкст гэтага Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Бягучая колькасць доступаў" - }, "createSend": { "message": "Стварыць новы Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Перад тым, як пачаць" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Для выкарыстання каляндарнага стылю выбару даты", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "націсніце тут", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "для адкрыцця ў новым акне.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Азначаная дата завяршэння тэрміну дзеяння з'яўляецца няправільнай." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Адбылася памылка пры захаванні дат выдалення і завяршэння тэрміну дзеяння." }, - "hideEmail": { - "message": "Схаваць мой адрас электроннай пошты ад атрымальнікаў." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "Адна або больш палітык арганізацыі ўплываюць на параметры Send." - }, "passwordPrompt": { "message": "Паўторны запыт асноўнага пароля" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Памылка" }, - "regenerateUsername": { - "message": "Паўторна генерыраваць імя карыстальніка" - }, "generateUsername": { "message": "Генерыраваць імя карыстальніка" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Тып імя карыстальніка" - }, "plusAddressedEmail": { "message": "Адрасы электроннай пошты з плюсам", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Назва вэб-сайта" }, - "whatWouldYouLikeToGenerate": { - "message": "Што вы хочаце генерыраваць?" - }, - "passwordType": { - "message": "Тып пароля" - }, "service": { "message": "Сэрвіс" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index b76b72001fe..7cbbdc07be4 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Самопопълваща се самоличност" }, + "fillVerificationCode": { + "message": "Попълване на кода за потвърждаване" + }, + "fillVerificationCodeAria": { + "message": "Попълване на кода за потвърждаване", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Генериране на парола (копирана)" }, @@ -447,9 +454,6 @@ "length": { "message": "Дължина" }, - "passwordMinLength": { - "message": "Минимална дължина на паролата" - }, "uppercase": { "message": "Главни букви (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Минимален брой специални знаци" }, - "avoidAmbChar": { - "message": "Без нееднозначни знаци", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Без нееднозначни знаци", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Оценяване на разширението" }, - "rateExtensionDesc": { - "message": "Молим да ни помогнете, като оставите положителен отзив!" - }, "browserNotSupportClipboard": { "message": "Браузърът не поддържа копиране в буфера, затова копирайте на ръка." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Показване на самоличностите в страницата с разделите, за лесно автоматично попълване." }, + "clickToAutofillOnVault": { + "message": "Щракнете върху елементите в трезора за автоматично попълване" + }, "clearClipboard": { "message": "Изчистване на буфера", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Преместване в организация" }, - "share": { - "message": "Споделяне" - }, "movedItemToOrg": { "message": "$ITEMNAME$ се премести в $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Клониране" }, - "passwordGeneratorPolicyInEffect": { - "message": "Поне една политика на организация влияе на настройките на генерирането на паролите." - }, "passwordGenerator": { "message": "Генератор на пароли" }, @@ -2385,14 +2379,6 @@ "message": "Подробности за Изпращането", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Търсене в изпратените", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Добавяне на изпращане", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Текст" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Скриване на текста по подразбиране" }, - "maxAccessCountReached": { - "message": "Достигнат е максималният брой достъпвания", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Изтекъл" }, - "pendingDeletion": { - "message": "Предстои изтриване" - }, "passwordProtected": { "message": "Защита с парола" }, @@ -2468,24 +2447,9 @@ "message": "Редактиране на изпращане", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Вид на изпратеното", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Описателно име за това изпращане.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Файл за изпращане." - }, "deletionDate": { "message": "Дата на изтриване" }, - "deletionDateDesc": { - "message": "Изпращането ще бъде окончателно изтрито на зададената дата и време.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Изпращането ще бъде окончателно изтрито на тази дата.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Срок на валидност" }, - "expirationDateDesc": { - "message": "При задаване — това изпращане ще се изключи на зададената дата и време.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 ден" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "По избор" }, - "maximumAccessCount": { - "message": "Максимален брой достъпвания." - }, - "maximumAccessCountDesc": { - "message": "При задаване — това изпращане ще се изключи след определен брой достъпвания.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Изискване на парола за достъп до това изпращане.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Добавете незадължителна парола, с която получателите да имат достъп до това Изпращане.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Скрити бележки за това изпращане.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Пълно спиране на това изпращане — никой няма да има достъп.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Копиране на връзката към това изпращане при запазването му.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Текст за изпращане." - }, - "sendHideText": { - "message": "Стандартно текстът на това изпращане да се крие.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Текущ брой на достъпванията" - }, "createSend": { "message": "Създаване на изпращане", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Преди да почнете" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "За избор на дата от каландар", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "натиснете тук", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "за изскачащ прозорец.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Неправилна дата на валидност." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Грешка при запазване на датата на валидност и изтриване." }, - "hideEmail": { - "message": "Скриване на е-пощата ми от получателите." - }, "hideYourEmail": { "message": "Скриване на Вашата е-поща от получателите." }, - "sendOptionsPolicyInEffect": { - "message": "Поне една политика на организация влияе на настройките за изпращане." - }, "passwordPrompt": { "message": "Повторно запитване за главната парола" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Грешка" }, - "regenerateUsername": { - "message": "Повторно генериране на потр. име" - }, "generateUsername": { "message": "Генериране на потр. име" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Тип потребителско име" - }, "plusAddressedEmail": { "message": "Адрес на е-поща с плюс", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Име на уеб сайт" }, - "whatWouldYouLikeToGenerate": { - "message": "Какво бихте искали да генерирате?" - }, - "passwordType": { - "message": "Тип парола" - }, "service": { "message": "Услуга" }, @@ -3174,7 +3071,7 @@ "message": "Вижте всички възможности за вписване" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Вижте всички възможности за вписване" }, "notificationSentDevice": { "message": "Към устройството Ви е изпратено известие." @@ -3580,6 +3477,14 @@ "message": "Отклюване на регистрацията, отваря се в нов прозорец", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Код за потвърждение на еднократната времево-ограничена парола", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Оставащо време преди изтичането на текущия код", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Попълване на данните за", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Показване на броя предложения за автоматично попълване на данни за вписване върху иконката на добавката" }, + "showQuickCopyActions": { + "message": "Показване на действията за бързо копиране в трезора" + }, "systemDefault": { "message": "По подразбиране за системата" }, @@ -4890,10 +4798,46 @@ "message": "Генерирана парола" }, "compactMode": { - "message": "Compact mode" + "message": "Компактен режим" }, "beta": { - "message": "Beta" + "message": "Бета" + }, + "importantNotice": { + "message": "Важно съобщение" + }, + "setupTwoStepLogin": { + "message": "Настройте двустепенно удостоверяване" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Битуорден ще изпрати код до е-пощата Ви, за потвърждаване на вписването от нови устройства. Това ще започне от февруари 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Можете да настроите двустепенно удостоверяване, като различен метод на защита, или ако е необходимо да промените е-пощата си с такава, до която имате достъп." + }, + "remindMeLater": { + "message": "Напомнете ми по-късно" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Имате ли сигурен достъп до е-пощата си – $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Не, нямам" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Да, имам достъп до е-пощата си" + }, + "turnOnTwoStepLogin": { + "message": "Включване на двустепенното удостоверяване" + }, + "changeAcctEmail": { + "message": "Промяна на е-пощата" }, "extensionWidth": { "message": "Ширина на разширението" diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index d37499d6748..2da7b40554c 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "পাসওয়ার্ড তৈরি করুন (অনুলিপিকৃত)" }, @@ -447,9 +454,6 @@ "length": { "message": "দৈর্ঘ্য" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "ন্যূনতম বিশেষ" }, - "avoidAmbChar": { - "message": "অস্পষ্ট বর্ণগুলি এড়িয়ে চলুন", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "এক্সটেনশনটি মূল্যায়ন করুন" }, - "rateExtensionDesc": { - "message": "দয়া করে একটি ভাল পর্যালোচনার মাধ্যমে সাহায্য করতে আমাদের বিবেচনা করুন!" - }, "browserNotSupportClipboard": { "message": "আপনার ওয়েব ব্রাউজার সহজে ক্লিপবোর্ড অনুলিপি সমর্থন করে না। পরিবর্তে এটি নিজেই অনুলিপি করুন।" }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "ক্লিপবোর্ড পরিষ্কার", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organization" }, - "share": { - "message": "ভাগ করুন" - }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "নকল" }, - "passwordGeneratorPolicyInEffect": { - "message": "এক বা একাধিক সংস্থার নীতিগুলি আপনার উৎপাদকের সেটিংসকে প্রভাবিত করছে।" - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "অবসায়িত" }, - "pendingDeletion": { - "message": "মুছে ফেলার জন্য অপেক্ষমান" - }, "passwordProtected": { "message": "পাসওয়ার্ড সুরক্ষিত" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Expiration date" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 day" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Custom" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index 4d024d25e77..9f5a610c351 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -447,9 +454,6 @@ "length": { "message": "Length" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum special" }, - "avoidAmbChar": { - "message": "Avoid ambiguous characters", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Clear clipboard", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organization" }, - "share": { - "message": "Share" - }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Clone" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Tekst" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Isteklo" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Expiration date" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 dan" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Custom" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index bc351b0faa2..b38783abc82 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Emplena automàticament l'identitat" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Genera contrasenya (copiada)" }, @@ -447,9 +454,6 @@ "length": { "message": "Longitud" }, - "passwordMinLength": { - "message": "Longitud mínima de la contrasenya" - }, "uppercase": { "message": "Majúscula (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Mínim de caràcters especials" }, - "avoidAmbChar": { - "message": "Eviteu caràcters ambigus", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Eviteu caràcters ambigus", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Valora aquesta extensió" }, - "rateExtensionDesc": { - "message": "Considereu ajudar-nos amb una bona valoració!" - }, "browserNotSupportClipboard": { "message": "El vostre navegador web no admet la còpia fàcil del porta-retalls. Copieu-ho manualment." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Llista els elements d'identitat de la pestanya de la pàgina per facilitar l'autoemplenat." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Buida el porta-retalls", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Desplaça a l'organització" }, - "share": { - "message": "Comparteix" - }, "movedItemToOrg": { "message": "$ITEMNAME$ desplaçat a $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Clona" }, - "passwordGeneratorPolicyInEffect": { - "message": "Una o més polítiques d’organització afecten la configuració del generador." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Detalls de l'enviament", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Cerca Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Afig Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Ocultar el text per defecte" }, - "maxAccessCountReached": { - "message": "S'ha assolit el recompte màxim d'accesos", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Caducat" }, - "pendingDeletion": { - "message": "Pendent de supressió" - }, "passwordProtected": { "message": "Protegit amb contrasenya" }, @@ -2468,24 +2447,9 @@ "message": "Edita Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Quin tipus de Send és aquest?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Un nom apropiat per descriure aquest Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "El fitxer que voleu enviar." - }, "deletionDate": { "message": "Data de supressió" }, - "deletionDateDesc": { - "message": "L'enviament se suprimirà permanentment a la data i hora especificades.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "L'enviament s'esborrarà permanentment en aquesta data.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Data de caducitat" }, - "expirationDateDesc": { - "message": "Si s'estableix, l'accés a aquest enviament caducarà en la data i hora especificades.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 dia" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Personalitzat" }, - "maximumAccessCount": { - "message": "Recompte màxim d'accessos" - }, - "maximumAccessCountDesc": { - "message": "Si s’estableix, els usuaris ja no podran accedir a aquest Send una vegada s’assolisca el nombre màxim d’accessos.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Opcionalment, necessiteu una contrasenya perquè els usuaris accedisquen a aquest Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Notes privades sobre aquest Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Desactiveu aquest Send perquè ningú no hi puga accedir.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copieu l'enllaç d'aquest Send al porta-retalls després de guardar-lo.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "El text que voleu enviar." - }, - "sendHideText": { - "message": "Amaga el text d'aquest Send per defecte.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Recompte d’accessos actual" - }, "createSend": { "message": "Crea un nou Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Abans de començar" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Per utilitzar un selector de dates d'estil calendari", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "feu clic ací", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "per eixir de la finestra.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "La data de caducitat proporcionada no és vàlida." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "S'ha produït un error en guardar les dates de supressió i caducitat." }, - "hideEmail": { - "message": "Amagueu la meua adreça de correu electrònic als destinataris." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "Una o més polítiques d'organització afecten les vostres opcions del Send." - }, "passwordPrompt": { "message": "Sol·licitud de la contrasenya mestra" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenera el nom d'usuari" - }, "generateUsername": { "message": "Genera un nom d'usuari" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Tipus de nom d'usuari" - }, "plusAddressedEmail": { "message": "Adreça amb sufix", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Nom del lloc web" }, - "whatWouldYouLikeToGenerate": { - "message": "Què voleu generar?" - }, - "passwordType": { - "message": "Tipus de contrasenya" - }, "service": { "message": "Servei" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Ompliu les credencials per a", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index f6ad593bd53..bcc7020ffb3 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Automaticky vyplnit identitu" }, + "fillVerificationCode": { + "message": "Vyplnit ověřovací kód" + }, + "fillVerificationCodeAria": { + "message": "Vyplnit ověřovací kód", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Vygenerovat heslo a zkopírovat do schránky" }, @@ -447,9 +454,6 @@ "length": { "message": "Délka" }, - "passwordMinLength": { - "message": "Minimální délka hesla" - }, "uppercase": { "message": "Velká písmena (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimální počet speciálních znaků" }, - "avoidAmbChar": { - "message": "Nepoužívat zaměnitelné znaky", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Nepoužívat zaměnitelné znaky", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Ohodnotit rozšíření" }, - "rateExtensionDesc": { - "message": "Pomozte nám napsáním dobré recenze!" - }, "browserNotSupportClipboard": { "message": "Váš webový prohlížeč nepodporuje automatické kopírování do schránky. Musíte ho zkopírovat ručně." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Pro snadné vyplnění zobrazí položky identit na obrazovce Karta." }, + "clickToAutofillOnVault": { + "message": "Klepněte na položky pro automatické vyplnění v zobrazení trezoru" + }, "clearClipboard": { "message": "Vymazat schránku", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Přesunout do organizace" }, - "share": { - "message": "Sdílet" - }, "movedItemToOrg": { "message": "$ITEMNAME$ přesunut do $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Duplikovat" }, - "passwordGeneratorPolicyInEffect": { - "message": "Jedna nebo více zásad organizace ovlivňují nastavení generátoru." - }, "passwordGenerator": { "message": "Generátor hesla" }, @@ -2385,14 +2379,6 @@ "message": "Podrobnosti Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Prohledat Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Přidat Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Ve výchozím nastavení skrýt text" }, - "maxAccessCountReached": { - "message": "Dosažen maximální počet přístupů", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Vypršela platnost" }, - "pendingDeletion": { - "message": "Čekání na smazání" - }, "passwordProtected": { "message": "Chráněno heslem" }, @@ -2468,24 +2447,9 @@ "message": "Upravit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Jakého typu je tento Send?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Přátelský název pro popis tohoto Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Soubor, který chcete odeslat." - }, "deletionDate": { "message": "Datum smazání" }, - "deletionDateDesc": { - "message": "Tento Send bude trvale smazán v určený datum a čas.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Tento Send bude trvale smazán v určené datum.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Datum vypršení platnosti" }, - "expirationDateDesc": { - "message": "Je-li nastaveno, přístup k tomuto Send vyprší v daný datum a čas.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 den" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Vlastní" }, - "maximumAccessCount": { - "message": "Maximální počet přístupů" - }, - "maximumAccessCountDesc": { - "message": "Je-li nastaveno, uživatelé již nebudou mít přístup k tomuto Send, jakmile bude dosaženo maximálního počtu přístupů.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Volitelně vyžadovat heslo pro přístup k tomuto Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Přidá volitelné heslo pro příjemce pro přístup k tomuto Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Soukromé poznámky o tomto Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deaktivuje tento Send, díky čemuž k němu nebude moci nikdo přistoupit.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Zkopíruje odkaz pro sdílení tohoto Send po uložení.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Text, který chcete odeslat." - }, - "sendHideText": { - "message": "Skrýt ve výchozím stavu text tohoto Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Aktuální počet přístupů" - }, "createSend": { "message": "Nový Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Než začnete" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Chcete-li použít k výběru data styl kalendáře", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "klepněte zde", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "pro zobrazení okna.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Uvedené datum vypršení platnosti není platné." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Došlo k chybě při ukládání datumu smzání a vypršení platnosti." }, - "hideEmail": { - "message": "Skrýt mou e-mailovou adresu před příjemci." - }, "hideYourEmail": { "message": "Skryje Vaši e-mailovou adresu před zobrazením." }, - "sendOptionsPolicyInEffect": { - "message": "Jedna nebo více zásad organizace ovlivňuje nastavení Send." - }, "passwordPrompt": { "message": "Zeptat se znovu na hlavní heslo" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Chyba" }, - "regenerateUsername": { - "message": "Znovu vygenerovat uživatelské jméno" - }, "generateUsername": { "message": "Vygenerovat uživatelské jméno" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Typ uživatelského jména" - }, "plusAddressedEmail": { "message": "E-mailová adresa s plusem", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Název webu" }, - "whatWouldYouLikeToGenerate": { - "message": "Co chcete vygenerovat?" - }, - "passwordType": { - "message": "Typ hesla" - }, "service": { "message": "Služba" }, @@ -3174,7 +3071,7 @@ "message": "Zobrazit všechny volby přihlášení" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Zobrazit všechny volby přihlášení" }, "notificationSentDevice": { "message": "Na Vaše zařízení bylo odesláno oznámení." @@ -3580,6 +3477,14 @@ "message": "Odemknout účet, otevře se v novém okně", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Ověřovací kód TOTP", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Zbývající čas před vypršením aktuálního TOTP", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Vyplnit přihlašovací údaje pro", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Zobrazit počet návrhů automatického vyplňování přihlášení na ikoně rozšíření" }, + "showQuickCopyActions": { + "message": "Zobrazit akce rychlé kopie v trezoru" + }, "systemDefault": { "message": "Systémový výchozí" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Důležité upozornění" + }, + "setupTwoStepLogin": { + "message": "Nastavit dvoufázové přihlášení" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden odešle kód na e-mail Vašeho účtu pro ověření přihlášení z nových zařízení počínaje únorem 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Dvoufázové přihlášení můžete nastavit jako alternativní způsob ochrany Vašeho účtu nebo změnit svůj e-mail na ten, k němuž můžete přistupovat." + }, + "remindMeLater": { + "message": "Připomenout později" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Máte spolehlivý přístup ke svému e-mailu $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Ne, nemám" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ano, ke svému e-mailu mám přístup" + }, + "turnOnTwoStepLogin": { + "message": "Zapnout dvoufázové přihlášení" + }, + "changeAcctEmail": { + "message": "Změnit e-mail účtu" + }, "extensionWidth": { "message": "Šířka rozšíření" }, diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 5d2a0a2e019..3bc85b10fb5 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Llenwi hunaniaeth" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Cynhyrchu cyfrinair (wedi'i gopïo)" }, @@ -447,9 +454,6 @@ "length": { "message": "Hyd" }, - "passwordMinLength": { - "message": "Hyd lleiaf cyfrineiriau" - }, "uppercase": { "message": "Priflythrennau (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Isafswm nodau arbennig" }, - "avoidAmbChar": { - "message": "Osgoi nodau amwys", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Rhoi eich barn ar yr estyniad" }, - "rateExtensionDesc": { - "message": "Ystyriwch ein helpu ni gydag adolygiad da!" - }, "browserNotSupportClipboard": { "message": "Dyw eich porwr gwe ddim yn cefnogi copïo drwy'r clipfwrdd yn hawdd. Copïwch â llaw yn lle." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Clirio'r clipfwrdd", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organization" }, - "share": { - "message": "Rhannu" - }, "movedItemToOrg": { "message": "Symudwyd $ITEMNAME$ i $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Clone" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Chwilio drwy Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Ychwanegu Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Testun" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Wedi dod i ben" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Dyddiad dileu" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Dyddiad dod i ben" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 diwrnod" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Addasedig" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Cyn i chi ddechrau" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "cliciwch yma", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Dyw'r dyddiad dod i ben a roddwyd ddim yn ddilys." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Cuddio fy nghyfeiriad ebost rhag derbynwyr." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Ailofyn am y prif gyfrinair" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Gwall" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Cynhyrchu enw defnyddiwr" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Math o enw defnyddiwr" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "Beth hoffech chi ei gynhyrchu?" - }, - "passwordType": { - "message": "Math o gyfrinair" - }, "service": { "message": "Gwasanaeth" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 5407b8298a0..433cdebbb17 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autoudfyld identitet" }, + "fillVerificationCode": { + "message": "Udfyld bekræftelseskode" + }, + "fillVerificationCodeAria": { + "message": "Udfyld bekræftelseskode", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generér adgangskode (kopieret)" }, @@ -447,9 +454,6 @@ "length": { "message": "Længde" }, - "passwordMinLength": { - "message": "Minimumslængde på adgangskode" - }, "uppercase": { "message": "Store bogstaver (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Mindste antal specialtegn" }, - "avoidAmbChar": { - "message": "Undgå tvetydige tegn", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Undgå tvetydige tegn", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Bedøm udvidelsen" }, - "rateExtensionDesc": { - "message": "Overvej om du vil hjælpe os med en god anmeldelse!" - }, "browserNotSupportClipboard": { "message": "Din webbrowser understøtter ikke udklipsholder kopiering. Kopiér det manuelt i stedet." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Vis identitetsemner på siden Fane for nem autoudfyldning." }, + "clickToAutofillOnVault": { + "message": "Klik på emner for at autoudfylde i Boks-visning" + }, "clearClipboard": { "message": "Ryd udklipsholder", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Flyt til organisation" }, - "share": { - "message": "Del" - }, "movedItemToOrg": { "message": "$ITEMNAME$ flyttet til $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Klon" }, - "passwordGeneratorPolicyInEffect": { - "message": "Én eller flere organisationspolitikker påvirker dine generatorindstillinger." - }, "passwordGenerator": { "message": "Adgangskodegenerator" }, @@ -2385,14 +2379,6 @@ "message": "Send-detaljer", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Søg i Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Tilføj Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Tekst" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Skjul tekst som standard" }, - "maxAccessCountReached": { - "message": "Maksimalt adgangsantal nået", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Udløbet" }, - "pendingDeletion": { - "message": "Afventer sletning" - }, "passwordProtected": { "message": "Kodeordsbeskyttet" }, @@ -2468,24 +2447,9 @@ "message": "Redigér Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Hvilken type Send er denne?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Et venligt navn til at beskrive denne Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Den fil, du vil sende." - }, "deletionDate": { "message": "Sletningsdato" }, - "deletionDateDesc": { - "message": "Send'en slettes permanent på den angivne dato og tid.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Denne Send slettes permanent på denne dato.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Udløbsdato" }, - "expirationDateDesc": { - "message": "Hvis angivet, vil adgangen til denne Send udløbe på den angivne dato og tidspunkt.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 dag" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Tilpasset" }, - "maximumAccessCount": { - "message": "Maksimalt antal tilgange" - }, - "maximumAccessCountDesc": { - "message": "Hvis opsat, vil brugere ikke længere kunne tilgå denne Send, når det maksimale adgangsantal er nået.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Valgfrit brugeradgangskodekrav for at tilgå denne Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Tilføj en valgfri adgangskode til modtagere for adgang til denne Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notater om denne Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deaktivér denne Send, så ingen kan tilgå den.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Kopiér denne Sends link til udklipsholderen når du gemmer.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Den tekst, du vil sende." - }, - "sendHideText": { - "message": "Skjul denne Sends tekst som standard.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Aktuelt antal tilgange" - }, "createSend": { "message": "Ny Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Før du starter" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "For at bruge en datovælger i kalenderstil skal du", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "klikke her", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "for at åbne dit vindue.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Den angivne udløbsdato er ikke gyldig." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Der opstod en fejl under forsøget på at gemme dine sletnings- og udløbsdatoer." }, - "hideEmail": { - "message": "Skjul min e-mailadresse for modtagere." - }, "hideYourEmail": { "message": "Skjul e-mailadressen for modtagere." }, - "sendOptionsPolicyInEffect": { - "message": "Én eller flere organisationspolitikker påvirker dine Send-valgmuligheder." - }, "passwordPrompt": { "message": "Genanmodning om hovedadgangskode" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Fejl" }, - "regenerateUsername": { - "message": "Regenerér brugernavn" - }, "generateUsername": { "message": "Generér brugernavn" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Brugernavnstype" - }, "plusAddressedEmail": { "message": "Plus-adresseret e-mail", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Hjemmeside navn" }, - "whatWouldYouLikeToGenerate": { - "message": "Hvad vil du gerne generere?" - }, - "passwordType": { - "message": "Adgangskodetype" - }, "service": { "message": "Tjeneste" }, @@ -3174,7 +3071,7 @@ "message": "Vis alle indlogningsmuligheder" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Vis alle indlogningsmuligheder" }, "notificationSentDevice": { "message": "En notifikation er sendt til din enhed." @@ -3580,6 +3477,14 @@ "message": "Oplås kontoen, åbnes i et nyt vindue", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Tidsbaseret engangs adgangskodebekræftelseskode", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Resterende tid før udløb af aktuel TOTP", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Angiv legitimationsoplysninger for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Vis antal login-autoudfyldningsforslag på udvidelsesikon" }, + "showQuickCopyActions": { + "message": "Vis hurtig-kopihandlinger på Boks" + }, "systemDefault": { "message": "Systemstandard" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Vigtig notits" + }, + "setupTwoStepLogin": { + "message": "Opsæt totrins-login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Startende i februar 2025, sender Bitwarden en kode til kontoe-mailadressen for at bekræfte logins fra nye enheder." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Man kan opsætte totrins-login som en alternativ måde at beskytte sin konto på eller ændre sin e-mail til en, man kan tilgå." + }, + "remindMeLater": { + "message": "Påmind senere" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Er der pålidelig adgang til e-mailadressen, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nej, jeg gør ikke" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ja, e-mailadressen kan pålideligt tilgås" + }, + "turnOnTwoStepLogin": { + "message": "Slå totrins-login til" + }, + "changeAcctEmail": { + "message": "Skift kontoe-mailadresse" + }, "extensionWidth": { "message": "Udvidelsesbredde" }, diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index e1873f0c3d8..2fc5297205a 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Identität automatisch ausfüllen" }, + "fillVerificationCode": { + "message": "Verifizierungscode ausfüllen" + }, + "fillVerificationCodeAria": { + "message": "Verifizierungscode ausfüllen", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Passwort generieren (kopiert)" }, @@ -447,9 +454,6 @@ "length": { "message": "Länge" }, - "passwordMinLength": { - "message": "Minimale Passwortlänge" - }, "uppercase": { "message": "Großbuchstaben (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Mindestanzahl Sonderzeichen" }, - "avoidAmbChar": { - "message": "Mehrdeutige Zeichen vermeiden", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Mehrdeutige Zeichen vermeiden", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Erweiterung bewerten" }, - "rateExtensionDesc": { - "message": "Wir würden uns freuen, wenn du uns mit einer positiven Bewertung helfen könntest!" - }, "browserNotSupportClipboard": { "message": "Den Browser unterstützt das einfache Kopieren nicht. Bitte kopiere es manuell." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Identitäten-Einträge auf der Tab Seite anzeigen, um das Auto-Ausfüllen zu vereinfachen." }, + "clickToAutofillOnVault": { + "message": "Klicke auf Einträge zum automatischen Ausfüllen in der Tresor-Ansicht" + }, "clearClipboard": { "message": "Zwischenablage leeren", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "In Organisation verschieben" }, - "share": { - "message": "Teilen" - }, "movedItemToOrg": { "message": "$ITEMNAME$ verschoben nach $ORGNAME$", "placeholders": { @@ -1320,10 +1317,10 @@ "message": "Gib den 6-stelligen Verifizierungscode aus deiner Authenticator App ein." }, "authenticationTimeout": { - "message": "Authentication timeout" + "message": "Authentifizierungs-Timeout" }, "authenticationSessionTimedOut": { - "message": "The authentication session timed out. Please restart the login process." + "message": "Die Authentifizierungssitzung ist abgelaufen. Bitte starte den Anmeldeprozess neu." }, "enterVerificationCodeEmail": { "message": "Gib den 6-stelligen Bestätigungscode ein, der an $EMAIL$ gesendet wurde.", @@ -2043,9 +2040,6 @@ "clone": { "message": "Duplizieren" }, - "passwordGeneratorPolicyInEffect": { - "message": "Eine oder mehrere Organisationsrichtlinien beeinflussen deine Generator-Einstellungen." - }, "passwordGenerator": { "message": "Passwort-Generator" }, @@ -2385,14 +2379,6 @@ "message": "Send-Details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Sends suchen", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Send hinzufügen", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Text standardmäßig ausblenden" }, - "maxAccessCountReached": { - "message": "Maximale Zugriffsanzahl erreicht", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Abgelaufen" }, - "pendingDeletion": { - "message": "Ausstehende Löschung" - }, "passwordProtected": { "message": "Passwortgeschützt" }, @@ -2468,24 +2447,9 @@ "message": "Send bearbeiten", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Welche Art von Send ist das?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Ein eigener Name, um dieses Send zu beschreiben.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Die Datei, die du senden möchtest." - }, "deletionDate": { "message": "Löschdatum" }, - "deletionDateDesc": { - "message": "Das Send wird am angegebenen Datum zur angegebenen Uhrzeit dauerhaft gelöscht.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Das Send wird an diesem Datum dauerhaft gelöscht.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Ablaufdatum" }, - "expirationDateDesc": { - "message": "Falls aktiviert, verfällt der Zugriff auf dieses Send am angegebenen Datum zur angegebenen Uhrzeit.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 Tag" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Benutzerdefiniert" }, - "maximumAccessCount": { - "message": "Maximale Zugriffsanzahl" - }, - "maximumAccessCountDesc": { - "message": "Falls aktiviert, können Benutzer nicht mehr auf dieses Send zugreifen, sobald die maximale Zugriffsanzahl erreicht ist.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optional ein Passwort verlangen, damit Benutzer auf dieses Send zugreifen können.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Füge ein optionales Passwort hinzu, mit dem Empfänger auf dieses Send zugreifen können.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private Notizen zu diesem Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Dieses Send deaktivieren, damit niemand darauf zugreifen kann.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Kopiere den Send-Link beim Speichern in die Zwischenablage.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Der Text, den du senden möchtest." - }, - "sendHideText": { - "message": "Send-Text standardmäßig ausblenden.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Aktuelle Zugriffsanzahl" - }, "createSend": { "message": "Neues Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Bevor du beginnst" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Um einen Kalender-Datumsauswahl zu verwenden", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "hier klicken", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "zum Öffnen in einem neuen Fenster.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Das angegebene Verfallsdatum ist nicht gültig." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Es gab einen Fehler beim Speichern deiner Lösch- und Verfallsdaten." }, - "hideEmail": { - "message": "Meine E-Mail-Adresse vor den Empfängern ausblenden." - }, "hideYourEmail": { "message": "Verberge deine E-Mail-Adresse vor Betrachtern." }, - "sendOptionsPolicyInEffect": { - "message": "Eine oder mehrere Organisationsrichtlinien beeinflussen deine Send Einstellungen." - }, "passwordPrompt": { "message": "Master-Passwort erneut abfragen" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Fehler" }, - "regenerateUsername": { - "message": "Benutzername neu generieren" - }, "generateUsername": { "message": "Benutzername generieren" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Benutzernamenstyp" - }, "plusAddressedEmail": { "message": "Plus-adressierte E-Mail-Adresse", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Websitename" }, - "whatWouldYouLikeToGenerate": { - "message": "Was möchtest du generieren?" - }, - "passwordType": { - "message": "Passworttyp" - }, "service": { "message": "Dienst" }, @@ -3174,7 +3071,7 @@ "message": "Alle Anmeldeoptionen anzeigen" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Alle Anmeldeoptionen anzeigen" }, "notificationSentDevice": { "message": "Eine Benachrichtigung wurde an dein Gerät gesendet." @@ -3580,6 +3477,14 @@ "message": "Dein Konto entsperren, öffnet sich in einem neuen Fenster", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Zeitbasierter einmaliger Passwort-Verifizierungscode", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Verbleibende Zeit bis zum Ablauf des aktuellen TOTP", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Zugangsdaten ausfüllen für", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Anzahl der Vorschläge zum Auto-Ausfüllen von Zugangsdaten auf dem Erweiterungssymbol anzeigen" }, + "showQuickCopyActions": { + "message": "Schnellkopier-Aktionen im Tresor anzeigen" + }, "systemDefault": { "message": "Systemstandard" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Wichtiger Hinweis" + }, + "setupTwoStepLogin": { + "message": "Zwei-Faktor-Authentifizierung einrichten" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Ab Februar 2025 wird Bitwarden einen Code an deine Konto-E-Mail-Adresse senden, um Anmeldungen von neuen Geräten zu verifizieren." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Du kannst die Zwei-Faktor-Authentifizierung als eine alternative Methode einrichten, um dein Konto zu schützen, oder deine E-Mail-Adresse zu einer anderen ändern, auf die du zugreifen kannst." + }, + "remindMeLater": { + "message": "Erinnere mich später" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Hast du zuverlässigen Zugriff auf deine E-Mail-Adresse $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nein, habe ich nicht" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ja, ich kann zuverlässig auf meine E-Mails zugreifen" + }, + "turnOnTwoStepLogin": { + "message": "Zwei-Faktor-Authentifizierung aktivieren" + }, + "changeAcctEmail": { + "message": "E-Mail-Adresse des Kontos ändern" + }, "extensionWidth": { "message": "Breite der Erweiterung" }, diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index ff14bfe30f2..a33bc4d0e20 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -20,16 +20,16 @@ "message": "Δημιουργία λογαριασμού" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Νέος/α στο Bitwarden;" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Σύνδεση με κλειδί πρόσβασης" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Χρήση ενιαίας σύνδεσης" }, "welcomeBack": { - "message": "Welcome back" + "message": "Καλώς ήρθατε" }, "setAStrongPassword": { "message": "Ορίστε έναν ισχυρό κωδικό πρόσβασης" @@ -120,7 +120,7 @@ "message": "Αντιγραφή κωδικού πρόσβασης" }, "copyPassphrase": { - "message": "Copy passphrase" + "message": "Αντιγραφή φράσης πρόσβασης" }, "copyNote": { "message": "Αντιγραφή σημείωσης" @@ -153,13 +153,13 @@ "message": "Αντιγραφή αριθμού άδειας" }, "copyPrivateKey": { - "message": "Copy private key" + "message": "Αντιγραφή ιδιωτικού κλειδιού" }, "copyPublicKey": { - "message": "Copy public key" + "message": "Αντιγραφή δημόσιου κλειδιού" }, "copyFingerprint": { - "message": "Copy fingerprint" + "message": "Αντιγραφή δακτυλικού αποτυπώματος" }, "copyCustomField": { "message": "Αντιγραφή του «$FIELD$»", @@ -177,7 +177,7 @@ "message": "Αντιγραφή σημειώσεων" }, "fill": { - "message": "Fill", + "message": "Συμπλήρωση", "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Αυτόματη συμπλήρωση ταυτότητας" }, + "fillVerificationCode": { + "message": "Συμπλήρωση κωδικού επαλήθευσης" + }, + "fillVerificationCodeAria": { + "message": "Συμπλήρωση Κωδικού Επαλήθευσης", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Δημιουργία κωδικού πρόσβασης (αντιγράφηκε)" }, @@ -436,7 +443,7 @@ "message": "Δημιουργία κωδικού πρόσβασης" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "Δημιουργία φράσης πρόσβασης" }, "regeneratePassword": { "message": "Επαναδημιουργία κωδικού πρόσβασης" @@ -447,9 +454,6 @@ "length": { "message": "Μήκος" }, - "passwordMinLength": { - "message": "Ελάχιστο μήκος κωδικού πρόσβασης" - }, "uppercase": { "message": "Κεφαλαία (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Ελάχιστοι ειδικοί χαρακτήρες" }, - "avoidAmbChar": { - "message": "Αποφυγή αμφιλεγόμενων χαρακτήρων", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Αποφυγή αμφιλεγόμενων χαρακτήρων", "description": "Label for the avoid ambiguous characters checkbox." @@ -600,7 +600,7 @@ "message": "Εκκίνηση ιστοσελίδας" }, "launchWebsiteName": { - "message": "Launch website $ITEMNAME$", + "message": "Εκκίνηση ιστοσελίδας $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -641,9 +641,6 @@ "rateExtension": { "message": "Βαθμολογήστε την επέκταση" }, - "rateExtensionDesc": { - "message": "Παρακαλούμε σκεφτείτε να μας βοηθήσετε με μια καλή κριτική!" - }, "browserNotSupportClipboard": { "message": "Το πρόγραμμα περιήγησης ιστού δεν υποστηρίζει εύκολη αντιγραφή πρόχειρου. Αντιγράψτε το με το χέρι αντ'αυτού." }, @@ -855,7 +852,7 @@ "message": "Σύνδεση" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Σύνδεση στο Bitwarden" }, "restartRegistration": { "message": "Επανεκκίνηση εγγραφής" @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Λίστα στοιχείων ταυτότητας στη σελίδα Καρτέλας για εύκολη αυτόματη συμπλήρωση." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Εκκαθάριση Πρόχειρου", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1126,7 +1126,7 @@ "description": "WARNING (should stay in capitalized letters if the language permits)" }, "warningCapitalized": { - "message": "Warning", + "message": "Προειδοποίηση", "description": "Warning (should maintain locale-relevant capitalization)" }, "confirmVaultExport": { @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Μετακίνηση σε οργανισμό" }, - "share": { - "message": "Κοινοποίηση" - }, "movedItemToOrg": { "message": "$ITEMNAME$ μετακινήθηκε στο $ORGNAME$", "placeholders": { @@ -1320,10 +1317,10 @@ "message": "Εισάγετε τον 6ψήφιο κωδικό από την εφαρμογή επαλήθευσης." }, "authenticationTimeout": { - "message": "Authentication timeout" + "message": "Χρονικό όριο επαλήθευσης" }, "authenticationSessionTimedOut": { - "message": "The authentication session timed out. Please restart the login process." + "message": "Λήξη χρονικού ορίου συνεδρίας επαλήθευσης. Παρακαλώ επανεκκινήστε τη διαδικασία σύνδεσης." }, "enterVerificationCodeEmail": { "message": "Εισάγετε τον 6ψήφιο κωδικό επαλήθευσης τον οποίο λάβατε στο $EMAIL$.", @@ -1443,7 +1440,7 @@ "message": "URL Διακομιστή" }, "selfHostBaseUrl": { - "message": "Self-host server URL", + "message": "Διεύθυνση URL διακομιστή αυτοεξυπηρετητή", "description": "Label for field requesting a self-hosted integration service URL" }, "apiUrl": { @@ -1771,7 +1768,7 @@ "message": "Ταυτότητα" }, "typeSshKey": { - "message": "SSH key" + "message": "Κλειδί SSH" }, "newItemHeader": { "message": "Νέα $TYPE$", @@ -1804,13 +1801,13 @@ "message": "Ιστορικό κωδικού πρόσβασης" }, "generatorHistory": { - "message": "Generator history" + "message": "Ιστορικό γεννήτριας" }, "clearGeneratorHistoryTitle": { - "message": "Clear generator history" + "message": "Εκκαθάριση ιστορικού γεννήτριας" }, "cleargGeneratorHistoryDescription": { - "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + "message": "Αν συνεχίσετε, όλες οι καταχωρήσεις θα διαγραφούν οριστικά από το ιστορικό της γεννήτριας. Είστε σίγουροι ότι θέλετε να συνεχίσετε;" }, "back": { "message": "Πίσω" @@ -1849,7 +1846,7 @@ "message": "Ασφαλείς σημειώσεις" }, "sshKeys": { - "message": "SSH Keys" + "message": "Κλειδιά SSH" }, "clear": { "message": "Εκκαθάριση", @@ -1932,10 +1929,10 @@ "message": "Διαγραφή ιστορικού" }, "nothingToShow": { - "message": "Nothing to show" + "message": "Τίποτα για προβολή" }, "nothingGeneratedRecently": { - "message": "You haven't generated anything recently" + "message": "Δεν έχετε δημιουργήσει τίποτα πρόσφατα" }, "remove": { "message": "Αφαίρεση" @@ -2043,9 +2040,6 @@ "clone": { "message": "Κλώνος" }, - "passwordGeneratorPolicyInEffect": { - "message": "Μία ή περισσότερες πολιτικές του οργανισμού επηρεάζουν τις ρυθμίσεις της γεννήτριας." - }, "passwordGenerator": { "message": "Γεννήτρια κωδικού πρόσβασης" }, @@ -2385,14 +2379,6 @@ "message": "Στοιχεία Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Αναζήτηση Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Προσθήκη Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Κείμενο" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Απόκρυψη κειμένου από προεπιλογή" }, - "maxAccessCountReached": { - "message": "Φτάσατε στον μέγιστο αριθμό πρόσβασης", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Έληξε" }, - "pendingDeletion": { - "message": "Εκκρεμεί διαγραφή" - }, "passwordProtected": { "message": "Προστατευμένο με κωδικό" }, @@ -2468,24 +2447,9 @@ "message": "Επεξεργασία Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Τι είδους Send είναι αυτό;", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Ένα φιλικό όνομα για την περιγραφή αυτού του Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Το αρχείο που θέλετε να στείλετε." - }, "deletionDate": { "message": "Ημερομηνία διαγραφής" }, - "deletionDateDesc": { - "message": "Το Send θα διαγραφεί οριστικά την καθορισμένη ημερομηνία και ώρα.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Το Send θα διαγραφεί οριστικά σε αυτήν την ημερομηνία.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Ημερομηνία λήξης" }, - "expirationDateDesc": { - "message": "Εάν οριστεί, η πρόσβαση σε αυτό το Send θα λήξει την καθορισμένη ημερομηνία και ώρα.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 ημέρα" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Προσαρμοσμένο" }, - "maximumAccessCount": { - "message": "Μέγιστος Αριθμός Πρόσβασης" - }, - "maximumAccessCountDesc": { - "message": "Εάν οριστεί, οι χρήστες δεν θα μπορούν πλέον να έχουν πρόσβαση σε αυτό το send μόλις επιτευχθεί ο μέγιστος αριθμός πρόσβασης.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Προαιρετικά απαιτείται κωδικός πρόσβασης για τους χρήστες για να έχουν πρόσβαση σε αυτό το Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { - "message": "Add an optional password for recipients to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNotesDesc": { - "message": "Ιδιωτικές σημειώσεις σχετικά με αυτό το Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Απενεργοποιήστε αυτό το Send έτσι ώστε κανείς να μην μπορεί να έχει πρόσβαση σε αυτό.", + "message": "Προσθέστε έναν προαιρετικό κωδικό πρόσβασης για τους παραλήπτες για πρόσβαση σε αυτήν την αποστολή.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendShareDesc": { - "message": "Αντιγραφή του συνδέσμου για αυτό το Send στο πρόχειρο κατά την αποθήκευση.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Το κείμενο που θέλετε να στείλετε." - }, - "sendHideText": { - "message": "Απόκρυψη του κειμένου αυτού του Send από προεπιλογή.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Τρέχων αριθμός πρόσβασης" - }, "createSend": { "message": "Νέο Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Πριν ξεκινήσετε" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Για να χρησιμοποιήσετε έναν επιλογέα ημερομηνίας στυλ ημερολογίου", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "κάντε κλικ εδώ", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "για να βγεις από το παράθυρο.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Η ημερομηνία λήξης που δόθηκε δεν είναι έγκυρη." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Παρουσιάστηκε σφάλμα κατά την αποθήκευση των ημερομηνιών διαγραφής και λήξης." }, - "hideEmail": { - "message": "Απόκρυψη της διεύθυνσης email μου από τους παραλήπτες." - }, "hideYourEmail": { "message": "Απόκρυψη της διεύθυνσης email σας από τους θεατές." }, - "sendOptionsPolicyInEffect": { - "message": "Μία ή περισσότερες οργανωτικές πολιτικές επηρεάζουν τις επιλογές send σας." - }, "passwordPrompt": { "message": "Προτροπή νέου κωδικού πρόσβασης" }, @@ -2880,17 +2789,14 @@ "error": { "message": "Σφάλμα" }, - "regenerateUsername": { - "message": "Επαναδημιουργία ονόματος χρήστη" - }, "generateUsername": { "message": "Δημιουργία ονόματος χρήστη" }, "generateEmail": { - "message": "Generate email" + "message": "Δημιουργία διεύθυνσης ηλ. ταχυδρομείου" }, "spinboxBoundariesHint": { - "message": "Value must be between $MIN$ and $MAX$.", + "message": "Η τιμή πρέπει να είναι μεταξύ $MIN$ και $MAX$.", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2904,7 +2810,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", + "message": " Χρησιμοποιήστε $RECOMMENDED$ ή περισσότερους χαρακτήρες για να δημιουργήσετε έναν ισχυρό κωδικό πρόσβασης.", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2914,7 +2820,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": " Χρησιμοποιήστε $RECOMMENDED$ ή περισσότερες λέξεις για να δημιουργήσετε μια ισχυρή φράση πρόσβασης.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Τύπος ονόματος χρήστη" - }, "plusAddressedEmail": { "message": "Συν διεύθυνση ηλ. ταχυδρομείου", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Όνομα ιστοσελίδας" }, - "whatWouldYouLikeToGenerate": { - "message": "Τι θα θέλατε να δημιουργήσετε?" - }, - "passwordType": { - "message": "Τύπος κωδικού πρόσβασης" - }, "service": { "message": "Υπηρεσία" }, @@ -2964,11 +2861,11 @@ "message": "Δημιουργήστε ένα alias email με μια εξωτερική υπηρεσία προώθησης." }, "forwarderDomainName": { - "message": "Email domain", + "message": "Τομέας ηλ. ταχυδρομείου", "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { - "message": "Choose a domain that is supported by the selected service", + "message": "Επιλέξτε ένα τομέα που υποστηρίζεται από την επιλεγμένη υπηρεσία", "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { @@ -3171,25 +3068,25 @@ "message": "Επαναποστολή ειδοποίησης" }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "Δείτε όλες τις επιλογές σύνδεσης" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Δείτε όλες τις επιλογές σύνδεσης" }, "notificationSentDevice": { "message": "Μια ειδοποίηση έχει σταλεί στη συσκευή σας." }, "aNotificationWasSentToYourDevice": { - "message": "A notification was sent to your device" + "message": "Μια ειδοποίηση στάλθηκε στη συσκευή σας" }, "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { - "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + "message": "Βεβαιωθείτε ότι ο λογαριασμός σας είναι ξεκλείδωτος και ότι η φράση δακτυλικού αποτυπώματος ταιριάζει στην άλλη συσκευή" }, "youWillBeNotifiedOnceTheRequestIsApproved": { - "message": "You will be notified once the request is approved" + "message": "Θα ειδοποιηθείτε μόλις εγκριθεί η αίτηση" }, "needAnotherOptionV1": { - "message": "Need another option?" + "message": "Χρειάζεστε μια άλλη επιλογή;" }, "loginInitiated": { "message": "Η σύνδεση ξεκίνησε" @@ -3285,16 +3182,16 @@ "message": "Ανοίγει σε νέο παράθυρο" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Απομνημόνευση αυτής της συσκευής για την αυτόματες συνδέσεις στο μέλλον" }, "deviceApprovalRequired": { "message": "Απαιτείται έγκριση συσκευής. Επιλέξτε μια επιλογή έγκρισης παρακάτω:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Απαιτείται έγκριση συσκευής" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Επιλέξτε μια επιλογή έγκρισης παρακάτω" }, "rememberThisDevice": { "message": "Απομνημόνευση αυτής της συσκευής" @@ -3370,7 +3267,7 @@ "message": "Η διεύθυνση ηλ. ταχυδρομείου του χρήστη λείπει" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Το ηλ. ταχυδρομείο χρήστη δεν βρέθηκε. Αποσυνδεθήκατε." }, "deviceTrusted": { "message": "Αξιόπιστη συσκευή" @@ -3580,6 +3477,14 @@ "message": "Ξεκλείδωμα του λογαριασμού σας, ανοίγει σε νέο παράθυρο", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Κωδικός Επαλήθευσης Κωδικού Πρόσβασης μιας φοράς", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Χρόνος που απομένει πριν λήξει του τρέχον ΚΕΚΠ", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Συμπλήρωση στοιχείων για", "description": "Screen reader text for when overlay item is in focused" @@ -3809,7 +3714,7 @@ "message": "Πρόσβαση" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Έχετε συνδεθεί!" }, "passkeyNotCopied": { "message": "Το κλειδί πρόσβασης δε θα αντιγραφεί" @@ -4303,13 +4208,13 @@ "message": "Φίλτρα" }, "filterVault": { - "message": "Filter vault" + "message": "Φίλτρο θησαυ/κιου" }, "filterApplied": { - "message": "One filter applied" + "message": "Ένα φίλτρο εφαρμόστηκε" }, "filterAppliedPlural": { - "message": "$COUNT$ filters applied", + "message": "$COUNT$ φίλτρα εφαρμόστηκαν", "placeholders": { "count": { "content": "$1", @@ -4641,13 +4546,13 @@ "message": "Τοποθεσία Αντικειμένου" }, "fileSend": { - "message": "File Send" + "message": "Δημιουργία Αποστολής" }, "fileSends": { "message": "Send αρχείων" }, "textSend": { - "message": "Text Send" + "message": "Κείμενο Αποστολής" }, "textSends": { "message": "Send κειμένων" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Εμφάνιση αριθμού προτάσεων αυτόματης συμπλήρωσης σύνδεσης στο εικονίδιο επέκτασης" }, + "showQuickCopyActions": { + "message": "Εμφάνιση ενεργειών γρήγορης αντιγραφής στο Θησαυ/κιο" + }, "systemDefault": { "message": "Προεπιλογή συστήματος" }, @@ -4671,16 +4579,16 @@ "message": "Οι απαιτήσεις της πολιτικής για επιχειρήσεις έχουν εφαρμοστεί σε αυτήν τη ρύθμιση" }, "sshPrivateKey": { - "message": "Private key" + "message": "Ιδιωτικό κλειδί" }, "sshPublicKey": { - "message": "Public key" + "message": "Δημόσιο κλειδί" }, "sshFingerprint": { - "message": "Fingerprint" + "message": "Δακτυλικό αποτύπωμα" }, "sshKeyAlgorithm": { - "message": "Key type" + "message": "Τύπος κλειδιού" }, "sshKeyAlgorithmED25519": { "message": "ED25519" @@ -4737,171 +4645,207 @@ "message": "Ταυτοποίηση" }, "fillGeneratedPassword": { - "message": "Fill generated password", + "message": "Συμπληρώση του δημιουργημένου κωδικού πρόσβασης", "description": "Heading for the password generator within the inline menu" }, "passwordRegenerated": { - "message": "Password regenerated", + "message": "Ο κωδικός επαναδημιουργήθηκε", "description": "Notification message for when a password has been regenerated" }, "saveLoginToBitwarden": { - "message": "Save login to Bitwarden?", + "message": "Αποθήκευση σύνδεσης στο Bitwarden;", "description": "Confirmation message for saving a login to Bitwarden" }, "spaceCharacterDescriptor": { - "message": "Space", + "message": "Κενό", "description": "Represents the space key in screen reader content as a readable word" }, "tildeCharacterDescriptor": { - "message": "Tilde", + "message": "Περισπωμένη", "description": "Represents the ~ key in screen reader content as a readable word" }, "backtickCharacterDescriptor": { - "message": "Backtick", + "message": "Ανάποδη απόστροφος", "description": "Represents the ` key in screen reader content as a readable word" }, "exclamationCharacterDescriptor": { - "message": "Exclamation mark", + "message": "Θαυμαστικό", "description": "Represents the ! key in screen reader content as a readable word" }, "atSignCharacterDescriptor": { - "message": "At sign", + "message": "Παπάκι", "description": "Represents the @ key in screen reader content as a readable word" }, "hashSignCharacterDescriptor": { - "message": "Hash sign", + "message": "Δίεση", "description": "Represents the # key in screen reader content as a readable word" }, "dollarSignCharacterDescriptor": { - "message": "Dollar sign", + "message": "Δολάριο", "description": "Represents the $ key in screen reader content as a readable word" }, "percentSignCharacterDescriptor": { - "message": "Percent sign", + "message": "Τοις εκατό", "description": "Represents the % key in screen reader content as a readable word" }, "caretCharacterDescriptor": { - "message": "Caret", + "message": "Εκθέτης", "description": "Represents the ^ key in screen reader content as a readable word" }, "ampersandCharacterDescriptor": { - "message": "Ampersand", + "message": "Και", "description": "Represents the & key in screen reader content as a readable word" }, "asteriskCharacterDescriptor": { - "message": "Asterisk", + "message": "Πολλαπλασιασμός", "description": "Represents the * key in screen reader content as a readable word" }, "parenLeftCharacterDescriptor": { - "message": "Left parenthesis", + "message": "Αριστερή παρένθεση", "description": "Represents the ( key in screen reader content as a readable word" }, "parenRightCharacterDescriptor": { - "message": "Right parenthesis", + "message": "Δεξιά παρένθεση", "description": "Represents the ) key in screen reader content as a readable word" }, "hyphenCharacterDescriptor": { - "message": "Underscore", + "message": "Κάτω παύλα", "description": "Represents the _ key in screen reader content as a readable word" }, "underscoreCharacterDescriptor": { - "message": "Hyphen", + "message": "Παύλα", "description": "Represents the - key in screen reader content as a readable word" }, "plusCharacterDescriptor": { - "message": "Plus", + "message": "Συν", "description": "Represents the + key in screen reader content as a readable word" }, "equalsCharacterDescriptor": { - "message": "Equals", + "message": "Ίσον", "description": "Represents the = key in screen reader content as a readable word" }, "braceLeftCharacterDescriptor": { - "message": "Left brace", + "message": "Αριστερό άγκιστρο", "description": "Represents the { key in screen reader content as a readable word" }, "braceRightCharacterDescriptor": { - "message": "Right brace", + "message": "Δεξί άγκιστρο", "description": "Represents the } key in screen reader content as a readable word" }, "bracketLeftCharacterDescriptor": { - "message": "Left bracket", + "message": "Αριστερή αγκύλη", "description": "Represents the [ key in screen reader content as a readable word" }, "bracketRightCharacterDescriptor": { - "message": "Right bracket", + "message": "Δεξιά αγκύλη", "description": "Represents the ] key in screen reader content as a readable word" }, "pipeCharacterDescriptor": { - "message": "Pipe", + "message": "Κάθετος γραμμή", "description": "Represents the | key in screen reader content as a readable word" }, "backSlashCharacterDescriptor": { - "message": "Back slash", + "message": "Πίσω κάθετος", "description": "Represents the back slash key in screen reader content as a readable word" }, "colonCharacterDescriptor": { - "message": "Colon", + "message": "Άνω κάτω τελεία", "description": "Represents the : key in screen reader content as a readable word" }, "semicolonCharacterDescriptor": { - "message": "Semicolon", + "message": "Ερωτηματικό", "description": "Represents the ; key in screen reader content as a readable word" }, "doubleQuoteCharacterDescriptor": { - "message": "Double quote", + "message": "Διπλά εισαγωγικά", "description": "Represents the double quote key in screen reader content as a readable word" }, "singleQuoteCharacterDescriptor": { - "message": "Single quote", + "message": "Μονά εισαγωγικά", "description": "Represents the ' key in screen reader content as a readable word" }, "lessThanCharacterDescriptor": { - "message": "Less than", + "message": "Λιγότερο από", "description": "Represents the < key in screen reader content as a readable word" }, "greaterThanCharacterDescriptor": { - "message": "Greater than", + "message": "Μεγαλύτερο από", "description": "Represents the > key in screen reader content as a readable word" }, "commaCharacterDescriptor": { - "message": "Comma", + "message": "Κόμμα", "description": "Represents the , key in screen reader content as a readable word" }, "periodCharacterDescriptor": { - "message": "Period", + "message": "Τελεία", "description": "Represents the . key in screen reader content as a readable word" }, "questionCharacterDescriptor": { - "message": "Question mark", + "message": "Αγγλικό ερωτηματικό", "description": "Represents the ? key in screen reader content as a readable word" }, "forwardSlashCharacterDescriptor": { - "message": "Forward slash", + "message": "Εμπρός κάθετος", "description": "Represents the / key in screen reader content as a readable word" }, "lowercaseAriaLabel": { - "message": "Lowercase" + "message": "Πεζά" }, "uppercaseAriaLabel": { - "message": "Uppercase" + "message": "Κεφαλαία" }, "generatedPassword": { - "message": "Generated password" + "message": "Τυχαίος κωδικός πρόσβασης" }, "compactMode": { - "message": "Compact mode" + "message": "Συμπαγής μορφή" }, "beta": { - "message": "Beta" + "message": "Beta (Δοκιμαστική)" + }, + "importantNotice": { + "message": "Σημαντική ειδοποίηση" + }, + "setupTwoStepLogin": { + "message": "Ρύθμιση σύνδεσης δύο βημάτων" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Το Bitwarden θα στείλει έναν κωδικό στο ηλ. ταχυδρομείο του λογαριασμού σας για να επαληθεύσει τις συνδέσεις από τις νέες συσκευές που ξεκινούν τον Φεβρουάριο του 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Μπορείτε να ορίσετε σύνδεση δύο βημάτων ως εναλλακτικό τρόπο προστασίας του λογαριασμού σας ή να αλλάξετε το ηλ. ταχυδρομείο σας σε ένα που μπορείτε να έχετε πρόσβαση." + }, + "remindMeLater": { + "message": "Υπενθύμιση αργότερα" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Έχετε αξιόπιστη πρόσβαση στο ηλ. ταχυδρομείο σας, $EMAIL$;", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Όχι, δεν έχω" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ναι, μπορώ να συνδεθώ αξιόπιστα στο ηλ. ταχυδρομείο μου" + }, + "turnOnTwoStepLogin": { + "message": "Ενεργοποίηση σύνδεσης δύο βημάτων" + }, + "changeAcctEmail": { + "message": "Αλλαγή ηλ. ταχυδρομείου λογαριασμού" }, "extensionWidth": { - "message": "Extension width" + "message": "Πλάτος εφαρμογής" }, "wide": { - "message": "Wide" + "message": "Φαρδύ" }, "extraWide": { - "message": "Extra wide" + "message": "Εξαιρετικά φαρδύ" } } diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index f4a498f3e05..06f11406b68 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -454,9 +454,6 @@ "length": { "message": "Length" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -528,10 +525,6 @@ "minSpecial": { "message": "Minimum special" }, - "avoidAmbChar": { - "message": "Avoid ambiguous characters", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -648,9 +641,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -1014,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Clear clipboard", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1160,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organization" }, - "share": { - "message": "Share" - }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", "placeholders": { @@ -2050,9 +2040,6 @@ "clone": { "message": "Clone" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2392,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2416,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expired" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2475,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2500,10 +2457,6 @@ "expirationDate": { "message": "Expiration date" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 day" }, @@ -2519,43 +2472,10 @@ "custom": { "message": "Custom" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2638,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2665,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2887,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2930,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2955,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -4679,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4910,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 5e24a807d0e..2c13f1b9259 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Auto-fill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -447,9 +454,6 @@ "length": { "message": "Length" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum special" }, - "avoidAmbChar": { - "message": "Avoid ambiguous characters", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy auto-fill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Clear clipboard", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organisation" }, - "share": { - "message": "Share" - }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Clone" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organisation policies are affecting your generator settings." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expired" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Expiration date" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 day" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Custom" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organisation policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 18b96eb4178..b6031381d2d 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Auto-fill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -447,9 +454,6 @@ "length": { "message": "Length" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum special" }, - "avoidAmbChar": { - "message": "Avoid ambiguous characters", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy auto-fill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Clear clipboard", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organisation" }, - "share": { - "message": "Share" - }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Clone" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organisation policies are affecting your generator settings." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expired" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion Date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Expiration Date" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 day" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Custom" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Disable this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current Access Count" - }, "createSend": { "message": "Create New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organisation policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate Username" - }, "generateUsername": { "message": "Generate Username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username Type" - }, "plusAddressedEmail": { "message": "Plus Addressed Email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website Name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password Type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 14576cd12c5..d26716dd909 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -20,7 +20,7 @@ "message": "Crear cuenta" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "¿Nuevo en Bitwarden?" }, "logInWithPasskey": { "message": "Log in with passkey" @@ -29,7 +29,7 @@ "message": "Use single sign-on" }, "welcomeBack": { - "message": "Welcome back" + "message": "Bienvenido de nuevo" }, "setAStrongPassword": { "message": "Establece una contraseña fuerte" @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autocompletar identidad" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generar contraseña (copiada)" }, @@ -447,9 +454,6 @@ "length": { "message": "Longitud" }, - "passwordMinLength": { - "message": "Longitud mínima de contraseña" - }, "uppercase": { "message": "Mayúsculas (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Mínimo de caracteres especiales" }, - "avoidAmbChar": { - "message": "Evitar caracteres ambiguos", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Evitar caracteres ambiguos", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Valora la extensión" }, - "rateExtensionDesc": { - "message": "¡Por favor, considera ayudarnos con una buena reseña!" - }, "browserNotSupportClipboard": { "message": "Tu navegador web no soporta copiar al portapapeles facilmente. Cópialo manualmente." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Listar los elementos de identidad en la página para facilitar el auto-rellenado." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Vaciar portapapeles", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Mover a la Organización" }, - "share": { - "message": "Compartir" - }, "movedItemToOrg": { "message": "$ITEMNAME$ se desplazó a $ORGNAME$", "placeholders": { @@ -1478,7 +1475,7 @@ "message": "Display identities as suggestions" }, "showInlineMenuCardsLabel": { - "message": "Display cards as suggestions" + "message": "Mostrar tarjetas como sugerencias" }, "showInlineMenuOnIconSelectionLabel": { "message": "Display suggestions when icon is selected" @@ -1517,7 +1514,7 @@ "message": "Los sitios web vulnerados o no confiables pueden explotar el autorelleno al cargar la página." }, "learnMoreAboutAutofillOnPageLoadLinkText": { - "message": "Learn more about risks" + "message": "Más información sobre riesgos" }, "learnMoreAboutAutofill": { "message": "Más información sobre el relleno automático" @@ -1586,7 +1583,7 @@ "message": "Booleano" }, "cfTypeCheckbox": { - "message": "Checkbox" + "message": "Casilla de verificación" }, "cfTypeLinked": { "message": "Vinculado", @@ -2043,9 +2040,6 @@ "clone": { "message": "Clonar" }, - "passwordGeneratorPolicyInEffect": { - "message": "Una o más políticas de la organización están afectando la configuración del generador" - }, "passwordGenerator": { "message": "Generador de contraseñas" }, @@ -2385,19 +2379,11 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Buscar Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Añadir Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Texto" }, "sendTypeTextToShare": { - "message": "Text to share" + "message": "Texto a compartir" }, "sendTypeFile": { "message": "Archivo" @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Número máximo de accesos alcanzado", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Caducado" }, - "pendingDeletion": { - "message": "Borrado pendiente" - }, "passwordProtected": { "message": "Protegido por contraseña" }, @@ -2468,24 +2447,9 @@ "message": "Editar Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "¿Qué tipo de Send es este?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Un nombre amigable para describir este Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "El archivo que desea enviar." - }, "deletionDate": { "message": "Fecha de eliminación" }, - "deletionDateDesc": { - "message": "El Send se eliminará permanentemente en la fecha y hora especificadas.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Fecha de caducidad" }, - "expirationDateDesc": { - "message": "Si se establece, el acceso a este Send caducará en la fecha y hora especificadas.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 día" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Personalizado" }, - "maximumAccessCount": { - "message": "Número máximo de accesos" - }, - "maximumAccessCountDesc": { - "message": "Si se establece, los usuarios ya no podrán acceder a este Send una vez que se alcance el número máximo de accesos.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Opcionalmente se requiere una contraseña para que los usuarios accedan a este Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Notas privadas sobre este Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Desactiva este Send para que nadie pueda acceder a él.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copiar el enlace del Send en el portapapeles al guardar.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "El texto que quieres enviar." - }, - "sendHideText": { - "message": "Ocultar el texto de este Envío por defecto.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Número de acceso actual" - }, "createSend": { "message": "Crear Envío nuevo", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Antes de empezar" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Para usar un selector de fechas de estilo calendario", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "haz click aquí", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "para abrir la ventana.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "La fecha de caducidad proporcionada no es válida." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Hubo un error al guardar las fechas de eliminación y caducidad." }, - "hideEmail": { - "message": "Ocultar mi dirección de correo electrónico a los destinatarios." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "Una o más políticas de organización están afectando sus opciones del Send." - }, "passwordPrompt": { "message": "Volver a preguntar contraseña maestra" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerar nombre de usuario" - }, "generateUsername": { "message": "Generar nombre de usuario" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Tipo de nombre de usuario" - }, "plusAddressedEmail": { "message": "Dirección con sufijo", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Nombre del sitio web" }, - "whatWouldYouLikeToGenerate": { - "message": "¿Qué te gustaría generar?" - }, - "passwordType": { - "message": "Tipo de contraseña" - }, "service": { "message": "Servicio" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Rellenar credenciales para", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 28da8976ddb..069135ddb08 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Täida identiteet" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Genereeri parool (kopeeritakse)" }, @@ -447,9 +454,6 @@ "length": { "message": "Pikkus" }, - "passwordMinLength": { - "message": "Lühim lubatud parooli pikkus" - }, "uppercase": { "message": "Suurtäht (A-Z) ", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Vähim arv spetsiaalmärke" }, - "avoidAmbChar": { - "message": "Väldi ebamääraseid kirjamärke", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Väldi raskesti eristatavaid tähti ja sümboleid", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Hinda seda laiendust" }, - "rateExtensionDesc": { - "message": "Soovi korral võid meid positiivse hinnanguga toetada!" - }, "browserNotSupportClipboard": { "message": "Kasutatav brauser ei toeta lihtsat lõikelaua kopeerimist. Kopeeri see käsitsi." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Kuvab \"Kaart\" vaates identiteete, et neid saaks kiiresti sisestada" }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Lõikelaua sisu kustutamine", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Teisalda organisatsiooni" }, - "share": { - "message": "Jaga" - }, "movedItemToOrg": { "message": "$ITEMNAME$ teisaldati $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Kloon" }, - "passwordGeneratorPolicyInEffect": { - "message": "Organisatsiooni seaded mõjutavad parooli genereerija sätteid." - }, "passwordGenerator": { "message": "Parooli genereerija" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Otsi Sende", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Lisa Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Tekst" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Maksimaalne ligipääsude arv on saavutatud", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Aegunud" }, - "pendingDeletion": { - "message": "Kustutamise ootel" - }, "passwordProtected": { "message": "Parooliga kaitstud" }, @@ -2468,24 +2447,9 @@ "message": "Muuda Sendi", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Mis tüüpi Send see on?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Sisesta Sendi nimi (kohustuslik).", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Fail, mida soovid saata." - }, "deletionDate": { "message": "Kustutamise kuupäev" }, - "deletionDateDesc": { - "message": "Send kustutatakse määratud kuupäeval ja kellaajal jäädavalt.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Aegumiskuupäev" }, - "expirationDateDesc": { - "message": "Selle valimisel ei pääse sellele Sendile enam pärast määratud kuupäeva ligi.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 päev" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Kohandatud" }, - "maximumAccessCount": { - "message": "Maksimaalne ligipääsude arv" - }, - "maximumAccessCountDesc": { - "message": "Selle valimisel ei saa kasutajad pärast maksimaalse ligipääsude arvu saavutamist sellele Sendile enam ligi.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Soovi korral nõua parooli, millega Sendile ligi pääseb.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Privaatne märkus selle Sendi kohta.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Keela see Send, et keegi ei pääseks sellele ligi.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Kopeeri Sendi salvestamisel link lõikelauale.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Tekst, mida soovid saata." - }, - "sendHideText": { - "message": "Vaikeolekus peida selle Sendi tekst.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Hetkeline ligipääsude arv" - }, "createSend": { "message": "Loo uus Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Enne alustamist" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Kalendri stiilis kuupäeva valimiseks", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "kliki siia,", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "et avada Bitwarden uues aknas.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Valitud aegumiskuupäev ei ole õige." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Kustutamis- ja aegumiskuupäevade salvestamisel ilmnes tõrge." }, - "hideEmail": { - "message": "Ära näita saajatele minu e-posti aadressi." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "Organisatsiooni seaded mõjutavad sinu Sendi sätteid." - }, "passwordPrompt": { "message": "Nõutav on ülemparool" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Viga" }, - "regenerateUsername": { - "message": "Genereeri kasutajanimi uuesti" - }, "generateUsername": { "message": "Genereeri kasutajanimi" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Kasutajanime tüüp" - }, "plusAddressedEmail": { "message": "Plussiga e-posti aadress", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Veebilehe nimi" }, - "whatWouldYouLikeToGenerate": { - "message": "Mida sa soovid genereerida?" - }, - "passwordType": { - "message": "Parooli tüüp" - }, "service": { "message": "Teenus" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 6dab0b5754a..47a56583c64 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Auto-bete nortasuna" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Sortu pasahitza (kopiatuta)" }, @@ -447,9 +454,6 @@ "length": { "message": "Luzera" }, - "passwordMinLength": { - "message": "Pasahitzaren gutxieneko luzera" - }, "uppercase": { "message": "Letra larria (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Gutxieneko karaktere bereziak" }, - "avoidAmbChar": { - "message": "Saihestu karaktere anbiguoak", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Baloratu gehigarria" }, - "rateExtensionDesc": { - "message": "Mesedez, aipu on batekin lagundu!" - }, "browserNotSupportClipboard": { "message": "Zure web nabigatzaileak ez du onartzen arbelean erraz kopiatzea. Eskuz kopiatu." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Erakutsi identitateak fitxa orrian, erraz auto-betetzeko." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Hustu arbela", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Mugitu erakundera" }, - "share": { - "message": "Partekatu" - }, "movedItemToOrg": { "message": "$ITEMNAME$ $ORGNAME$-ra mugituta", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Klonatu" }, - "passwordGeneratorPolicyInEffect": { - "message": "Erakundeko politika batek edo gehiagok sortzailearen konfigurazioari eragiten diote." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Send-ak bilatu", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Gehitu Send-a", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Testua" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Sarbide kopuru maximoa gaindituta", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Iraungita" }, - "pendingDeletion": { - "message": "Ezabatzea egiteke" - }, "passwordProtected": { "message": "Pasahitz babestua" }, @@ -2468,24 +2447,9 @@ "message": "Editatu Send-a", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Zein Send mota da hau?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Send hau deskribatzeko izena.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Bidali nahi duzun fitxategia." - }, "deletionDate": { "message": "Ezabatze data" }, - "deletionDateDesc": { - "message": "Send-a betiko ezabatuko da zehaztutako datan eta orduan.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Iraungitze data" }, - "expirationDateDesc": { - "message": "Hala ezartzen bada, Send honetarako sarbidea zehaztutako egunean eta orduan amaituko da.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "Egun 1" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Pertsonalizatua" }, - "maximumAccessCount": { - "message": "Sarbide kopuru maximoa" - }, - "maximumAccessCountDesc": { - "message": "Hala ezartzen bada, erabiltzaileak ezin izango dira Send honetara sartu gehienezko sarbide kopurura iritsi ondoren.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Nahi izanez gero, pasahitza eskatu erabiltzaileak Send honetara sar daitezen.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Send honi buruzko ohar pribatuak.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Desgaitu Send hau inor sar ez dadin.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Gordetzean, kopiatu Send honen esteka arbelean.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Bidali nahi duzun testua." - }, - "sendHideText": { - "message": "Ezkutatu Send-eko testu hau, modu lehenetsian.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Uneko sarbide kopurua" - }, "createSend": { "message": "Sortu Send berria", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Hasi aurretik" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Data aukeratzeko egutegi modua erabiltzeko", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "klikatu hemen", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "leihoa irekitzeko.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Iraungitze data ez da baliozkoa." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Akatsa gertatu da ezabatze eta iraungitze datak gordetzean." }, - "hideEmail": { - "message": "Ezkutatu nire emaila hartzaileei." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "Erakundeko politika batek edo gehiagok Send-eko aukerei eragiten diote." - }, "passwordPrompt": { "message": "Berriro eskatu pasahitz nagusia" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Akatsa" }, - "regenerateUsername": { - "message": "Berrezarri erabiltzaile izena" - }, "generateUsername": { "message": "Sortu erabiltzaile izena" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Erabiltzaile izen mota" - }, "plusAddressedEmail": { "message": "Atzizkidun emaila", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Webgune izena" }, - "whatWouldYouLikeToGenerate": { - "message": "Zer sortu nahi duzu?" - }, - "passwordType": { - "message": "Pasahitz mota" - }, "service": { "message": "Zerbitzua" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 177ca744651..2b8a5e20da1 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "پر کردن خودکار هویت" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "ساخت کلمه عبور (کپی شد)" }, @@ -447,9 +454,6 @@ "length": { "message": "طول" }, - "passwordMinLength": { - "message": "حداقل طول گذرواژه" - }, "uppercase": { "message": "حروف بزرگ (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "حداقل حرف خاص" }, - "avoidAmbChar": { - "message": "از کاراکترهای مبهم اجتناب کن", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "به این افزونه امتیاز دهید" }, - "rateExtensionDesc": { - "message": "لطفاً با یک بررسی خوب به ما کمک کنید!" - }, "browserNotSupportClipboard": { "message": "مرورگر شما از کپی کلیپ بورد آسان پشتیبانی نمی‌کند. به جای آن به صورت دستی کپی کنید." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "موارد هویتی را در صفحه برگه برای پر کردن خودکار آسان فهرست کن." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "پاکسازی کلیپ بورد", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "انتقال به سازمان" }, - "share": { - "message": "اشتراک گذاری" - }, "movedItemToOrg": { "message": "$ITEMNAME$ منتقل شد به $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "شبیه سازی" }, - "passwordGeneratorPolicyInEffect": { - "message": "یک یا چند سیاست سازمان بر تنظیمات تولید کننده شما تأثیر می‌گذارد." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "جستجوی ارسال‌ها", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "افزودن ارسال", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "متن" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "به حداکثر تعداد دسترسی رسیده است", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "منقضی شده" }, - "pendingDeletion": { - "message": "در انتظار حذف" - }, "passwordProtected": { "message": "محافظت ‌شده با کلمه عبور" }, @@ -2468,24 +2447,9 @@ "message": "ویرایش ارسال", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "این چه نوع ارسالی است؟", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "یک نام دوستانه برای توصیف این ارسال.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "پرونده ای که می‌خواهید ارسال کنید." - }, "deletionDate": { "message": "تاریخ حذف" }, - "deletionDateDesc": { - "message": "ارسال در تاریخ و ساعت مشخص شده برای همیشه حذف خواهد شد.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "تاريخ انقضاء" }, - "expirationDateDesc": { - "message": "در صورت تنظیم، دسترسی به این ارسال در تاریخ و ساعت مشخص شده منقضی می‌شود.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "۱ روز" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "سفارشی" }, - "maximumAccessCount": { - "message": "تعداد دسترسی حداکثر" - }, - "maximumAccessCountDesc": { - "message": "در صورت تنظیم، با رسیدن به حداکثر تعداد دسترسی، کاربران دیگر نمی‌توانند به این ارسال دسترسی پیدا کنند.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "به صورت اختیاری برای دسترسی کاربران به این ارسال به یک کلمه عبور نیاز دارید.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "یادداشت های خصوصی در مورد این ارسال.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "این ارسال را غیرفعال کنید تا کسی نتواند به آن دسترسی پیدا کند.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "پس از ذخیره، این پیوند ارسال را به کلیپ بورد کپی کن.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "متنی که می‌خواهید ارسال کنید." - }, - "sendHideText": { - "message": "متن این ارسال را به طور پیش فرض پنهان کن.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "تعداد دسترسی فعلی" - }, "createSend": { "message": "ارسال جدید", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "قبل از اینکه شروع کنی" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "برای استفاده از انتخابگر تاریخ به سبک تقویم", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "اینجا کلیک کنید", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "برای خروج از پنجره خود.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "تاریخ انقضاء ارائه شده معتبر نیست." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "هنگام ذخیره حذف و تاریخ انقضاء شما خطایی روی داد." }, - "hideEmail": { - "message": "نشانی ایمیلم را از گیرندگان مخفی کن." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "یک یا چند سیاست سازمان بر گزینه های ارسال شما تأثیر می‌گذارد." - }, "passwordPrompt": { "message": "درخواست مجدد کلمه عبور اصلی" }, @@ -2880,9 +2789,6 @@ "error": { "message": "خطا" }, - "regenerateUsername": { - "message": "ایجاد مجدد نام کاربری" - }, "generateUsername": { "message": "ایجاد نام کاربری" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "نوع نام کاربری" - }, "plusAddressedEmail": { "message": "به علاوه نشانی ایمیل داده شده", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "نام وب‌سایت" }, - "whatWouldYouLikeToGenerate": { - "message": "چه چیزی دوست دارید تولید کنید؟" - }, - "passwordType": { - "message": "نوع کلمه عبور" - }, "service": { "message": "سرویس" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 308f2563ab8..2c621dc4621 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Automaattitäytä henkilöllisyys" }, + "fillVerificationCode": { + "message": "Täytä vahvistuskoodi" + }, + "fillVerificationCodeAria": { + "message": "Täytä vahvistuskoodi", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Luo salasana (leikepöydälle)" }, @@ -447,9 +454,6 @@ "length": { "message": "Pituus" }, - "passwordMinLength": { - "message": "Salasanan vähimmäispituus" - }, "uppercase": { "message": "Isot kirjaimet (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Erikoismerkkejä vähintään" }, - "avoidAmbChar": { - "message": "Vältä epäselviä merkkejä", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Vältä epäselviä merkkejä", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Arvioi laajennus" }, - "rateExtensionDesc": { - "message": "Harkitse tukemistamme hyvällä arvostelulla!" - }, "browserNotSupportClipboard": { "message": "Selaimesi ei tue helppoa leikepöydälle kopiointia. Kopioi kohde manuaalisesti." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Näytä henkilöllisyydet Välilehti-sivulla automaattitäytön helpottamiseksi." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Tyhjennä leikepöytä", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Siirrä organisaatiolle" }, - "share": { - "message": "Jaa" - }, "movedItemToOrg": { "message": "$ITEMNAME$ siirrettiin organisaatioon $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Kloonaa" }, - "passwordGeneratorPolicyInEffect": { - "message": "Yksi tai useampi organisaatiokäytäntö vaikuttaa generaattorisi asetuksiin." - }, "passwordGenerator": { "message": "Salasanageneraattori" }, @@ -2385,14 +2379,6 @@ "message": "Sendin tiedot", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Etsi Sendeistä", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Lisää Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Teksti" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Piilota teksti oletuksena" }, - "maxAccessCountReached": { - "message": "Käyttökertojen enimmäismäärä saavutettu", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Erääntynyt" }, - "pendingDeletion": { - "message": "Odottaa poistoa" - }, "passwordProtected": { "message": "Salasanasuojattu" }, @@ -2468,24 +2447,9 @@ "message": "Muokkaa Sendiä", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Minkä tyyppinen Send tämä on?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Kuvaava nimi Sendille.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Tiedosto, jonka haluat lähettää." - }, "deletionDate": { "message": "Poistoajankohta" }, - "deletionDateDesc": { - "message": "Send poistuu pysyvästi määritettynä ajankohtana.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Send poistuu pysyvästi tänä päivänä.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Erääntymisajankohta" }, - "expirationDateDesc": { - "message": "Jos määritetty, Send erääntyy määritettynä ajankohtana.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 päivä" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Mukautettu" }, - "maximumAccessCount": { - "message": "Käyttöoikeuksien enimmäismäärä" - }, - "maximumAccessCountDesc": { - "message": "Jos määritetty, käyttäjät eivät voi avata Sendiä käyttökertojen enimmäismäärän täytyttyä.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Halutessasi, vaadi käyttäjiä syöttämään salasana Sendin avaamiseksi.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Lisää valinnainen salasana vastaanottajille tähän Sendiin.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Yksityisiä merkintöjä tästä Sendistä.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Poista Send käytöstä, jottei kukaan voi avata sitä.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Kopioi Sendin linkki leikepöydälle tallennettaessa.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Teksti, jonka haluat lähettää." - }, - "sendHideText": { - "message": "Piilota Sendin teksti oletuksena.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Käyttökertojen nykyinen määrä" - }, "createSend": { "message": "Uusi Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Ennen kuin aloitat" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Käyttääksesi kalenterityylistä päivämäärän valintaa", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "klikkaa tästä", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "erillistä valintaa varten.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Määritetty erääntymismisajankohta on virheellinen." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Tapahtui virhe tallennettaessa poisto- ja erääntymisajankohtia." }, - "hideEmail": { - "message": "Piilota sähköpostiosoitteeni vastaanottajilta." - }, "hideYourEmail": { "message": "Piilota sähköpostiosoitteeni avaajilta." }, - "sendOptionsPolicyInEffect": { - "message": "Yksi tai useampi organisaatiokäytäntö vaikuttaa Send-asetuksiisi." - }, "passwordPrompt": { "message": "Pääsalasanan uudelleenkysely" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Virhe" }, - "regenerateUsername": { - "message": "Luo uusi käyttäjätunnus" - }, "generateUsername": { "message": "Luo käyttäjätunnus" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Käyttäjätunnuksen tyyppi" - }, "plusAddressedEmail": { "message": "Plus+merkkinen sähköposti", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Verkkosivuston nimi" }, - "whatWouldYouLikeToGenerate": { - "message": "Mitä haluat luoda?" - }, - "passwordType": { - "message": "Salasanan tyyppi" - }, "service": { "message": "Palvelu" }, @@ -3174,7 +3071,7 @@ "message": "Näytä kaikki kirjautumisvaihtoehdot" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Näytä kaikki kirjautumisvaihtoehdot" }, "notificationSentDevice": { "message": "Laitteellesi on lähetetty ilmoitus." @@ -3580,6 +3477,14 @@ "message": "Avaa tilisi lukitus. Avautuu uudessa ikkunassa.", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Aikaperusteinen kertakäyttöinen salasanan vahvistuskoodi", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Täytä kirjautumistiedot kohteesta", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Näytä automaattitäytön ehdotusten määrä laajennuksen kuvakkeessa" }, + "showQuickCopyActions": { + "message": "Näytä pikakopiointitoiminnot holvissa" + }, "systemDefault": { "message": "Järjestelmän oletus" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Tärkeä ilmoitus" + }, + "setupTwoStepLogin": { + "message": "Määritä kaksivaiheinen kirjautuminen" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Muistuta myöhemmin" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Ota kaksivaiheinen kirjautuminen käyttöön" + }, + "changeAcctEmail": { + "message": "Muuta tilin sähköpostiosoitetta" + }, "extensionWidth": { "message": "Laajennuksen leveys" }, diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index a4e98843112..a504cbd3174 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Awtomatikong punan ang pagkakakilanlan" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Maglagay ng Password" }, @@ -447,9 +454,6 @@ "length": { "message": "Kahabaan" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Inakamababang espesyal" }, - "avoidAmbChar": { - "message": "Iwasan ang mga hindi malinaw na character", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "I-rate ang extension" }, - "rateExtensionDesc": { - "message": "Paki-isipan ang pagtulong sa amin sa pamamagitan ng isang magandang review!" - }, "browserNotSupportClipboard": { "message": "Hindi suportado ng iyong web browser ang madaling pag-copy ng clipboard. Kopya ito manually sa halip." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Itala ang mga item ng pagkatao sa Tab page para sa madaling auto-fill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Linisin ang clipboard", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Lumipat sa organisasyon" }, - "share": { - "message": "I-share" - }, "movedItemToOrg": { "message": "$ITEMNAME$ lumipat sa $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Kopyahin ang item" }, - "passwordGeneratorPolicyInEffect": { - "message": "Isang o higit pang patakaran ng organisasyon ay nakakaapekto sa iyong mga setting ng generator." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Maghanap ng Mga Padala", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Idagdag ang Padala", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Teksto" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Ang pinaka-access count ay nakaabot na", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Paso na" }, - "pendingDeletion": { - "message": "Nakabinbing pagbura" - }, "passwordProtected": { "message": "Protektado ng Password" }, @@ -2468,24 +2447,9 @@ "message": "I-edit ang Ipadala", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Anong uri ng Ipadala ang ito?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Isang friendly name upang ilarawan ang Ipadala na ito.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Ang file na gusto mong ipadala." - }, "deletionDate": { "message": "Petsa ng Pagtanggal" }, - "deletionDateDesc": { - "message": "Ang Ipadala ay tatanggalin nang permanente sa tinukoy na petsa at oras.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Petsa ng pag-expire" }, - "expirationDateDesc": { - "message": "Kung nakatakda, ang access sa Send na ito ay mag-expire sa tinukoy na petsa at oras.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 araw" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Pasadyang" }, - "maximumAccessCount": { - "message": "Pinakamataas na Bilang ng Access" - }, - "maximumAccessCountDesc": { - "message": "Kung nakatakda, ang mga user ay hindi na maaaring ma-access ang Send na ito pagkatapos makarating sa maximum access count.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Maipapayo na mag-require ng password para sa mga user na ma-access ang Send na ito.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Pribadong mga tala tungkol sa Send na ito.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "I-deactivate ang Send na ito para hindi na ito ma-access.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Kopyahin ang link ng Send na ito sa clipboard pagkatapos i-save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Ang teksto na nais mong ipadala." - }, - "sendHideText": { - "message": "Itago ang default na teksto ng Send na ito.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Kasalukuyang access count" - }, "createSend": { "message": "Bagong Ipadala", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Bago ka magsimula" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Upang gamitin ang isang calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "mag-click dito", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "upang bumalik sa iyong window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Ang ibinigay na petsa ng pagpaso ay hindi wasto." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Nagkaroon ng error sa pag-save ng iyong mga petsa ng pagbura at pagpaso." }, - "hideEmail": { - "message": "Itago ang aking email address mula sa mga tatanggap." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "Isang o higit pang mga patakaran ng organisasyon ay nakaapekto sa iyong mga pagpipilian sa Pagpadala." - }, "passwordPrompt": { "message": "Muling pagsusuri sa master password" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Mali" }, - "regenerateUsername": { - "message": "Muling bumuo ng username" - }, "generateUsername": { "message": "Lumikha ng username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Uri ng username" - }, "plusAddressedEmail": { "message": "Plus na naka-address na email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Pangalan ng website" }, - "whatWouldYouLikeToGenerate": { - "message": "Ano ang nais mong bumuo?" - }, - "passwordType": { - "message": "Uri ng Password" - }, "service": { "message": "Serbisyo" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 01997f9ca52..9566f457861 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Saisie automatique de l'identité" }, + "fillVerificationCode": { + "message": "Remplir le code de vérification" + }, + "fillVerificationCodeAria": { + "message": "Remplir le code de vérification", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Générer un mot de passe (copié)" }, @@ -447,9 +454,6 @@ "length": { "message": "Longueur" }, - "passwordMinLength": { - "message": "Longueur minimale du mot de passe" - }, "uppercase": { "message": "Majuscules (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum de caractères spéciaux" }, - "avoidAmbChar": { - "message": "Éviter les caractères ambigus", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Éviter les caractères ambigus", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Noter l'extension" }, - "rateExtensionDesc": { - "message": "Merci de nous aider en mettant une bonne note !" - }, "browserNotSupportClipboard": { "message": "Votre navigateur web ne supporte pas la copie rapide depuis le presse-papier. Copiez-le manuellement à la place." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Liste les éléments d'identité sur la Page d'onglet pour faciliter la saisie automatique." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Effacer le presse-papiers", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Déplacer vers l'organisation" }, - "share": { - "message": "Partager" - }, "movedItemToOrg": { "message": "$ITEMNAME$ a été déplacé vers $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Cloner" }, - "passwordGeneratorPolicyInEffect": { - "message": "Une ou plusieurs politiques de sécurité de l'organisation affectent les paramètres de votre générateur." - }, "passwordGenerator": { "message": "Générateur de mot de passe" }, @@ -2385,14 +2379,6 @@ "message": "Détails du Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Rechercher dans les Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Ajouter un Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Texte" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Masquer le texte par défaut" }, - "maxAccessCountReached": { - "message": "Nombre maximum d'accès atteint", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expiré" }, - "pendingDeletion": { - "message": "En attente de suppression" - }, "passwordProtected": { "message": "Protégé par un mot de passe" }, @@ -2468,24 +2447,9 @@ "message": "Modifier le Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "De quel type de Send s'agit-il ?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Un nom convivial pour décrire ce Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Le fichier que vous voulez envoyer." - }, "deletionDate": { "message": "Date de suppression" }, - "deletionDateDesc": { - "message": "Le Send sera définitivement supprimé à la date et heure spécifiées.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Le Send sera définitivement supprimé à la date spécifiée.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Date d'expiration" }, - "expirationDateDesc": { - "message": "Si défini, l'accès à ce Send expirera à la date et heure spécifiées.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 jour" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Personnalisé" }, - "maximumAccessCount": { - "message": "Nombre maximum d'accès" - }, - "maximumAccessCountDesc": { - "message": "Si défini, les utilisateurs ne seront plus en mesure d'accéder à ce Send une fois que le nombre maximum d'accès sera atteint.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Vous pouvez, si vous le souhaitez, exiger un mot de passe pour accéder à ce Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Ajouter un mot de passe facultatif pour que les destinataires puissent accéder à ce Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Notes privées à propos de ce Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Désactiver ce Send pour que personne ne puisse y accéder.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copier le lien de ce Send dans le presse-papiers lors de l'enregistrement.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Le texte que vous voulez envoyer." - }, - "sendHideText": { - "message": "Masquer le texte de ce Send par défaut.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Nombre d'accès actuel" - }, "createSend": { "message": "Nouveau Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Avant de commencer" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Pour utiliser un sélecteur de date en forme de calendrier,", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "cliquez ici", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "pour ouvrir une nouvelle fenêtre.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "La date d'expiration indiquée n'est pas valide." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Une erreur s'est produite lors de l'enregistrement de vos dates de suppression et d'expiration." }, - "hideEmail": { - "message": "Masquer mon adresse électronique aux destinataires." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "Une ou plusieurs politiques de sécurité de l'organisation affectent vos options Send." - }, "passwordPrompt": { "message": "Ressaisir le mot de passe principal" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Erreur" }, - "regenerateUsername": { - "message": "Régénérer un nom d'utilisateur" - }, "generateUsername": { "message": "Générer un nom d'utilisateur" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Type de nom d'utilisateur" - }, "plusAddressedEmail": { "message": "Courriel sous-adressé", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Nom du site web" }, - "whatWouldYouLikeToGenerate": { - "message": "Que souhaitez-vous générer ?" - }, - "passwordType": { - "message": "Type de mot de passe" - }, "service": { "message": "Service" }, @@ -3174,7 +3071,7 @@ "message": "Afficher toutes les options de connexion" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Afficher toutes les options de connexion" }, "notificationSentDevice": { "message": "Une notification a été envoyée à votre appareil." @@ -3580,6 +3477,14 @@ "message": "Déverrouiller votre compte, s'ouvre dans une nouvelle fenêtre", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Code de vérification de mot de passe unique basé sur le temps", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Temps restant avant l'expiration du TOTP actuel", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Remplir les identifiants pour", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Afficher le nombre de suggestions de saisie automatique d'identifiant sur l'icône d'extension" }, + "showQuickCopyActions": { + "message": "Afficher les actions de copie rapide dans le coffre" + }, "systemDefault": { "message": "Par défaut" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Avis important" + }, + "setupTwoStepLogin": { + "message": "Configurer l'identification à deux facteurs" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden enverra un code au courriel de votre compte pour vérifier les connexions depuis de nouveaux appareils à partir de février 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Vous pouvez configurer l'identification à deux facteurs comme un moyen alternatif de protéger votre compte ou de changer votre adresse courriel à une autre à laquelle vous pouvez accéder." + }, + "remindMeLater": { + "message": "Me le rappeler plus tard" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Avez-vous un accès fiable à votre courriel $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Non, je ne l'ai pas" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Oui, je peux accéder à mon courriel de manière fiable" + }, + "turnOnTwoStepLogin": { + "message": "Activer l'identification à deux facteurs" + }, + "changeAcctEmail": { + "message": "Changer le courriel du compte" + }, "extensionWidth": { "message": "Largeur de l'extension" }, diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index aa0d9a17762..b3eaae64486 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -3,39 +3,39 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Xestor de contrasinais", + "message": "Xestor de Contrasinais Bitwarden", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Na casa, no traballo ou en ruta, Bitwarden protexe os teus contrasinais, chaves de acceso e datos sensíbeis", + "message": "Na casa, no traballo ou en movemento, Bitwarden protexe os teus contrasinais, Claves de acceso e datos sensíbeis", "description": "Extension description, MUST be less than 112 characters (Safari restriction)" }, "loginOrCreateNewAccount": { - "message": "Rexístrate ou crea unha nova conta para acceder á túa caixa forte." + "message": "Inicia sesión ou rexístrate para acceder á túa caixa forte." }, "inviteAccepted": { - "message": "Invitation accepted" + "message": "Invitación aceptada" }, "createAccount": { "message": "Crea unha conta" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Novo en Bitwarden?" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Iniciar sesión con Clave de acceso" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Usar inicio de sesión único" }, "welcomeBack": { - "message": "Welcome back" + "message": "Benvido de novo" }, "setAStrongPassword": { - "message": "Set a strong password" + "message": "Crea un contrasinal forte" }, "finishCreatingYourAccountBySettingAPassword": { - "message": "Finish creating your account by setting a password" + "message": "Remata de crear a túa conta creando un contrasinal" }, "enterpriseSingleSignOn": { "message": "Inicio de sesión único empresarial" @@ -62,7 +62,7 @@ "message": "Unha pista do contrasinal mestre pode axudarte a lembrar o teu contrasinal se o esqueces." }, "masterPassHintText": { - "message": "If you forget your password, the password hint can be sent to your email. $CURRENT$/$MAXIMUM$ character maximum.", + "message": "Se esqueces o teu contrasinal, pódese enviar a pista do contrasinal ó teu correo. Máximo de $CURRENT$/$MAXIMUM$ caracteres.", "placeholders": { "current": { "content": "$1", @@ -81,10 +81,10 @@ "message": "Pista do contrasinal mestre (opcional)" }, "joinOrganization": { - "message": "Join organization" + "message": "Unirse a esta organización" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "Unirse a $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -93,10 +93,10 @@ } }, "finishJoiningThisOrganizationBySettingAMasterPassword": { - "message": "Finish joining this organization by setting a master password." + "message": "Remata de unirte á organización creando un contrasinal mestre." }, "tab": { - "message": "Separador" + "message": "Pestana" }, "vault": { "message": "Caixa forte" @@ -114,13 +114,13 @@ "message": "Axustes" }, "currentTab": { - "message": "Separador actual" + "message": "Pestana actual" }, "copyPassword": { "message": "Copiar contrasinal" }, "copyPassphrase": { - "message": "Copy passphrase" + "message": "Copiar frase de contrasinal" }, "copyNote": { "message": "Copiar nota" @@ -138,31 +138,31 @@ "message": "Copiar código de seguranza" }, "copyName": { - "message": "Copy name" + "message": "Copiar nome" }, "copyCompany": { - "message": "Copy company" + "message": "Copiar empresa" }, "copySSN": { - "message": "Copy Social Security number" + "message": "Copiar número de Seguridade Social" }, "copyPassportNumber": { - "message": "Copy passport number" + "message": "Copiar número de pasaporte" }, "copyLicenseNumber": { - "message": "Copy license number" + "message": "Copiar número de matrícula" }, "copyPrivateKey": { - "message": "Copy private key" + "message": "Copiar clave privada" }, "copyPublicKey": { - "message": "Copy public key" + "message": "Copiar clave pública" }, "copyFingerprint": { - "message": "Copy fingerprint" + "message": "Copiar pegada dixital" }, "copyCustomField": { - "message": "Copy $FIELD$", + "message": "Copiar $FIELD$", "placeholders": { "field": { "content": "$1", @@ -171,26 +171,33 @@ } }, "copyWebsite": { - "message": "Copy website" + "message": "Copiar sitio web" }, "copyNotes": { - "message": "Copy notes" + "message": "Copiar notas" }, "fill": { - "message": "Fill", + "message": "Encher", "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { - "message": "Auto-encher" + "message": "Autoencher" }, "autoFillLogin": { - "message": "Encher automaticamente inicio de sesión" + "message": "Autoencher credenciais" }, "autoFillCard": { - "message": "Encher automaticamente tarxeta" + "message": "Autoencher tarxeta" }, "autoFillIdentity": { - "message": "Encher automaticamente identidade" + "message": "Autoencher automaticamente identidade" + }, + "fillVerificationCode": { + "message": "Encher código de verificación" + }, + "fillVerificationCodeAria": { + "message": "Encher código de verificación", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" }, "generatePasswordCopied": { "message": "Xerar contrasinal (copiado)" @@ -199,7 +206,7 @@ "message": "Copiar nome de campo personalizado" }, "noMatchingLogins": { - "message": "Sen inicios de sesión coincidentes" + "message": "Sen credenciais coincidentes" }, "noCards": { "message": "Sen tarxetas" @@ -208,7 +215,7 @@ "message": "Sen identidades" }, "addLoginMenu": { - "message": "Engadir inicio de sesión" + "message": "Engadir credenciais" }, "addCardMenu": { "message": "Engadir tarxeta" @@ -217,31 +224,31 @@ "message": "Engadir identidade" }, "unlockVaultMenu": { - "message": "Desbloquear a súa caixa forte" + "message": "Desbloquear a caixa forte" }, "loginToVaultMenu": { - "message": "Rexistrarse na súa caixa forte" + "message": "Iniciar sesión na caixa forte" }, "autoFillInfo": { - "message": "Non hai inicios de sesión dispoñíbeis para encher automaticamente para o separador actual do navegador." + "message": "Non hai credenciais dispoñibles para autoenchido na pestana actual." }, "addLogin": { - "message": "Engadir inicio de sesión" + "message": "Engadir unha credencial" }, "addItem": { - "message": "Engadir elemento" + "message": "Engadir entrada" }, "accountEmail": { - "message": "Account email" + "message": "Correo electrónico da conta" }, "requestHint": { - "message": "Request hint" + "message": "Solicitar pista" }, "requestPasswordHint": { - "message": "Request password hint" + "message": "Solicitar pista do contrasinal" }, "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { - "message": "Enter your account email address and your password hint will be sent to you" + "message": "Introduce o teu correo electrónico e enviarémosche a túa pista do contrasinal mestre" }, "passwordHint": { "message": "Pista do contrasinal" @@ -277,29 +284,29 @@ "message": "Continuar á aplicación web?" }, "continueToWebAppDesc": { - "message": "Explore more features of your Bitwarden account on the web app." + "message": "Descobre máis funcionalidades da túa conta de Bitwarden na aplicación web." }, "continueToHelpCenter": { - "message": "Continue to Help Center?" + "message": "Ir ó Centro de Axuda?" }, "continueToHelpCenterDesc": { - "message": "Learn more about how to use Bitwarden on the Help Center." + "message": "Aprende máis acerca de como usar Bitwarden no Centro de Axuda." }, "continueToBrowserExtensionStore": { - "message": "Continue to browser extension store?" + "message": "Ir á tenda de extensións do navegador?" }, "continueToBrowserExtensionStoreDesc": { - "message": "Help others find out if Bitwarden is right for them. Visit your browser's extension store and leave a rating now." + "message": "Axuda a outros a saber que Bitwarden é o que buscan! Visita a tenda de extensións do navegador e déixanos a túa opinión." }, "changeMasterPasswordOnWebConfirmation": { "message": "Podes cambiar o teu contrasinal mestre na aplicación web de Bitwarden." }, "fingerprintPhrase": { - "message": "Frase de pegada dactilar", + "message": "Frase de pegada dixital", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." }, "yourAccountsFingerprint": { - "message": "Frase de pegada dactilar da túa conta", + "message": "Frase de pegada dixital da túa conta", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." }, "twoStepLogin": { @@ -309,43 +316,43 @@ "message": "Pechar sesión" }, "aboutBitwarden": { - "message": "About Bitwarden" + "message": "Acerca de Bitwarden" }, "about": { "message": "Acerca de" }, "moreFromBitwarden": { - "message": "More from Bitwarden" + "message": "Máis de Bitwarden" }, "continueToBitwardenDotCom": { - "message": "Continue to bitwarden.com?" + "message": "Ir a bitwarden.com?" }, "bitwardenForBusiness": { - "message": "Bitwarden for Business" + "message": "Bitwarden para Empresas" }, "bitwardenAuthenticator": { - "message": "Bitwarden Authenticator" + "message": "Autenticador Bitwarden" }, "continueToAuthenticatorPageDesc": { - "message": "Bitwarden Authenticator allows you to store authenticator keys and generate TOTP codes for 2-step verification flows. Learn more on the bitwarden.com website" + "message": "O Autenticador Bitwarden permite almacenar claves de autenticación e xerar códigos de un uso para fluxos de verificación en 2 pasos. Aprende máis en bitwarden.com" }, "bitwardenSecretsManager": { - "message": "Bitwarden Secrets Manager" + "message": "Xestor de Segredos de Bitwarden" }, "continueToSecretsManagerPageDesc": { - "message": "Securely store, manage, and share developer secrets with Bitwarden Secrets Manager. Learn more on the bitwarden.com website." + "message": "Almacenar, xestionar e compartir de maneira segura segredos de desarrollo co Xestor de Segredos de Bitwarden. Aprende máis en bitwarden.com." }, "passwordlessDotDev": { "message": "Passwordless.dev" }, "continueToPasswordlessDotDevPageDesc": { - "message": "Create smooth and secure login experiences free from traditional passwords with Passwordless.dev. Learn more on the bitwarden.com website." + "message": "Crea experiencias de inicio de sesión cómodas e seguras libres de contrasinais tradicionais con Passwordless.dev. Descobre como en bitwarden.com." }, "freeBitwardenFamilies": { - "message": "Free Bitwarden Families" + "message": "Plan Familiar Gratuíto Bitwarden" }, "freeBitwardenFamiliesPageDesc": { - "message": "You are eligible for Free Bitwarden Families. Redeem this offer today in the web app." + "message": "Es candidato para Bitwarden Familiar Gratuíto. Troca esta oferta hoxe mesmo na aplicación web." }, "version": { "message": "Versión" @@ -366,22 +373,22 @@ "message": "Editar cartafol" }, "newFolder": { - "message": "New folder" + "message": "Novo cartafol" }, "folderName": { - "message": "Folder name" + "message": "Nome do cartafol" }, "folderHintText": { - "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums" + "message": "Aniñar un cartafol engadindo o nome do cartafol pai seguido dun \"/\". Exemplo: Social/Foros" }, "noFoldersAdded": { - "message": "No folders added" + "message": "Sen cartafois" }, "createFoldersToOrganize": { - "message": "Create folders to organize your vault items" + "message": "Crea cartafoles para organizar as entradas da túa caixa forte" }, "deleteFolderPermanently": { - "message": "Are you sure you want to permanently delete this folder?" + "message": "Estás seguro de querer eliminar permanentemente este cartafol?" }, "deleteFolder": { "message": "Eliminar cartafol" @@ -421,13 +428,13 @@ "description": "Short for 'credential generator'." }, "passGenInfo": { - "message": "Xera automaticamente contrasinais fortes e únicos para os seus inicios de sesión." + "message": "Xera automaticamente contrasinais fortes e únicos para os teus inicios de sesión." }, "bitWebVaultApp": { - "message": "Bitwarden web app" + "message": "Aplicación web de Bitwarden" }, "importItems": { - "message": "Importar elementos" + "message": "Importar entradas" }, "select": { "message": "Seleccionar" @@ -436,7 +443,7 @@ "message": "Xerar contrasinal" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "Xerar frase de contrasinal" }, "regeneratePassword": { "message": "Volver xerar contrasinal" @@ -447,9 +454,6 @@ "length": { "message": "Lonxitude" }, - "passwordMinLength": { - "message": "Lonxitude mínima do contrasinal" - }, "uppercase": { "message": "Maiúsculas (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -467,11 +471,11 @@ "description": "deprecated. Use specialCharactersLabel instead." }, "include": { - "message": "Include", + "message": "Incluír", "description": "Card header for password generator include block" }, "uppercaseDescription": { - "message": "Include uppercase characters", + "message": "Incluír caracteres en maiúsculas", "description": "Tooltip for the password generator uppercase character checkbox" }, "uppercaseLabel": { @@ -479,7 +483,7 @@ "description": "Label for the password generator uppercase character checkbox" }, "lowercaseDescription": { - "message": "Include lowercase characters", + "message": "Incluír caracteres en minúsculas", "description": "Full description for the password generator lowercase character checkbox" }, "lowercaseLabel": { @@ -487,7 +491,7 @@ "description": "Label for the password generator lowercase character checkbox" }, "numbersDescription": { - "message": "Include numbers", + "message": "Incluír números", "description": "Full description for the password generator numbers checkbox" }, "numbersLabel": { @@ -495,7 +499,7 @@ "description": "Label for the password generator numbers checkbox" }, "specialCharactersDescription": { - "message": "Include special characters", + "message": "Incluír caracteres especiais", "description": "Full description for the password generator special characters checkbox" }, "specialCharactersLabel": { @@ -509,28 +513,24 @@ "message": "Separador de palabras" }, "capitalize": { - "message": "Facer a primeira letra da palabra maiúscula", + "message": "Primeira letra maiúscula", "description": "Make the first letter of a work uppercase." }, "includeNumber": { "message": "Incluír número" }, "minNumbers": { - "message": "Mínimo de números" + "message": "Mínimo de cifras" }, "minSpecial": { "message": "Mínimo de caracteres especiais" }, - "avoidAmbChar": { - "message": "Evitar caracteres ambiguos", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { - "message": "Avoid ambiguous characters", + "message": "Evitar caracteres ambiguos", "description": "Label for the avoid ambiguous characters checkbox." }, "generatorPolicyInEffect": { - "message": "Enterprise policy requirements have been applied to your generator options.", + "message": "Os requisitos da política empresarial foron aplicados ás opcións do teu xerador.", "description": "Indicates that a policy limits the credential generator screen." }, "searchVault": { @@ -543,10 +543,10 @@ "message": "Ver" }, "noItemsInList": { - "message": "Non hai elementos que listar." + "message": "Non hai entradas que listar." }, "itemInformation": { - "message": "Información do elemento" + "message": "Información da entrada" }, "username": { "message": "Nome de usuario" @@ -555,7 +555,7 @@ "message": "Contrasinal" }, "totp": { - "message": "Secreto de autenticador" + "message": "Segredo de autenticador" }, "passphrase": { "message": "Frase de contrasinal" @@ -564,19 +564,19 @@ "message": "Favorito" }, "unfavorite": { - "message": "Unfavorite" + "message": "Suprimir dos favoritos" }, "itemAddedToFavorites": { - "message": "Item added to favorites" + "message": "Engadido a favoritos" }, "itemRemovedFromFavorites": { - "message": "Item removed from favorites" + "message": "Eliminado dos favoritos" }, "notes": { "message": "Notas" }, "privateNote": { - "message": "Private note" + "message": "Nota privada" }, "note": { "message": "Nota" @@ -597,10 +597,10 @@ "message": "Iniciar" }, "launchWebsite": { - "message": "Launch website" + "message": "Abrir web" }, "launchWebsiteName": { - "message": "Launch website $ITEMNAME$", + "message": "Abrir web $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -624,16 +624,16 @@ "message": "Opcións de desbloqueo" }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Configura un método de desbloqueo para cambiar a túa acción de peche de sesión da caixa forte." + "message": "Configura un método de desbloqueo para cambiar a acción do temporizador da caixa forte." }, "unlockMethodNeeded": { "message": "Configura un método de desbloqueo nos axustes" }, "sessionTimeoutHeader": { - "message": "Tempo de sesión esgotado" + "message": "Temporizador da sesión esgotado" }, "vaultTimeoutHeader": { - "message": "Vault timeout" + "message": "Temporizador da caixa forte" }, "otherOptions": { "message": "Outras opcións" @@ -641,11 +641,8 @@ "rateExtension": { "message": "Valorar a extensión" }, - "rateExtensionDesc": { - "message": "Por favor, considera axudarnos cunha boa recensión!" - }, "browserNotSupportClipboard": { - "message": "O teu navegador web non soporta copia doada ao portapapeis. Cópiao manualmente no seu lugar." + "message": "O navegador non permite copia doada ó portapapeis. Debes copialo manualmente." }, "verifyIdentity": { "message": "Verificar identidade" @@ -654,19 +651,19 @@ "message": "A túa caixa forte está bloqueada. Verifica a túa identidade para continuar." }, "yourVaultIsLockedV2": { - "message": "Your vault is locked" + "message": "A caixa forte está pechada" }, "yourAccountIsLocked": { - "message": "Your account is locked" + "message": "A conta está pechada" }, "or": { - "message": "or" + "message": "ou" }, "unlock": { "message": "Desbloquear" }, "loggedInAsOn": { - "message": "Conectado coma $EMAIL$ en $HOSTNAME$.", + "message": "Conectado como $EMAIL$ en $HOSTNAME$.", "placeholders": { "email": { "content": "$1", @@ -685,13 +682,13 @@ "message": "Tempo de espera da caixa forte" }, "vaultTimeout1": { - "message": "Timeout" + "message": "Tempo esgotado" }, "lockNow": { - "message": "Bloquear agora" + "message": "Pechar agora" }, "lockAll": { - "message": "Bloquear todo" + "message": "Pechar todo" }, "immediately": { "message": "Inmediatamente" @@ -727,10 +724,10 @@ "message": "4 horas" }, "onLocked": { - "message": "Ao bloquear o sistema" + "message": "Ó bloquear o sistema" }, "onRestart": { - "message": "Ao reiniciar o navegador" + "message": "Ó reiniciar o navegador" }, "never": { "message": "Nunca" @@ -739,16 +736,16 @@ "message": "Seguridade" }, "confirmMasterPassword": { - "message": "Confirm master password" + "message": "Repetir o contrasinal mestre" }, "masterPassword": { - "message": "Master password" + "message": "Contrasinal mestre" }, "masterPassImportant": { - "message": "Your master password cannot be recovered if you forget it!" + "message": "O contrasinal mestre non pode ser recuperado se o esqueces!" }, "masterPassHintLabel": { - "message": "Master password hint" + "message": "Obter pista do contrasinal mestre" }, "errorOccurred": { "message": "Produciuse un erro" @@ -766,7 +763,7 @@ "message": "É preciso volver escribir o contrasinal mestre." }, "masterPasswordMinlength": { - "message": "O contrasinal mestre debe ter polo menos $VALUE$ caracteres de lonxitude.", + "message": "O contrasinal mestre debe ter polo menos $VALUE$ caracteres.", "description": "The Master Password must be at least a specific number of characters long.", "placeholders": { "value": { @@ -776,19 +773,19 @@ } }, "masterPassDoesntMatch": { - "message": "A confirmación de contrasinal mestre non coincide." + "message": "O contrasinal mestre repetido non coincide." }, "newAccountCreated": { "message": "A túa nova conta foi creada! Podes iniciar sesión agora." }, "newAccountCreated2": { - "message": "Your new account has been created!" + "message": "A túa nova conta foi creada!" }, "youHaveBeenLoggedIn": { - "message": "You have been logged in!" + "message": "A túa sesión foi iniciada!" }, "youSuccessfullyLoggedIn": { - "message": "Iniciaches sesión correctamente" + "message": "Sesión iniciada con éxito" }, "youMayCloseThisWindow": { "message": "Podes pechar está ventá" @@ -797,10 +794,10 @@ "message": "Enviámoste un correo electrónico coa túa pista de contrasinal mestre." }, "verificationCodeRequired": { - "message": "É preciso código de verificación." + "message": "É preciso un código de verificación." }, "webauthnCancelOrTimeout": { - "message": "The authentication was cancelled or took too long. Please try again." + "message": "O proceso de autenticación foi cancelado ou levou moito tempo. Por favor, volve intentalo." }, "invalidVerificationCode": { "message": "Código de verificación non válido" @@ -816,28 +813,28 @@ } }, "autofillError": { - "message": "Incapaz de encher automaticamente o elemento seleccionado nesta páxina. Copia e pega a información no seu lugar." + "message": "Non se puido autoencher o formulario nesta páxina. Copia e pega a información manualmente." }, "totpCaptureError": { - "message": "Incapaz escanear o código QR da páxina web actual" + "message": "Non se puido escanear o código QR da páxina web" }, "totpCaptureSuccess": { "message": "Clave de autenticación engadida" }, "totpCapture": { - "message": "Escanea o código QR autenticador da páxina web actual" + "message": "Escanea o código QR do autenticador da páxina web" }, "totpHelperTitle": { - "message": "Make 2-step verification seamless" + "message": "Fai a verificación en 2 pasos imperceptible" }, "totpHelper": { - "message": "Bitwarden can store and fill 2-step verification codes. Copy and paste the key into this field." + "message": "Bitwarden pode gardar e encher códigos de verificación en 2 pasos. Copia e pega a clave neste campo." }, "totpHelperWithCapture": { - "message": "Bitwarden can store and fill 2-step verification codes. Select the camera icon to take a screenshot of this website's authenticator QR code, or copy and paste the key into this field." + "message": "Bitwarden pode gardar e encher códigos de verificación en 2 pasos. Preme a icona da cámara para extraer o código QR do autenticador desta web, ou copia e pega a clave neste campo." }, "learnMoreAboutAuthenticators": { - "message": "Learn more about authenticators" + "message": "Aprende máis sobre autenticadores." }, "copyTOTP": { "message": "Copiar clave de autenticación (TOTP)" @@ -846,31 +843,31 @@ "message": "Sesión pechada" }, "loggedOutDesc": { - "message": "You have been logged out of your account." + "message": "A túa sesión foi pechada." }, "loginExpired": { "message": "A túa sesión caducou." }, "logIn": { - "message": "Log in" + "message": "Iniciar sesión" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Iniciar sesión en Bitwarden" }, "restartRegistration": { - "message": "Restart registration" + "message": "Reiniciar rexistro" }, "expiredLink": { - "message": "Expired link" + "message": "Ligazón caducada" }, "pleaseRestartRegistrationOrTryLoggingIn": { - "message": "Please restart registration or try logging in." + "message": "Por favor, reinicia o rexistro ou tenta iniciar sesión." }, "youMayAlreadyHaveAnAccount": { - "message": "You may already have an account" + "message": "Pode que xa teñas unha conta" }, "logOutConfirmation": { - "message": "Are you sure you want to log out?" + "message": "Estás seguro de que queres pechar a sesión?" }, "yes": { "message": "Si" @@ -882,19 +879,19 @@ "message": "Produciuse un erro inesperado." }, "nameRequired": { - "message": "Requírese o nome." + "message": "Nome requirido." }, "addedFolder": { "message": "Cartafol engadido" }, "twoStepLoginConfirmation": { - "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" + "message": "A verificación en 2 pasos (2FA) fai a túa conta máis segura requirindo confirmar o inicio de sesión con métodos como unha clave de seguridade, aplicación de autenticación, SMS, chamada telefónica ou correo electrónico. A 2FA pode configurarse na aplicación web. Queres ir agora?" }, "twoStepLoginConfirmationContent": { - "message": "Make your account more secure by setting up two-step login in the Bitwarden web app." + "message": "Fai a túa conta máis segura activando a verificación en 2 pasos na aplicación web." }, "twoStepLoginConfirmationTitle": { - "message": "Continue to web app?" + "message": "Ir á aplicación web?" }, "editedFolder": { "message": "Cartafol gardado" @@ -906,10 +903,10 @@ "message": "Cartafol eliminado" }, "gettingStartedTutorial": { - "message": "Tutorial introdutivo" + "message": "Titorial de Primeiros pasos" }, "gettingStartedTutorialVideo": { - "message": "Watch our getting started tutorial to learn how to get the most out of the browser extension." + "message": "Mira o noso titorial de Primeiros pasos para aprender a sacar partido á extensión de navegador." }, "syncingComplete": { "message": "Sincronización completa" @@ -937,32 +934,32 @@ "message": "Nova URI" }, "addDomain": { - "message": "Add domain", + "message": "Engadir dominio", "description": "'Domain' here refers to an internet domain name (e.g. 'bitwarden.com') and the message in whole described the act of putting a domain value into the context." }, "addedItem": { - "message": "Elemento engadido" + "message": "Entrada engadida" }, "editedItem": { - "message": "Elemento gardado" + "message": "Entrada gardada" }, "deleteItemConfirmation": { - "message": "Realmente queres enviar ao lixo?" + "message": "Seguro que queres envialo ó lixo?" }, "deletedItem": { - "message": "Elemento enviado ao lixo" + "message": "Entrada enviada ó lixo" }, "overwritePassword": { "message": "Sobrescribir contrasinal" }, "overwritePasswordConfirmation": { - "message": "Estás seguro de que queres sobrescribir o contrasinal actual?" + "message": "Seguro que queres sobrescribir o contrasinal actual?" }, "overwriteUsername": { "message": "Sobrescribir nome de usuario" }, "overwriteUsernameConfirmation": { - "message": "Estás seguro de que queres sobrescribir o nome de usuario actual?" + "message": "Seguro que queres sobrescribir o nome de usuario actual?" }, "searchFolder": { "message": "Buscar cartafol" @@ -978,103 +975,106 @@ "description": "This is the folder for uncategorized items" }, "enableAddLoginNotification": { - "message": "Solicita engadir inicio de sesión" + "message": "Ofrecer gardar credenciais" }, "vaultSaveOptionsTitle": { - "message": "Save to vault options" + "message": "Gardar nas opcións da caixa forte" }, "addLoginNotificationDesc": { - "message": "Ask to add an item if one isn't found in your vault." + "message": "Ofrecer gardar un elemento se non se atopa na caixa forte." }, "addLoginNotificationDescAlt": { - "message": "Ask to add an item if one isn't found in your vault. Applies to all logged in accounts." + "message": "Ofrecer gardar un elemento se non se atopa na caixa forte. Aplica a tódalas sesións iniciadas." }, "showCardsInVaultView": { - "message": "Show cards as Autofill suggestions on Vault view" + "message": "Na caixa forte, amosar tarxetas como suxestións de Autoenchido" }, "showCardsCurrentTab": { - "message": "Amosar tarxetas no separador" + "message": "Amosar tarxetas na pestana" }, "showCardsCurrentTabDesc": { - "message": "Lista os elementos de tarxeta no separador para fácil auto-completado." + "message": "Lista na pestana actual as tarxetas gardadas para autoenchido." }, "showIdentitiesInVaultView": { - "message": "Show identities as Autofill suggestions on Vault view" + "message": "Na caixa forte, amosar identidades como suxestións de Autoenchido" }, "showIdentitiesCurrentTab": { - "message": "Mostrar identidades no separador" + "message": "Amosar identidades na pestana" }, "showIdentitiesCurrentTabDesc": { - "message": "Lista os elementos de identidade no separador para fácil auto-completado." + "message": "Lista na pestana actual as identidades gardadas para autoenchido." + }, + "clickToAutofillOnVault": { + "message": "Clicar nas entradas da caixa forte para autoencher" }, "clearClipboard": { - "message": "Limpar portapapeis", + "message": "Baleirar portapapeis", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "clearClipboardDesc": { - "message": "Automatically clear copied values from your clipboard.", + "message": "Limpar automaticamente os valores copiados do teu portapapeis.", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "notificationAddDesc": { - "message": "Should Bitwarden remember this password for you?" + "message": "Debería Bitwarden lembrar este contrasinal?" }, "notificationAddSave": { "message": "Gardar" }, "enableChangedPasswordNotification": { - "message": "Ask to update existing login" + "message": "Ofrecer actualizar as credenciais xa gardadas" }, "changedPasswordNotificationDesc": { - "message": "Ask to update a login's password when a change is detected on a website." + "message": "Ofrecer actualizar o contrasinal dunha credencial ó detectar un cambio." }, "changedPasswordNotificationDescAlt": { - "message": "Ask to update a login's password when a change is detected on a website. Applies to all logged in accounts." + "message": "Ofrecer actualizar o contrasinal dunha credencial ó detectar un cambio. Aplica a tódalas sesións iniciadas." }, "enableUsePasskeys": { - "message": "Ask to save and use passkeys" + "message": "Ofrecer gardar e empregar Claves de acceso" }, "usePasskeysDesc": { - "message": "Ask to save new passkeys or log in with passkeys stored in your vault. Applies to all logged in accounts." + "message": "Ofrecer gardar e empregar Claves de acceso na caixa forte. Aplica a tódalas sesións iniciadas." }, "notificationChangeDesc": { - "message": "Do you want to update this password in Bitwarden?" + "message": "Queres actualizar este contrasinal en Bitwarden?" }, "notificationChangeSave": { "message": "Actualizar" }, "notificationUnlockDesc": { - "message": "Unlock your Bitwarden vault to complete the autofill request." + "message": "Abre a caixa forte de Bitwarden para poder continuar co autoenchido." }, "notificationUnlock": { - "message": "Desbloquear" + "message": "Abrir" }, "additionalOptions": { - "message": "Additional options" + "message": "Opcións adicionais" }, "enableContextMenuItem": { - "message": "Show context menu options" + "message": "Amosar opcións no menú contextual" }, "contextMenuItemDesc": { - "message": "Use a secondary click to access password generation and matching logins for the website." + "message": "Usar o clic dereito para acceder á xeración de contrasinais e autoenchido para a web." }, "contextMenuItemDescAlt": { - "message": "Use a secondary click to access password generation and matching logins for the website. Applies to all logged in accounts." + "message": "Usar o clic dereito para acceder á xeración de contrasinais e autoenchido para a web. Aplica a tódalas sesións iniciadas." }, "defaultUriMatchDetection": { - "message": "Default URI match detection", + "message": "Detección de coincidencias de URI por defecto", "description": "Default URI match detection for autofill." }, "defaultUriMatchDetectionDesc": { - "message": "Choose the default way that URI match detection is handled for logins when performing actions such as autofill." + "message": "Selecciona o tipo de Detección de coincidencias de URI que se empregará por defecto para accións como o autoenchido." }, "theme": { "message": "Tema" }, "themeDesc": { - "message": "Change the application's color theme." + "message": "Cambiar o tema de cor da aplicación." }, "themeDescAlt": { - "message": "Change the application's color theme. Applies to all logged in accounts." + "message": "Cambiar o tema de cor da aplicación. Aplica a tódalas sesións iniciadas." }, "dark": { "message": "Escuro", @@ -1089,7 +1089,7 @@ "description": "'Solarized' is a noun and the name of a color scheme. It should not be translated." }, "exportFrom": { - "message": "Export from" + "message": "Exportar dende" }, "exportVault": { "message": "Exportar caixa forte" @@ -1098,28 +1098,28 @@ "message": "Formato de ficheiro" }, "fileEncryptedExportWarningDesc": { - "message": "This file export will be password protected and require the file password to decrypt." + "message": "O arquivo exportado estará protexido e requirirá o contrasinal do arquivo para descifralo." }, "filePassword": { - "message": "File password" + "message": "Contrasinal do arquivo" }, "exportPasswordDescription": { - "message": "This password will be used to export and import this file" + "message": "Este contrasinal empregarase para exportar e importar o arquivo" }, "accountRestrictedOptionDescription": { - "message": "Use your account encryption key, derived from your account's username and Master Password, to encrypt the export and restrict import to only the current Bitwarden account." + "message": "Empregar a clave de cifrado da túa conta, derivada do teu nome de usuario e contrasinal, para cifrar o arquivo exportado e limitar o importado á conta de usuario actual." }, "passwordProtectedOptionDescription": { - "message": "Set a file password to encrypt the export and import it to any Bitwarden account using the password for decryption." + "message": "Crea un contrasinal para cifrar o arquivo exportado e importalo en calquera conta de Bitwarden empregando este contrasinal." }, "exportTypeHeading": { - "message": "Export type" + "message": "Tipo de exportado" }, "accountRestricted": { - "message": "Account restricted" + "message": "Conta restrinxida" }, "filePasswordAndConfirmFilePasswordDoNotMatch": { - "message": "“File password” and “Confirm file password“ do not match." + "message": "\"Contrasinal do arquivo\" e \"Repetir contrasinal do arquivo\" non coinciden." }, "warning": { "message": "ADVERTENCIA", @@ -1130,34 +1130,31 @@ "description": "Warning (should maintain locale-relevant capitalization)" }, "confirmVaultExport": { - "message": "Confirm vault export" + "message": "Confirmar o exportado da caixa forte" }, "exportWarningDesc": { - "message": "This export contains your vault data in an unencrypted format. You should not store or send the exported file over unsecure channels (such as email). Delete it immediately after you are done using it." + "message": "Esta exportación contén os teus datos nun formato sen cifrar. Non deberías gardar ou enviar o arquivo por canais inseguros (como correo electrónico). Elimínao inmediatamente despois de finalizar o seu uso." }, "encExportKeyWarningDesc": { - "message": "This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file." + "message": "Este tipo de exportado cifra os teus datos empregando a clave de cifrado da túa conta. Se cambias o teu contrasinal ou nome de usuario deberás volver xerar o arquivo." }, "encExportAccountWarningDesc": { - "message": "Account encryption keys are unique to each Bitwarden user account, so you can't import an encrypted export into a different account." + "message": "As claves de cifrado son únicas para cada usuario, polo que non poderás importar un arquivo exportado desta maneira nunha conta distinta." }, "exportMasterPassword": { - "message": "Enter your master password to export your vault data." + "message": "Introduce o teu contrasinal mestre para exportar a caixa forte." }, "shared": { "message": "Compartido" }, "bitwardenForBusinessPageDesc": { - "message": "Bitwarden for Business allows you to share your vault items with others by using an organization. Learn more on the bitwarden.com website." + "message": "Bitwarden para Empresas permite compartir entradas da caixa forte empregando unha organización. Aprende máis en bitwarden.com." }, "moveToOrganization": { - "message": "Move to organization" - }, - "share": { - "message": "Compartir" + "message": "Mover á organización" }, "movedItemToOrg": { - "message": "$ITEMNAME$ moved to $ORGNAME$", + "message": "$ITEMNAME$ moveuse a $ORGNAME$", "placeholders": { "itemname": { "content": "$1", @@ -1170,7 +1167,7 @@ } }, "moveToOrgDesc": { - "message": "Choose an organization that you wish to move this item to. Moving to an organization transfers ownership of the item to that organization. You will no longer be the direct owner of this item once it has been moved." + "message": "Escolle a organización á que queres transferir esta entrada. Ó transferila, tamén transfires a súa propiedade á organización. Unha vez transferida, xa non serás o propietario directo desde elemento." }, "learnMore": { "message": "Máis información" @@ -1206,82 +1203,82 @@ "message": "Anexo gardado" }, "file": { - "message": "Ficheiro" + "message": "Arquivo" }, "fileToShare": { - "message": "File to share" + "message": "Arquivo a compartir" }, "selectFile": { - "message": "Selecciona un ficheiro" + "message": "Selecciona un arquivo" }, "maxFileSize": { - "message": "O tamaño máximo de ficheiro é de 500 MB." + "message": "O tamaño máximo é de 500 MB." }, "featureUnavailable": { - "message": "Característica non dispoñible" + "message": "Función non dispoñible" }, "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "message": "Requírese mudar a clave de cifrado. Por favor, inicia sesión na aplicación web para actualizala." }, "premiumMembership": { - "message": "Membresía de luxo" + "message": "Plan Prémium" }, "premiumManage": { - "message": "Xestionar membresía" + "message": "Xestionar plan" }, "premiumManageAlert": { - "message": "You can manage your membership on the bitwarden.com web vault. Do you want to visit the website now?" + "message": "Podes xestionar o teu plan na caixa forte web de bitwarden.com. Queres visitala agora?" }, "premiumRefresh": { - "message": "Refresh membership" + "message": "Actualizar o teu plan" }, "premiumNotCurrentMember": { - "message": "You are not currently a Premium member." + "message": "Actualmente non es usuario Prémium." }, "premiumSignUpAndGet": { - "message": "Sign up for a Premium membership and get:" + "message": "Actualiza a unha conta Premium e recibe:" }, "ppremiumSignUpStorage": { - "message": "1 GB encrypted storage for file attachments." + "message": "1 GB de almacenamento cifrado para arquivos anexos." }, "premiumSignUpEmergency": { - "message": "Emergency access." + "message": "Acceso de emerxencia." }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "Opcións de verificación en 2 pasos privadas tales coma YubiKey ou Duo." }, "ppremiumSignUpReports": { - "message": "Password hygiene, account health, and data breach reports to keep your vault safe." + "message": "Limpeza de contrasinais, saúde de contas e informes de filtración de datos para manter a túa caixa forte segura." }, "ppremiumSignUpTotp": { - "message": "TOTP verification code (2FA) generator for logins in your vault." + "message": "Xerador de códigos TOTP (2FA) para as credenciais da túa caixa forte." }, "ppremiumSignUpSupport": { - "message": "Priority customer support." + "message": "Atención ó cliente prioritaria." }, "ppremiumSignUpFuture": { - "message": "All future Premium features. More coming soon!" + "message": "Tódalas funcións Prémium futuras!" }, "premiumPurchase": { - "message": "Purchase Premium" + "message": "Adquirir Prémium" }, "premiumPurchaseAlert": { - "message": "You can purchase Premium membership on the bitwarden.com web vault. Do you want to visit the website now?" + "message": "Podes adquirir o plan Prémium na aplicación web de bitwarden.com. Queres visitala agora mesmo?" }, "premiumPurchaseAlertV2": { - "message": "You can purchase Premium from your account settings on the Bitwarden web app." + "message": "Podes adquirir o plan Prémium dende os axustes de conta da aplicación web de Bitwarden." }, "premiumCurrentMember": { - "message": "You are a Premium member!" + "message": "Xa es un usuario Prémium!" }, "premiumCurrentMemberThanks": { - "message": "Thank you for supporting Bitwarden." + "message": "Grazas por apoiar Bitwarden." }, "premiumFeatures": { - "message": "Upgrade to Premium and receive:" + "message": "Mellora a Prémium e recibe:" }, "premiumPrice": { - "message": "All for just $PRICE$ /year!", + "message": "Todo por só $PRICE$/ano!", "placeholders": { "price": { "content": "$1", @@ -1290,7 +1287,7 @@ } }, "premiumPriceV2": { - "message": "All for just $PRICE$ per year!", + "message": "Todo por só $PRICE$ ó ano!", "placeholders": { "price": { "content": "$1", @@ -1299,34 +1296,34 @@ } }, "refreshComplete": { - "message": "Refresh complete" + "message": "Actualización completada" }, "enableAutoTotpCopy": { - "message": "Copy TOTP automatically" + "message": "Copiar TOTP automaticamente" }, "disableAutoTotpCopyDesc": { - "message": "If a login has an authenticator key, copy the TOTP verification code to your clip-board when you autofill the login." + "message": "Se unha credencial ten clave de autenticación, copia o código TOTP no portapapeis ó autoencher os datos." }, "enableAutoBiometricsPrompt": { - "message": "Ask for biometrics on launch" + "message": "Requirir biometría no inicio" }, "premiumRequired": { - "message": "Premium required" + "message": "Plan Prémium requirido" }, "premiumRequiredDesc": { - "message": "A Premium membership is required to use this feature." + "message": "Requírese un plan Prémium para poder empregar esta función." }, "enterVerificationCodeApp": { - "message": "Enter the 6 digit verification code from your authenticator app." + "message": "Insire o código de 6 díxitos da túa aplicación de autenticación." }, "authenticationTimeout": { - "message": "Authentication timeout" + "message": "Tempo límite de autenticación superado" }, "authenticationSessionTimedOut": { - "message": "The authentication session timed out. Please restart the login process." + "message": "Superouse o tempo límite da sesión de autenticación. Recomeza o proceso." }, "enterVerificationCodeEmail": { - "message": "Enter the 6 digit verification code that was emailed to $EMAIL$.", + "message": "Insire o código de verificación de 6 díxitos enviado a $EMAIL$.", "placeholders": { "email": { "content": "$1", @@ -1335,7 +1332,7 @@ } }, "verificationCodeEmailSent": { - "message": "Verification email sent to $EMAIL$.", + "message": "Enviouse un correo de verificación a $EMAIL$.", "placeholders": { "email": { "content": "$1", @@ -1347,40 +1344,40 @@ "message": "Lémbrame" }, "sendVerificationCodeEmailAgain": { - "message": "Send verification code email again" + "message": "Enviar un novo correo de verificación" }, "useAnotherTwoStepMethod": { - "message": "Use another two-step login method" + "message": "Empregar outro método de verificación en 2 pasos" }, "insertYubiKey": { - "message": "Insert your YubiKey into your computer's USB port, then touch its button." + "message": "Conecta a túa YubiKey no porto USB, despois preme o seu botón." }, "insertU2f": { - "message": "Insert your security key into your computer's USB port. If it has a button, touch it." + "message": "Conecta a túa YubiKey no porto USB. Se ten un botón, prémeo." }, "webAuthnNewTab": { - "message": "To start the WebAuthn 2FA verification. Click the button below to open a new tab and follow the instructions provided in the new tab." + "message": "Para iniciar o proceso de verificación WebAuthn 2FA, clica no botón embaixo e segue as instrucións na pestana que se abrirá." }, "webAuthnNewTabOpen": { - "message": "Abrir novo separador" + "message": "Abrir nova pestana" }, "webAuthnAuthenticate": { "message": "Autenticar con WebAuthn" }, "loginUnavailable": { - "message": "Inicio de sesión non dispoñíbel" + "message": "Inicio de sesión non dispoñible" }, "noTwoStepProviders": { - "message": "This account has two-step login set up, however, none of the configured two-step providers are supported by this web browser." + "message": "Esta conta ten activada a verificación en 2 pasos, pero ningún dos métodos dispoñibles é compatible con este navegador." }, "noTwoStepProviders2": { - "message": "Please use a supported web browser (such as Chrome) and/or add additional providers that are better supported across web browsers (such as an authenticator app)." + "message": "Por favor, emprega un navegador compatible (como Chrome) ou un método de maior compatibilidade con distintos navegadores (como unha aplicación de autenticación)." }, "twoStepOptions": { - "message": "Opcións de inicio de sesión en dous pasos" + "message": "Opcións de verificación en dous pasos" }, "recoveryCodeDesc": { - "message": "Lost access to all of your two-factor providers? Use your recovery code to turn off all two-factor providers from your account." + "message": "Perdiche o acceso ós provedores de verificación en dous pasos (2FA)? Emprega o teu código de recuperación para desactivalos." }, "recoveryCodeTitle": { "message": "Código de recuperación" @@ -1389,61 +1386,61 @@ "message": "Aplicación de autenticación" }, "authenticatorAppDescV2": { - "message": "Enter a code generated by an authenticator app like Bitwarden Authenticator.", + "message": "Insire un código xerado por unha aplicación de autenticación coma Bitwarden Authenticator.", "description": "'Bitwarden Authenticator' is a product name and should not be translated." }, "yubiKeyTitleV2": { - "message": "Yubico OTP Security Key" + "message": "Clave de seguridade OTP de Yubico" }, "yubiKeyDesc": { - "message": "Use a YubiKey to access your account. Works with YubiKey 4, 4 Nano, 4C, and NEO devices." + "message": "Emprega YubiKey para acceder á túa conta. Funciona con dispositivos YubiKey 4, 4 Nano, 4C e Neo." }, "duoDescV2": { - "message": "Enter a code generated by Duo Security.", + "message": "Emprega un código xerado por Duo Security.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { - "message": "Verify with Duo Security for your organization using the Duo Mobile app, SMS, phone call, or U2F security key.", + "message": "Protexe a túa organización con Duo Security ca app móbil Duo Mobile, SMS, chamada telefónica ou chave de seguridade U2F.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "webAuthnTitle": { "message": "FIDO2 WebAuthn" }, "webAuthnDesc": { - "message": "Use any WebAuthn compatible security key to access your account." + "message": "Emprega unha clave de seguridade compatible con WebAuthn para acceder á túa conta." }, "emailTitle": { "message": "Correo electrónico" }, "emailDescV2": { - "message": "Enter a code sent to your email." + "message": "Insire o código enviado ó teu correo electrónico." }, "selfHostedEnvironment": { - "message": "Entorno de auto-aloxamento" + "message": "Entorno de aloxamento propio" }, "selfHostedEnvironmentFooter": { - "message": "Specify the base URL of your on-premises hosted Bitwarden installation." + "message": "Especifica a URL base do teu servidor Bitwarden de aloxamento propio." }, "selfHostedBaseUrlHint": { - "message": "Specify the base URL of your on-premises hosted Bitwarden installation. Example: https://bitwarden.company.com" + "message": "Especifica a URL base do teu servidor Bitwarden de aloxamento propio. Ex.: https://bitwarden.compañia.com" }, "selfHostedCustomEnvHeader": { - "message": "For advanced configuration, you can specify the base URL of each service independently." + "message": "Para configuración avanzada, específica de forma independente a URL base de cada servizo." }, "selfHostedEnvFormInvalid": { - "message": "You must add either the base Server URL or at least one custom environment." + "message": "Debes engadir ou a URL base do servidor ou, polo menos, un entorno personalizado." }, "customEnvironment": { "message": "Entorno personalizado" }, "customEnvironmentFooter": { - "message": "Para usuarios avanzados. Poder especificar o URL base de cada servizo de xeito independente." + "message": "Para usuarios avanzados. Podes especificar o URL base de cada servizo de xeito independente." }, "baseUrl": { "message": "URL do servidor" }, "selfHostBaseUrl": { - "message": "Self-host server URL", + "message": "URL do servidor de aloxamento propio", "description": "Label for field requesting a self-hosted integration service URL" }, "apiUrl": { @@ -1465,101 +1462,101 @@ "message": "URLs do entorno gardadas" }, "showAutoFillMenuOnFormFields": { - "message": "Show autofill menu on form fields", + "message": "Amosar menú de autoenchido en formularios", "description": "Represents the message for allowing the user to enable the autofill overlay" }, "autofillSuggestionsSectionTitle": { - "message": "Autofill suggestions" + "message": "Suxestións de autoenchido" }, "showInlineMenuLabel": { - "message": "Show autofill suggestions on form fields" + "message": "Amosar suxestións de autoenchido en formularios" }, "showInlineMenuIdentitiesLabel": { - "message": "Display identities as suggestions" + "message": "Amosar identidades como suxestións" }, "showInlineMenuCardsLabel": { - "message": "Display cards as suggestions" + "message": "Amosar tarxetas como suxestións" }, "showInlineMenuOnIconSelectionLabel": { - "message": "Display suggestions when icon is selected" + "message": "Amosar as suxestións ó premer a icona de Bitwarden" }, "showInlineMenuOnFormFieldsDescAlt": { - "message": "Applies to all logged in accounts." + "message": "Aplica a tódalas sesións iniciadas." }, "turnOffBrowserBuiltInPasswordManagerSettings": { - "message": "Turn off your browser's built in password manager settings to avoid conflicts." + "message": "Desactiva o xestor de contrasinais do teu navegador para evitar conflitos." }, "turnOffBrowserBuiltInPasswordManagerSettingsLink": { - "message": "Edit browser settings." + "message": "Cambiar axustes do navegador." }, "autofillOverlayVisibilityOff": { "message": "Apagado", "description": "Overlay setting select option for disabling autofill overlay" }, "autofillOverlayVisibilityOnFieldFocus": { - "message": "When field is selected (on focus)", + "message": "Cando o campo está seleccionado (activo)", "description": "Overlay appearance select option for showing the field on focus of the input element" }, "autofillOverlayVisibilityOnButtonClick": { - "message": "When autofill icon is selected", + "message": "Cando a icona de autoenchido está seleccionada", "description": "Overlay appearance select option for showing the field on click of the overlay icon" }, "enableAutoFillOnPageLoadSectionTitle": { - "message": "Autofill on page load" + "message": "Autoencher ó cargar a páxina" }, "enableAutoFillOnPageLoad": { - "message": "Autofill on page load" + "message": "Autoencher ó cargar a páxina" }, "enableAutoFillOnPageLoadDesc": { - "message": "If a login form is detected, autofill when the web page loads." + "message": "Se se detecta un formulario de inicio de sesión, autoenchelo ó cargar a páxina." }, "experimentalFeature": { - "message": "Compromised or untrusted websites can exploit autofill on page load." + "message": "As webs comprometidas ou non fiables poden aproveitarse do autoenchido ó cargar a páxina." }, "learnMoreAboutAutofillOnPageLoadLinkText": { - "message": "Learn more about risks" + "message": "Saber máis dos riscos" }, "learnMoreAboutAutofill": { - "message": "Learn more about autofill" + "message": "Saber máis do autoenchido" }, "defaultAutoFillOnPageLoad": { - "message": "Default autofill setting for login items" + "message": "Axustes de autoenchido por defecto para as credenciais" }, "defaultAutoFillOnPageLoadDesc": { - "message": "You can turn off autofill on page load for individual login items from the item's Edit view." + "message": "Podes apagar o autoenchido para entradas concretas nas súas páxinas de edición." }, "itemAutoFillOnPageLoad": { - "message": "Autofill on page load (if set up in Options)" + "message": "Autoenchido ó cargar a páxina (se está activado nos Axustes)" }, "autoFillOnPageLoadUseDefault": { - "message": "Use default setting" + "message": "Usar axustes por defecto" }, "autoFillOnPageLoadYes": { - "message": "Autofill on page load" + "message": "Autoencher ó cargar a páxina" }, "autoFillOnPageLoadNo": { - "message": "Do not autofill on page load" + "message": "Non autoencher ó cargar a páxina" }, "commandOpenPopup": { - "message": "Open vault popup" + "message": "Abrir ventá emerxente da caixa forte" }, "commandOpenSidebar": { - "message": "Open vault in sidebar" + "message": "Abrir caixa forte na barra lateral" }, "commandAutofillLoginDesc": { - "message": "Autofill the last used login for the current website" + "message": "Autoencher a última credencial empregada nesta web" }, "commandAutofillCardDesc": { - "message": "Autofill the last used card for the current website" + "message": "Autoencher a última tarxeta empregada nesta web" }, "commandAutofillIdentityDesc": { - "message": "Autofill the last used identity for the current website" + "message": "Autoencher a última identidade empregada nesta web" }, "commandGeneratePasswordDesc": { - "message": "Generate and copy a new random password to the clipboard" + "message": "Xerar e copiar un novo contrasinal aleatorio ó portapapeis" }, "commandLockVaultDesc": { - "message": "Lock the vault" + "message": "Cerrar a caixa forte" }, "customFields": { "message": "Campos personalizados" @@ -1586,7 +1583,7 @@ "message": "Booleano" }, "cfTypeCheckbox": { - "message": "Checkbox" + "message": "Caixa" }, "cfTypeLinked": { "message": "Vinculado", @@ -1597,28 +1594,28 @@ "description": "This describes a value that is 'linked' (tied) to another value." }, "popup2faCloseMessage": { - "message": "Clicking outside the popup window to check your email for your verification code will cause this popup to close. Do you want to open this popup in a new window so that it does not close?" + "message": "Se clicas fóra desta ventá emerxente para comprobar o código do correo, cerrarase. Queres convertila nunha nova ventá completa para que non se cerre?" }, "popupU2fCloseMessage": { - "message": "This browser cannot process U2F requests in this popup window. Do you want to open this popup in a new window so that you can log in using U2F?" + "message": "Este navegador non pode procesar peticións U2F nesta ventá emerxente. Queres convertila nunha ventá completa para poder continuar?" }, "enableFavicon": { - "message": "Show website icons" + "message": "Amosar iconas web" }, "faviconDesc": { - "message": "Show a recognizable image next to each login." + "message": "Amosar unha imaxe recoñecible xunto a cada credencial." }, "faviconDescAlt": { - "message": "Show a recognizable image next to each login. Applies to all logged in accounts." + "message": "Amosar unha imaxe recoñecible xunto a cada credencial. Aplica a tódalas sesións iniciadas en Bitwarden." }, "enableBadgeCounter": { - "message": "Show badge counter" + "message": "Amosar contador na icona" }, "badgeCounterDesc": { - "message": "Indicate how many logins you have for the current web page." + "message": "Indicar cantas credenciais tes gardadas para a web aberta." }, "cardholderName": { - "message": "Nome do titular da tarxeta" + "message": "Titular da tarxeta" }, "number": { "message": "Número" @@ -1627,7 +1624,7 @@ "message": "Marca" }, "expirationMonth": { - "message": "Mes de expiración" + "message": "Mes de vencemento" }, "expirationYear": { "message": "Ano de vencemento" @@ -1693,7 +1690,7 @@ "message": "Dr" }, "mx": { - "message": "Mx" + "message": "Srx" }, "firstName": { "message": "Nome" @@ -1708,19 +1705,19 @@ "message": "Nome completo" }, "identityName": { - "message": "Nome de identidade" + "message": "Nome da identidade" }, "company": { "message": "Empresa" }, "ssn": { - "message": "Número da seguridade social" + "message": "Número da Seguridade Social" }, "passportNumber": { "message": "Número de pasaporte" }, "licenseNumber": { - "message": "Número de licencia" + "message": "Número de matrícula" }, "email": { "message": "Correo electrónico" @@ -1759,7 +1756,7 @@ "message": "Inicio de sesión" }, "typeLogins": { - "message": "Inicios se sesión" + "message": "Inicios de sesión" }, "typeSecureNote": { "message": "Nota segura" @@ -1771,10 +1768,10 @@ "message": "Identidade" }, "typeSshKey": { - "message": "SSH key" + "message": "Clave SSH" }, "newItemHeader": { - "message": "New $TYPE$", + "message": "Novo $TYPE$", "placeholders": { "type": { "content": "$1", @@ -1783,7 +1780,7 @@ } }, "editItemHeader": { - "message": "Edit $TYPE$", + "message": "Modificar $TYPE$", "placeholders": { "type": { "content": "$1", @@ -1792,7 +1789,7 @@ } }, "viewItemHeader": { - "message": "View $TYPE$", + "message": "Ver $TYPE$", "placeholders": { "type": { "content": "$1", @@ -1804,13 +1801,13 @@ "message": "Historial de contrasinais" }, "generatorHistory": { - "message": "Generator history" + "message": "Historial do xerador" }, "clearGeneratorHistoryTitle": { - "message": "Clear generator history" + "message": "Baleirar historial do xerador" }, "cleargGeneratorHistoryDescription": { - "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + "message": "Se continúas, tódalas entradas serán eliminadas permanentemente do historial do xerador. Seguro que queres continuar?" }, "back": { "message": "Atrás" @@ -1819,7 +1816,7 @@ "message": "Coleccións" }, "nCollections": { - "message": "$COUNT$ collections", + "message": "$COUNT$ coleccións", "placeholders": { "count": { "content": "$1", @@ -1831,7 +1828,7 @@ "message": "Favoritos" }, "popOutNewWindow": { - "message": "Pop out to a new window" + "message": "Sacar a unha nova ventá" }, "refresh": { "message": "Actualizar" @@ -1843,23 +1840,23 @@ "message": "Identidades" }, "logins": { - "message": "Inicios de sesión" + "message": "Credenciais" }, "secureNotes": { "message": "Notas seguras" }, "sshKeys": { - "message": "SSH Keys" + "message": "Claves SSH" }, "clear": { "message": "Limpar", "description": "To clear something out. example: To clear browser history." }, "checkPassword": { - "message": "Check if password has been exposed." + "message": "Comprobar se o contrasinal foi filtrado." }, "passwordExposed": { - "message": "This password has been exposed $VALUE$ time(s) in data breaches. You should change it.", + "message": "Este contrasinal foi filtrado $VALUE$ vez/veces. Deberías cambialo.", "placeholders": { "value": { "content": "$1", @@ -1868,14 +1865,14 @@ } }, "passwordSafe": { - "message": "This password was not found in any known data breaches. It should be safe to use." + "message": "Este contrasinal non foi atopado en ningunha filtración de datos. Debería ser seguro." }, "baseDomain": { "message": "Dominio base", "description": "Domain name. Ex. website.com" }, "baseDomainOptionRecommended": { - "message": "Base domain (recommended)", + "message": "Dominio base (recomendado)", "description": "Domain name. Ex. website.com" }, "domainName": { @@ -1883,7 +1880,7 @@ "description": "Domain name. Ex. website.com" }, "host": { - "message": "Anfitrión", + "message": "Servidor", "description": "A URL's host value. For example, the host of https://sub.domain.com:443 is 'sub.domain.com:443'." }, "exact": { @@ -1897,18 +1894,18 @@ "description": "A programming term, also known as 'RegEx'." }, "matchDetection": { - "message": "Match detection", + "message": "Detección de coincidencias", "description": "URI match detection for autofill." }, "defaultMatchDetection": { - "message": "Default match detection", + "message": "Detección de coincidencias por defecto", "description": "Default URI match detection for autofill." }, "toggleOptions": { - "message": "Toggle options" + "message": "Alternar opcións" }, "toggleCurrentUris": { - "message": "Toggle current URIs", + "message": "Alternar URIs actuais", "description": "Toggle the display of the URIs of the currently open tabs in the browser." }, "currentUri": { @@ -1923,19 +1920,19 @@ "message": "Tipos" }, "allItems": { - "message": "Todos os elementos" + "message": "Todas as entradas" }, "noPasswordsInList": { "message": "Non hai contrasinais que listar." }, "clearHistory": { - "message": "Clear history" + "message": "Baleirar historial" }, "nothingToShow": { - "message": "Nothing to show" + "message": "Nada que amosar" }, "nothingGeneratedRecently": { - "message": "You haven't generated anything recently" + "message": "Non xerache nada recentemente" }, "remove": { "message": "Eliminar" @@ -1956,26 +1953,26 @@ "description": "ex. Date this password was updated" }, "neverLockWarning": { - "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + "message": "Seguro que queres marcar a opción \"Nunca\"? Isto almacenará a clave de cifrado da túa caixa forte no teu dispositivo. Se usas esta opción debes asegurarte de ter o dispositivo ben protexido." }, "noOrganizationsList": { - "message": "You do not belong to any organizations. Organizations allow you to securely share items with other users." + "message": "Non pertences a ningunha organización. As organizacións permiten compartir entradas con outros usuarios." }, "noCollectionsInList": { - "message": "There are no collections to list." + "message": "Non hai coleccións que listar." }, "ownership": { "message": "Propiedade" }, "whoOwnsThisItem": { - "message": "Quen posúe este elemento?" + "message": "Quen posúe esta entrada?" }, "strong": { "message": "Forte", "description": "ex. A strong password. Scale: Weak -> Good -> Strong" }, "good": { - "message": "Boa", + "message": "Bo", "description": "ex. A good password. Scale: Weak -> Good -> Strong" }, "weak": { @@ -1986,94 +1983,91 @@ "message": "Contrasinal mestre feble" }, "weakMasterPasswordDesc": { - "message": "The master password you have chosen is weak. You should use a strong master password (or a passphrase) to properly protect your Bitwarden account. Are you sure you want to use this master password?" + "message": "O contrasinal mestre creado é feble. Debes empregar un contrasinal ou frase de contrasinal mestre forte para protexer debidamente a túa conta. Seguro que queres continuar con este contrasinal?" }, "pin": { "message": "PIN", "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." }, "unlockWithPin": { - "message": "Unlock with PIN" + "message": "Abrir con PIN" }, "setYourPinTitle": { - "message": "Set PIN" + "message": "Definir PIN" }, "setYourPinButton": { - "message": "Set PIN" + "message": "Crear PIN" }, "setYourPinCode": { - "message": "Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application." + "message": "Crea un PIN para abrir a caixa forte. Se algunha vez pechas a sesión en Bitwarden perderase a configuración deste PIN." }, "setYourPinCode1": { - "message": "Your PIN will be used to unlock Bitwarden instead of your master password. Your PIN will reset if you ever fully log out of Bitwarden." + "message": "O PIN empregarase no lugar do contrasinal mestre para abrir a caixa forte. Se pechas a sesión en Bitwarden perderase a configuración do PIN." }, "pinRequired": { - "message": "PIN code is required." + "message": "PIN requirido." }, "invalidPin": { - "message": "Invalid PIN code." + "message": "PIN incorrecto." }, "tooManyInvalidPinEntryAttemptsLoggingOut": { - "message": "Too many invalid PIN entry attempts. Logging out." + "message": "Múltiples intentos fallidos de PIN. Pechando a sesión." }, "unlockWithBiometrics": { - "message": "Unlock with biometrics" + "message": "Abrir con biometría" }, "unlockWithMasterPassword": { - "message": "Unlock with master password" + "message": "Abrir con contrasinal mestre" }, "awaitDesktop": { - "message": "Awaiting confirmation from desktop" + "message": "Agardando pola confirmación do escritorio" }, "awaitDesktopDesc": { - "message": "Please confirm using biometrics in the Bitwarden desktop application to set up biometrics for browser." + "message": "Por favor confirma o uso de biometría na aplicación de escritorio de Bitwarden para activar a biometría no navegador." }, "lockWithMasterPassOnRestart": { - "message": "Lock with master password on browser restart" + "message": "Bloquear con contrasinal mestre ó pechar o navegador" }, "lockWithMasterPassOnRestart1": { - "message": "Require master password on browser restart" + "message": "Requirir contrasinal mestre ó abrir o navegador" }, "selectOneCollection": { - "message": "You must select at least one collection." + "message": "Debes seleccionar polo menos unha colección." }, "cloneItem": { - "message": "Clonar elemento" + "message": "Duplicar entrada" }, "clone": { "message": "Clonar" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." - }, "passwordGenerator": { - "message": "Password generator" + "message": "Xerador de contrasinais" }, "usernameGenerator": { - "message": "Username generator" + "message": "Xerador de nomes de usuario" }, "useThisPassword": { - "message": "Use this password" + "message": "Usar este contrasinal" }, "useThisUsername": { - "message": "Use this username" + "message": "Usar este nome de usuario" }, "securePasswordGenerated": { - "message": "Secure password generated! Don't forget to also update your password on the website." + "message": "Contrasinal seguro xerado! Non esquezas actualizar o contrasinal na web." }, "useGeneratorHelpTextPartOne": { - "message": "Use the generator", + "message": "Usar o xerador", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "useGeneratorHelpTextPartTwo": { - "message": "to create a strong unique password", + "message": "para crear un contrasinal forte", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "vaultTimeoutAction": { - "message": "Vault timeout action" + "message": "Acción do temporizador da caixa forte" }, "vaultTimeoutAction1": { - "message": "Timeout action" + "message": "Acción do temporizador" }, "lock": { "message": "Bloquear", @@ -2084,55 +2078,55 @@ "description": "Noun: a special folder to hold deleted items" }, "searchTrash": { - "message": "Search trash" + "message": "Buscar no lixo" }, "permanentlyDeleteItem": { - "message": "Permanently delete item" + "message": "Eliminar entrada permanentemente" }, "permanentlyDeleteItemConfirmation": { - "message": "Are you sure you want to permanently delete this item?" + "message": "Seguro que queres eliminar permanentemente esta entrada?" }, "permanentlyDeletedItem": { - "message": "Item permanently deleted" + "message": "Entrada eliminada permanente" }, "restoreItem": { - "message": "Restore item" + "message": "Restaurar entrada" }, "restoredItem": { - "message": "Item restored" + "message": "Entrada restaurada" }, "alreadyHaveAccount": { - "message": "Already have an account?" + "message": "Xa tes unha conta?" }, "vaultTimeoutLogOutConfirmation": { - "message": "Logging out will remove all access to your vault and requires online authentication after the timeout period. Are you sure you want to use this setting?" + "message": "Cerrar a sesión despois dun tempo elimina o acceso á caixa forte ata que se volva iniciar sesión. Seguro que queres empregar esta opción?" }, "vaultTimeoutLogOutConfirmationTitle": { - "message": "Timeout action confirmation" + "message": "Confirmación de acción do temporizador" }, "autoFillAndSave": { - "message": "Autofill and save" + "message": "Autoencher e gardar" }, "fillAndSave": { - "message": "Fill and save" + "message": "Encher e gardar" }, "autoFillSuccessAndSavedUri": { - "message": "Item autofilled and URI saved" + "message": "Entrada autoenchida e URI gardada" }, "autoFillSuccess": { - "message": "Item autofilled " + "message": "Entrada autoenchida " }, "insecurePageWarning": { - "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + "message": "Aviso: esta é unha páxina web HTTP sen cifrar, e calquera información que envíes é visible para terceiros. A credencial foi orixinalmente gardada nunha web HTTPS cifrada." }, "insecurePageWarningFillPrompt": { - "message": "Do you still wish to fill this login?" + "message": "Aínda queres autoencher esta credencial?" }, "autofillIframeWarning": { - "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to autofill anyway, or Cancel to stop." + "message": "O formulario está aloxado nun dominio diferente da URI que gardaches. Clica Aceptar para autoencher igualmente, ou Cancelar para parar." }, "autofillIframeWarningTip": { - "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "message": "Para prever este aviso no futuro, garda este URI, $HOSTNAME$, na túa entrada en Bitwarden para este sitio.", "placeholders": { "hostname": { "content": "$1", @@ -2141,22 +2135,22 @@ } }, "setMasterPassword": { - "message": "Set master password" + "message": "Definir contrasinal mestre" }, "currentMasterPass": { - "message": "Current master password" + "message": "Contrasinal mestre actual" }, "newMasterPass": { - "message": "New master password" + "message": "Novo contrasinal mestre" }, "confirmNewMasterPass": { - "message": "Confirm new master password" + "message": "Repetir novo contrasinal mestre" }, "masterPasswordPolicyInEffect": { - "message": "One or more organization policies require your master password to meet the following requirements:" + "message": "As directivas da túa organización esixen que o teu contrasinal mestre cumpra os seguintes requisitos:" }, "policyInEffectMinComplexity": { - "message": "Minimum complexity score of $SCORE$", + "message": "Complexidade mínima de $SCORE$ puntos", "placeholders": { "score": { "content": "$1", @@ -2165,7 +2159,7 @@ } }, "policyInEffectMinLength": { - "message": "Minimum length of $LENGTH$", + "message": "Mínimo de $LENGTH$ caracteres", "placeholders": { "length": { "content": "$1", @@ -2174,16 +2168,16 @@ } }, "policyInEffectUppercase": { - "message": "Contain one or more uppercase characters" + "message": "Conter polo menos unha maiúscula" }, "policyInEffectLowercase": { - "message": "Contain one or more lowercase characters" + "message": "Conter polo menos unha minúscula" }, "policyInEffectNumbers": { - "message": "Contain one or more numbers" + "message": "Conter polo menos un número" }, "policyInEffectSpecial": { - "message": "Contain one or more of the following special characters $CHARS$", + "message": "Conter polo menos un dos seguintes caracteres: $CHARS$", "placeholders": { "chars": { "content": "$1", @@ -2192,155 +2186,155 @@ } }, "masterPasswordPolicyRequirementsNotMet": { - "message": "Your new master password does not meet the policy requirements." + "message": "Este contrasinal mestre non cumpre cas directivas." }, "receiveMarketingEmailsV2": { - "message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox." + "message": "Recibe consellos, anuncios e oportunidades de investigación de Bitwarden no teu correo." }, "unsubscribe": { - "message": "Unsubscribe" + "message": "Cancelar subscrición" }, "atAnyTime": { - "message": "at any time." + "message": "en calquera momento." }, "byContinuingYouAgreeToThe": { - "message": "By continuing, you agree to the" + "message": "Ó continuar, estarás aceptando os" }, "and": { - "message": "and" + "message": "e" }, "acceptPolicies": { - "message": "By checking this box you agree to the following:" + "message": "Ó marcar esta caixa aceptas o seguinte:" }, "acceptPoliciesRequired": { - "message": "Terms of Service and Privacy Policy have not been acknowledged." + "message": "Os Termos de Servizo e a Política de Privacidade non foron aceptados." }, "termsOfService": { - "message": "Terms of Service" + "message": "Termos de Servizo" }, "privacyPolicy": { - "message": "Privacy Policy" + "message": "Política de Privacidade" }, "hintEqualsPassword": { - "message": "Your password hint cannot be the same as your password." + "message": "A pista do contrasinal non pode ser o contrasinal." }, "ok": { - "message": "Ok" + "message": "Aceptar" }, "errorRefreshingAccessToken": { - "message": "Access Token Refresh Error" + "message": "Erro de actualización do Token de Acceso" }, "errorRefreshingAccessTokenDesc": { - "message": "No refresh token or API keys found. Please try logging out and logging back in." + "message": "Non se atoparon token de actualización nin claves da API. Por favor trata de volver iniciar sesión." }, "desktopSyncVerificationTitle": { - "message": "Desktop sync verification" + "message": "Verificación de sincronización co escritorio" }, "desktopIntegrationVerificationText": { - "message": "Please verify that the desktop application shows this fingerprint: " + "message": "Por favor verifica que a app de escritorio amosa esta pegada dixital: " }, "desktopIntegrationDisabledTitle": { - "message": "Browser integration is not set up" + "message": "Integración co navegador non configurada" }, "desktopIntegrationDisabledDesc": { - "message": "Browser integration is not set up in the Bitwarden desktop application. Please set it up in the settings within the desktop application." + "message": "A integración co navegador non está activada na app de escritorio de Bitwarden. Por favor configúraa dende os axustes da app de escritorio." }, "startDesktopTitle": { - "message": "Start the Bitwarden desktop application" + "message": "Abrir a app de escritorio de Bitwarden" }, "startDesktopDesc": { - "message": "The Bitwarden desktop application needs to be started before unlock with biometrics can be used." + "message": "Para poder empregar a biometría a aplicación de escritorio de Bitwarden debe estar executada." }, "errorEnableBiometricTitle": { - "message": "Unable to set up biometrics" + "message": "Non foi posible activar a biometría" }, "errorEnableBiometricDesc": { - "message": "Action was canceled by the desktop application" + "message": "Proceso interrompido pola aplicación de escritorio" }, "nativeMessagingInvalidEncryptionDesc": { - "message": "Desktop application invalidated the secure communication channel. Please retry this operation" + "message": "A aplicación de escritorio invalidou a canle de comunicación segura. Por favor volve intentalo" }, "nativeMessagingInvalidEncryptionTitle": { - "message": "Desktop communication interrupted" + "message": "Comunicación co escritorio interrompida" }, "nativeMessagingWrongUserDesc": { - "message": "The desktop application is logged into a different account. Please ensure both applications are logged into the same account." + "message": "A aplicación de escritorio ten unha conta distinta. Por favor asegúrate de que ambas aplicacións iniciaron sesión na mesma conta." }, "nativeMessagingWrongUserTitle": { - "message": "Account missmatch" + "message": "Conta non coincidente" }, "nativeMessagingWrongUserKeyTitle": { - "message": "Biometric key missmatch" + "message": "Clave biométrica non coincidente" }, "nativeMessagingWrongUserKeyDesc": { - "message": "Biometric unlock failed. The biometric secret key failed to unlock the vault. Please try to set up biometrics again." + "message": "Desbloqueo biométrico fallido. A clave biométrica non puido abrir a caixa forte. Por favor proba a volver configurar a biometría." }, "biometricsNotEnabledTitle": { - "message": "Biometrics not set up" + "message": "Biometría non activada" }, "biometricsNotEnabledDesc": { - "message": "Browser biometrics requires desktop biometric to be set up in the settings first." + "message": "A biometría no navegador require que se configure primeiro na app de escritorio." }, "biometricsNotSupportedTitle": { - "message": "Biometrics not supported" + "message": "Biometría non compatible" }, "biometricsNotSupportedDesc": { - "message": "Browser biometrics is not supported on this device." + "message": "Biometría do navegador non compatible con este dispositivo." }, "biometricsNotUnlockedTitle": { - "message": "User locked or logged out" + "message": "Usuario cerrado ou desconectado" }, "biometricsNotUnlockedDesc": { - "message": "Please unlock this user in the desktop application and try again." + "message": "Por favor desbloquea o usuario na aplicación de escritorio e volve intentalo." }, "biometricsNotAvailableTitle": { - "message": "Biometric unlock unavailable" + "message": "Desbloqueo biométrico non dispoñible" }, "biometricsNotAvailableDesc": { - "message": "Biometric unlock is currently unavailable. Please try again later." + "message": "Desbloqueo biométrico non dispoñible neste momento. Volve intentalo máis tarde." }, "biometricsFailedTitle": { - "message": "Biometrics failed" + "message": "Fallo da biometría" }, "biometricsFailedDesc": { - "message": "Biometrics cannot be completed, consider using a master password or logging out. If this persists, please contact Bitwarden support." + "message": "A biometría non puido ser completada. Proba a usar o contrasinal mestre ou pechar a sesión. Se o problema persiste, contacta con atención ó cliente." }, "nativeMessaginPermissionErrorTitle": { - "message": "Permission not provided" + "message": "Permiso non concedido" }, "nativeMessaginPermissionErrorDesc": { - "message": "Without permission to communicate with the Bitwarden Desktop Application we cannot provide biometrics in the browser extension. Please try again." + "message": "Sen permiso de comunicación ca aplicación de escritorio non podemos prover biometría na extensión de navegador. Por favor, volve intentalo." }, "nativeMessaginPermissionSidebarTitle": { - "message": "Permission request error" + "message": "Error de petición de permisos" }, "nativeMessaginPermissionSidebarDesc": { - "message": "This action cannot be done in the sidebar, please retry the action in the popup or popout." + "message": "Esta acción non pode ser realizada na barra lateral. Inténtao dende a ventá emerxente." }, "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." + "message": "Debido a unha directiva da empresa, non podes gardar entradas na túa caixa forte. Cambia a opción de propiedade a unha organización e elixe unha das coleccións dispoñibles." }, "personalOwnershipPolicyInEffect": { - "message": "An organization policy is affecting your ownership options." + "message": "Unha directiva da empresa está a afectar ás túas opcións de propiedade." }, "personalOwnershipPolicyInEffectImports": { - "message": "An organization policy has blocked importing items into your individual vault." + "message": "Unha directiva da empresa impide importar entradas á túa caixa forte individual." }, "domainsTitle": { - "message": "Domains", + "message": "Dominios", "description": "A category title describing the concept of web domains" }, "excludedDomains": { - "message": "Excluded domains" + "message": "Dominios excluídos" }, "excludedDomainsDesc": { - "message": "Bitwarden will not ask to save login details for these domains. You must refresh the page for changes to take effect." + "message": "Bitwarden non ofrecerá gardar contas para estes dominios. Recarga a páxina para que os cambios fagan efecto." }, "excludedDomainsDescAlt": { - "message": "Bitwarden will not ask to save login details for these domains for all logged in accounts. You must refresh the page for changes to take effect." + "message": "Bitwarden non ofrecerá gardar contas para estes dominios en ningunha das sesións iniciadas. Recarga a páxina para que os cambios fornezan efecto." }, "websiteItemLabel": { - "message": "Website $number$ (URI)", + "message": "Web $number$ (URI)", "placeholders": { "number": { "content": "$1", @@ -2349,7 +2343,7 @@ } }, "excludedDomainsInvalidDomain": { - "message": "$DOMAIN$ is not a valid domain", + "message": "$DOMAIN$ non é un dominio válido", "placeholders": { "domain": { "content": "$1", @@ -2358,17 +2352,17 @@ } }, "excludedDomainsSavedSuccess": { - "message": "Excluded domain changes saved" + "message": "Dominios excluídos gardados" }, "limitSendViews": { - "message": "Limit views" + "message": "Limitar visionados" }, "limitSendViewsHint": { - "message": "No one can view this Send after the limit is reached.", + "message": "Ninguén pode ver este Send dende que se alcance o límite.", "description": "Displayed under the limit views field on Send" }, "limitSendViewsCount": { - "message": "$ACCESSCOUNT$ views left", + "message": "$ACCESSCOUNT$ visionados restantes", "description": "Displayed under the limit views field on Send", "placeholders": { "accessCount": { @@ -2382,22 +2376,14 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDetails": { - "message": "Send details", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "searchSends": { - "message": "Buscar Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Engadir Send", + "message": "Detalles do Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTypeText": { "message": "Texto" }, "sendTypeTextToShare": { - "message": "Text to share" + "message": "Texto a compartir" }, "sendTypeFile": { "message": "Ficheiro" @@ -2407,36 +2393,29 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "hideTextByDefault": { - "message": "Hide text by default" - }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." + "message": "Ocultar texto por defecto" }, "expired": { - "message": "Caducado" - }, - "pendingDeletion": { - "message": "Pending deletion" + "message": "Vencido" }, "passwordProtected": { - "message": "Password protected" + "message": "Protexido con contrasinal" }, "copyLink": { - "message": "Copy link" + "message": "Copiar ligazón" }, "copySendLink": { "message": "Copiar ligazón Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "removePassword": { - "message": "Remove Password" + "message": "Eliminar contrasinal" }, "delete": { "message": "Eliminar" }, "removedPassword": { - "message": "Password removed" + "message": "Contrasinal eliminado" }, "deletedSend": { "message": "Send eliminado", @@ -2447,55 +2426,36 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "disabled": { - "message": "Disabled" + "message": "Deshabilitado" }, "removePasswordConfirmation": { - "message": "Are you sure you want to remove the password?" + "message": "Seguro que queres eliminar o contrasinal?" }, "deleteSend": { "message": "Eliminar Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendConfirmation": { - "message": "Are you sure you want to delete this Send?", + "message": "Seguro que queres eliminar este Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendPermanentConfirmation": { - "message": "Are you sure you want to permanently delete this Send?", + "message": "Seguro que queres eliminar permanentemente este Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editSend": { "message": "Editar Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { - "message": "Deletion date" - }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + "message": "Data de eliminación" }, "deletionDateDescV2": { - "message": "The Send will be permanently deleted on this date.", + "message": "Este Send será permanente eliminado nesta data.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "expirationDate": { - "message": "Expiration date" - }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + "message": "Data de vencemento" }, "oneDay": { "message": "1 día" @@ -2512,43 +2472,10 @@ "custom": { "message": "Personalizado" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { - "message": "Add an optional password for recipients to access this Send.", + "message": "Engade un contrasinal opcional para os destinatarios deste Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "Novo Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2561,7 +2488,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { - "message": "Due to an enterprise policy, you are only able to delete an existing Send.", + "message": "Por unha directiva de empresa só podes eliminar un Send existente.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSend": { @@ -2569,15 +2496,15 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSendSuccessfully": { - "message": "Send created successfully!", + "message": "Send creado con éxito!", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendExpiresInHoursSingle": { - "message": "The Send will be available to anyone with the link for the next 1 hour.", + "message": "O Send estará dispoñible para calquera ca ligazón durante 1 hora.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendExpiresInHours": { - "message": "The Send will be available to anyone with the link for the next $HOURS$ hours.", + "message": "O Send estará dispoñible para calquera ca ligazón durante $HOURS$ horas.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.", "placeholders": { "hours": { @@ -2587,11 +2514,11 @@ } }, "sendExpiresInDaysSingle": { - "message": "The Send will be available to anyone with the link for the next 1 day.", + "message": "O Send estará dispoñible para calquera ca ligazón durante 1 día.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendExpiresInDays": { - "message": "The Send will be available to anyone with the link for the next $DAYS$ days.", + "message": "O Send estará dispoñible para calquera ca ligazón durante $DAYS$ días.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.", "placeholders": { "days": { @@ -2601,7 +2528,7 @@ } }, "sendLinkCopied": { - "message": "Send link copied", + "message": "Ligazón do Send copiado", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editedSend": { @@ -2609,120 +2536,102 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFilePopoutDialogText": { - "message": "Pop out extension?", + "message": "Sacar a extensión?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFilePopoutDialogDesc": { - "message": "To create a file Send, you need to pop out the extension to a new window.", + "message": "Para crear un arquivo Send, necesitas sacar a extensión a unha nova ventá.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinuxChromiumFileWarning": { - "message": "In order to choose a file, open the extension in the sidebar (if possible) or pop out to a new window by clicking this banner." + "message": "Para escoller un arquivo, abre a extensión na barra lateral (se é posible) ou sácaa a unha nova ventá premendo este botón." }, "sendFirefoxFileWarning": { - "message": "In order to choose a file using Firefox, open the extension in the sidebar or pop out to a new window by clicking this banner." + "message": "Para escoller un arquivo empregando Firefox, abre a extensión na barra lateral ou sácaa a unha nova ventá premendo este botón." }, "sendSafariFileWarning": { - "message": "In order to choose a file using Safari, pop out to a new window by clicking this banner." + "message": "Para escoller un arquivo empregando Safari, saca a extensión a unha nova ventá premendo este botón." }, "popOut": { - "message": "Pop out" + "message": "Sacar" }, "sendFileCalloutHeader": { - "message": "Before you start" - }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" + "message": "Antes de comezar" }, "expirationDateIsInvalid": { - "message": "The expiration date provided is not valid." + "message": "A data de vencemento non é válida." }, "deletionDateIsInvalid": { - "message": "The deletion date provided is not valid." + "message": "A data de eliminación non é válida." }, "expirationDateAndTimeRequired": { - "message": "An expiration date and time are required." + "message": "Unha data e hora de vencemento son requiridas." }, "deletionDateAndTimeRequired": { - "message": "A deletion date and time are required." + "message": "Unha data e hora de eliminación son requiridas." }, "dateParsingError": { - "message": "There was an error saving your deletion and expiration dates." - }, - "hideEmail": { - "message": "Hide my email address from recipients." + "message": "Produciuse un erro ó gardar as datas de vencemento e eliminación." }, "hideYourEmail": { - "message": "Hide your email address from viewers." - }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." + "message": "Oculta o teu enderezo electrónico ós visitantes." }, "passwordPrompt": { - "message": "Master password re-prompt" + "message": "Volver solicitar o contrasinal mestre" }, "passwordConfirmation": { - "message": "Master password confirmation" + "message": "Repetir o contrasinal mestre" }, "passwordConfirmationDesc": { - "message": "This action is protected. To continue, please re-enter your master password to verify your identity." + "message": "Esta acción está protexida. Para continuar por favor volve introducir o teu contrasinal mestre." }, "emailVerificationRequired": { - "message": "Email verification required" + "message": "Verificación do correo electrónico requirida" }, "emailVerifiedV2": { - "message": "Email verified" + "message": "Correo electrónico verificado" }, "emailVerificationRequiredDesc": { - "message": "You must verify your email to use this feature. You can verify your email in the web vault." + "message": "Debes verificar o teu correo electrónico para empregar esta función. Podes facelo dende a aplicación web." }, "updatedMasterPassword": { - "message": "Updated master password" + "message": "Contrasinal mestre actualizado" }, "updateMasterPassword": { - "message": "Update master password" + "message": "Actualizar contrasinal mestre" }, "updateMasterPasswordWarning": { - "message": "Your master password was recently changed by an administrator in your organization. In order to access the vault, you must update it now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." + "message": "O teu contrasinal mestre foi recentemente cambiado por un administrador da túa organización. Para poder acceder á caixa forte debes actualizalo. A continuación cerrarase a túa sesión, requirindo volver iniciala. As sesións activas noutros dispositivos poden permanecer activas por unha hora." }, "updateWeakMasterPasswordWarning": { - "message": "Your master password does not meet one or more of your organization policies. In order to access the vault, you must update your master password now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." + "message": "O teu contrasinal mestre non cumpre algún dos requisitos das directivas da empresa. Para poder acceder a caixa forte debes actualizar o teu contrasinal mestre. A continuación cerrarase a túa sesión, requirindo volver iniciala. As sesións activas noutros dispositivos poden permanecer activas por unha hora." }, "tdeDisabledMasterPasswordRequired": { - "message": "Your organization has disabled trusted device encryption. Please set a master password to access your vault." + "message": "A túa organización desactivou cifrado en dispositivos de confianza. Por favor, crea un contrasinal mestre para acceder á túa caixa forte." }, "resetPasswordPolicyAutoEnroll": { - "message": "Automatic enrollment" + "message": "Rexistro automático" }, "resetPasswordAutoEnrollInviteWarning": { - "message": "This organization has an enterprise policy that will automatically enroll you in password reset. Enrollment will allow organization administrators to change your master password." + "message": "Esta organización ten unha directiva de empresa que te rexistrará automaticamente nun restablecemento de contrasinal. O rexistro permitirá a organización cambiar o teu contrasinal mestre." }, "selectFolder": { - "message": "Select folder..." + "message": "Seleccionar cartafol..." }, "noFoldersFound": { - "message": "No folders found", + "message": "Cartafol non atopado", "description": "Used as a message within the notification bar when no folders are found" }, "orgPermissionsUpdatedMustSetPassword": { - "message": "Your organization permissions were updated, requiring you to set a master password.", + "message": "Os permisos da organización foron actualizados, requirindo que crees un contrasinal mestre.", "description": "Used as a card title description on the set password page to explain why the user is there" }, "orgRequiresYouToSetPassword": { - "message": "Your organization requires you to set a master password.", + "message": "A túa urbanización require que crees un contrasinal mestre.", "description": "Used as a card title description on the set password page to explain why the user is there" }, "cardMetrics": { - "message": "out of $TOTAL$", + "message": "de $TOTAL$", "placeholders": { "total": { "content": "$1", @@ -2731,7 +2640,7 @@ } }, "verificationRequired": { - "message": "Verification required", + "message": "Verificación requirida", "description": "Default title for the user verification dialog." }, "hours": { @@ -2741,10 +2650,10 @@ "message": "Minutos" }, "vaultTimeoutPolicyAffectingOptions": { - "message": "Enterprise policy requirements have been applied to your timeout options" + "message": "Directivas da empresa foron aplicadas ás opcións do temporizador da túa Caixa forte" }, "vaultTimeoutPolicyInEffect": { - "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "message": "Directivas da empresa restrinxen o temporizador da túa Caixa forte a un máximo de $HOURS$ hora(s) e $MINUTES$ minuto(s).", "placeholders": { "hours": { "content": "$1", @@ -2757,7 +2666,7 @@ } }, "vaultTimeoutPolicyInEffect1": { - "message": "$HOURS$ hour(s) and $MINUTES$ minute(s) maximum.", + "message": "$HOURS$ hora(s) e $MINUTES$ minuto(s) de máximo.", "placeholders": { "hours": { "content": "$1", @@ -2770,7 +2679,7 @@ } }, "vaultTimeoutPolicyMaximumError": { - "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "message": "O tempo indicado excede as restricións definidas pola túa organización: un máximo de $HOURS$ hora(s) e $MINUTES$ minuto(s)", "placeholders": { "hours": { "content": "$1", @@ -2783,7 +2692,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s). Your vault timeout action is set to $ACTION$.", + "message": "As directivas da empresa restrinxen o temporizador da túa Caixa forte a $HOURS$ hora(s) e $MINUTES$ minuto(s) de máximo. A acción a realizar será: $ACTION$.", "placeholders": { "hours": { "content": "$1", @@ -2800,7 +2709,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "Your organization policies have set your vault timeout action to $ACTION$.", + "message": "As directivas da empresa definiron o temporizador da túa Caixa forte en $ACTION$.", "placeholders": { "action": { "content": "$1", @@ -2809,22 +2718,22 @@ } }, "vaultTimeoutTooLarge": { - "message": "Your vault timeout exceeds the restrictions set by your organization." + "message": "O temporizador da túa Caixa forte excede as restricións definidas pola túa organización." }, "vaultExportDisabled": { - "message": "Vault export unavailable" + "message": "Exportado da Caixa forte non dispoñible" }, "personalVaultExportPolicyInEffect": { - "message": "One or more organization policies prevents you from exporting your individual vault." + "message": "Directivas da empresa impiden exportar a túa caixa forte individual." }, "copyCustomFieldNameInvalidElement": { - "message": "Unable to identify a valid form element. Try inspecting the HTML instead." + "message": "No se puido identificar un elemento de formulario válido. Intenta inspeccionar no HTML en seu lugar." }, "copyCustomFieldNameNotUnique": { - "message": "No unique identifier found." + "message": "Non se atopou ningún identificador único." }, "convertOrganizationEncryptionDesc": { - "message": "$ORGANIZATION$ is using SSO with a self-hosted key server. A master password is no longer required to log in for members of this organization.", + "message": "$ORGANIZATION$ emprega SSO con un servidor de claves propio. Os membros da organización xa non precisan dun contrasinal mestre para iniciar sesión.", "placeholders": { "organization": { "content": "$1", @@ -2833,31 +2742,31 @@ } }, "leaveOrganization": { - "message": "Leave organization" + "message": "Deixar a organización" }, "removeMasterPassword": { - "message": "Remove master password" + "message": "Eliminar contrasinal mestre" }, "removedMasterPassword": { - "message": "Master password removed" + "message": "Contrasinal mestre eliminado" }, "leaveOrganizationConfirmation": { - "message": "Are you sure you want to leave this organization?" + "message": "Estás seguro de que queres deixar esta organización?" }, "leftOrganization": { - "message": "You have left the organization." + "message": "Deixache a organización." }, "toggleCharacterCount": { - "message": "Toggle character count" + "message": "Alternar contador de caracteres" }, "sessionTimeout": { - "message": "Your session has timed out. Please go back and try logging in again." + "message": "O temporizador da sesión rematou. Por favor, volve iniciar a sesión." }, "exportingPersonalVaultTitle": { - "message": "Exporting individual vault" + "message": "Exportando caixa forte individual" }, "exportingIndividualVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", + "message": "Só se exportarán as entradas persoais da caixa forte asociada a $EMAIL$. As entradas da Caixa forte da organización non se incluirán. Tampouco se incluirá ningún arquivo anexo ás entradas.", "placeholders": { "email": { "content": "$1", @@ -2866,10 +2775,10 @@ } }, "exportingOrganizationVaultTitle": { - "message": "Exporting organization vault" + "message": "Exportar Caixa forte da organización" }, "exportingOrganizationVaultDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", + "message": "Só se exportará a Caixa forte asociada a $ORGANIZATION$. As entradas en Caixas fortes individuais ou doutras organizacións non se incluirán.", "placeholders": { "organization": { "content": "$1", @@ -2880,17 +2789,14 @@ "error": { "message": "Erro" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { - "message": "Generate username" + "message": "Xerar nome de usuario" }, "generateEmail": { - "message": "Generate email" + "message": "Xerar enderezo electrónico" }, "spinboxBoundariesHint": { - "message": "Value must be between $MIN$ and $MAX$.", + "message": "O valor debe estar entre $MIN$ e $MAX$.", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2904,7 +2810,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", + "message": "Usar $RECOMMENDED$ caracteres ou máis para xerar un contrasinal seguro.", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2914,7 +2820,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": "Usar $RECOMMENDED$ palabras ou máis para xerar unha frase de contrasinal segura.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2923,21 +2829,18 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { - "message": "Plus addressed email", + "message": "Correo con alias", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" }, "plusAddressedEmailDesc": { - "message": "Use your email provider's sub-addressing capabilities." + "message": "Usar as funcións de subdireccionamento do teu provedor de correo." }, "catchallEmail": { - "message": "Catch-all email" + "message": "Correo \"catch-all\"" }, "catchallEmailDesc": { - "message": "Use your domain's configured catch-all inbox." + "message": "Usar a caixa de entrada \"catch-all\" configurada para o teu dominio." }, "random": { "message": "Aleatorio" @@ -2948,31 +2851,25 @@ "websiteName": { "message": "Nome do sitio web" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { - "message": "Service" + "message": "Servizo" }, "forwardedEmail": { - "message": "Forwarded email alias" + "message": "Alias de correo redirixido" }, "forwardedEmailDesc": { - "message": "Generate an email alias with an external forwarding service." + "message": "Xerar un alias de correo cun servizo de redirecionado externo." }, "forwarderDomainName": { - "message": "Email domain", + "message": "Dominio de correo", "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { - "message": "Choose a domain that is supported by the selected service", + "message": "Seleccionar un dominio compatible co servizo seleccionado", "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { - "message": "$SERVICENAME$ error: $ERRORMESSAGE$", + "message": "Erro de $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", "placeholders": { "servicename": { @@ -2986,11 +2883,11 @@ } }, "forwarderGeneratedBy": { - "message": "Generated by Bitwarden.", + "message": "Xerado por Bitwarden.", "description": "Displayed with the address on the forwarding service's configuration screen." }, "forwarderGeneratedByWithWebsite": { - "message": "Website: $WEBSITE$. Generated by Bitwarden.", + "message": "Web: $WEBSITE$. Xerada por Bitwarden.", "description": "Displayed with the address on the forwarding service's configuration screen.", "placeholders": { "WEBSITE": { @@ -3000,7 +2897,7 @@ } }, "forwaderInvalidToken": { - "message": "Invalid $SERVICENAME$ API token", + "message": "Token da API de $SERVICENAME$ inválido", "description": "Displayed when the user's API token is empty or rejected by the forwarding service.", "placeholders": { "servicename": { @@ -3010,7 +2907,7 @@ } }, "forwaderInvalidTokenWithMessage": { - "message": "Invalid $SERVICENAME$ API token: $ERRORMESSAGE$", + "message": "Token da API de $SERVICENAME$ inválido: $ERRORMESSAGE$", "description": "Displayed when the user's API token is rejected by the forwarding service with an error message.", "placeholders": { "servicename": { @@ -3024,7 +2921,7 @@ } }, "forwarderNoAccountId": { - "message": "Unable to obtain $SERVICENAME$ masked email account ID.", + "message": "Non foi posible obter o ID de correo enmascarado de $SERVICENAME$.", "description": "Displayed when the forwarding service fails to return an account ID.", "placeholders": { "servicename": { @@ -3034,7 +2931,7 @@ } }, "forwarderNoDomain": { - "message": "Invalid $SERVICENAME$ domain.", + "message": "Dominio de $SERVICENAME$ inválido.", "description": "Displayed when the domain is empty or domain authorization failed at the forwarding service.", "placeholders": { "servicename": { @@ -3044,7 +2941,7 @@ } }, "forwarderNoUrl": { - "message": "Invalid $SERVICENAME$ url.", + "message": "Url de $SERVICENAME$ inválido.", "description": "Displayed when the url of the forwarding service wasn't supplied.", "placeholders": { "servicename": { @@ -3054,7 +2951,7 @@ } }, "forwarderUnknownError": { - "message": "Unknown $SERVICENAME$ error occurred.", + "message": "Ocorreu un erro de $SERVICENAME$ descoñecido.", "description": "Displayed when the forwarding service failed due to an unknown error.", "placeholders": { "servicename": { @@ -3064,7 +2961,7 @@ } }, "forwarderUnknownForwarder": { - "message": "Unknown forwarder: '$SERVICENAME$'.", + "message": "Redireccionador descoñecido: '$SERVICENAME$'.", "description": "Displayed when the forwarding service is not supported.", "placeholders": { "servicename": { @@ -3084,19 +2981,19 @@ "message": "Clave da API" }, "ssoKeyConnectorError": { - "message": "Key connector error: make sure key connector is available and working correctly." + "message": "Erro de conector de clave: asegúrate de que o conector de clave está dispoñible e funcionando correctamente." }, "premiumSubcriptionRequired": { - "message": "Premium subscription required" + "message": "Requírese plan Prémium" }, "organizationIsDisabled": { - "message": "Organization suspended." + "message": "Organización suspendida." }, "disabledOrganizationFilterError": { - "message": "Items in suspended Organizations cannot be accessed. Contact your Organization owner for assistance." + "message": "As entradas de organizacións suspendidas son inaccesibles. Contacta co propietario da organización para asistencia." }, "loggingInTo": { - "message": "Logging in to $DOMAIN$", + "message": "Iniciando sesión en $DOMAIN$", "placeholders": { "domain": { "content": "$1", @@ -3105,25 +3002,25 @@ } }, "settingsEdited": { - "message": "Settings have been edited" + "message": "Os axustes foron modificados" }, "environmentEditedClick": { - "message": "Click here" + "message": "Preme aquí" }, "environmentEditedReset": { - "message": "to reset to pre-configured settings" + "message": "para volver á configuración por defecto" }, "serverVersion": { - "message": "Server version" + "message": "Versión do Servidor" }, "selfHostedServer": { "message": "autoaloxado" }, "thirdParty": { - "message": "Third-party" + "message": "De terceiros" }, "thirdPartyServerMessage": { - "message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.", + "message": "Conectado a un servidor dun terceiro, $SERVERNAME$. Por favor verifica bugs empregando o servidor oficial, ou comunícallos ó servidor da terceira parte.", "placeholders": { "servername": { "content": "$1", @@ -3132,7 +3029,7 @@ } }, "lastSeenOn": { - "message": "last seen on: $DATE$", + "message": "Visto por última vez o: $DATE$", "placeholders": { "date": { "content": "$1", @@ -3141,82 +3038,82 @@ } }, "loginWithMasterPassword": { - "message": "Log in with master password" + "message": "Iniciar sesión co contrasinal mestre" }, "loggingInAs": { - "message": "Logging in as" + "message": "Iniciando sesión como" }, "notYou": { - "message": "Not you?" + "message": "Non es ti?" }, "newAroundHere": { - "message": "New around here?" + "message": "Novo por aquí?" }, "rememberEmail": { - "message": "Remember email" + "message": "Lembrar correo" }, "loginWithDevice": { - "message": "Log in with device" + "message": "Iniciar sesión cun dispositivo" }, "loginWithDeviceEnabledInfo": { - "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" + "message": "O inicio de sesión con dispositivos debe estar activado nos axustes da app de Bitwarden. Precisas doutro método?" }, "fingerprintPhraseHeader": { - "message": "Fingerprint phrase" + "message": "Frase de pegada dixital" }, "fingerprintMatchInfo": { - "message": "Please make sure your vault is unlocked and the Fingerprint phrase matches on the other device." + "message": "Por favor asegúrate de que a caixa forte está desbloqueada e a frase de pegada dixital coincide ca do outro dispositivo." }, "resendNotification": { - "message": "Resend notification" + "message": "Volver enviar notificación" }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "Ver todas as opcións de inicio de sesión" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Ver todas as opcións de inicio de sesión" }, "notificationSentDevice": { - "message": "A notification has been sent to your device." + "message": "Enviouse unha notificación ó teu dispositivo." }, "aNotificationWasSentToYourDevice": { - "message": "A notification was sent to your device" + "message": "Enviouse unha notificación ó teu dispositivo" }, "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { - "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + "message": "Por favor asegúrate de que a sesión está aberta e a frase de pegada dixital coincide ca do outro dispositivo" }, "youWillBeNotifiedOnceTheRequestIsApproved": { - "message": "You will be notified once the request is approved" + "message": "Serás notificado unha vez se aprobe a solicitude" }, "needAnotherOptionV1": { - "message": "Need another option?" + "message": "Precisas doutro método?" }, "loginInitiated": { - "message": "Login initiated" + "message": "Inicio de sesión comezado" }, "exposedMasterPassword": { - "message": "Exposed Master Password" + "message": "Contrasinal mestre filtrado" }, "exposedMasterPasswordDesc": { - "message": "Password found in a data breach. Use a unique password to protect your account. Are you sure you want to use an exposed password?" + "message": "Contrasinal atopado nunha filtración de datos. Emprega un contrasinal único para a túa conta. Seguro que queres utilizar un contrasinal filtrado?" }, "weakAndExposedMasterPassword": { - "message": "Weak and Exposed Master Password" + "message": "Contrasinal mestre filtrado e feble" }, "weakAndBreachedMasterPasswordDesc": { - "message": "Weak password identified and found in a data breach. Use a strong and unique password to protect your account. Are you sure you want to use this password?" + "message": "Contrasinal feble e atopado nunha filtración de datos. Emprega un contrasinal forte e único para a túa conta. Seguro que queres utilizar este contrasinal?" }, "checkForBreaches": { - "message": "Check known data breaches for this password" + "message": "Comprobar este contrasinal en filtracións de datos coñecidas" }, "important": { - "message": "Important:" + "message": "Importante:" }, "masterPasswordHint": { - "message": "Your master password cannot be recovered if you forget it!" + "message": "O contrasinal mestre non pode ser recuperado se o esqueces!" }, "characterMinimum": { - "message": "$LENGTH$ character minimum", + "message": "Mínimo $LENGTH$ caracteres", "placeholders": { "length": { "content": "$1", @@ -3225,13 +3122,13 @@ } }, "autofillPageLoadPolicyActivated": { - "message": "Your organization policies have turned on autofill on page load." + "message": "As directivas da organización activaron o autoenchido ó cargar a páxina." }, "howToAutofill": { - "message": "How to autofill" + "message": "Como autoencher" }, "autofillSelectInfoWithCommand": { - "message": "Select an item from this screen, use the shortcut $COMMAND$, or explore other options in settings.", + "message": "Selecciona unha entrada desta pantalla, emprega o atallo $COMMAND$, ou explora outras opcións en axustes.", "placeholders": { "command": { "content": "$1", @@ -3240,31 +3137,31 @@ } }, "autofillSelectInfoWithoutCommand": { - "message": "Select an item from this screen, or explore other options in settings." + "message": "Selecciona unha entrada desta pantalla ou explora outras opcións en axustes." }, "gotIt": { - "message": "Got it" + "message": "Entendido" }, "autofillSettings": { - "message": "Autofill settings" + "message": "Axustes de autoenchido" }, "autofillKeyboardShortcutSectionTitle": { - "message": "Autofill shortcut" + "message": "Atallo de autoenchido" }, "autofillKeyboardShortcutUpdateLabel": { - "message": "Change shortcut" + "message": "Mudar atallo" }, "autofillKeyboardManagerShortcutsLabel": { - "message": "Manage shortcuts" + "message": "Xestionar atallos" }, "autofillShortcut": { - "message": "Autofill keyboard shortcut" + "message": "Atallo de teclado de autoenchido" }, "autofillLoginShortcutNotSet": { - "message": "The autofill login shortcut is not set. Change this in the browser's settings." + "message": "O atallo de autoenchido de credenciais non está configurado. Cámbiao nos axustes do navegador." }, "autofillLoginShortcutText": { - "message": "The autofill login shortcut is $COMMAND$. Manage all shortcuts in the browser's settings.", + "message": "O atallo de autoenchido de credenciais é $COMMAND$. Xestiona os atallos nos axustes do navegador.", "placeholders": { "command": { "content": "$1", @@ -3273,7 +3170,7 @@ } }, "autofillShortcutTextSafari": { - "message": "Default autofill shortcut: $COMMAND$.", + "message": "Atallo de autoenchido por defecto: $COMMAND$.", "placeholders": { "command": { "content": "$1", @@ -3282,65 +3179,65 @@ } }, "opensInANewWindow": { - "message": "Opens in a new window" + "message": "Abrir nunha ventá nova" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Lembrar este dispositivo para futuros inicios de sesión imperceptibles" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "Aprobación de dispositivo requirida. Selecciona unha das seguintes opcións:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Aprobación de dispositivo requirida" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Selecciona unha das seguintes opcións de aprobado" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "Lembrar este dispositivo" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "Desmarcar se se emprega un dispositivo público" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "Aprobar dende o teu outro dispositivo" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "Solicitar aprobación do administrador" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "Aprobar con contrasinal mestre" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "Identificador SSO da organización requirido." }, "creatingAccountOn": { - "message": "Creating account on" + "message": "Creando conta en" }, "checkYourEmail": { - "message": "Check your email" + "message": "Revisa o teu correo" }, "followTheLinkInTheEmailSentTo": { - "message": "Follow the link in the email sent to" + "message": "Segue o enlace no correo enviado a" }, "andContinueCreatingYourAccount": { - "message": "and continue creating your account." + "message": "e continúa ca creación da conta." }, "noEmail": { - "message": "No email?" + "message": "Sen correo?" }, "goBack": { - "message": "Go back" + "message": "Volver atrás" }, "toEditYourEmailAddress": { - "message": "to edit your email address." + "message": "para modificar o teu enderezo electrónico." }, "eu": { "message": "UE", "description": "European Union" }, "accessDenied": { - "message": "Access denied. You do not have permission to view this page." + "message": "Acceso denegado. Non tes permiso para ver esta páxina." }, "general": { "message": "Xeral" @@ -3349,51 +3246,51 @@ "message": "Amosar" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "Conta creada con éxito!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "Aprobación do administrador solicitada" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "A solicitude foi enviada ó teu administrador." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "Serás notificado cando se aprobe." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "Problemas ao iniciar sesión?" }, "loginApproved": { - "message": "Login approved" + "message": "Inicio de sesión aprobado" }, "userEmailMissing": { - "message": "User email missing" + "message": "Falta o correo electrónico" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Correo electrónico usuario activo non atopado. Cerrando a sesión." }, "deviceTrusted": { - "message": "Device trusted" + "message": "Dispositivo de confianza" }, "sendsNoItemsTitle": { - "message": "No active Sends", + "message": "Sen Sends activos", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", + "message": "Usar send para compartir información cifrada con quen queiras.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "inputRequired": { - "message": "Input is required." + "message": "Requírese algunha entrada." }, "required": { - "message": "required" + "message": "requirido" }, "search": { - "message": "Search" + "message": "Busca" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "A entrada debe ser de polo menos $COUNT$ caracteres.", "placeholders": { "count": { "content": "$1", @@ -3402,7 +3299,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "A entrada debe ser de máximo $COUNT$ caracteres.", "placeholders": { "count": { "content": "$1", @@ -3411,7 +3308,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "Os seguintes caracteres non están permitidos: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -3420,7 +3317,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "O valor de entrada debe ser de mínimo $MIN$.", "placeholders": { "min": { "content": "$1", @@ -3429,7 +3326,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "O valor de entrada nun debe exceder $MAX$.", "placeholders": { "max": { "content": "$1", @@ -3438,17 +3335,17 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "1 ou máis correos son inválidos" }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "A entrada non debe conter só espazos en branco.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "A entrada non é un enderezo de correo." }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "$COUNT$ campo(s) máis arriba precisan revisión.", "placeholders": { "count": { "content": "$1", @@ -3457,10 +3354,10 @@ } }, "singleFieldNeedsAttention": { - "message": "1 field needs your attention." + "message": "Un campo precisa revisión." }, "multipleFieldsNeedAttention": { - "message": "$COUNT$ fields need your attention.", + "message": "$COUNT$ campos precisan revisión.", "placeholders": { "count": { "content": "$1", @@ -3469,22 +3366,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- Seleccionar --" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- Escribir para filtrar --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "Cargando opcións..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "Non se atoparon entradas" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "Quitar seleccións" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ $QUANTITY$ máis", "placeholders": { "quantity": { "content": "$1", @@ -3493,168 +3390,176 @@ } }, "submenu": { - "message": "Submenu" + "message": "Submenú" }, "toggleCollapse": { - "message": "Toggle collapse", + "message": "Colapsar/Expandir", "description": "Toggling an expand/collapse state." }, "filelessImport": { - "message": "Import your data to Bitwarden?", + "message": "Importar os teus datos a Bitwarden?", "description": "Default notification title for triggering a fileless import." }, "lpFilelessImport": { - "message": "Protect your LastPass data and import to Bitwarden?", + "message": "Protexer os teus datos de LastPass e importar a Bitwarden?", "description": "LastPass specific notification title for triggering a fileless import." }, "lpCancelFilelessImport": { - "message": "Save as unencrypted file", + "message": "Gardar como arquivo sen cifrar", "description": "LastPass specific notification button text for cancelling a fileless import." }, "startFilelessImport": { - "message": "Import to Bitwarden", + "message": "Importar a Bitwarden", "description": "Notification button text for starting a fileless import." }, "importing": { - "message": "Importing...", + "message": "Importando...", "description": "Notification message for when an import is in progress." }, "dataSuccessfullyImported": { - "message": "Data successfully imported!", + "message": "Datos importados con éxito!", "description": "Notification message for when an import has completed successfully." }, "dataImportFailed": { - "message": "Error importing. Check console for details.", + "message": "Erro importando. Comproba a consola para máis detalle.", "description": "Notification message for when an import has failed." }, "importNetworkError": { - "message": "Network error encountered during import.", + "message": "Aconteceu un erro de rede durante a importación.", "description": "Notification message for when an import has failed due to a network error." }, "aliasDomain": { - "message": "Alias domain" + "message": "Alias do dominio" }, "passwordRepromptDisabledAutofillOnPageLoad": { - "message": "Items with master password re-prompt cannot be autofilled on page load. Autofill on page load turned off.", + "message": "As entradas que requiran volver a inserir o contrasinal mestre non poden ser autoenchidas ó cargar a páxina.", "description": "Toast message for describing that master password re-prompt cannot be autofilled on page load." }, "autofillOnPageLoadSetToDefault": { - "message": "Autofill on page load set to use default setting.", + "message": "Axuste de autoenchido ó cargar a páxina por defecto.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "turnOffMasterPasswordPromptToEditField": { - "message": "Turn off master password re-prompt to edit this field", + "message": "Desactiva volver a requirir o contrasinal mestre para modificar este campo", "description": "Message appearing below the autofill on load message when master password reprompt is set for a vault item." }, "toggleSideNavigation": { - "message": "Toggle side navigation" + "message": "Activar/desactivar navegación lateral" }, "skipToContent": { - "message": "Skip to content" + "message": "Ir ó contido" }, "bitwardenOverlayButton": { - "message": "Bitwarden autofill menu button", + "message": "Botón do menú de autoenchido", "description": "Page title for the iframe containing the overlay button" }, "toggleBitwardenVaultOverlay": { - "message": "Toggle Bitwarden autofill menu", + "message": "Des/Activar o menú de autoenchido", "description": "Screen reader and tool tip label for the overlay button" }, "bitwardenVault": { - "message": "Bitwarden autofill menu", + "message": "Menú de autoenchido", "description": "Page title in overlay" }, "unlockYourAccountToViewMatchingLogins": { - "message": "Unlock your account to view matching logins", + "message": "Abre a caixa forte para ver as credenciais coincidentes", "description": "Text to display in overlay when the account is locked." }, "unlockYourAccountToViewAutofillSuggestions": { - "message": "Unlock your account to view autofill suggestions", + "message": "Abre a caixa forte para ver as suxestións de autoenchido", "description": "Text to display in overlay when the account is locked." }, "unlockAccount": { - "message": "Unlock account", + "message": "Abrir caixa forte", "description": "Button text to display in overlay when the account is locked." }, "unlockAccountAria": { - "message": "Unlock your account, opens in a new window", + "message": "Abrir caixa forte nunha nova ventá", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Código de verificación basado en tempo de un uso", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Tempo para que o TOTP actual venza", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { - "message": "Fill credentials for", + "message": "Encher credenciais para", "description": "Screen reader text for when overlay item is in focused" }, "partialUsername": { - "message": "Partial username", + "message": "Nome de usuario parcial", "description": "Screen reader text for when a login item is focused where a partial username is displayed. SR will announce this phrase before reading the text of the partial username" }, "noItemsToShow": { - "message": "No items to show", + "message": "Sen entradas que amosar", "description": "Text to show in overlay if there are no matching items" }, "newItem": { - "message": "New item", + "message": "Nova entrada", "description": "Button text to display in overlay when there are no matching items" }, "addNewVaultItem": { - "message": "Add new vault item", + "message": "Engadir unha nova entrada", "description": "Screen reader text (aria-label) for new item button in overlay" }, "newLogin": { - "message": "New login", + "message": "Nova credencial", "description": "Button text to display within inline menu when there are no matching items on a login field" }, "addNewLoginItemAria": { - "message": "Add new vault login item, opens in a new window", + "message": "Engadir unha nova credencial nunha nova ventá", "description": "Screen reader text (aria-label) for new login button within inline menu" }, "newCard": { - "message": "New card", + "message": "Nova tarxeta", "description": "Button text to display within inline menu when there are no matching items on a credit card field" }, "addNewCardItemAria": { - "message": "Add new vault card item, opens in a new window", + "message": "Engadir unha nova tarxeta nunha nova ventá", "description": "Screen reader text (aria-label) for new card button within inline menu" }, "newIdentity": { - "message": "New identity", + "message": "Nova identidade", "description": "Button text to display within inline menu when there are no matching items on an identity field" }, "addNewIdentityItemAria": { - "message": "Add new vault identity item, opens in a new window", + "message": "Engadir unha nova identidade nunha nova ventá", "description": "Screen reader text (aria-label) for new identity button within inline menu" }, "bitwardenOverlayMenuAvailable": { - "message": "Bitwarden autofill menu available. Press the down arrow key to select.", + "message": "Menú de autoenchido dispoñible. Pulsa a tecla de frecha abaixo para seleccionar.", "description": "Screen reader text for announcing when the overlay opens on the page" }, "turnOn": { - "message": "Turn on" + "message": "Activar" }, "ignore": { - "message": "Ignore" + "message": "Ignorar" }, "importData": { - "message": "Import data", + "message": "Importar datos", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" }, "importError": { - "message": "Import error" + "message": "Erro ó importar" }, "importErrorDesc": { - "message": "There was a problem with the data you tried to import. Please resolve the errors listed below in your source file and try again." + "message": "Houbo un problema cos datos que intentas importar. Por favor, corrixe os erros listados a continuación e volve intentalo." }, "resolveTheErrorsBelowAndTryAgain": { - "message": "Resolve the errors below and try again." + "message": "Corrixe os erros listados a continuación e volve intentalo." }, "description": { - "message": "Description" + "message": "Descrición" }, "importSuccess": { - "message": "Data successfully imported" + "message": "Datos importados con éxito" }, "importSuccessNumberOfItems": { - "message": "A total of $AMOUNT$ items were imported.", + "message": "Importáronse $AMOUNT$ entradas.", "placeholders": { "amount": { "content": "$1", @@ -3663,46 +3568,46 @@ } }, "tryAgain": { - "message": "Try again" + "message": "Tentar de novo" }, "verificationRequiredForActionSetPinToContinue": { - "message": "Verification required for this action. Set a PIN to continue." + "message": "Verificación requirida para esta acción. Crea un PIN para continuar." }, "setPin": { - "message": "Set PIN" + "message": "Crear PIN" }, "verifyWithBiometrics": { - "message": "Verify with biometrics" + "message": "Verificar con biometría" }, "awaitingConfirmation": { - "message": "Awaiting confirmation" + "message": "Agardando confirmación" }, "couldNotCompleteBiometrics": { - "message": "Could not complete biometrics." + "message": "Non se puido completar a biometría." }, "needADifferentMethod": { - "message": "Need a different method?" + "message": "Precisas dun método alternativo?" }, "useMasterPassword": { - "message": "Use master password" + "message": "Usar contrasinal mestre" }, "usePin": { - "message": "Use PIN" + "message": "Usar PIN" }, "useBiometrics": { - "message": "Use biometrics" + "message": "Usar biometría" }, "enterVerificationCodeSentToEmail": { - "message": "Enter the verification code that was sent to your email." + "message": "Insire o código de verificación enviado ó teu correo." }, "resendCode": { - "message": "Resend code" + "message": "Volver enviar o código" }, "total": { "message": "Total" }, "importWarning": { - "message": "You are importing data to $ORGANIZATION$. Your data may be shared with members of this organization. Do you want to proceed?", + "message": "Estás importando datos a $ORGANIZATION$. Estes datos poden ser compartidos con membros da organización. Queres continuar?", "placeholders": { "organization": { "content": "$1", @@ -3711,49 +3616,49 @@ } }, "duoHealthCheckResultsInNullAuthUrlError": { - "message": "Error connecting with the Duo service. Use a different two-step login method or contact Duo for assistance." + "message": "Erro conectando co servizo de Duo. Usa un método de verificación en 2 pasos alternativo ou contacta con Duo." }, "launchDuoAndFollowStepsToFinishLoggingIn": { - "message": "Launch Duo and follow the steps to finish logging in." + "message": "Executa Duo e segue os pasos para finalizar o inicio de sesión." }, "duoRequiredForAccount": { - "message": "Duo two-step login is required for your account." + "message": "A túa conta require a verificación en 2 pasos de Duo." }, "popoutTheExtensionToCompleteLogin": { - "message": "Popout the extension to complete login." + "message": "Saca a extensión nunha ventá para continuar." }, "popoutExtension": { - "message": "Popout extension" + "message": "Sacar a extensión" }, "launchDuo": { - "message": "Launch Duo" + "message": "Executar Duo" }, "importFormatError": { - "message": "Data is not formatted correctly. Please check your import file and try again." + "message": "Datos non estruturados correctamente. Por favor comproba o arquivo e téntao de novo." }, "importNothingError": { - "message": "Nothing was imported." + "message": "Nada foi importado." }, "importEncKeyError": { - "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." + "message": "Erro descifrando o arquivo exportado. A túa clave de cifrado non coincide ca utilizada ó exportar os datos." }, "invalidFilePassword": { - "message": "Invalid file password, please use the password you entered when you created the export file." + "message": "Contrasinal do arquivo inválido. Emprega o contrasinal que creaches cando se exportou o arquivo." }, "destination": { - "message": "Destination" + "message": "Destino" }, "learnAboutImportOptions": { - "message": "Learn about your import options" + "message": "Aprende acerca das opcións de importado" }, "selectImportFolder": { - "message": "Select a folder" + "message": "Seleccionar un cartafol" }, "selectImportCollection": { - "message": "Select a collection" + "message": "Seleccionar unha colección" }, "importTargetHint": { - "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "message": "Selecciona esta opción se queres que o contido importado se mova a $DESTINATION$", "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", "placeholders": { "destination": { @@ -3763,25 +3668,25 @@ } }, "importUnassignedItemsError": { - "message": "File contains unassigned items." + "message": "O arquivo contén entradas sen asignar." }, "selectFormat": { - "message": "Select the format of the import file" + "message": "Selecciona o formato do arquivo de importado" }, "selectImportFile": { - "message": "Select the import file" + "message": "Selecciona o arquivo de importado" }, "chooseFile": { - "message": "Choose File" + "message": "Seleccionar arquivo" }, "noFileChosen": { - "message": "No file chosen" + "message": "Ningún arquivo escollido" }, "orCopyPasteFileContents": { - "message": "or copy/paste the import file contents" + "message": "ou copia/pega o contido do arquivo de importado" }, "instructionsFor": { - "message": "$NAME$ Instructions", + "message": "Instrucións para $NAME$", "description": "The title for the import tool instructions.", "placeholders": { "name": { @@ -3791,200 +3696,200 @@ } }, "confirmVaultImport": { - "message": "Confirm vault import" + "message": "Confirmar o importado da caixa forte" }, "confirmVaultImportDesc": { - "message": "This file is password-protected. Please enter the file password to import data." + "message": "Este arquivo está protexido. Insire o contrasinal para importar os datos." }, "confirmFilePassword": { - "message": "Confirm file password" + "message": "Repetir contrasinal do arquivo" }, "exportSuccess": { - "message": "Vault data exported" + "message": "Caixa forte exportada" }, "typePasskey": { - "message": "Passkey" + "message": "Clave de acceso" }, "accessing": { - "message": "Accessing" + "message": "Accedendo" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Sesión iniciada!" }, "passkeyNotCopied": { - "message": "Passkey will not be copied" + "message": "A Clave de acceso non se vai copiar" }, "passkeyNotCopiedAlert": { - "message": "The passkey will not be copied to the cloned item. Do you want to continue cloning this item?" + "message": "A Clave de acceso non se incluirá na entrada clonada. Queres continuar co duplicado?" }, "passkeyFeatureIsNotImplementedForAccountsWithoutMasterPassword": { - "message": "Verification required by the initiating site. This feature is not yet implemented for accounts without master password." + "message": "Verificación requirida polo sitio inicial. Esta función aínda non está implementada para contas sen contrasinal mestre." }, "logInWithPasskeyQuestion": { - "message": "Log in with passkey?" + "message": "Iniciar sesión con Clave de acceso?" }, "passkeyAlreadyExists": { - "message": "A passkey already exists for this application." + "message": "Xa existe unha Clave de acceso para esta aplicación." }, "noPasskeysFoundForThisApplication": { - "message": "No passkeys found for this application." + "message": "Non se atoparon Claves de acceso para esta aplicación." }, "noMatchingPasskeyLogin": { - "message": "You do not have a matching login for this site." + "message": "Non tes Claves de acceso coincidentes para este sitio." }, "noMatchingLoginsForSite": { - "message": "No matching logins for this site" + "message": "Non tes credenciais coincidentes para este sitio" }, "searchSavePasskeyNewLogin": { - "message": "Search or save passkey as new login" + "message": "Busca ou garda a Clave de acceso como nova credencial" }, "confirm": { - "message": "Confirm" + "message": "Confirmar" }, "savePasskey": { - "message": "Save passkey" + "message": "Gardar Clave de acceso" }, "savePasskeyNewLogin": { - "message": "Save passkey as new login" + "message": "Gardar Clave de acceso como nova credencial" }, "chooseCipherForPasskeySave": { - "message": "Choose a login to save this passkey to" + "message": "Escolle unha credencial na que gardar esta Clave de acceso" }, "chooseCipherForPasskeyAuth": { - "message": "Choose a passkey to log in with" + "message": "Escolle unha Clave de acceso ca que iniciar sesión" }, "passkeyItem": { - "message": "Passkey Item" + "message": "Entrada de Clave de acceso" }, "overwritePasskey": { - "message": "Overwrite passkey?" + "message": "Sobrescribir Clave de acceso?" }, "overwritePasskeyAlert": { - "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + "message": "Esta entrada xa contén unha Clave de acceso. Seguro que queres sobreescribir a existente?" }, "featureNotSupported": { - "message": "Feature not yet supported" + "message": "Función aínda non implementada" }, "yourPasskeyIsLocked": { - "message": "Authentication required to use passkey. Verify your identity to continue." + "message": "A autenticación require unha Clave de acceso. Verifica a túa identidade para continuar." }, "multifactorAuthenticationCancelled": { - "message": "Multifactor authentication cancelled" + "message": "Autenticación multifactor cancelada" }, "noLastPassDataFound": { - "message": "No LastPass data found" + "message": "Non se atoparon datos de LastPass" }, "incorrectUsernameOrPassword": { - "message": "Incorrect username or password" + "message": "Nome de usuario ou contrasinal incorrectos" }, "incorrectPassword": { - "message": "Incorrect password" + "message": "Contrasinal incorrecto" }, "incorrectCode": { - "message": "Incorrect code" + "message": "Código incorrecto" }, "incorrectPin": { - "message": "Incorrect PIN" + "message": "PIN incorrecto" }, "multifactorAuthenticationFailed": { - "message": "Multifactor authentication failed" + "message": "Autenticación multifactor fallida" }, "includeSharedFolders": { - "message": "Include shared folders" + "message": "Incluír cartafois compartidos" }, "lastPassEmail": { - "message": "LastPass Email" + "message": "Correo de LastPass" }, "importingYourAccount": { - "message": "Importing your account..." + "message": "Importar a túa conta..." }, "lastPassMFARequired": { - "message": "LastPass multifactor authentication required" + "message": "Autenticación multifactor de LastPass requirida" }, "lastPassMFADesc": { - "message": "Enter your one-time passcode from your authentication app" + "message": "Insire o código de un uso da túa app de autenticación" }, "lastPassOOBDesc": { - "message": "Approve the login request in your authentication app or enter a one-time passcode." + "message": "Aproba a petición de inicio de sesión na túa app de autenticación ou insire o código dun uso." }, "passcode": { - "message": "Passcode" + "message": "Código de acceso" }, "lastPassMasterPassword": { - "message": "LastPass master password" + "message": "Contrasinal mestre de LastPass" }, "lastPassAuthRequired": { - "message": "LastPass authentication required" + "message": "Autenticación de LastPass requirida" }, "awaitingSSO": { - "message": "Awaiting SSO authentication" + "message": "Agardando pola autenticación SSO" }, "awaitingSSODesc": { - "message": "Please continue to log in using your company credentials." + "message": "Por favor, continúa empregando as credenciais da túa compañía." }, "seeDetailedInstructions": { - "message": "See detailed instructions on our help site at", + "message": "Ver instrucións detalladas no noso sitio de axuda en", "description": "This is followed a by a hyperlink to the help website." }, "importDirectlyFromLastPass": { - "message": "Import directly from LastPass" + "message": "Importar directamente de LastPass" }, "importFromCSV": { - "message": "Import from CSV" + "message": "Importar dun CSV" }, "lastPassTryAgainCheckEmail": { - "message": "Try again or look for an email from LastPass to verify it's you." + "message": "Volver intentar ou buscar un correo de LastPass para verificar que es ti." }, "collection": { - "message": "Collection" + "message": "Colección" }, "lastPassYubikeyDesc": { - "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + "message": "Conecta a YubiKey asociada á túa conta de LastPass, e preme o seu botón." }, "switchAccount": { - "message": "Switch account" + "message": "Cambiar de conta" }, "switchAccounts": { - "message": "Switch accounts" + "message": "Cambiar as contas" }, "switchToAccount": { - "message": "Switch to account" + "message": "Cambiar a conta" }, "activeAccount": { - "message": "Active account" + "message": "Activar conta" }, "availableAccounts": { - "message": "Available accounts" + "message": "Contas dispoñibles" }, "accountLimitReached": { - "message": "Account limit reached. Log out of an account to add another." + "message": "Límite de contas alcanzado. Cerra sesión nunha delas para engadir outra." }, "active": { - "message": "active" + "message": "activo/a" }, "locked": { - "message": "locked" + "message": "bloqueado" }, "unlocked": { - "message": "unlocked" + "message": "desbloqueado" }, "server": { - "message": "server" + "message": "servidor" }, "hostedAt": { - "message": "hosted at" + "message": "aloxado en" }, "useDeviceOrHardwareKey": { - "message": "Use your device or hardware key" + "message": "Usa o teu dispositivo ou chave de hardware" }, "justOnce": { - "message": "Just once" + "message": "Só unha vez" }, "alwaysForThisSite": { - "message": "Always for this site" + "message": "Sempre para este sitio" }, "domainAddedToExcludedDomains": { - "message": "$DOMAIN$ added to excluded domains.", + "message": "$DOMAIN$ engadido ós dominios excluídos.", "placeholders": { "domain": { "content": "$1", @@ -3993,103 +3898,103 @@ } }, "commonImportFormats": { - "message": "Common formats", + "message": "Formatos comúns", "description": "Label indicating the most common import formats" }, "confirmContinueToBrowserSettingsTitle": { - "message": "Continue to browser settings?", + "message": "Ir ós axustes do navegador?", "description": "Title for dialog which asks if the user wants to proceed to a relevant browser settings page" }, "confirmContinueToHelpCenter": { - "message": "Continue to Help Center?", + "message": "Ir ó Centro de Axuda?", "description": "Title for dialog which asks if the user wants to proceed to a relevant Help Center page" }, "confirmContinueToHelpCenterPasswordManagementContent": { - "message": "Change your browser's autofill and password management settings.", + "message": "Cambiar os axustes de autoenchido e xestión de contrasinais do navegador.", "description": "Body content for dialog which asks if the user wants to proceed to the Help Center's page about browser password management settings" }, "confirmContinueToHelpCenterKeyboardShortcutsContent": { - "message": "You can view and set extension shortcuts in your browser's settings.", + "message": "Podes ver e crear atallos da extensión nos axustes do teu navegador.", "description": "Body content for dialog which asks if the user wants to proceed to the Help Center's page about browser keyboard shortcut settings" }, "confirmContinueToBrowserPasswordManagementSettingsContent": { - "message": "Change your browser's autofill and password management settings.", + "message": "Cambiar os axustes de autoenchido e xestión de contrasinais do navegador.", "description": "Body content for dialog which asks if the user wants to proceed to the browser's password management settings page" }, "confirmContinueToBrowserKeyboardShortcutSettingsContent": { - "message": "You can view and set extension shortcuts in your browser's settings.", + "message": "Podes ver e crear atallos da extensión nos axustes do teu navegador.", "description": "Body content for dialog which asks if the user wants to proceed to the browser's keyboard shortcut settings page" }, "overrideDefaultBrowserAutofillTitle": { - "message": "Make Bitwarden your default password manager?", + "message": "Facer de Bitwarden o teu xestor de contrasinais por defecto?", "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" }, "overrideDefaultBrowserAutofillDescription": { - "message": "Ignoring this option may cause conflicts between Bitwarden autofill suggestions and your browser's.", + "message": "Ignorar esta opción pode causar conflitos entre Bitwarden e o xestor de contrasinais do teu navegador.", "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" }, "overrideDefaultBrowserAutoFillSettings": { - "message": "Make Bitwarden your default password manager", + "message": "Facer de Bitwarden o teu xestor de contrasinais por defecto", "description": "Label for the setting that allows overriding the default browser autofill settings" }, "privacyPermissionAdditionNotGrantedTitle": { - "message": "Unable to set Bitwarden as the default password manager", + "message": "Non se puido facer de Bitwarden o xestor de contrasinais por defecto", "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" }, "privacyPermissionAdditionNotGrantedDescription": { - "message": "You must grant browser privacy permissions to Bitwarden to set it as the default password manager.", + "message": "Debes conceder ó navegador permisos de privacidade sobre Bitwarden para facelo o xestor de contrasinais por defecto.", "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" }, "makeDefault": { - "message": "Make default", + "message": "Facer o predefinido", "description": "Button text for the setting that allows overriding the default browser autofill settings" }, "saveCipherAttemptSuccess": { - "message": "Credentials saved successfully!", + "message": "Credenciais gardadas con éxito!", "description": "Notification message for when saving credentials has succeeded." }, "passwordSaved": { - "message": "Password saved!", + "message": "Contrasinal gardado!", "description": "Notification message for when saving credentials has succeeded." }, "updateCipherAttemptSuccess": { - "message": "Credentials updated successfully!", + "message": "Credenciais actualizadas con éxito!", "description": "Notification message for when updating credentials has succeeded." }, "passwordUpdated": { - "message": "Password updated!", + "message": "Contrasinal actualizado!", "description": "Notification message for when updating credentials has succeeded." }, "saveCipherAttemptFailed": { - "message": "Error saving credentials. Check console for details.", + "message": "Erro ó gardar as credenciais. Comproba a consola para máis detalle.", "description": "Notification message for when saving credentials has failed." }, "success": { - "message": "Success" + "message": "Éxito" }, "removePasskey": { - "message": "Remove passkey" + "message": "Eliminar Clave de acceso" }, "passkeyRemoved": { - "message": "Passkey removed" + "message": "Clave de acceso eliminada" }, "autofillSuggestions": { - "message": "Autofill suggestions" + "message": "Suxestións de autoenchido" }, "autofillSuggestionsTip": { - "message": "Save a login item for this site to autofill" + "message": "Gardar unha credencial como suxestión para este sitio" }, "yourVaultIsEmpty": { - "message": "Your vault is empty" + "message": "A caixa forte está baleira" }, "noItemsMatchSearch": { - "message": "No items match your search" + "message": "Non hai entradas que coincidan ca túa busca" }, "clearFiltersOrTryAnother": { - "message": "Clear filters or try another search term" + "message": "Quitar filtros e tentar outro termo de busca" }, "copyInfoTitle": { - "message": "Copy info - $ITEMNAME$", + "message": "Copiar información - $ITEMNAME$", "description": "Title for a button that opens a menu with options to copy information from an item.", "placeholders": { "itemname": { @@ -4099,7 +4004,7 @@ } }, "copyNoteTitle": { - "message": "Copy Note - $ITEMNAME$", + "message": "Copiar nota - $ITEMNAME$", "description": "Title for a button copies a note to the clipboard.", "placeholders": { "itemname": { @@ -4109,7 +4014,7 @@ } }, "moreOptionsLabel": { - "message": "More options, $ITEMNAME$", + "message": "Máis opcións, $ITEMNAME$", "description": "Aria label for a button that opens a menu with more options for an item.", "placeholders": { "itemname": { @@ -4119,7 +4024,7 @@ } }, "moreOptionsTitle": { - "message": "More options - $ITEMNAME$", + "message": "Máis opcións - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", "placeholders": { "itemname": { @@ -4129,7 +4034,7 @@ } }, "viewItemTitle": { - "message": "View item - $ITEMNAME$", + "message": "Ver entrada - $ITEMNAME$", "description": "Title for a link that opens a view for an item.", "placeholders": { "itemname": { @@ -4139,7 +4044,7 @@ } }, "autofillTitle": { - "message": "Autofill - $ITEMNAME$", + "message": "Autoenchido - $ITEMNAME$", "description": "Title for a button that autofills a login item.", "placeholders": { "itemname": { @@ -4149,40 +4054,40 @@ } }, "noValuesToCopy": { - "message": "No values to copy" + "message": "Non hai valores que copiar" }, "assignToCollections": { - "message": "Assign to collections" + "message": "Vincular con coleccións" }, "copyEmail": { - "message": "Copy email" + "message": "Copiar correo" }, "copyPhone": { - "message": "Copy phone" + "message": "Copiar teléfono" }, "copyAddress": { - "message": "Copy address" + "message": "Copiar enderezo" }, "adminConsole": { - "message": "Admin Console" + "message": "Consola do administrador" }, "accountSecurity": { - "message": "Account security" + "message": "Seguridade da conta" }, "notifications": { - "message": "Notifications" + "message": "Notificacións" }, "appearance": { - "message": "Appearance" + "message": "Aparencia" }, "errorAssigningTargetCollection": { - "message": "Error assigning target collection." + "message": "Erro ó vincular esta colección." }, "errorAssigningTargetFolder": { - "message": "Error assigning target folder." + "message": "Erro ó vincular este cartafol." }, "viewItemsIn": { - "message": "View items in $NAME$", + "message": "Ver entradas en $NAME$", "description": "Button to view the contents of a folder or collection", "placeholders": { "name": { @@ -4192,7 +4097,7 @@ } }, "backTo": { - "message": "Back to $NAME$", + "message": "Volver a $NAME$", "description": "Navigate back to a previous folder or collection", "placeholders": { "name": { @@ -4202,10 +4107,10 @@ } }, "new": { - "message": "New" + "message": "Novo" }, "removeItem": { - "message": "Remove $NAME$", + "message": "Eliminar $NAME$", "description": "Remove a selected option, such as a folder or collection", "placeholders": { "name": { @@ -4215,16 +4120,16 @@ } }, "itemsWithNoFolder": { - "message": "Items with no folder" + "message": "Entradas sen carpeta" }, "itemDetails": { - "message": "Item details" + "message": "Detalles do entrada" }, "itemName": { - "message": "Item name" + "message": "Nome da entrada" }, "cannotRemoveViewOnlyCollections": { - "message": "You cannot remove collections with View only permissions: $COLLECTIONS$", + "message": "Non podes eliminar coleccións con permisos de Só lectura: $COLLECTIONS$", "placeholders": { "collections": { "content": "$1", @@ -4233,47 +4138,47 @@ } }, "organizationIsDeactivated": { - "message": "Organization is deactivated" + "message": "A organización está desactivada" }, "owner": { - "message": "Owner" + "message": "Propietario" }, "selfOwnershipLabel": { - "message": "You", + "message": "Ti", "description": "Used as a label to indicate that the user is the owner of an item." }, "contactYourOrgAdmin": { - "message": "Items in deactivated organizations cannot be accessed. Contact your organization owner for assistance." + "message": "As entradas en organizacións suspendidas son inaccesibles. Contacta co propietario da organización para asistencia." }, "additionalInformation": { - "message": "Additional information" + "message": "Información adicional" }, "itemHistory": { - "message": "Item history" + "message": "Historial da entrada" }, "lastEdited": { - "message": "Last edited" + "message": "Modificado o" }, "ownerYou": { - "message": "Owner: You" + "message": "Propietario: Ti" }, "linked": { - "message": "Linked" + "message": "Vinculado" }, "copySuccessful": { - "message": "Copy Successful" + "message": "Copiado realizado" }, "upload": { - "message": "Upload" + "message": "Subir" }, "addAttachment": { - "message": "Add attachment" + "message": "Anexar arquivo" }, "maxFileSizeSansPunctuation": { - "message": "Maximum file size is 500 MB" + "message": "O tamaño máximo é de 500 MB" }, "deleteAttachmentName": { - "message": "Delete attachment $NAME$", + "message": "Eliminar anexos $NAME$", "placeholders": { "name": { "content": "$1", @@ -4282,7 +4187,7 @@ } }, "downloadAttachmentName": { - "message": "Download $NAME$", + "message": "Descargar $NAME$", "placeholders": { "name": { "content": "$1", @@ -4291,25 +4196,25 @@ } }, "permanentlyDeleteAttachmentConfirmation": { - "message": "Are you sure you want to permanently delete this attachment?" + "message": "Estás seguro de que queres eliminar permanentemente este anexo?" }, "premium": { - "message": "Premium" + "message": "Prémium" }, "freeOrgsCannotUseAttachments": { - "message": "Free organizations cannot use attachments" + "message": "As organizacións gratuitas non poden empregar anexos" }, "filters": { - "message": "Filters" + "message": "Filtros" }, "filterVault": { - "message": "Filter vault" + "message": "Filtros da caixa forte" }, "filterApplied": { - "message": "One filter applied" + "message": "Un filtro en uso" }, "filterAppliedPlural": { - "message": "$COUNT$ filters applied", + "message": "$COUNT$ filtros en uso", "placeholders": { "count": { "content": "$1", @@ -4318,16 +4223,16 @@ } }, "personalDetails": { - "message": "Personal details" + "message": "Detalles persoais" }, "identification": { - "message": "Identification" + "message": "Identificación" }, "contactInfo": { - "message": "Contact info" + "message": "Información de contacto" }, "downloadAttachment": { - "message": "Download - $ITEMNAME$", + "message": "Descargar - $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -4336,23 +4241,23 @@ } }, "cardNumberEndsWith": { - "message": "card number ends with", + "message": "o número de tarxeta remata en", "description": "Used within the inline menu to provide an aria description when users are attempting to fill a card cipher." }, "loginCredentials": { - "message": "Login credentials" + "message": "Credenciais de inicio de sesión" }, "authenticatorKey": { - "message": "Authenticator key" + "message": "Clave de autenticación" }, "autofillOptions": { - "message": "Autofill options" + "message": "Opcións de autoenchido" }, "websiteUri": { - "message": "Website (URI)" + "message": "Dirección web (URI)" }, "websiteUriCount": { - "message": "Website (URI) $COUNT$", + "message": "Dirección web (URI) $COUNT$", "description": "Label for an input field that contains a website URI. The input field is part of a list of fields, and the count indicates the position of the field in the list.", "placeholders": { "count": { @@ -4362,16 +4267,16 @@ } }, "websiteAdded": { - "message": "Website added" + "message": "Dirección web engadida" }, "addWebsite": { - "message": "Add website" + "message": "Engadir dirección web" }, "deleteWebsite": { - "message": "Delete website" + "message": "Eliminar dirección web" }, "defaultLabel": { - "message": "Default ($VALUE$)", + "message": "Predeterminado ($VALUE$)", "description": "A label that indicates the default value for a field with the current default value in parentheses.", "placeholders": { "value": { @@ -4381,7 +4286,7 @@ } }, "showMatchDetection": { - "message": "Show match detection $WEBSITE$", + "message": "Mostrar detección de coincidencia $WEBSITE$", "placeholders": { "website": { "content": "$1", @@ -4390,7 +4295,7 @@ } }, "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", + "message": "Agochar detección de coincidencia $WEBSITE$", "placeholders": { "website": { "content": "$1", @@ -4399,19 +4304,19 @@ } }, "autoFillOnPageLoad": { - "message": "Autofill on page load?" + "message": "Autoencher ó cargar a páxina?" }, "cardExpiredTitle": { - "message": "Expired card" + "message": "Tarxeta vencida" }, "cardExpiredMessage": { - "message": "If you've renewed it, update the card's information" + "message": "Se a renovas, actualiza a información da tarxeta" }, "cardDetails": { - "message": "Card details" + "message": "Detalles da tarxeta" }, "cardBrandDetails": { - "message": "$BRAND$ details", + "message": "Detalles da $BRAND$", "placeholders": { "brand": { "content": "$1", @@ -4420,43 +4325,43 @@ } }, "enableAnimations": { - "message": "Enable animations" + "message": "Activar animacións" }, "showAnimations": { - "message": "Show animations" + "message": "Amosar animacións" }, "addAccount": { - "message": "Add account" + "message": "Engadir conta" }, "loading": { - "message": "Loading" + "message": "Cargando" }, "data": { - "message": "Data" + "message": "Datos" }, "passkeys": { - "message": "Passkeys", + "message": "Claves de acceso", "description": "A section header for a list of passkeys." }, "passwords": { - "message": "Passwords", + "message": "Contrasinais", "description": "A section header for a list of passwords." }, "logInWithPasskeyAriaLabel": { - "message": "Log in with passkey", + "message": "Iniciar sesión con Clave de acceso", "description": "ARIA label for the inline menu button that logs in with a passkey." }, "assign": { - "message": "Assign" + "message": "Asignar" }, "bulkCollectionAssignmentDialogDescriptionSingular": { - "message": "Only organization members with access to these collections will be able to see the item." + "message": "Só os membros da organización con acceso a estas coleccións poderán ver esta entrada." }, "bulkCollectionAssignmentDialogDescriptionPlural": { - "message": "Only organization members with access to these collections will be able to see the items." + "message": "Só os membros da organización con acceso a estas coleccións poderán ver estas entradas." }, "bulkCollectionAssignmentWarning": { - "message": "You have selected $TOTAL_COUNT$ items. You cannot update $READONLY_COUNT$ of the items because you do not have edit permissions.", + "message": "Seleccionaches $TOTAL_COUNT$ entradas. Non vas poder modificar $READONLY_COUNT$ delas porque non tes permisos de edición.", "placeholders": { "total_count": { "content": "$1", @@ -4468,37 +4373,37 @@ } }, "addField": { - "message": "Add field" + "message": "Engadir un campo" }, "add": { - "message": "Add" + "message": "Engadir" }, "fieldType": { - "message": "Field type" + "message": "Tipo de campo" }, "fieldLabel": { - "message": "Field label" + "message": "Título do campo" }, "textHelpText": { - "message": "Use text fields for data like security questions" + "message": "Emprega campos de texto para datos como preguntas de seguridade" }, "hiddenHelpText": { - "message": "Use hidden fields for sensitive data like a password" + "message": "Emprega campos agochados para datos sensibles como contrasinais" }, "checkBoxHelpText": { - "message": "Use checkboxes if you'd like to autofill a form's checkbox, like a remember email" + "message": "Emprega caixas de verificación cando queiras autoencher as dun formulario, como \"Lembrar correo\"" }, "linkedHelpText": { - "message": "Use a linked field when you are experiencing autofill issues for a specific website." + "message": "Emprega un campo vinculado cando teñas problemas coa autoenchido dalgunha web." }, "linkedLabelHelpText": { - "message": "Enter the the field's html id, name, aria-label, or placeholder." + "message": "Insire o ID HTML, nome, aria-label ou exemplo foi campo." }, "editField": { - "message": "Edit field" + "message": "Modificar campo" }, "editFieldLabel": { - "message": "Edit $LABEL$", + "message": "Modificar $LABEL$", "placeholders": { "label": { "content": "$1", @@ -4507,7 +4412,7 @@ } }, "deleteCustomField": { - "message": "Delete $LABEL$", + "message": "Eliminar $LABEL$", "placeholders": { "label": { "content": "$1", @@ -4516,7 +4421,7 @@ } }, "fieldAdded": { - "message": "$LABEL$ added", + "message": "$LABEL$ engadido", "placeholders": { "label": { "content": "$1", @@ -4525,7 +4430,7 @@ } }, "reorderToggleButton": { - "message": "Reorder $LABEL$. Use arrow key to move item up or down.", + "message": "Recolocar $LABEL$. Emprega as frechas do teclado.", "placeholders": { "label": { "content": "$1", @@ -4534,7 +4439,7 @@ } }, "reorderFieldUp": { - "message": "$LABEL$ moved up, position $INDEX$ of $LENGTH$", + "message": "Subiuse $LABEL$ á posición $INDEX$ de $LENGTH$", "placeholders": { "label": { "content": "$1", @@ -4551,13 +4456,13 @@ } }, "selectCollectionsToAssign": { - "message": "Select collections to assign" + "message": "Seleccionar coleccións a vincular" }, "personalItemTransferWarningSingular": { - "message": "1 item will be permanently transferred to the selected organization. You will no longer own this item." + "message": "Unha entrada será irreversiblemente transferida á organización. Xa non serás o seu propietario." }, "personalItemsTransferWarningPlural": { - "message": "$PERSONAL_ITEMS_COUNT$ items will be permanently transferred to the selected organization. You will no longer own these items.", + "message": "$PERSONAL_ITEMS_COUNT$ entradas serán irreversiblemente transferidas á organización. Xa non serás o seu propietario.", "placeholders": { "personal_items_count": { "content": "$1", @@ -4566,7 +4471,7 @@ } }, "personalItemWithOrgTransferWarningSingular": { - "message": "1 item will be permanently transferred to $ORG$. You will no longer own this item.", + "message": "Unha entrada será irreversiblemente transferida a $ORG$. Xa non serás o seu propietario.", "placeholders": { "org": { "content": "$1", @@ -4575,7 +4480,7 @@ } }, "personalItemsWithOrgTransferWarningPlural": { - "message": "$PERSONAL_ITEMS_COUNT$ items will be permanently transferred to $ORG$. You will no longer own these items.", + "message": "$PERSONAL_ITEMS_COUNT$ entradas serán irreversiblemente transferidas a $ORG$. Xa non serás o seu propietario.", "placeholders": { "personal_items_count": { "content": "$1", @@ -4588,13 +4493,13 @@ } }, "successfullyAssignedCollections": { - "message": "Successfully assigned collections" + "message": "Coleccións vinculadas" }, "nothingSelected": { - "message": "You have not selected anything." + "message": "Non tes nada seleccionado." }, "movedItemsToOrg": { - "message": "Selected items moved to $ORGNAME$", + "message": "Entradas seleccionadas transferidas a $ORGNAME$", "placeholders": { "orgname": { "content": "$1", @@ -4603,7 +4508,7 @@ } }, "itemsMovedToOrg": { - "message": "Items moved to $ORGNAME$", + "message": "Entradas transferidas a $ORGNAME$", "placeholders": { "orgname": { "content": "$1", @@ -4612,7 +4517,7 @@ } }, "itemMovedToOrg": { - "message": "Item moved to $ORGNAME$", + "message": "Entrada transferida a $ORGNAME$", "placeholders": { "orgname": { "content": "$1", @@ -4621,7 +4526,7 @@ } }, "reorderFieldDown": { - "message": "$LABEL$ moved down, position $INDEX$ of $LENGTH$", + "message": "Baixouse $LABEL$ á posición $INDEX$ de $LENGTH$", "placeholders": { "label": { "content": "$1", @@ -4638,49 +4543,52 @@ } }, "itemLocation": { - "message": "Item Location" + "message": "Ubicación da entrada" }, "fileSend": { - "message": "File Send" + "message": "Arquivo Send" }, "fileSends": { - "message": "File Sends" + "message": "Arquivos Send" }, "textSend": { - "message": "Text Send" + "message": "Texto Send" }, "textSends": { - "message": "Text Sends" + "message": "Textos Send" }, "bitwardenNewLook": { - "message": "Bitwarden has a new look!" + "message": "Bitwarden ten un novo look!" }, "bitwardenNewLookDesc": { - "message": "It's easier and more intuitive than ever to autofill and search from the Vault tab. Take a look around!" + "message": "É máis fácil e intuitivo que nunca autoencher e buscar dende a caixa forte. Bota un ollo!" }, "accountActions": { - "message": "Account actions" + "message": "Accións da conta" }, "showNumberOfAutofillSuggestions": { - "message": "Show number of login autofill suggestions on extension icon" + "message": "Amosar o número de suxestións de credenciais na icona da extensión" + }, + "showQuickCopyActions": { + "message": "Amosar accións rápidas de copiado na caixa forte" }, "systemDefault": { - "message": "System default" + "message": "Predefinido do sistema" }, "enterprisePolicyRequirementsApplied": { - "message": "Enterprise policy requirements have been applied to this setting" + "message": "Directivas da empresa foron aplicadas a esta opción" }, "sshPrivateKey": { - "message": "Private key" + "message": "Clave privada" }, "sshPublicKey": { - "message": "Public key" + "message": "Clave pública" }, "sshFingerprint": { - "message": "Fingerprint" + "message": "Pegada dixital" }, "sshKeyAlgorithm": { - "message": "Key type" + "message": "Tipo de clave" }, "sshKeyAlgorithmED25519": { "message": "ED25519" @@ -4695,213 +4603,249 @@ "message": "RSA 4096-Bit" }, "retry": { - "message": "Retry" + "message": "Reintentar" }, "vaultCustomTimeoutMinimum": { - "message": "Minimum custom timeout is 1 minute." + "message": "O tempo mínimo personalizado é de 1 minuto." }, "additionalContentAvailable": { - "message": "Additional content is available" + "message": "Hai dispoñibles contidos adicionais" }, "fileSavedToDevice": { - "message": "File saved to device. Manage from your device downloads." + "message": "Arquivo gardado no dispositivo. Xestiónao dende as descargas do dispositivo." }, "showCharacterCount": { - "message": "Show character count" + "message": "Amosar contador de caracteres" }, "hideCharacterCount": { - "message": "Hide character count" + "message": "Ocultar contador de caracteres" }, "itemsInTrash": { - "message": "Items in trash" + "message": "Entradas no lixo" }, "noItemsInTrash": { - "message": "No items in trash" + "message": "Sen entradas no lixo" }, "noItemsInTrashDesc": { - "message": "Items you delete will appear here and be permanently deleted after 30 days" + "message": "As entradas que elimines aparecerán aquí serán permanente eliminadas despois de 30 días" }, "trashWarning": { - "message": "Items that have been in trash more than 30 days will automatically be deleted" + "message": "As entradas que estean no lixo máis de 30 días serán automaticamente eliminadas" }, "restore": { - "message": "Restore" + "message": "Restaurar" }, "deleteForever": { - "message": "Delete forever" + "message": "Eliminar permanentemente" }, "noEditPermissions": { - "message": "You don't have permission to edit this item" + "message": "Non tes permiso para modificar esta entrada" }, "authenticating": { - "message": "Authenticating" + "message": "Autenticando" }, "fillGeneratedPassword": { - "message": "Fill generated password", + "message": "Encher co contrasinal xerado", "description": "Heading for the password generator within the inline menu" }, "passwordRegenerated": { - "message": "Password regenerated", + "message": "Contrasinal xerado de novo", "description": "Notification message for when a password has been regenerated" }, "saveLoginToBitwarden": { - "message": "Save login to Bitwarden?", + "message": "Gardar credenciais en Bitwarden?", "description": "Confirmation message for saving a login to Bitwarden" }, "spaceCharacterDescriptor": { - "message": "Space", + "message": "Espazo", "description": "Represents the space key in screen reader content as a readable word" }, "tildeCharacterDescriptor": { - "message": "Tilde", + "message": "Til", "description": "Represents the ~ key in screen reader content as a readable word" }, "backtickCharacterDescriptor": { - "message": "Backtick", + "message": "Plica", "description": "Represents the ` key in screen reader content as a readable word" }, "exclamationCharacterDescriptor": { - "message": "Exclamation mark", + "message": "Exclamación", "description": "Represents the ! key in screen reader content as a readable word" }, "atSignCharacterDescriptor": { - "message": "At sign", + "message": "Arroba", "description": "Represents the @ key in screen reader content as a readable word" }, "hashSignCharacterDescriptor": { - "message": "Hash sign", + "message": "Símbolo de almohadilla", "description": "Represents the # key in screen reader content as a readable word" }, "dollarSignCharacterDescriptor": { - "message": "Dollar sign", + "message": "Símbolo do dólar", "description": "Represents the $ key in screen reader content as a readable word" }, "percentSignCharacterDescriptor": { - "message": "Percent sign", + "message": "Símbolo de porcentaxe", "description": "Represents the % key in screen reader content as a readable word" }, "caretCharacterDescriptor": { - "message": "Caret", + "message": "Símbolo circunflexo", "description": "Represents the ^ key in screen reader content as a readable word" }, "ampersandCharacterDescriptor": { - "message": "Ampersand", + "message": "Símbolo E comercial", "description": "Represents the & key in screen reader content as a readable word" }, "asteriskCharacterDescriptor": { - "message": "Asterisk", + "message": "Símbolo Asterisco", "description": "Represents the * key in screen reader content as a readable word" }, "parenLeftCharacterDescriptor": { - "message": "Left parenthesis", + "message": "Símbolo abrir paréntese", "description": "Represents the ( key in screen reader content as a readable word" }, "parenRightCharacterDescriptor": { - "message": "Right parenthesis", + "message": "Símbolo pechar paréntese", "description": "Represents the ) key in screen reader content as a readable word" }, "hyphenCharacterDescriptor": { - "message": "Underscore", + "message": "Símbolo guión baixo", "description": "Represents the _ key in screen reader content as a readable word" }, "underscoreCharacterDescriptor": { - "message": "Hyphen", + "message": "Símbolo guión", "description": "Represents the - key in screen reader content as a readable word" }, "plusCharacterDescriptor": { - "message": "Plus", + "message": "Símbolo máis", "description": "Represents the + key in screen reader content as a readable word" }, "equalsCharacterDescriptor": { - "message": "Equals", + "message": "Símbolo igual", "description": "Represents the = key in screen reader content as a readable word" }, "braceLeftCharacterDescriptor": { - "message": "Left brace", + "message": "Símbolo abrir chave", "description": "Represents the { key in screen reader content as a readable word" }, "braceRightCharacterDescriptor": { - "message": "Right brace", + "message": "Símbolo pechar chave", "description": "Represents the } key in screen reader content as a readable word" }, "bracketLeftCharacterDescriptor": { - "message": "Left bracket", + "message": "Símbolo abrir corchete", "description": "Represents the [ key in screen reader content as a readable word" }, "bracketRightCharacterDescriptor": { - "message": "Right bracket", + "message": "Símbolo pechar corchete", "description": "Represents the ] key in screen reader content as a readable word" }, "pipeCharacterDescriptor": { - "message": "Pipe", + "message": "Símbolo de liña vertical (pipe)", "description": "Represents the | key in screen reader content as a readable word" }, "backSlashCharacterDescriptor": { - "message": "Back slash", + "message": "Símbolo de barra invertida", "description": "Represents the back slash key in screen reader content as a readable word" }, "colonCharacterDescriptor": { - "message": "Colon", + "message": "Símbolo de dous puntos", "description": "Represents the : key in screen reader content as a readable word" }, "semicolonCharacterDescriptor": { - "message": "Semicolon", + "message": "Símbolo de punto e coma", "description": "Represents the ; key in screen reader content as a readable word" }, "doubleQuoteCharacterDescriptor": { - "message": "Double quote", + "message": "Símbolo de dobre comilla", "description": "Represents the double quote key in screen reader content as a readable word" }, "singleQuoteCharacterDescriptor": { - "message": "Single quote", + "message": "Símbolo de comilla simple", "description": "Represents the ' key in screen reader content as a readable word" }, "lessThanCharacterDescriptor": { - "message": "Less than", + "message": "Símbolo de menor que", "description": "Represents the < key in screen reader content as a readable word" }, "greaterThanCharacterDescriptor": { - "message": "Greater than", + "message": "Símbolo de maior que", "description": "Represents the > key in screen reader content as a readable word" }, "commaCharacterDescriptor": { - "message": "Comma", + "message": "Símbolo de coma", "description": "Represents the , key in screen reader content as a readable word" }, "periodCharacterDescriptor": { - "message": "Period", + "message": "Símbolo de punto", "description": "Represents the . key in screen reader content as a readable word" }, "questionCharacterDescriptor": { - "message": "Question mark", + "message": "Símbolo de interrogación", "description": "Represents the ? key in screen reader content as a readable word" }, "forwardSlashCharacterDescriptor": { - "message": "Forward slash", + "message": "Símbolo de barra inclinada", "description": "Represents the / key in screen reader content as a readable word" }, "lowercaseAriaLabel": { - "message": "Lowercase" + "message": "Minúsculas" }, "uppercaseAriaLabel": { - "message": "Uppercase" + "message": "Maiúsculas" }, "generatedPassword": { - "message": "Generated password" + "message": "Contrasinal xerado" }, "compactMode": { - "message": "Compact mode" + "message": "Modo compacto" }, "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Aviso importante" + }, + "setupTwoStepLogin": { + "message": "Configurar verificación en dous pasos" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "A partir de febreiro de 2025 Bitwarden comezará a enviar correos con códigos de verificación para confirmar novos inicios de sesión á túa conta." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Podes configurar a verificación en 2 pasos como alternativa para protexer a túa conta ou cambiar o enderezo electrónico a un ó que teñas acceso." + }, + "remindMeLater": { + "message": "Lembrarmo máis tarde" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Tes acceso fiable ó teu correo? ($EMAIL$)", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Non, non o teño" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Si, teño acceso fiable ó meu correo" + }, + "turnOnTwoStepLogin": { + "message": "Activar verificación en dous pasos" + }, + "changeAcctEmail": { + "message": "Mudar de correo electrónico" + }, "extensionWidth": { - "message": "Extension width" + "message": "Ancho da extensión" }, "wide": { - "message": "Wide" + "message": "Ancho" }, "extraWide": { - "message": "Extra wide" + "message": "Moi ancho" } } diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 3c418682003..5101c11a653 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "מילוי פרטי זיהוי אוטומטית" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "צור סיסמה (העתק)" }, @@ -447,9 +454,6 @@ "length": { "message": "אורך" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "מינימום תוים מיוחדים" }, - "avoidAmbChar": { - "message": "המנע מאותיות ותוים דומים", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "דירוג הרחבה" }, - "rateExtensionDesc": { - "message": "אם נהנית מהתוכנה, בבקשה דרג את התוכנה וכתוב דירוג עם חוות דעת טובה!" - }, "browserNotSupportClipboard": { "message": "הדפדפן שלך לא תומך בהעתקה ללוח. אנא העתק בצורה ידנית." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "נקה לוח העתקות", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organization" }, - "share": { - "message": "שתף" - }, "movedItemToOrg": { "message": "$ITEMNAME$ הועבר ל- $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "שכפול" }, - "passwordGeneratorPolicyInEffect": { - "message": "מדיניות ארגונית אחת או יותר משפיעה על הגדרות המחולל שלך." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expired" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "הקובץ שברצונך לשלוח." - }, "deletionDate": { "message": "תאריך מחיקה" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "תאריך תפוגה" }, - "expirationDateDesc": { - "message": "במידה ויוגדר, הגישה ל Send זה תושבת בתאריך ובשעה שהוגדרו.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "יום אחד" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "מותאם אישית" }, - "maximumAccessCount": { - "message": "כמות גישות מרבית" - }, - "maximumAccessCountDesc": { - "message": "במידה ויוגדר, משתמשים לא יוכלו יותר לגשת ל Send זה לאחר שמספר הגישות המרבי יושג.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "שגיאה" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "סוג שם משתמש" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index b4c03a8f829..10ad502be82 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -20,16 +20,16 @@ "message": "Create Account" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "बिटवार्डन का परिचय" }, "logInWithPasskey": { "message": "Log in with passkey" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "सिंगल साइन-ऑन प्रयोग करें" }, "welcomeBack": { - "message": "Welcome back" + "message": "आपका पुन: स्वागत है!" }, "setAStrongPassword": { "message": "मजबूत पासवर्ड सेट करें" @@ -138,10 +138,10 @@ "message": "Copy Security Code" }, "copyName": { - "message": "Copy name" + "message": "नाम कॉपी करें" }, "copyCompany": { - "message": "Copy company" + "message": "कंपनी के नाम को कॉपी करें" }, "copySSN": { "message": "Copy Social Security number" @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "स्वचालित पहचान विवरण" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generate Password (copied)" }, @@ -447,9 +454,6 @@ "length": { "message": "लंबाई" }, - "passwordMinLength": { - "message": "न्यूनतम पासवर्ड लंबाई" - }, "uppercase": { "message": "बड़े अक्षर (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum Special" }, - "avoidAmbChar": { - "message": "Avoid Ambiguous Characters", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Rate the Extension" }, - "rateExtensionDesc": { - "message": "कृपया एक अच्छी समीक्षा के साथ हमारी मदत करने पर विचार करें!" - }, "browserNotSupportClipboard": { "message": "आपका वेब ब्राउज़र आसान क्लिपबोर्ड कॉपीिंग का समर्थन नहीं करता है। इसके बजाय इसे मैन्युअल रूप से कॉपी करें।" }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "आसान ऑटो-फिल के लिए टैब पेज पर कार्ड आइटम सूचीबद्ध करें।" }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "क्लिपबोर्ड खाली करें", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "संगठन में ले जाएँ" }, - "share": { - "message": "शेयर करें" - }, "movedItemToOrg": { "message": "$ITEMNAME$ $ORGNAME$ गया ", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "क्लोन" }, - "passwordGeneratorPolicyInEffect": { - "message": "एक या एक से अधिक संगठन नीतियां आपकी जनरेटर सेटिंग को प्रभावित कर रही हैं।" - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Sends मे खोजे", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Send जोड़ें", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "शब्द" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "मैक्स एक्सेस काउंट पहुंच गया है", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": " गतावधिक" }, - "pendingDeletion": { - "message": "हटाना लंबित" - }, "passwordProtected": { "message": "पासवर्ड सुरक्षित है" }, @@ -2468,24 +2447,9 @@ "message": "एडिट Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "यह किस प्रकार का सेंड है?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "इस सेंड का वर्णन करने के लिए एक दोस्ताना नाम।", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "वह फाइल जो आप सेंड करना चाहते हैं।" - }, "deletionDate": { "message": "हटाने की तारीख" }, - "deletionDateDesc": { - "message": " यह सेंड निर्धारित तिथि और समय पर स्थायी रूप से हटा दिया जाएगा।", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "समाप्ति तिथि" }, - "expirationDateDesc": { - "message": "यदि सेट किया जाता है, तो यह सेंड निर्दिष्ट तिथि और समय पर समाप्त हो जाएगा।", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 दिन" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "कस्टम" }, - "maximumAccessCount": { - "message": "अधिकतम एक्सेस काउंट" - }, - "maximumAccessCountDesc": { - "message": "यदि सेट किया जाता है, तो अधिकतम एक्सेस काउंट तक पहुंचने के बाद उपयोगकर्ता अब इस सेंड को एक्सेस नहीं कर पाएंगे।", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "वैकल्पिक रूप से उपयोगकर्ताओं को इस सेंड तक पहुंचने के लिए पासवर्ड की आवश्यकता होगी।", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "इस सेंड के बारे में निजी नोट्स।", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "इस सेंड को अक्षम करें ताकि कोई भी इसे एक्सेस न कर सके।", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "सेव पर क्लिपबोर्ड पर इस सेंड के लिंक को कॉपी करें।", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "वह टेक्स्ट जो आप सेंड करना चाहते हैं।" - }, - "sendHideText": { - "message": "इस सेंड के टेक्स्ट को डिफ़ॉल्ट रूप से छिपाएं।", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "वर्तमान एक्सेस काउंट" - }, "createSend": { "message": "नया सेंड बनाएं", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "शुरू करने से पहले" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "कैलेंडर शैली तिथि बीनने वाले का उपयोग करने के लिए", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "यहां क्लिक करें", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "अपनी विंडो पॉप आउट करने के लिए यहां क्लिक करें।", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "प्रदान की गई समाप्ति तिथि मान्य नहीं है।" }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "आपके विलोपन और समाप्ति तिथियों को सहेजने में एक त्रुटि थी।" }, - "hideEmail": { - "message": "प्राप्तकर्ताओं से मेरा ईमेल पता छिपाएं।" - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "एक या एक से अधिक संगठन नीतियां आपके सेंड विकल्पों को प्रभावित कर रही हैं।" - }, "passwordPrompt": { "message": "मास्टर पासवर्ड रि-प्रॉम्प्ट" }, @@ -2880,9 +2789,6 @@ "error": { "message": "एरर" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "उपयोगकर्ता नाम बनाएँ" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "वेबसाइट का नाम" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "महत्वपूर्ण सूचना" + }, + "setupTwoStepLogin": { + "message": "टू-स्टेप लॉगइन" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "बाद में मुझे याद कराएं" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "नहीं, मैं नहीं" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "हाँ, मैं आराम से अपना ईमेल देख सकता हूँ" + }, + "turnOnTwoStepLogin": { + "message": "टू-स्टेप लॉगइन सक्षम करें" + }, + "changeAcctEmail": { + "message": "अकाउंट का ईमेल बदलें" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 80eb68be8ac..3b60cfe45c8 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -20,16 +20,16 @@ "message": "Stvori račun" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Novi u Bitwardenu?" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Prijava pristupnim ključem" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Jedinstvena prijava (SSO)" }, "welcomeBack": { - "message": "Welcome back" + "message": "Dobro došli natrag" }, "setAStrongPassword": { "message": "Postavi jaku lozinku" @@ -84,7 +84,7 @@ "message": "Pridruži se organizaciji" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "Pidruži se $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -120,7 +120,7 @@ "message": "Kopiraj lozinku" }, "copyPassphrase": { - "message": "Copy passphrase" + "message": "Kopiraj fraznu lozinku" }, "copyNote": { "message": "Kopiraj bilješku" @@ -153,13 +153,13 @@ "message": "Kopiraj OIB" }, "copyPrivateKey": { - "message": "Copy private key" + "message": "Kopiraj privatni ključ" }, "copyPublicKey": { - "message": "Copy public key" + "message": "Kopiraj javni ključ" }, "copyFingerprint": { - "message": "Copy fingerprint" + "message": "Kopiraj otisak prsta" }, "copyCustomField": { "message": "Kopiraj $FIELD$", @@ -177,7 +177,7 @@ "message": "Kopiraj bilješke" }, "fill": { - "message": "Fill", + "message": "Ispuni", "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Auto-ispuna identiteta" }, + "fillVerificationCode": { + "message": "Ispuni kôd za provjeru" + }, + "fillVerificationCodeAria": { + "message": "Ispuni kôd za provjeru", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generiraj lozinku (i kopiraj)" }, @@ -277,7 +284,7 @@ "message": "Nastavi na web aplikaciju?" }, "continueToWebAppDesc": { - "message": "Pronađi viđe značajki svojeg Bitwarden računa u web aplikaciji." + "message": "Pronađi više značajki svojeg Bitwarden računa u web aplikaciji." }, "continueToHelpCenter": { "message": "Nastavi u centar za pomoć?" @@ -342,10 +349,10 @@ "message": "Stvori laka i sigurna iskustva prijave bez tradicionalnih lozinki uz Passwordless.dev. Saznaj više na web stranici bitwarden.com." }, "freeBitwardenFamilies": { - "message": "Besplatan obiteljski Bitwarden" + "message": "Besplatni Bitwarden Families" }, "freeBitwardenFamiliesPageDesc": { - "message": "Ispunjavaš uvjete za besplatni obiteljski Bitwarden. Iskoristi ovu ponudu u web aplikaciji već danas." + "message": "Ispunjavaš uvjete za besplatni Bitwarden Families. Iskoristi ovu ponudu u web aplikaciji već danas." }, "version": { "message": "Verzija" @@ -436,7 +443,7 @@ "message": "Generiraj lozinku" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "Generiraj frazu lozinke" }, "regeneratePassword": { "message": "Ponovno generiraj lozinku" @@ -447,9 +454,6 @@ "length": { "message": "Duljina" }, - "passwordMinLength": { - "message": "Minimalna duljina lozinke" - }, "uppercase": { "message": "Velika slova (A - Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Najmanje posebnih" }, - "avoidAmbChar": { - "message": "Izbjegavaj dvosmislene znakove", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Izbjegavaj dvosmislene znakove", "description": "Label for the avoid ambiguous characters checkbox." @@ -600,7 +600,7 @@ "message": "Pokreni web stranicu" }, "launchWebsiteName": { - "message": "Launch website $ITEMNAME$", + "message": "Otvori stranicu $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -641,9 +641,6 @@ "rateExtension": { "message": "Ocijeni proširenje" }, - "rateExtensionDesc": { - "message": "Razmotri da nam pomogneš dobrom recenzijom!" - }, "browserNotSupportClipboard": { "message": "Web preglednik ne podržava jednostavno kopiranje međuspremnika. Umjesto toga ručno kopirajte." }, @@ -797,7 +794,7 @@ "message": "Poslali smo e-poštu s podsjetnikom glavne lozinke." }, "verificationCodeRequired": { - "message": "Potvrdni kôd je obavezan." + "message": "Kôd za provjeru je obavezan." }, "webauthnCancelOrTimeout": { "message": "Autentifikacija je otkazana ili je trajala predugo. Molimo pokušaj ponovno." @@ -855,7 +852,7 @@ "message": "Prijavi se" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Prijavi se u Bitwarden" }, "restartRegistration": { "message": "Ponovno pokreni registraciju" @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Prikazuj identitete za jednostavnu auto-ispunu." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Očisti međuspremnik", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1126,7 +1126,7 @@ "description": "WARNING (should stay in capitalized letters if the language permits)" }, "warningCapitalized": { - "message": "Warning", + "message": "Upozorenje", "description": "Warning (should maintain locale-relevant capitalization)" }, "confirmVaultExport": { @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Premjesti u organizaciju" }, - "share": { - "message": "Podijeli" - }, "movedItemToOrg": { "message": "$ITEMNAME$ premješteno u $ORGNAME$", "placeholders": { @@ -1305,7 +1302,7 @@ "message": "Automatski kopiraj TOTP" }, "disableAutoTotpCopyDesc": { - "message": "Ako za prijavu postoji autentifikatorski ključ, kopiraj TOTP kontrolni kôd u međuspremnik nakon auto-ispune prijave." + "message": "Ako za prijavu postoji autentifikatorski ključ, kopiraj TOTP kôd za provjeru u međuspremnik nakon auto-ispune prijave." }, "enableAutoBiometricsPrompt": { "message": "Traži biometrijsku autentifikaciju pri pokretanju" @@ -1317,16 +1314,16 @@ "message": "Za korištenje ove značajke potrebno je Premium članstvo." }, "enterVerificationCodeApp": { - "message": "Unesi 6-znamenkasti kontrolni kôd iz autentifikatorske aplikacije." + "message": "Unesi 6-znamenkasti kôd za provjeru iz autentifikatorske aplikacije." }, "authenticationTimeout": { - "message": "Authentication timeout" + "message": "Istek vremena za autentifikaciju" }, "authenticationSessionTimedOut": { - "message": "The authentication session timed out. Please restart the login process." + "message": "Sesija za autentifikaciju je istekla. Ponovi proces prijave." }, "enterVerificationCodeEmail": { - "message": "Unesi 6-znamenkasti kontrolni kôd poslan e-poštom na $EMAIL$.", + "message": "Unesi 6-znamenkasti kôd za provjeru poslan e-poštom na $EMAIL$.", "placeholders": { "email": { "content": "$1", @@ -1443,7 +1440,7 @@ "message": "URL poslužitelja" }, "selfHostBaseUrl": { - "message": "Self-host server URL", + "message": "URL vlastitog poslužitelja", "description": "Label for field requesting a self-hosted integration service URL" }, "apiUrl": { @@ -1475,10 +1472,10 @@ "message": "Prikaži prijedloge auto-ispune na poljima obrazaca" }, "showInlineMenuIdentitiesLabel": { - "message": "Display identities as suggestions" + "message": "Prikaži identitete kao prijedloge" }, "showInlineMenuCardsLabel": { - "message": "Display cards as suggestions" + "message": "Prikaži platne kartice kao prijedloge" }, "showInlineMenuOnIconSelectionLabel": { "message": "Prikaži prijedloge kada je odabrana ikona" @@ -1597,7 +1594,7 @@ "description": "This describes a value that is 'linked' (tied) to another value." }, "popup2faCloseMessage": { - "message": "Ako klikneš izvan iskočnog prozora, za provjeru kontrolnog kôda iz e-pošte, on će se zatvoriti. Želiš li ovaj iskočni prozor otvoriti u novom prozoru kako se ne bi zatvorio?" + "message": "Ako klikneš izvan iskočnog prozora, za provjeru kôda za provjeru iz e-pošte, on će se zatvoriti. Želiš li ovaj iskočni prozor otvoriti u novom prozoru kako se ne bi zatvorio?" }, "popupU2fCloseMessage": { "message": "Ovaj preglednik ne može obraditi U2F zahtjeve u ovom iskočnom prozoru. Želiš li otvoriti ovaj iskočni prozor u novom prozoru za prijavu putem U2F?" @@ -1672,7 +1669,7 @@ "message": "prosinac" }, "securityCode": { - "message": "Kontrolni broj" + "message": "Sigurnosni kôd" }, "ex": { "message": "npr." @@ -1771,7 +1768,7 @@ "message": "Identitet" }, "typeSshKey": { - "message": "SSH key" + "message": "SSH ključ" }, "newItemHeader": { "message": "Novi $TYPE$", @@ -1804,13 +1801,13 @@ "message": "Povijest" }, "generatorHistory": { - "message": "Generator history" + "message": "Povijest generatora" }, "clearGeneratorHistoryTitle": { - "message": "Clear generator history" + "message": "Očisti povijest generatora" }, "cleargGeneratorHistoryDescription": { - "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + "message": "Cijela povijest generatora biti će trajno izbirsana. Sigurno želiš nastaviti?" }, "back": { "message": "Natrag" @@ -1849,7 +1846,7 @@ "message": "Sigurne bilješke" }, "sshKeys": { - "message": "SSH Keys" + "message": "SSH ključevi" }, "clear": { "message": "Očisti", @@ -1932,10 +1929,10 @@ "message": "Očisti povijest" }, "nothingToShow": { - "message": "Nothing to show" + "message": "Ništa za prikazati" }, "nothingGeneratedRecently": { - "message": "You haven't generated anything recently" + "message": "Ništa nije generirano" }, "remove": { "message": "Ukloni" @@ -2043,9 +2040,6 @@ "clone": { "message": "Kloniraj" }, - "passwordGeneratorPolicyInEffect": { - "message": "Jedno ili više pravila organizacije utječe na postavke generatora." - }, "passwordGenerator": { "message": "Generator lozinki" }, @@ -2385,14 +2379,6 @@ "message": "Detalji Senda", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Pretraži Sendove", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Dodaj Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Tekst" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Zadano sakrij tekst" }, - "maxAccessCountReached": { - "message": "Dostignut najveći broj pristupanja", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Isteklo" }, - "pendingDeletion": { - "message": "Čeka brisanje" - }, "passwordProtected": { "message": "Zaštićeno lozinkom" }, @@ -2468,24 +2447,9 @@ "message": "Uredi Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Koja je ovo vrsta Senda?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Nadimak za ovaj Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Datoteka koju želiš poslati" - }, "deletionDate": { "message": "Obriši za" }, - "deletionDateDesc": { - "message": "Send će nakon navedenog vremena biti trajno izbrisan.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Send će na ovaj datum biti trajno izbrisan.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Vremenski ograničeni pristup" }, - "expirationDateDesc": { - "message": "Pristup ovom Sendu neće biti moguć nakon navednog roka.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 dan" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Prilagođeno" }, - "maximumAccessCount": { - "message": "Ograničeni broj pristupanja" - }, - "maximumAccessCountDesc": { - "message": "Ako je određen, ovom Sendu će se moći pristupiti samo ograničeni broj puta.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Neobavezno zahtijevaj korisnika lozinku za pristup ovom Sendu.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { - "message": "Add an optional password for recipients to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNotesDesc": { - "message": "Privatne bilješke o Sendu.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Onemogući ovaj Send da mu nitko ne može pristupiti.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Kopiraj vezu na Send nakon spremanja", + "message": "Dodaj opcionalnu lozinku za primatelje ovog Senda.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTextDesc": { - "message": "Tekst kojeg želiš poslati" - }, - "sendHideText": { - "message": "Sakrij tekst ovog Senda.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Trenutni broj pristupanja" - }, "createSend": { "message": "Stvori novi Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Prije početka" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Biranje datuma na kalendaru", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "klikni ovjde", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "za iskočni prozor", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Navedeni rok isteka nije valjan." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Došlo je do greške kod spremanja vaših datuma isteka i brisanja." }, - "hideEmail": { - "message": "Sakrij moju adresu e-pošte od primatelja." - }, "hideYourEmail": { "message": "Autentifikacija" }, - "sendOptionsPolicyInEffect": { - "message": "Jedno ili više pravila organizacije utječe na postavke Senda." - }, "passwordPrompt": { "message": "Ponovno zatraži glavnu lozinku" }, @@ -2722,7 +2631,7 @@ "description": "Used as a card title description on the set password page to explain why the user is there" }, "cardMetrics": { - "message": "out of $TOTAL$", + "message": "od $TOTAL$", "placeholders": { "total": { "content": "$1", @@ -2880,17 +2789,14 @@ "error": { "message": "Pogreška" }, - "regenerateUsername": { - "message": "Ponovno generiraj korisničko ime" - }, "generateUsername": { "message": "Generiraj korisničko ime" }, "generateEmail": { - "message": "Generate email" + "message": "Generiraj e-poštu" }, "spinboxBoundariesHint": { - "message": "Value must be between $MIN$ and $MAX$.", + "message": "Vrijednost mora biti u rasponu $MIN$ - $MAX$.", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2904,7 +2810,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", + "message": " Koristi $RECOMMENDED$ i više znakova za generiranje jake lozinke.", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2914,7 +2820,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": " Koristi $RECOMMENDED$ i više riječi za generiranje jake frazne lozinke.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Tip korisničkog imena" - }, "plusAddressedEmail": { "message": "Plus adresa e-pošte", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Naziv web mjesta" }, - "whatWouldYouLikeToGenerate": { - "message": "Što želiš generirati?" - }, - "passwordType": { - "message": "Tip lozinke" - }, "service": { "message": "Usluga" }, @@ -2964,11 +2861,11 @@ "message": "Generiraj pseudonim e-pošte s vanjskom uslugom prosljeđivanja." }, "forwarderDomainName": { - "message": "Email domain", + "message": "Domena e-pošte", "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { - "message": "Choose a domain that is supported by the selected service", + "message": "Odaberi domenu koju podržava odabrani servis", "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { @@ -3171,25 +3068,25 @@ "message": "Ponovno pošalji obavijest" }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "Pogledaj sve mogućnosti prijave" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Pogledaj sve mogućnosti prijave" }, "notificationSentDevice": { "message": "Obavijest je poslana na tvoj uređaj." }, "aNotificationWasSentToYourDevice": { - "message": "A notification was sent to your device" + "message": "Obavijest je poslana na tvoj uređaj" }, "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { - "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + "message": "Provjeri je li trezor otključan i slaže li se jedinstvena fraza s drugim uređajem" }, "youWillBeNotifiedOnceTheRequestIsApproved": { - "message": "You will be notified once the request is approved" + "message": "Dobiti ćeš obavijest kada je tvoj zahtjev odobren" }, "needAnotherOptionV1": { - "message": "Need another option?" + "message": "Trebaš drugu opciju?" }, "loginInitiated": { "message": "Prijava pokrenuta" @@ -3285,16 +3182,16 @@ "message": "Otvara u novom prozoru" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Zapamti ovaj uređaj kako bi buduće prijave bile brže" }, "deviceApprovalRequired": { "message": "Potrebno je odobriti uređaj. Odaberi metodu odobravanja:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Potrebno odobrenje uređaja" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Odaberi opciju odobrenja" }, "rememberThisDevice": { "message": "Zapamti ovaj uređaj" @@ -3370,7 +3267,7 @@ "message": "Nedostaje e-pošta korisnika" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Nije pronađena e-pošta aktivnog korisnika. Odjava u tijeku..." }, "deviceTrusted": { "message": "Uređaj pouzdan" @@ -3580,6 +3477,14 @@ "message": "Otključaj račun; otvara se u novom prozoru", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Kôd za provjeru jednokratne lozinka zasnovane na vremenu (TOTP) ", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Preostalo vrijeme koda za provjeru", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Unesi vjerodajnice za", "description": "Screen reader text for when overlay item is in focused" @@ -3809,7 +3714,7 @@ "message": "Pristupanje" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Prijava uspješna!" }, "passkeyNotCopied": { "message": "Pristupni ključ neće biti kopiran" @@ -4273,7 +4178,7 @@ "message": "Najveća veličina datoteke je 500 MB" }, "deleteAttachmentName": { - "message": "Izbriši privitak", + "message": "Izbriši privitak $NAME$", "placeholders": { "name": { "content": "$1", @@ -4303,13 +4208,13 @@ "message": "Filtri" }, "filterVault": { - "message": "Filter vault" + "message": "Filtriraj trezor" }, "filterApplied": { - "message": "One filter applied" + "message": "Uključen jedan filter" }, "filterAppliedPlural": { - "message": "$COUNT$ filters applied", + "message": "Uključeno filtera: $COUNT$", "placeholders": { "count": { "content": "$1", @@ -4641,13 +4546,13 @@ "message": "Lokacija stavke" }, "fileSend": { - "message": "File Send" + "message": "Send datoteke" }, "fileSends": { "message": "Send datoteke" }, "textSend": { - "message": "Text Send" + "message": "Send teksta" }, "textSends": { "message": "Send tekstovi" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Prikaži broj prijedloga auto-ispune na ikoni proširenja" }, + "showQuickCopyActions": { + "message": "Prikaži akcije brzog kopiranja na trezoru" + }, "systemDefault": { "message": "Zadano sustavom" }, @@ -4671,16 +4579,16 @@ "message": "Pravila tvrtke primijenjena su na ovu postavku" }, "sshPrivateKey": { - "message": "Private key" + "message": "Privatni ključ" }, "sshPublicKey": { - "message": "Public key" + "message": "Javni ključ" }, "sshFingerprint": { - "message": "Fingerprint" + "message": "Otisak prsta" }, "sshKeyAlgorithm": { - "message": "Key type" + "message": "Vrsta ključa" }, "sshKeyAlgorithmED25519": { "message": "ED25519" @@ -4737,171 +4645,207 @@ "message": "Autentifikacija" }, "fillGeneratedPassword": { - "message": "Fill generated password", + "message": "Ispuni generiranu lozinku", "description": "Heading for the password generator within the inline menu" }, "passwordRegenerated": { - "message": "Password regenerated", + "message": "Lozinka re-generirana", "description": "Notification message for when a password has been regenerated" }, "saveLoginToBitwarden": { - "message": "Save login to Bitwarden?", + "message": "Spremi prijavu u Bitwarden?", "description": "Confirmation message for saving a login to Bitwarden" }, "spaceCharacterDescriptor": { - "message": "Space", + "message": "Razmak", "description": "Represents the space key in screen reader content as a readable word" }, "tildeCharacterDescriptor": { - "message": "Tilde", + "message": "znak ˜", "description": "Represents the ~ key in screen reader content as a readable word" }, "backtickCharacterDescriptor": { - "message": "Backtick", + "message": "znak `", "description": "Represents the ` key in screen reader content as a readable word" }, "exclamationCharacterDescriptor": { - "message": "Exclamation mark", + "message": "znak !", "description": "Represents the ! key in screen reader content as a readable word" }, "atSignCharacterDescriptor": { - "message": "At sign", + "message": "znak @", "description": "Represents the @ key in screen reader content as a readable word" }, "hashSignCharacterDescriptor": { - "message": "Hash sign", + "message": "znak #", "description": "Represents the # key in screen reader content as a readable word" }, "dollarSignCharacterDescriptor": { - "message": "Dollar sign", + "message": "znak $", "description": "Represents the $ key in screen reader content as a readable word" }, "percentSignCharacterDescriptor": { - "message": "Percent sign", + "message": "znak %", "description": "Represents the % key in screen reader content as a readable word" }, "caretCharacterDescriptor": { - "message": "Caret", + "message": "znak ^", "description": "Represents the ^ key in screen reader content as a readable word" }, "ampersandCharacterDescriptor": { - "message": "Ampersand", + "message": "znak &", "description": "Represents the & key in screen reader content as a readable word" }, "asteriskCharacterDescriptor": { - "message": "Asterisk", + "message": "znak *", "description": "Represents the * key in screen reader content as a readable word" }, "parenLeftCharacterDescriptor": { - "message": "Left parenthesis", + "message": "lijeva zagrada (", "description": "Represents the ( key in screen reader content as a readable word" }, "parenRightCharacterDescriptor": { - "message": "Right parenthesis", + "message": "desna zagrada )", "description": "Represents the ) key in screen reader content as a readable word" }, "hyphenCharacterDescriptor": { - "message": "Underscore", + "message": "donja crtica _", "description": "Represents the _ key in screen reader content as a readable word" }, "underscoreCharacterDescriptor": { - "message": "Hyphen", + "message": "crtica -", "description": "Represents the - key in screen reader content as a readable word" }, "plusCharacterDescriptor": { - "message": "Plus", + "message": "znak +", "description": "Represents the + key in screen reader content as a readable word" }, "equalsCharacterDescriptor": { - "message": "Equals", + "message": "znak =", "description": "Represents the = key in screen reader content as a readable word" }, "braceLeftCharacterDescriptor": { - "message": "Left brace", + "message": "znak {", "description": "Represents the { key in screen reader content as a readable word" }, "braceRightCharacterDescriptor": { - "message": "Right brace", + "message": "znak }", "description": "Represents the } key in screen reader content as a readable word" }, "bracketLeftCharacterDescriptor": { - "message": "Left bracket", + "message": "znak [", "description": "Represents the [ key in screen reader content as a readable word" }, "bracketRightCharacterDescriptor": { - "message": "Right bracket", + "message": "zank ]", "description": "Represents the ] key in screen reader content as a readable word" }, "pipeCharacterDescriptor": { - "message": "Pipe", + "message": "znak |", "description": "Represents the | key in screen reader content as a readable word" }, "backSlashCharacterDescriptor": { - "message": "Back slash", + "message": "znak \\", "description": "Represents the back slash key in screen reader content as a readable word" }, "colonCharacterDescriptor": { - "message": "Colon", + "message": "znak :", "description": "Represents the : key in screen reader content as a readable word" }, "semicolonCharacterDescriptor": { - "message": "Semicolon", + "message": "znak ;", "description": "Represents the ; key in screen reader content as a readable word" }, "doubleQuoteCharacterDescriptor": { - "message": "Double quote", + "message": "znak \"", "description": "Represents the double quote key in screen reader content as a readable word" }, "singleQuoteCharacterDescriptor": { - "message": "Single quote", + "message": "znak '", "description": "Represents the ' key in screen reader content as a readable word" }, "lessThanCharacterDescriptor": { - "message": "Less than", + "message": "znak <", "description": "Represents the < key in screen reader content as a readable word" }, "greaterThanCharacterDescriptor": { - "message": "Greater than", + "message": "znak >", "description": "Represents the > key in screen reader content as a readable word" }, "commaCharacterDescriptor": { - "message": "Comma", + "message": "znak ,", "description": "Represents the , key in screen reader content as a readable word" }, "periodCharacterDescriptor": { - "message": "Period", + "message": "znak .", "description": "Represents the . key in screen reader content as a readable word" }, "questionCharacterDescriptor": { - "message": "Question mark", + "message": "znak ?", "description": "Represents the ? key in screen reader content as a readable word" }, "forwardSlashCharacterDescriptor": { - "message": "Forward slash", + "message": "znak /", "description": "Represents the / key in screen reader content as a readable word" }, "lowercaseAriaLabel": { - "message": "Lowercase" + "message": "Mala slova" }, "uppercaseAriaLabel": { - "message": "Uppercase" + "message": "Velika slova" }, "generatedPassword": { - "message": "Generated password" + "message": "Generiraj lozinku" }, "compactMode": { - "message": "Compact mode" + "message": "Kompaktni način" }, "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Važna napomena" + }, + "setupTwoStepLogin": { + "message": "Postavi dvostruku autentifikaciju" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden će, počevši od veljače 2025., za provjeru prijava s novih uređaja poslati kôd na e-poštu tvog računa." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Prijavu dvostrukom autentifikacijom možeš postaviti kao alternativni način zaštite svog računa ili promijeni svoju e-poštu u onu kojoj možeš pristupiti." + }, + "remindMeLater": { + "message": "Podsjeti me kasnije" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Imaš li pouzdan pristup svojoj e-pošti: $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Ne, nemam" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Da, pouzdano mogu pristupiti svojoj e-pošti" + }, + "turnOnTwoStepLogin": { + "message": "Uključi prijavu dvostrukom autentifikacijom" + }, + "changeAcctEmail": { + "message": "Promjeni e-poštu računa" + }, "extensionWidth": { - "message": "Extension width" + "message": "Širina proširenja" }, "wide": { - "message": "Wide" + "message": "Široko" }, "extraWide": { - "message": "Extra wide" + "message": "Ekstra široko" } } diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index 25ea3d440b7..8b8c9722909 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Automatikus kitöltés személyazonosság" }, + "fillVerificationCode": { + "message": "Ellenőrző kód kitöltése" + }, + "fillVerificationCodeAria": { + "message": "Ellenőrző Kód kitöltése", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Jelszó generálás (másolt)" }, @@ -447,9 +454,6 @@ "length": { "message": "Hossz" }, - "passwordMinLength": { - "message": "Minimum jelszó hosszúság" - }, "uppercase": { "message": "Nagybetűs (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimális speciális" }, - "avoidAmbChar": { - "message": "Félreérthető karakterek mellőzése", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Félreérthető karakterek mellőzése", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Bővítmény értékelése" }, - "rateExtensionDesc": { - "message": "Kérlek, fontold meg egy jó értékelés hagyását, ezzel segítve nekünk!" - }, "browserNotSupportClipboard": { "message": "A webböngésződ nem támogat könnyű vágólap másolást. Másold manuálisan inkább." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Azonosítás elemek listázása a Fül oldalon a könnyű automatikus kitöltéshez." }, + "clickToAutofillOnVault": { + "message": "Kattintsunk az elemekre az automatikus kitöltéshez a Széf nézetben" + }, "clearClipboard": { "message": "Vágólap ürítése", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Áthelyezés szervezethez" }, - "share": { - "message": "Megosztás" - }, "movedItemToOrg": { "message": "$ITEMNAME$ átkerült $ORGNAME$ szervezethez", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Klónozás" }, - "passwordGeneratorPolicyInEffect": { - "message": "Egy vagy több szervezeti szabály érinti a generátor beállításokat." - }, "passwordGenerator": { "message": "Jelszó generátor" }, @@ -2385,14 +2379,6 @@ "message": "Send részletek", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Send keresése", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Send hozzáadása", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Szöveg" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Szöveg elrejtése alapértelmezetten" }, - "maxAccessCountReached": { - "message": "A maximális hozzáférések száma elérésre került.", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Lejárt" }, - "pendingDeletion": { - "message": "Függőben lévő törlés" - }, "passwordProtected": { "message": "Jelszóval védett" }, @@ -2468,24 +2447,9 @@ "message": "Send szerkesztése", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Milyen típusú ez a Send?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Barátságos név a Send leírására.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "A küldendő fájl." - }, "deletionDate": { "message": "Törlési dátum" }, - "deletionDateDesc": { - "message": "A Send véglegesen törölve lesz a meghatározott időpontban.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "A Send véglegesen törölve lesz ebben az időpontban.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Lejárati dátum" }, - "expirationDateDesc": { - "message": "Amennyiben be van állítva, a hozzáférés ehhez a Sendhez a meghatározott időpontban lejár", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 nap" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Egyedi" }, - "maximumAccessCount": { - "message": "Maximális elérési szám" - }, - "maximumAccessCountDesc": { - "message": "Beállítva a Küldés elérhetetlen lesz a meghatározott hozzáférések számának elérése után.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Opcionálisan megadhatunk egy jelszót a felhasználók számára a Küldés eléréséhez. ", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Adjunk meg egy opcionális jelszót a címzetteknek a Send eléréséhez.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Személyes megjegyzések erről a Küldésről.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "A Send letiltásával senki nem férhet hozzá.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Mentéskor másoljuk a Küldés hivatkozását a vágólapra.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "A küldendő fájl." - }, - "sendHideText": { - "message": "Alapértelmezés szerint elrejti a Küldés szövegét.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Aktuális elérési szám" - }, "createSend": { "message": "Új Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Mielőtt belevágnánk" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Naptár-stílusú dátumválasztáshoz", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "kattintás ide", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "az ablak megnyitásához", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "A megadott lejárati idő nem érvényes." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Hiba történt a törlési és a lejárati dátum mentésekor." }, - "hideEmail": { - "message": "Saját email cím elrejtése a címzettek elől." - }, "hideYourEmail": { "message": "Saját email cím elrejtése a megtekintések elől." }, - "sendOptionsPolicyInEffect": { - "message": "Egy vagy több szervezeti szabály érinti a Send opciókat." - }, "passwordPrompt": { "message": "Mesterjelszó ismételt megadás" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Hiba" }, - "regenerateUsername": { - "message": "Felhasználónév ismételt geneálása" - }, "generateUsername": { "message": "Felhasználónév generálása" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Felhasználónév típusa" - }, "plusAddressedEmail": { "message": "További címzési email cím", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Webhelynév" }, - "whatWouldYouLikeToGenerate": { - "message": "Mit szeretnénk generálni?" - }, - "passwordType": { - "message": "Jelszótípus" - }, "service": { "message": "Szolgáltatás" }, @@ -3174,7 +3071,7 @@ "message": "Összes bejelentkezési opció megtekintése" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Összes bejelentkezési opció megtekintése" }, "notificationSentDevice": { "message": "Egy értesítés lett elküldve az eszközre." @@ -3580,6 +3477,14 @@ "message": "Oldjuk fel a fiók zárolását, új ablakban nyílik meg.", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Időalapú, egyszeri jelszó ellenőrző kód", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "A jelenlegi TOTP lejártáig hátralévő idő", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Töltse kia hitelesítő adatokat", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Az automatikus bejelentkezési kitöltési javaslatok számának megjelenítése a bővítmény ikonján" }, + "showQuickCopyActions": { + "message": "Gyors másolási műveletek megjelenítése a Széfen" + }, "systemDefault": { "message": "Rendszer alapértelmezett" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Béta" }, + "importantNotice": { + "message": "Fontos megjegyzés" + }, + "setupTwoStepLogin": { + "message": "Kétlépéses bejelentkezés beüzemelése" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "A Bitwarden 2025 februárjától kódot küld a fiókhoz tartozó email-címre, amellyel ellenőrizhetők az új eszközökről történő bejelentkezések." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "A fiók védelmének alternatív módjaként beállíthatunk kétlépcsős bejelentkezést vagy módosíthatjuk az email címet egy elérhetőre." + }, + "remindMeLater": { + "message": "Emlékeztetés később" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Megbízható a hozzáférés $EMAIL$ email címhez?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nem, nem érem el" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Igen, megbízhatóan hozzáférek az emailjeimhez" + }, + "turnOnTwoStepLogin": { + "message": "Kétlépéses bejelentkezés bekapcsolása" + }, + "changeAcctEmail": { + "message": "Fiók email cím megváltoztatása" + }, "extensionWidth": { "message": "Kiterjesztés szélesség" }, diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 8a341c59b8d..7b1ad51e0b7 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -3,7 +3,7 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden Pengelola Sandi", + "message": "Pengelola Sandi Bitwarden", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofill identitas" }, + "fillVerificationCode": { + "message": "Isikan kode verifikasi" + }, + "fillVerificationCodeAria": { + "message": "Isikan Kode Verifikasi", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Membuat Kata Sandi (tersalin)" }, @@ -447,9 +454,6 @@ "length": { "message": "Panjang" }, - "passwordMinLength": { - "message": "Panjang kata sandi minimum" - }, "uppercase": { "message": "Huruf besar (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Spesial Minimum" }, - "avoidAmbChar": { - "message": "Hindari Karakter Ambigu", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Hindari karakter ambigu", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Nilai Ekstensi" }, - "rateExtensionDesc": { - "message": "Mohon pertimbangkan membantu kami dengan ulasan yang baik!" - }, "browserNotSupportClipboard": { "message": "Peramban Anda tidak mendukung menyalin clipboard dengan mudah. Salin secara manual." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Buat tampilan daftar benda dari identitas pada halaman Tab untuk isi otomatis yang mudah." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Hapus Papan Klip", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Pindah ke Organisasi" }, - "share": { - "message": "Bagikan" - }, "movedItemToOrg": { "message": "$ITEMNAME$ pindah ke $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Duplikat" }, - "passwordGeneratorPolicyInEffect": { - "message": "Satu atau lebih kebijakan organisasi mempengaruhi pengaturan pembuat sandi Anda." - }, "passwordGenerator": { "message": "Pembuat kata sandi" }, @@ -2385,14 +2379,6 @@ "message": "Rincian Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Pencarian Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Tambahkan Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Teks" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Sembunyikan teks secara bawaan" }, - "maxAccessCountReached": { - "message": "Jumlah akses maksimum tercapai", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Kedaluwarsa" }, - "pendingDeletion": { - "message": "Penghapusan menunggu keputusan" - }, "passwordProtected": { "message": "Dilindungi kata sandi" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Jenis Send apakah ini?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Nama yang bersahabat untuk menggambarkan Send ini.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "File yang ingin Anda kirim." - }, "deletionDate": { "message": "Tanggal Penghapusan" }, - "deletionDateDesc": { - "message": "Send akan dihapus secara permanen pada tanggal dan waktu yang ditentukan.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Send akan dihapus selamanya pada tanggal ini.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Tanggal habis tempo" }, - "expirationDateDesc": { - "message": "Jika disetel, akses ke Send ini akan berakhir pada tanggal dan waktu yang ditentukan.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 hari" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Kustom" }, - "maximumAccessCount": { - "message": "Hitungan Akses Maksimum" - }, - "maximumAccessCountDesc": { - "message": "Jika disetel, pengguna tidak dapat lagi mengakses Send ini setelah jumlah akses maksimum tercapai.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Secara opsional, minta kata sandi bagi pengguna untuk mengakses Send ini.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Tambahkan kata sandi tidak wajib untuk penerima untuk mengakses Send ini.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Catatan pribadi tentang Send ini.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Nonaktifkan Send ini sehingga tidak ada yang dapat mengaksesnya.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Salin tautan Send ini ke papan klip setelah disimpan.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Teks yang ingin Anda kirim." - }, - "sendHideText": { - "message": "Sembunyikan teks Send ini secara default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Hitungan Akses Saat Ini" - }, "createSend": { "message": "Buat Send Baru", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Sebelum kamu memulai" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Untuk menggunakan pemilih tanggal gaya kalender", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "klik disini", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "untuk memunculkan jendela Anda.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Tanggal kedaluwarsa yang diberikan tidak valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Terjadi kesalahan saat menyimpan tanggal penghapusan dan kedaluwarsa Anda." }, - "hideEmail": { - "message": "Sembunyikan alamat surel dari penerima." - }, "hideYourEmail": { "message": "Sembunyikan alamat surel Anda dari penonton." }, - "sendOptionsPolicyInEffect": { - "message": "Satu atau lebih kebijakan organisasi mempengaruhi pengaturan feature Send anda." - }, "passwordPrompt": { "message": "Master password ditanyakan kembali" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Galat" }, - "regenerateUsername": { - "message": "Buat nama pengguna baru" - }, "generateUsername": { "message": "Buat nama pengguna baru" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Jenis nama pengguna" - }, "plusAddressedEmail": { "message": "Surel dengan alamat plus", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Nama situs web" }, - "whatWouldYouLikeToGenerate": { - "message": "Apa yang ingin Anda buat?" - }, - "passwordType": { - "message": "Jenis kata sandi" - }, "service": { "message": "Layanan" }, @@ -3174,7 +3071,7 @@ "message": "Lihat semua pilihan masuk" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Lihat semua pilihan masuk" }, "notificationSentDevice": { "message": "Sebuah pemberitahuan dikirim ke perangkat Anda." @@ -3580,6 +3477,14 @@ "message": "Buka akun Anda, membukanya di jendela baru", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Kode Verifikasi Kata Sandi Sekali-Waktu Berbasis Waktu", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Waktu tersisa sebelum TOTP sekarang kadaluwarsa", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Isi tanda pengenal untuk", "description": "Screen reader text for when overlay item is in focused" @@ -4324,10 +4229,10 @@ "message": "Pengenalan" }, "contactInfo": { - "message": "Contact info" + "message": "Info kontak" }, "downloadAttachment": { - "message": "Download - $ITEMNAME$", + "message": "Unduh - $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -4336,23 +4241,23 @@ } }, "cardNumberEndsWith": { - "message": "card number ends with", + "message": "nomor kartu berakhiran", "description": "Used within the inline menu to provide an aria description when users are attempting to fill a card cipher." }, "loginCredentials": { - "message": "Login credentials" + "message": "Kredensial login" }, "authenticatorKey": { - "message": "Authenticator key" + "message": "Kunci Otentikator" }, "autofillOptions": { - "message": "Autofill options" + "message": "Pilihan isi otomatis" }, "websiteUri": { - "message": "Website (URI)" + "message": "Situs web (URI)" }, "websiteUriCount": { - "message": "Website (URI) $COUNT$", + "message": "$COUNT$ Situs web (URI)", "description": "Label for an input field that contains a website URI. The input field is part of a list of fields, and the count indicates the position of the field in the list.", "placeholders": { "count": { @@ -4362,16 +4267,16 @@ } }, "websiteAdded": { - "message": "Website added" + "message": "Situs web ditambahkan" }, "addWebsite": { - "message": "Add website" + "message": "Tambah situs web" }, "deleteWebsite": { - "message": "Delete website" + "message": "Hapus situs web" }, "defaultLabel": { - "message": "Default ($VALUE$)", + "message": "Bawaan ($VALUE$)", "description": "A label that indicates the default value for a field with the current default value in parentheses.", "placeholders": { "value": { @@ -4381,7 +4286,7 @@ } }, "showMatchDetection": { - "message": "Show match detection $WEBSITE$", + "message": "Tampilkan deteksi kecocokan $WEBSITE$", "placeholders": { "website": { "content": "$1", @@ -4390,7 +4295,7 @@ } }, "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", + "message": "Sembunyikan deteksi kecocokan $WEBSITE$", "placeholders": { "website": { "content": "$1", @@ -4399,19 +4304,19 @@ } }, "autoFillOnPageLoad": { - "message": "Autofill on page load?" + "message": "Isi otomatis ketika halaman dimuat?" }, "cardExpiredTitle": { - "message": "Expired card" + "message": "Kartu kadaluwarsa" }, "cardExpiredMessage": { - "message": "If you've renewed it, update the card's information" + "message": "Jika Anda telah memperpanjangnya, perbarui informasi kartu" }, "cardDetails": { - "message": "Card details" + "message": "Rincian kartu" }, "cardBrandDetails": { - "message": "$BRAND$ details", + "message": "Rincian $BRAND$", "placeholders": { "brand": { "content": "$1", @@ -4420,43 +4325,43 @@ } }, "enableAnimations": { - "message": "Enable animations" + "message": "Nyalakan animasi" }, "showAnimations": { - "message": "Show animations" + "message": "Tampilkan animasi" }, "addAccount": { - "message": "Add account" + "message": "Tambah akun" }, "loading": { - "message": "Loading" + "message": "Memuat" }, "data": { "message": "Data" }, "passkeys": { - "message": "Passkeys", + "message": "Kunci sandi", "description": "A section header for a list of passkeys." }, "passwords": { - "message": "Passwords", + "message": "Kata Sandi", "description": "A section header for a list of passwords." }, "logInWithPasskeyAriaLabel": { - "message": "Log in with passkey", + "message": "Masuk dengan kunci sandi", "description": "ARIA label for the inline menu button that logs in with a passkey." }, "assign": { - "message": "Assign" + "message": "Terapkan" }, "bulkCollectionAssignmentDialogDescriptionSingular": { - "message": "Only organization members with access to these collections will be able to see the item." + "message": "Hanya anggota organisasi dengan akses ke koleksi berikut yang dapat melihat isinya." }, "bulkCollectionAssignmentDialogDescriptionPlural": { - "message": "Only organization members with access to these collections will be able to see the items." + "message": "Hanya anggota organisasi dengan akses ke koleksi berikut yang dapat melihat isinya." }, "bulkCollectionAssignmentWarning": { - "message": "You have selected $TOTAL_COUNT$ items. You cannot update $READONLY_COUNT$ of the items because you do not have edit permissions.", + "message": "Anda telah memilih $TOTAL_COUNT$ benda. Anda tidak dapat memperbarui $READONLY_COUNT$ dari benda karena Anda tidak memiliki izin untuk menyunting.", "placeholders": { "total_count": { "content": "$1", @@ -4468,37 +4373,37 @@ } }, "addField": { - "message": "Add field" + "message": "Tambahkan bidang" }, "add": { - "message": "Add" + "message": "Tambah" }, "fieldType": { - "message": "Field type" + "message": "Jenis bidang" }, "fieldLabel": { - "message": "Field label" + "message": "Label bidang" }, "textHelpText": { - "message": "Use text fields for data like security questions" + "message": "Gunakan bidang teks untuk data seperti pertanyaan keamanan" }, "hiddenHelpText": { - "message": "Use hidden fields for sensitive data like a password" + "message": "Gunakan bidang tersembunyi untuk data sensitif seperti kata sandi" }, "checkBoxHelpText": { - "message": "Use checkboxes if you'd like to autofill a form's checkbox, like a remember email" + "message": "Gunakan kotak centang jika Anda ingin mengisi sebuah kotak centang di formullir, seperti mengingat surel" }, "linkedHelpText": { - "message": "Use a linked field when you are experiencing autofill issues for a specific website." + "message": "Gunakan bidang tertaut ketika Anda mengalami masalah pengisian otomatis untuk situs web tertentu." }, "linkedLabelHelpText": { - "message": "Enter the the field's html id, name, aria-label, or placeholder." + "message": "Masukkan id, name, aria-label, atau placeholder html dari bidang." }, "editField": { - "message": "Edit field" + "message": "Sunting bidang" }, "editFieldLabel": { - "message": "Edit $LABEL$", + "message": "Sunting $LABEL$", "placeholders": { "label": { "content": "$1", @@ -4507,7 +4412,7 @@ } }, "deleteCustomField": { - "message": "Delete $LABEL$", + "message": "Hapus $LABEL$", "placeholders": { "label": { "content": "$1", @@ -4516,7 +4421,7 @@ } }, "fieldAdded": { - "message": "$LABEL$ added", + "message": "$LABEL$ ditambahkan", "placeholders": { "label": { "content": "$1", @@ -4525,7 +4430,7 @@ } }, "reorderToggleButton": { - "message": "Reorder $LABEL$. Use arrow key to move item up or down.", + "message": "Urutkan $LABEL$. Gunakan tombol panah untuk memindahkan benda ke atas atau ke bawah.", "placeholders": { "label": { "content": "$1", @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4753,155 +4661,191 @@ "description": "Represents the space key in screen reader content as a readable word" }, "tildeCharacterDescriptor": { - "message": "Tilde", + "message": "Tanda gelombang", "description": "Represents the ~ key in screen reader content as a readable word" }, "backtickCharacterDescriptor": { - "message": "Backtick", + "message": "Tanda petik terbalik", "description": "Represents the ` key in screen reader content as a readable word" }, "exclamationCharacterDescriptor": { - "message": "Exclamation mark", + "message": "Tanda seru", "description": "Represents the ! key in screen reader content as a readable word" }, "atSignCharacterDescriptor": { - "message": "At sign", + "message": "Tanda pada", "description": "Represents the @ key in screen reader content as a readable word" }, "hashSignCharacterDescriptor": { - "message": "Hash sign", + "message": "Tanda pagar", "description": "Represents the # key in screen reader content as a readable word" }, "dollarSignCharacterDescriptor": { - "message": "Dollar sign", + "message": "Tanda dolar", "description": "Represents the $ key in screen reader content as a readable word" }, "percentSignCharacterDescriptor": { - "message": "Percent sign", + "message": "Tanda persen", "description": "Represents the % key in screen reader content as a readable word" }, "caretCharacterDescriptor": { - "message": "Caret", + "message": "Tanda sisipan", "description": "Represents the ^ key in screen reader content as a readable word" }, "ampersandCharacterDescriptor": { - "message": "Ampersand", + "message": "Tanda dan", "description": "Represents the & key in screen reader content as a readable word" }, "asteriskCharacterDescriptor": { - "message": "Asterisk", + "message": "Tanda bintang", "description": "Represents the * key in screen reader content as a readable word" }, "parenLeftCharacterDescriptor": { - "message": "Left parenthesis", + "message": "Tanda kurung kiri", "description": "Represents the ( key in screen reader content as a readable word" }, "parenRightCharacterDescriptor": { - "message": "Right parenthesis", + "message": "Tanda kurung kanan", "description": "Represents the ) key in screen reader content as a readable word" }, "hyphenCharacterDescriptor": { - "message": "Underscore", + "message": "Garis bawah", "description": "Represents the _ key in screen reader content as a readable word" }, "underscoreCharacterDescriptor": { - "message": "Hyphen", + "message": "Tanda penghubung", "description": "Represents the - key in screen reader content as a readable word" }, "plusCharacterDescriptor": { - "message": "Plus", + "message": "Tanda tambah", "description": "Represents the + key in screen reader content as a readable word" }, "equalsCharacterDescriptor": { - "message": "Equals", + "message": "Tanda sama dengan", "description": "Represents the = key in screen reader content as a readable word" }, "braceLeftCharacterDescriptor": { - "message": "Left brace", + "message": "Kurung kurawal kiri", "description": "Represents the { key in screen reader content as a readable word" }, "braceRightCharacterDescriptor": { - "message": "Right brace", + "message": "Kurung kurawal kanan", "description": "Represents the } key in screen reader content as a readable word" }, "bracketLeftCharacterDescriptor": { - "message": "Left bracket", + "message": "Tanda kurung siku kiri", "description": "Represents the [ key in screen reader content as a readable word" }, "bracketRightCharacterDescriptor": { - "message": "Right bracket", + "message": "Tanda kurung siku kanan", "description": "Represents the ] key in screen reader content as a readable word" }, "pipeCharacterDescriptor": { - "message": "Pipe", + "message": "Garis tegak lurus", "description": "Represents the | key in screen reader content as a readable word" }, "backSlashCharacterDescriptor": { - "message": "Back slash", + "message": "Garis miring terbalik", "description": "Represents the back slash key in screen reader content as a readable word" }, "colonCharacterDescriptor": { - "message": "Colon", + "message": "Tanda titik dua", "description": "Represents the : key in screen reader content as a readable word" }, "semicolonCharacterDescriptor": { - "message": "Semicolon", + "message": "Tanda titik koma", "description": "Represents the ; key in screen reader content as a readable word" }, "doubleQuoteCharacterDescriptor": { - "message": "Double quote", + "message": "Tanda petik ganda", "description": "Represents the double quote key in screen reader content as a readable word" }, "singleQuoteCharacterDescriptor": { - "message": "Single quote", + "message": "Tanda petik tunggal", "description": "Represents the ' key in screen reader content as a readable word" }, "lessThanCharacterDescriptor": { - "message": "Less than", + "message": "Tanda kurang dari", "description": "Represents the < key in screen reader content as a readable word" }, "greaterThanCharacterDescriptor": { - "message": "Greater than", + "message": "Tanda lebih besar dari", "description": "Represents the > key in screen reader content as a readable word" }, "commaCharacterDescriptor": { - "message": "Comma", + "message": "Tanda koma", "description": "Represents the , key in screen reader content as a readable word" }, "periodCharacterDescriptor": { - "message": "Period", + "message": "Tanda titik", "description": "Represents the . key in screen reader content as a readable word" }, "questionCharacterDescriptor": { - "message": "Question mark", + "message": "Tanda tanya", "description": "Represents the ? key in screen reader content as a readable word" }, "forwardSlashCharacterDescriptor": { - "message": "Forward slash", + "message": "Tanda garis miring ke depan", "description": "Represents the / key in screen reader content as a readable word" }, "lowercaseAriaLabel": { - "message": "Lowercase" + "message": "Huruf kecil" }, "uppercaseAriaLabel": { - "message": "Uppercase" + "message": "Huruf kapital" }, "generatedPassword": { - "message": "Generated password" + "message": "Kata sandi yang dihasilkan" }, "compactMode": { - "message": "Compact mode" + "message": "Mode ringkas" }, "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { - "message": "Extension width" + "message": "Lebar ekstensi" }, "wide": { - "message": "Wide" + "message": "Lebar" }, "extraWide": { - "message": "Extra wide" + "message": "Ekstra lebar" } } diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index ff2326809a8..711f62f4dea 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Riempi automaticamente identità" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Genera password e copiala" }, @@ -447,9 +454,6 @@ "length": { "message": "Lunghezza" }, - "passwordMinLength": { - "message": "Lunghezza minima della password" - }, "uppercase": { "message": "Maiuscole (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimo caratteri speciali" }, - "avoidAmbChar": { - "message": "Evita caratteri ambigui", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Evita caratteri ambigui", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Valuta l'estensione" }, - "rateExtensionDesc": { - "message": "Aiutaci lasciando una buona recensione!" - }, "browserNotSupportClipboard": { "message": "Il tuo browser non supporta copiare dagli appunti. Copialo manualmente." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Mostra le identità nella sezione Scheda per riempirle automaticamente." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Cancella appunti", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Sposta in organizzazione" }, - "share": { - "message": "Condividi" - }, "movedItemToOrg": { "message": "$ITEMNAME$ spostato in $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Clona" }, - "passwordGeneratorPolicyInEffect": { - "message": "Una o più politiche dell'organizzazione stanno influenzando le impostazioni del tuo generatore." - }, "passwordGenerator": { "message": "Generatore di password" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Cerca Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Aggiungi Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Testo" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Numero massimo di accessi raggiunto", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Scaduto" }, - "pendingDeletion": { - "message": "In attesa di eliminazione" - }, "passwordProtected": { "message": "Protetto da password" }, @@ -2468,24 +2447,9 @@ "message": "Modifica Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Che tipo di Send è questo?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Un nome intuitivo per descrivere questo Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Il file da inviare." - }, "deletionDate": { "message": "Data di eliminazione" }, - "deletionDateDesc": { - "message": "Il Send sarà eliminato definitivamente alla data e ora specificate.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Data di scadenza" }, - "expirationDateDesc": { - "message": "Se impostato, l'accesso a questo Send scadrà alla data e ora specificate.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 giorno" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Personalizzato" }, - "maximumAccessCount": { - "message": "Numero massimo di accessi" - }, - "maximumAccessCountDesc": { - "message": "Se impostata, gli utenti non potranno più accedere a questo Send una volta raggiunto il numero massimo di accessi.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Richiedi una password agli utenti per accedere a questo Send (facoltativo).", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Note private sul Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Disattiva il Send per renderlo inaccessibile.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copia il link al Send negli appunti dopo averlo salvato.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Il testo che vuoi inviare." - }, - "sendHideText": { - "message": "Nascondi il testo di questo Send in modo predefinito.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Numero di accessi correnti" - }, "createSend": { "message": "Nuovo Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Prima di iniziare" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Per usare un selettore di date stile calendario", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "clicca qui", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "per aprire la finestra.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "La data di scadenza fornita non è valida." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Si è verificato un errore durante il salvataggio delle date di eliminazione e scadenza." }, - "hideEmail": { - "message": "Nascondi il mio indirizzo email dai destinatari." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "Una o più politiche dell'organizzazione stanno influenzando le tue opzioni di Send." - }, "passwordPrompt": { "message": "Richiedi di inserire la password principale di nuovo per visualizzare questo elemento" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Errore" }, - "regenerateUsername": { - "message": "Rigenera nome utente" - }, "generateUsername": { "message": "Genera nome utente" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Tipo di nome utente" - }, "plusAddressedEmail": { "message": "Indirizzo email alternativo", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Nome sito web" }, - "whatWouldYouLikeToGenerate": { - "message": "Cosa vuoi generare?" - }, - "passwordType": { - "message": "Tipo di password" - }, "service": { "message": "Servizio" }, @@ -3580,6 +3477,14 @@ "message": "Sblocca il tuo account, apri in una nuova finestra", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Riempi le credenziali per", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Mostra il numero di suggerimenti di riempimento automatico sull'icona dell'estensione" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "Predefinito del sistema" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index fa12570fc50..0ffe01e7992 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -20,16 +20,16 @@ "message": "アカウントの作成" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Bitwarden は初めてですか?" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "パスキーでログイン" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "シングルサインオンを使用する" }, "welcomeBack": { - "message": "Welcome back" + "message": "ようこそ" }, "setAStrongPassword": { "message": "強力なパスワードを設定する" @@ -84,7 +84,7 @@ "message": "組織に参加" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "$ORGANIZATIONNAME$ に参加", "placeholders": { "organizationName": { "content": "$1", @@ -120,7 +120,7 @@ "message": "パスワードをコピー" }, "copyPassphrase": { - "message": "Copy passphrase" + "message": "パスフレーズをコピー" }, "copyNote": { "message": "メモをコピー" @@ -153,13 +153,13 @@ "message": "免許証番号をコピー" }, "copyPrivateKey": { - "message": "Copy private key" + "message": "秘密鍵をコピー" }, "copyPublicKey": { - "message": "Copy public key" + "message": "公開鍵をコピー" }, "copyFingerprint": { - "message": "Copy fingerprint" + "message": "フィンガープリントをコピー" }, "copyCustomField": { "message": "$FIELD$ をコピー", @@ -177,7 +177,7 @@ "message": "メモをコピー" }, "fill": { - "message": "Fill", + "message": "入力", "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "自動入力 ID" }, + "fillVerificationCode": { + "message": "認証コードを入力" + }, + "fillVerificationCodeAria": { + "message": "認証コードを入力", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "パスワードを生成 (コピー)" }, @@ -436,7 +443,7 @@ "message": "パスワードの自動生成" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "パスフレーズを生成" }, "regeneratePassword": { "message": "パスワードの再生成" @@ -447,9 +454,6 @@ "length": { "message": "長さ" }, - "passwordMinLength": { - "message": "パスワードの最低文字数" - }, "uppercase": { "message": "大文字(A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "記号の最小数" }, - "avoidAmbChar": { - "message": "あいまいな文字を省く", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "あいまいな文字を避ける", "description": "Label for the avoid ambiguous characters checkbox." @@ -600,7 +600,7 @@ "message": "ウェブサイトを開く" }, "launchWebsiteName": { - "message": "Launch website $ITEMNAME$", + "message": "ウェブサイト $ITEMNAME$ を開く", "placeholders": { "itemname": { "content": "$1", @@ -641,9 +641,6 @@ "rateExtension": { "message": "拡張機能の評価" }, - "rateExtensionDesc": { - "message": "良いレビューで私たちを助けてください!" - }, "browserNotSupportClipboard": { "message": "お使いのブラウザはクリップボードへのコピーに対応していません。手動でコピーしてください" }, @@ -855,7 +852,7 @@ "message": "ログイン" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Bitwarden にログイン" }, "restartRegistration": { "message": "登録を再度始める" @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "自動入力を簡単にするために、タブページに ID アイテムを表示します" }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "クリップボードの消去", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1126,7 +1126,7 @@ "description": "WARNING (should stay in capitalized letters if the language permits)" }, "warningCapitalized": { - "message": "Warning", + "message": "注意", "description": "Warning (should maintain locale-relevant capitalization)" }, "confirmVaultExport": { @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "組織に移動" }, - "share": { - "message": "共有" - }, "movedItemToOrg": { "message": "$ITEMNAME$ を $ORGNAME$ に移動しました", "placeholders": { @@ -1320,10 +1317,10 @@ "message": "認証アプリに表示された6桁の認証コードを入力してください。" }, "authenticationTimeout": { - "message": "Authentication timeout" + "message": "認証のタイムアウト" }, "authenticationSessionTimedOut": { - "message": "The authentication session timed out. Please restart the login process." + "message": "認証セッションの有効期限が切れました。ログインプロセスを再開してください。" }, "enterVerificationCodeEmail": { "message": "$EMAIL$に送信された6桁の認証コードを入力してください。", @@ -1443,7 +1440,7 @@ "message": "サーバー URL" }, "selfHostBaseUrl": { - "message": "Self-host server URL", + "message": "自己ホスト型サーバー URL", "description": "Label for field requesting a self-hosted integration service URL" }, "apiUrl": { @@ -1475,10 +1472,10 @@ "message": "フォームフィールドに自動入力の候補を表示する" }, "showInlineMenuIdentitiesLabel": { - "message": "Display identities as suggestions" + "message": "ID を候補として表示する" }, "showInlineMenuCardsLabel": { - "message": "Display cards as suggestions" + "message": "カードを候補として表示する" }, "showInlineMenuOnIconSelectionLabel": { "message": "アイコンが選択されているときに候補を表示する" @@ -1771,7 +1768,7 @@ "message": "ID" }, "typeSshKey": { - "message": "SSH key" + "message": "SSH 鍵" }, "newItemHeader": { "message": "$TYPE$ を新規作成", @@ -1804,13 +1801,13 @@ "message": "パスワードの履歴" }, "generatorHistory": { - "message": "Generator history" + "message": "生成履歴" }, "clearGeneratorHistoryTitle": { - "message": "Clear generator history" + "message": "生成履歴を消去" }, "cleargGeneratorHistoryDescription": { - "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + "message": "続行すると、すべてのエントリは生成履歴から完全に削除されます。続行してもよろしいですか?" }, "back": { "message": "戻る" @@ -1849,7 +1846,7 @@ "message": "セキュアメモ" }, "sshKeys": { - "message": "SSH Keys" + "message": "SSH 鍵" }, "clear": { "message": "消去する", @@ -1932,10 +1929,10 @@ "message": "履歴を消去" }, "nothingToShow": { - "message": "Nothing to show" + "message": "表示するものがありません" }, "nothingGeneratedRecently": { - "message": "You haven't generated anything recently" + "message": "最近生成したものはありません" }, "remove": { "message": "削除" @@ -2043,9 +2040,6 @@ "clone": { "message": "複製" }, - "passwordGeneratorPolicyInEffect": { - "message": "一つ以上の組織のポリシーがパスワード生成の設定に影響しています。" - }, "passwordGenerator": { "message": "パスワード生成ツール" }, @@ -2385,14 +2379,6 @@ "message": "Send の詳細", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Send を検索", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Send を追加", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "テキスト" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "デフォルトでテキストを隠す" }, - "maxAccessCountReached": { - "message": "最大アクセス数に達しました", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "有効期限切れ" }, - "pendingDeletion": { - "message": "削除の保留中" - }, "passwordProtected": { "message": "パスワード保護あり" }, @@ -2468,24 +2447,9 @@ "message": "Send を編集", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "この Send の種類は何ですか?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "この Send を説明するわかりやすい名前", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "送信するファイル" - }, "deletionDate": { "message": "削除日時" }, - "deletionDateDesc": { - "message": "Send は指定された日時に完全に削除されます。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Send はこの日付で完全に削除されます。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "有効期限" }, - "expirationDateDesc": { - "message": "設定されている場合、この Send へのアクセスは指定された日時に失効します。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1日" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "カスタム" }, - "maximumAccessCount": { - "message": "最大アクセス数" - }, - "maximumAccessCountDesc": { - "message": "設定されている場合、最大アクセス数に達するとユーザーはこの Send にアクセスできなくなります。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "必要に応じて、ユーザーがこの Send にアクセスするためのパスワードを要求します。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { - "message": "Add an optional password for recipients to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNotesDesc": { - "message": "この Send に関するプライベートメモ", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "誰もアクセスできないように、この Send を無効にする", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "保存時にこの Send のリンクをクリップボードにコピーする", + "message": "受信者がこの Send にアクセスするための任意のパスワードを追加します。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTextDesc": { - "message": "送信したいテキスト" - }, - "sendHideText": { - "message": "この Send のテキストをデフォルトで非表示にする", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "現在のアクセス数" - }, "createSend": { "message": "新しい Send を作成", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "はじめる前に" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "カレンダースタイルの日付ピッカーを使用するには", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "こちらをクリック", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "してください。", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "入力された有効期限は正しくありません。" }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "削除と有効期限の保存中にエラーが発生しました。" }, - "hideEmail": { - "message": "メールアドレスを受信者に表示しない" - }, "hideYourEmail": { "message": "閲覧者にメールアドレスを見せないようにします。" }, - "sendOptionsPolicyInEffect": { - "message": "一つ以上の組織ポリシーが Send の設定に影響しています。" - }, "passwordPrompt": { "message": "マスターパスワードの再要求" }, @@ -2880,17 +2789,14 @@ "error": { "message": "エラー" }, - "regenerateUsername": { - "message": "ユーザー名を再生成" - }, "generateUsername": { "message": "ユーザー名を生成" }, "generateEmail": { - "message": "Generate email" + "message": "メールアドレスを生成" }, "spinboxBoundariesHint": { - "message": "Value must be between $MIN$ and $MAX$.", + "message": "値は $MIN$ から $MAX$ の間でなければなりません。", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2904,7 +2810,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", + "message": " 強力なパスワードを生成するには、 $RECOMMENDED$ 文字以上を使用してください。", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2914,7 +2820,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": " 強力なパスフレーズを生成するには、 $RECOMMENDED$ 単語以上を使用してください。", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "ユーザー名の種類" - }, "plusAddressedEmail": { "message": "プラス付きのメールアドレス", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "ウェブサイト名" }, - "whatWouldYouLikeToGenerate": { - "message": "何を生成しますか?" - }, - "passwordType": { - "message": "パスワードの種類" - }, "service": { "message": "サービス" }, @@ -2964,11 +2861,11 @@ "message": "外部転送サービスを使用してメールエイリアスを生成します。" }, "forwarderDomainName": { - "message": "Email domain", + "message": "メールアドレスのドメイン", "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { - "message": "Choose a domain that is supported by the selected service", + "message": "選択したサービスでサポートされているドメインを選択してください", "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { @@ -3171,25 +3068,25 @@ "message": "通知を再送信する" }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "すべてのログインオプションを表示" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "すべてのログインオプションを表示" }, "notificationSentDevice": { "message": "デバイスに通知を送信しました。" }, "aNotificationWasSentToYourDevice": { - "message": "A notification was sent to your device" + "message": "お使いのデバイスに通知が送信されました" }, "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { - "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + "message": "アカウントがロック解除されていることと、フィンガープリントフレーズが他の端末で一致していることを確認してください" }, "youWillBeNotifiedOnceTheRequestIsApproved": { - "message": "You will be notified once the request is approved" + "message": "リクエストが承認されると通知されます" }, "needAnotherOptionV1": { - "message": "Need another option?" + "message": "別の選択肢が必要ですか?" }, "loginInitiated": { "message": "ログイン開始" @@ -3285,16 +3182,16 @@ "message": "新しいウィンドウで開く" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "このデバイスを記憶して今後のログインをシームレスにする" }, "deviceApprovalRequired": { "message": "デバイスの承認が必要です。以下から承認オプションを選択してください:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "デバイスの承認が必要です" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "以下の承認オプションを選択してください" }, "rememberThisDevice": { "message": "このデバイスを記憶する" @@ -3370,7 +3267,7 @@ "message": "ユーザーのメールアドレスがありません" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "アクティブなユーザーメールアドレスが見つかりません。ログアウトします。" }, "deviceTrusted": { "message": "信頼されたデバイス" @@ -3580,6 +3477,14 @@ "message": "アカウントのロックを解除し、新しいウィンドウで開く", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "時間ベースのワンタイムパスワード認証コード", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "現在の TOTP 有効期限が切れるまでの残り時間", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "資格情報を入力:", "description": "Screen reader text for when overlay item is in focused" @@ -3809,7 +3714,7 @@ "message": "アクセス中" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "ログインしました!" }, "passkeyNotCopied": { "message": "パスキーはコピーされません" @@ -4303,13 +4208,13 @@ "message": "フィルター" }, "filterVault": { - "message": "Filter vault" + "message": "保管庫をフィルター" }, "filterApplied": { - "message": "One filter applied" + "message": "1 個のフィルタを適用しました" }, "filterAppliedPlural": { - "message": "$COUNT$ filters applied", + "message": "$COUNT$ 個のフィルタを適用しました", "placeholders": { "count": { "content": "$1", @@ -4641,13 +4546,13 @@ "message": "アイテムの場所" }, "fileSend": { - "message": "File Send" + "message": "ファイル Send" }, "fileSends": { "message": "ファイル Send" }, "textSend": { - "message": "Text Send" + "message": "テキスト Send" }, "textSends": { "message": "テキスト Send" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "拡張機能アイコンにログイン自動入力の候補の数を表示する" }, + "showQuickCopyActions": { + "message": "保管庫にクイックコピー操作を表示する" + }, "systemDefault": { "message": "システムのデフォルト" }, @@ -4671,16 +4579,16 @@ "message": "エンタープライズポリシー要件がこの設定に適用されました" }, "sshPrivateKey": { - "message": "Private key" + "message": "秘密鍵" }, "sshPublicKey": { - "message": "Public key" + "message": "公開鍵" }, "sshFingerprint": { - "message": "Fingerprint" + "message": "フィンガープリント" }, "sshKeyAlgorithm": { - "message": "Key type" + "message": "鍵の種類" }, "sshKeyAlgorithmED25519": { "message": "ED25519" @@ -4737,171 +4645,207 @@ "message": "認証中" }, "fillGeneratedPassword": { - "message": "Fill generated password", + "message": "生成したパスワードを入力", "description": "Heading for the password generator within the inline menu" }, "passwordRegenerated": { - "message": "Password regenerated", + "message": "パスワードを再生成しました", "description": "Notification message for when a password has been regenerated" }, "saveLoginToBitwarden": { - "message": "Save login to Bitwarden?", + "message": "Bitwarden にログイン情報を保存しますか?", "description": "Confirmation message for saving a login to Bitwarden" }, "spaceCharacterDescriptor": { - "message": "Space", + "message": "スペース", "description": "Represents the space key in screen reader content as a readable word" }, "tildeCharacterDescriptor": { - "message": "Tilde", + "message": "チルダ", "description": "Represents the ~ key in screen reader content as a readable word" }, "backtickCharacterDescriptor": { - "message": "Backtick", + "message": "バッククォート", "description": "Represents the ` key in screen reader content as a readable word" }, "exclamationCharacterDescriptor": { - "message": "Exclamation mark", + "message": "エクスクラメーションマーク", "description": "Represents the ! key in screen reader content as a readable word" }, "atSignCharacterDescriptor": { - "message": "At sign", + "message": "アットマーク", "description": "Represents the @ key in screen reader content as a readable word" }, "hashSignCharacterDescriptor": { - "message": "Hash sign", + "message": "ハッシュ記号", "description": "Represents the # key in screen reader content as a readable word" }, "dollarSignCharacterDescriptor": { - "message": "Dollar sign", + "message": "ドル記号", "description": "Represents the $ key in screen reader content as a readable word" }, "percentSignCharacterDescriptor": { - "message": "Percent sign", + "message": "パーセント記号", "description": "Represents the % key in screen reader content as a readable word" }, "caretCharacterDescriptor": { - "message": "Caret", + "message": "キャレット", "description": "Represents the ^ key in screen reader content as a readable word" }, "ampersandCharacterDescriptor": { - "message": "Ampersand", + "message": "アンパサンド", "description": "Represents the & key in screen reader content as a readable word" }, "asteriskCharacterDescriptor": { - "message": "Asterisk", + "message": "アスタリスク", "description": "Represents the * key in screen reader content as a readable word" }, "parenLeftCharacterDescriptor": { - "message": "Left parenthesis", + "message": "左かっこ", "description": "Represents the ( key in screen reader content as a readable word" }, "parenRightCharacterDescriptor": { - "message": "Right parenthesis", + "message": "右かっこ", "description": "Represents the ) key in screen reader content as a readable word" }, "hyphenCharacterDescriptor": { - "message": "Underscore", + "message": "アンダースコア", "description": "Represents the _ key in screen reader content as a readable word" }, "underscoreCharacterDescriptor": { - "message": "Hyphen", + "message": "ハイフン", "description": "Represents the - key in screen reader content as a readable word" }, "plusCharacterDescriptor": { - "message": "Plus", + "message": "プラス", "description": "Represents the + key in screen reader content as a readable word" }, "equalsCharacterDescriptor": { - "message": "Equals", + "message": "イコール", "description": "Represents the = key in screen reader content as a readable word" }, "braceLeftCharacterDescriptor": { - "message": "Left brace", + "message": "左中かっこ", "description": "Represents the { key in screen reader content as a readable word" }, "braceRightCharacterDescriptor": { - "message": "Right brace", + "message": "右中かっこ", "description": "Represents the } key in screen reader content as a readable word" }, "bracketLeftCharacterDescriptor": { - "message": "Left bracket", + "message": "左大かっこ", "description": "Represents the [ key in screen reader content as a readable word" }, "bracketRightCharacterDescriptor": { - "message": "Right bracket", + "message": "右大かっこ", "description": "Represents the ] key in screen reader content as a readable word" }, "pipeCharacterDescriptor": { - "message": "Pipe", + "message": "パイプ", "description": "Represents the | key in screen reader content as a readable word" }, "backSlashCharacterDescriptor": { - "message": "Back slash", + "message": "バックスラッシュ", "description": "Represents the back slash key in screen reader content as a readable word" }, "colonCharacterDescriptor": { - "message": "Colon", + "message": "コロン", "description": "Represents the : key in screen reader content as a readable word" }, "semicolonCharacterDescriptor": { - "message": "Semicolon", + "message": "セミコロン", "description": "Represents the ; key in screen reader content as a readable word" }, "doubleQuoteCharacterDescriptor": { - "message": "Double quote", + "message": "ダブルクォート", "description": "Represents the double quote key in screen reader content as a readable word" }, "singleQuoteCharacterDescriptor": { - "message": "Single quote", + "message": "シングルクォート", "description": "Represents the ' key in screen reader content as a readable word" }, "lessThanCharacterDescriptor": { - "message": "Less than", + "message": "小なり", "description": "Represents the < key in screen reader content as a readable word" }, "greaterThanCharacterDescriptor": { - "message": "Greater than", + "message": "大なり", "description": "Represents the > key in screen reader content as a readable word" }, "commaCharacterDescriptor": { - "message": "Comma", + "message": "コンマ", "description": "Represents the , key in screen reader content as a readable word" }, "periodCharacterDescriptor": { - "message": "Period", + "message": "ピリオド", "description": "Represents the . key in screen reader content as a readable word" }, "questionCharacterDescriptor": { - "message": "Question mark", + "message": "クエスチョンマーク", "description": "Represents the ? key in screen reader content as a readable word" }, "forwardSlashCharacterDescriptor": { - "message": "Forward slash", + "message": "スラッシュ", "description": "Represents the / key in screen reader content as a readable word" }, "lowercaseAriaLabel": { - "message": "Lowercase" + "message": "小文字" }, "uppercaseAriaLabel": { - "message": "Uppercase" + "message": "大文字" }, "generatedPassword": { - "message": "Generated password" + "message": "生成したパスワード" }, "compactMode": { - "message": "Compact mode" + "message": "コンパクトモード" }, "beta": { - "message": "Beta" + "message": "ベータ" + }, + "importantNotice": { + "message": "重要なお知らせ" + }, + "setupTwoStepLogin": { + "message": "2段階認証を設定する" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "後で再通知" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "新しいメールアドレス $EMAIL$ はあなたが管理しているものですか?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "いいえ、違います。" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" }, "extensionWidth": { - "message": "Extension width" + "message": "拡張機能の幅" }, "wide": { - "message": "Wide" + "message": "ワイド" }, "extraWide": { - "message": "Extra wide" + "message": "エクストラワイド" } } diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index ed909731d78..5da73c6755b 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -447,9 +454,6 @@ "length": { "message": "სიგრძე" }, - "passwordMinLength": { - "message": "პაროლის მინიმალური სიგრძე" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum special" }, - "avoidAmbChar": { - "message": "Avoid ambiguous characters", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "ბუფერის გასუფთავება", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organization" }, - "share": { - "message": "გაზიარება" - }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "კლონი" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "ტექსტი" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "ვადაგასულია" }, - "pendingDeletion": { - "message": "ელოდება წაშლას" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "ვადა" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 დღე" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "განსხვავებული" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "აქ დააწკაპუნეთ", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "შეცდომა" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "სერვისი" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "სისტემურად ნაგულისხმევი" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 6aa17c1d7e3..6ab3755c8f4 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -447,9 +454,6 @@ "length": { "message": "Length" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum special" }, - "avoidAmbChar": { - "message": "Avoid ambiguous characters", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Clear clipboard", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organization" }, - "share": { - "message": "Share" - }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Clone" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expired" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Expiration date" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 day" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Custom" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 40fc910b088..09e26c18b5b 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "ಪಾಸ್ವರ್ಡ್ ರಚಿಸಿ (ನಕಲಿಸಲಾಗಿದೆ)" }, @@ -447,9 +454,6 @@ "length": { "message": "ಉದ್ದ" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "ಕನಿಷ್ಠ ವಿಶೇಷ" }, - "avoidAmbChar": { - "message": "ಅಸ್ಪಷ್ಟ ಅಕ್ಷರಗಳನ್ನು ತಪ್ಪಿಸಿ", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "ವಿಸ್ತರಣೆಯನ್ನು ರೇಟ್ ಮಾಡಿ" }, - "rateExtensionDesc": { - "message": "ಉತ್ತಮ ವಿಮರ್ಶೆಯೊಂದಿಗೆ ನಮಗೆ ಸಹಾಯ ಮಾಡಲು ದಯವಿಟ್ಟು ಪರಿಗಣಿಸಿ!" - }, "browserNotSupportClipboard": { "message": "ನಿಮ್ಮ ವೆಬ್ ಬ್ರೌಸರ್ ಸುಲಭವಾದ ಕ್ಲಿಪ್‌ಬೋರ್ಡ್ ನಕಲು ಮಾಡುವುದನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ. ಬದಲಿಗೆ ಅದನ್ನು ಹಸ್ತಚಾಲಿತವಾಗಿ ನಕಲಿಸಿ." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "ಕ್ಲಿಪ್‌ಬೋರ್ಡ್ ತೆರವುಗೊಳಿಸಿ", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "ಸಂಸ್ಥೆಗೆ ಸರಿಸಿ" }, - "share": { - "message": "ಹಂಚಿಕೊಳ್ಳಿ" - }, "movedItemToOrg": { "message": "$ITEMNAME$ ಅನ್ನು $ORGNAME$ ಗೆ ಸರಿಸಲಾಗಿದೆ", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "ಕ್ಲೋನ್" }, - "passwordGeneratorPolicyInEffect": { - "message": "ಒಂದು ಅಥವಾ ಹೆಚ್ಚಿನ ಸಂಸ್ಥೆ ನೀತಿಗಳು ನಿಮ್ಮ ಜನರೇಟರ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರುತ್ತವೆ" - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "ಹುಡುಕಾಟ ಕಳುಹಿಸುತ್ತದೆ", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "ಕಳುಹಿಸು ಸೇರಿಸಿ", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "ಪಠ್ಯ" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "ಗರಿಷ್ಠ ಪ್ರವೇಶ ಎಣಿಕೆ ತಲುಪಿದೆ", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "ಅವಧಿ ಮೀರಿದೆ" }, - "pendingDeletion": { - "message": "ಅಳಿಸುವಿಕೆ ಬಾಕಿ ಉಳಿದಿದೆ" - }, "passwordProtected": { "message": "ಪಾಸ್ವರ್ಡ್ ರಕ್ಷಿತ" }, @@ -2468,24 +2447,9 @@ "message": "ಕಳುಹಿಸು ಸಂಪಾದಿಸಿ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "ಇದು ಯಾವ ರೀತಿಯ ಕಳುಹಿಸುತ್ತದೆ?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "ಇದನ್ನು ಕಳುಹಿಸಲು ವಿವರಿಸಲು ಸ್ನೇಹಪರ ಹೆಸರು.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "ನೀವು ಕಳುಹಿಸಲು ಬಯಸುವ ಫೈಲ್." - }, "deletionDate": { "message": "ಅಳಿಸುವ ದಿನಾಂಕ" }, - "deletionDateDesc": { - "message": "ಕಳುಹಿಸಿದ ದಿನಾಂಕ ಮತ್ತು ಸಮಯದ ಮೇಲೆ ಕಳುಹಿಸುವಿಕೆಯನ್ನು ಶಾಶ್ವತವಾಗಿ ಅಳಿಸಲಾಗುತ್ತದೆ.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "ಮುಕ್ತಾಯ ದಿನಾಂಕ" }, - "expirationDateDesc": { - "message": "ಹೊಂದಿಸಿದ್ದರೆ, ಈ ಕಳುಹಿಸುವಿಕೆಯ ಪ್ರವೇಶವು ನಿಗದಿತ ದಿನಾಂಕ ಮತ್ತು ಸಮಯದ ಮೇಲೆ ಮುಕ್ತಾಯಗೊಳ್ಳುತ್ತದೆ.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 ದಿನ" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "ಕಸ್ಟಮ್" }, - "maximumAccessCount": { - "message": "ಗರಿಷ್ಠ ಪ್ರವೇಶ ಎಣಿಕೆ" - }, - "maximumAccessCountDesc": { - "message": "ಹೊಂದಿಸಿದ್ದರೆ, ಗರಿಷ್ಠ ಪ್ರವೇಶ ಎಣಿಕೆ ತಲುಪಿದ ನಂತರ ಬಳಕೆದಾರರಿಗೆ ಈ ಕಳುಹಿಸುವಿಕೆಯನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "ಈ ಕಳುಹಿಸುವಿಕೆಯನ್ನು ಪ್ರವೇಶಿಸಲು ಬಳಕೆದಾರರಿಗೆ ಪಾಸ್‌ವರ್ಡ್ ಐಚ್ ಗತ್ಯವಿದೆ.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "ಈ ಕಳುಹಿಸುವ ಬಗ್ಗೆ ಖಾಸಗಿ ಟಿಪ್ಪಣಿಗಳು.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "ಇದನ್ನು ಕಳುಹಿಸುವುದನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ ಇದರಿಂದ ಯಾರೂ ಅದನ್ನು ಪ್ರವೇಶಿಸಲಾಗುವುದಿಲ್ಲ.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "ಉಳಿಸಿದ ನಂತರ ಈ ಕಳುಹಿಸುವ ಲಿಂಕ್ ಅನ್ನು ಕ್ಲಿಪ್‌ಬೋರ್ಡ್‌ಗೆ ನಕಲಿಸಿ.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "ನೀವು ಕಳುಹಿಸಲು ಬಯಸುವ ಪಠ್ಯ." - }, - "sendHideText": { - "message": "ಪೂರ್ವನಿಯೋಜಿತವಾಗಿ ಈ ಕಳುಹಿಸುವ ಪಠ್ಯವನ್ನು ಮರೆಮಾಡಿ.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "ಪ್ರಸ್ತುತ ಪ್ರವೇಶ ಎಣಿಕೆ" - }, "createSend": { "message": "ಹೊಸ ಕಳುಹಿಸುವಿಕೆಯನ್ನು ರಚಿಸಿ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "ನೀವು ಪ್ರಾರಂಭಿಸುವ ಮೊದಲು" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "ಕ್ಯಾಲೆಂಡರ್ ಶೈಲಿಯ ದಿನಾಂಕ ಆಯ್ದುಕೊಳ್ಳುವಿಕೆಯನ್ನು ಬಳಸಲು", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "ಕ್ಲಿಕ್ ಮಾಡಿ", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "ನಿಮ್ಮ ವಿಂಡೋವನ್ನು ಪಾಪ್ಔಟ್ ಮಾಡಲು.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "ಒದಗಿಸಿದ ಮುಕ್ತಾಯ ದಿನಾಂಕವು ಮಾನ್ಯವಾಗಿಲ್ಲ." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "ನಿಮ್ಮ ಅಳಿಸುವಿಕೆ ಮತ್ತು ಮುಕ್ತಾಯ ದಿನಾಂಕಗಳನ್ನು ಉಳಿಸುವಲ್ಲಿ ದೋಷ ಕಂಡುಬಂದಿದೆ." }, - "hideEmail": { - "message": "ಸ್ವೀಕರಿಸುವವರಿಂದ ನನ್ನ ಇಮೇಲ್ ವಿಳಾಸವನ್ನು ಮರೆಮಾಡಿ." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "ಒಂದು ಅಥವಾ ಹೆಚ್ಚಿನ ಸಂಸ್ಥೆಯ ನೀತಿಗಳು ನಿಮ್ಮ ಕಳುಹಿಸುವ ಆಯ್ಕೆಗಳ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರುತ್ತವೆ." - }, "passwordPrompt": { "message": "ಮಾಸ್ಟರ್ ಪಾಸ್ವರ್ಡ್ ಮರು-ಪ್ರಾಂಪ್ಟ್" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 0b527149d15..d6da55f600d 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -20,16 +20,16 @@ "message": "계정 만들기" }, "newToBitwarden": { - "message": "New to Bitwarden?" + "message": "Bitwarden을 처음 이용하시나요?" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "패스키를 사용하여 로그인하기" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "통합인증(SSO) 사용하기" }, "welcomeBack": { - "message": "Welcome back" + "message": "돌아온 것을 환영합니다." }, "setAStrongPassword": { "message": "비밀번호 설정" @@ -81,10 +81,10 @@ "message": "마스터 비밀번호 힌트 (선택)" }, "joinOrganization": { - "message": "Join organization" + "message": "\"조직\"에 가입하기" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "$ORGANIZATIONNAME$에 참가하기", "placeholders": { "organizationName": { "content": "$1", @@ -93,7 +93,7 @@ } }, "finishJoiningThisOrganizationBySettingAMasterPassword": { - "message": "Finish joining this organization by setting a master password." + "message": "마지막으로, 마스터 비밀번호를 설정하여 조직에 참가하십시오" }, "tab": { "message": "탭" @@ -120,7 +120,7 @@ "message": "비밀번호 복사" }, "copyPassphrase": { - "message": "Copy passphrase" + "message": "암호 복사" }, "copyNote": { "message": "메모 복사" @@ -153,16 +153,16 @@ "message": "운전면허 번호 복사" }, "copyPrivateKey": { - "message": "Copy private key" + "message": "개인 키 복사" }, "copyPublicKey": { - "message": "Copy public key" + "message": "공개 키 복사" }, "copyFingerprint": { - "message": "Copy fingerprint" + "message": "핑거프린트 복사" }, "copyCustomField": { - "message": "Copy $FIELD$", + "message": "$FIELD$ 복사", "placeholders": { "field": { "content": "$1", @@ -171,13 +171,13 @@ } }, "copyWebsite": { - "message": "Copy website" + "message": "웹사이트 복사" }, "copyNotes": { - "message": "Copy notes" + "message": "노트 복사" }, "fill": { - "message": "Fill", + "message": "채우기", "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "신원 자동 완성" }, + "fillVerificationCode": { + "message": "인증 코드를 입력하세요" + }, + "fillVerificationCodeAria": { + "message": "인증 코드를 입력하세요", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "비밀번호 생성 및 클립보드에 복사" }, @@ -232,16 +239,16 @@ "message": "항목 추가" }, "accountEmail": { - "message": "Account email" + "message": "계정 이메일" }, "requestHint": { - "message": "Request hint" + "message": "힌트 요청" }, "requestPasswordHint": { - "message": "Request password hint" + "message": "마스터 비밀번호 힌트 얻기" }, "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { - "message": "Enter your account email address and your password hint will be sent to you" + "message": "계정 이메일 주소를 입력하세요. 그 주소로 비밀번호 힌트가 전송될 것 입니다." }, "passwordHint": { "message": "비밀번호 힌트" @@ -274,7 +281,7 @@ "message": "마스터 비밀번호 변경" }, "continueToWebApp": { - "message": "웹 앱에서 계속하시겠용?" + "message": "웹 앱에서 계속하시겠나요?" }, "continueToWebAppDesc": { "message": "웹 앱에서 Bitwarden 계정의 더 많은 기능을 탐색해보세요." @@ -289,7 +296,7 @@ "message": "브라우저 확장 스토어로 이동하시겠습니까?" }, "continueToBrowserExtensionStoreDesc": { - "message": "Help others find out if Bitwarden is right for them. Visit your browser's extension store and leave a rating now." + "message": "다른 사람들이 Bitwarden이 적합한지 알 수 있도록 도와주세요. 당신의 브라우저 확장 스토어로 방문하여 별점을 남겨주세요." }, "changeMasterPasswordOnWebConfirmation": { "message": "Bitwarden 웹 앱에서 마스터 비밀번호를 변경할 수 있습니다." @@ -315,37 +322,37 @@ "message": "정보" }, "moreFromBitwarden": { - "message": "More from Bitwarden" + "message": "Bitwarden에 대한 더 많은 정보" }, "continueToBitwardenDotCom": { "message": "bitwarden.com 으로 이동할까요?" }, "bitwardenForBusiness": { - "message": "Bitwarden for Business" + "message": "비지니스용 Bitwarden" }, "bitwardenAuthenticator": { - "message": "Bitwarden Authenticator" + "message": "Bitwarden 인증 도구" }, "continueToAuthenticatorPageDesc": { - "message": "Bitwarden Authenticator allows you to store authenticator keys and generate TOTP codes for 2-step verification flows. Learn more on the bitwarden.com website" + "message": "Bitwarden 인증 도구를 사용하면, 인증키를 저장하고, 2단계 인증을 위한 TOTP 코드를 생성할 수 있습니다. 자세한 내용은 bitwarden.com 사이트에서 확인해주세요." }, "bitwardenSecretsManager": { - "message": "Bitwarden Secrets Manager" + "message": "Bitwarden 보안 매니저" }, "continueToSecretsManagerPageDesc": { - "message": "Securely store, manage, and share developer secrets with Bitwarden Secrets Manager. Learn more on the bitwarden.com website." + "message": "Bitwarden 보안 매니저를 이용하여, 개발자의 기밀을 안전하게 저장하고, 관리하고, 공유하세요. 자세한 내용은 bitwarden.com 사이트에서 확인해주요." }, "passwordlessDotDev": { "message": "Passwordless.dev" }, "continueToPasswordlessDotDevPageDesc": { - "message": "Create smooth and secure login experiences free from traditional passwords with Passwordless.dev. Learn more on the bitwarden.com website." + "message": "Passwordless.dev와 함께, 기존의 비밀번호 로그인 방식으로 부터 벗어나, 매끄럽고 안전한 로그인 경험을 만들어보세요. 자세한 내용은 bitwarden.com 사이트에서 확인해주요" }, "freeBitwardenFamilies": { - "message": "Free Bitwarden Families" + "message": "무료 bitwarden 가족 플랜" }, "freeBitwardenFamiliesPageDesc": { - "message": "You are eligible for Free Bitwarden Families. Redeem this offer today in the web app." + "message": "무료 Bitwarden 가족 플랜을 이용하실 수 있습니다. 오늘 웹앱에서 이 혜택을 사용하세요." }, "version": { "message": "버전" @@ -366,22 +373,22 @@ "message": "폴더 편집" }, "newFolder": { - "message": "New folder" + "message": "새 폴더" }, "folderName": { - "message": "Folder name" + "message": "폴더 이름" }, "folderHintText": { - "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums" + "message": "상위 폴더 이름 뒤에 \"/\"를 추가하여 폴더를 계층적으로 구성합니다. 예: Social/Forums" }, "noFoldersAdded": { - "message": "No folders added" + "message": "추가된 폴더가 없습니다." }, "createFoldersToOrganize": { - "message": "Create folders to organize your vault items" + "message": "폴더를 만들어 보관함의 항목들을 정리해보세요" }, "deleteFolderPermanently": { - "message": "Are you sure you want to permanently delete this folder?" + "message": "정말로 이 폴더를 영구적으로 삭제하시겠습니까?" }, "deleteFolder": { "message": "폴더 삭제" @@ -424,7 +431,7 @@ "message": "유일무이하고 강력한 비밀번호를 자동으로 생성합니다." }, "bitWebVaultApp": { - "message": "Bitwarden web app" + "message": "Bitwarden 웹 앱" }, "importItems": { "message": "항목 가져오기" @@ -436,7 +443,7 @@ "message": "비밀번호 생성" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "암호 생성" }, "regeneratePassword": { "message": "비밀번호 재생성" @@ -447,9 +454,6 @@ "length": { "message": "길이" }, - "passwordMinLength": { - "message": "최소 비밀번호 길이" - }, "uppercase": { "message": "대문자 (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -467,11 +471,11 @@ "description": "deprecated. Use specialCharactersLabel instead." }, "include": { - "message": "Include", + "message": "포함", "description": "Card header for password generator include block" }, "uppercaseDescription": { - "message": "Include uppercase characters", + "message": "대문자 포함", "description": "Tooltip for the password generator uppercase character checkbox" }, "uppercaseLabel": { @@ -479,7 +483,7 @@ "description": "Label for the password generator uppercase character checkbox" }, "lowercaseDescription": { - "message": "Include lowercase characters", + "message": "소문자 포함", "description": "Full description for the password generator lowercase character checkbox" }, "lowercaseLabel": { @@ -487,7 +491,7 @@ "description": "Label for the password generator lowercase character checkbox" }, "numbersDescription": { - "message": "Include numbers", + "message": "숫자 포함", "description": "Full description for the password generator numbers checkbox" }, "numbersLabel": { @@ -495,7 +499,7 @@ "description": "Label for the password generator numbers checkbox" }, "specialCharactersDescription": { - "message": "Include special characters", + "message": "특수 문자 포함", "description": "Full description for the password generator special characters checkbox" }, "specialCharactersLabel": { @@ -521,16 +525,12 @@ "minSpecial": { "message": "특수 문자 최소 개수" }, - "avoidAmbChar": { - "message": "모호한 문자 사용 안 함", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { - "message": "Avoid ambiguous characters", + "message": "모호한 문자 사용 안 함", "description": "Label for the avoid ambiguous characters checkbox." }, "generatorPolicyInEffect": { - "message": "Enterprise policy requirements have been applied to your generator options.", + "message": "기업 정책에 따른 요구사항들이 당신의 생성기 옵션들에 적용되었습니다.", "description": "Indicates that a policy limits the credential generator screen." }, "searchVault": { @@ -567,16 +567,16 @@ "message": "즐겨찾기 해제" }, "itemAddedToFavorites": { - "message": "Item added to favorites" + "message": "항목이 즐겨찾기에 추가되었습니다." }, "itemRemovedFromFavorites": { - "message": "Item removed from favorites" + "message": "항목이 즐겨찾기에서 삭제되었습니다." }, "notes": { "message": "메모" }, "privateNote": { - "message": "Private note" + "message": "개인 메모" }, "note": { "message": "메모" @@ -600,7 +600,7 @@ "message": "웹사이트 열기" }, "launchWebsiteName": { - "message": "Launch website $ITEMNAME$", + "message": "$ITEMNAME$ 웹사이드 열기", "placeholders": { "itemname": { "content": "$1", @@ -633,7 +633,7 @@ "message": "세션 만료" }, "vaultTimeoutHeader": { - "message": "Vault timeout" + "message": "보관함 시간초과" }, "otherOptions": { "message": "기타 옵션" @@ -641,9 +641,6 @@ "rateExtension": { "message": "확장 프로그램 평가" }, - "rateExtensionDesc": { - "message": "좋은 리뷰를 남겨 저희를 도와주세요!" - }, "browserNotSupportClipboard": { "message": "사용하고 있는 웹 브라우저가 쉬운 클립보드 복사를 지원하지 않습니다. 직접 복사하세요." }, @@ -654,13 +651,13 @@ "message": "보관함이 잠겨 있습니다. 마스터 비밀번호를 입력하여 계속하세요." }, "yourVaultIsLockedV2": { - "message": "Your vault is locked" + "message": "당신의 보관함이 잠겼습니다." }, "yourAccountIsLocked": { - "message": "Your account is locked" + "message": "당신의 계정이 잠겼습니다." }, "or": { - "message": "or" + "message": "또는" }, "unlock": { "message": "잠금 해제" @@ -685,7 +682,7 @@ "message": "보관함 시간 제한" }, "vaultTimeout1": { - "message": "Timeout" + "message": "시간초과" }, "lockNow": { "message": "지금 잠그기" @@ -739,16 +736,16 @@ "message": "보안" }, "confirmMasterPassword": { - "message": "Confirm master password" + "message": "마스터 비밀번호 확정" }, "masterPassword": { - "message": "Master password" + "message": "마스터 비밀번호" }, "masterPassImportant": { - "message": "Your master password cannot be recovered if you forget it!" + "message": "마스터 비밀번호는 잊어버려도 복구할 수 없습니다!" }, "masterPassHintLabel": { - "message": "Master password hint" + "message": "마스터 비밀번호 힌트" }, "errorOccurred": { "message": "오류가 발생했습니다" @@ -782,10 +779,10 @@ "message": "계정 생성이 완료되었습니다! 이제 로그인하실 수 있습니다." }, "newAccountCreated2": { - "message": "Your new account has been created!" + "message": "계정 생성이 완료되었습니다!" }, "youHaveBeenLoggedIn": { - "message": "You have been logged in!" + "message": "로그인이 이미 되어있습니다." }, "youSuccessfullyLoggedIn": { "message": "로그인에 성공했습니다." @@ -800,7 +797,7 @@ "message": "인증 코드는 반드시 입력해야 합니다." }, "webauthnCancelOrTimeout": { - "message": "The authentication was cancelled or took too long. Please try again." + "message": "인증이 너무 오래 걸리거나 취소되었습니다. 다시 시도하여 주십시오." }, "invalidVerificationCode": { "message": "유효하지 않은 확인 코드" @@ -828,16 +825,16 @@ "message": "현재 웹페이지에서 QR 코드 스캔하기" }, "totpHelperTitle": { - "message": "Make 2-step verification seamless" + "message": "간편하게 2단계 인증을 만들기" }, "totpHelper": { - "message": "Bitwarden can store and fill 2-step verification codes. Copy and paste the key into this field." + "message": "Bitwarden은 2단계 인증 코드들을 저장하고, 채워넣을 수 있습니다. 키를 복사하여 이 필드에 붙여넣으세요." }, "totpHelperWithCapture": { - "message": "Bitwarden can store and fill 2-step verification codes. Select the camera icon to take a screenshot of this website's authenticator QR code, or copy and paste the key into this field." + "message": "Bitwarden은 2단계 인증 코드들을 저장하고, 채워넣을 수 있습니다. 카메라 아이콘을 선택하고, 이 웹사이드의 인증 도구 QR코드를 스크린샷을 찍거나, 키를 복사하여 이 필드에 붙여넣으세요." }, "learnMoreAboutAuthenticators": { - "message": "Learn more about authenticators" + "message": "인증 도구에 대해 더 알아보기" }, "copyTOTP": { "message": "인증서 키 (TOTP) 복사" @@ -846,7 +843,7 @@ "message": "로그아웃됨" }, "loggedOutDesc": { - "message": "You have been logged out of your account." + "message": "계정이 로그아웃 되었습니다." }, "loginExpired": { "message": "로그인 세션이 만료되었습니다." @@ -855,19 +852,19 @@ "message": "로그인" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Bitwarden에 로그인" }, "restartRegistration": { - "message": "Restart registration" + "message": "등록 재시작" }, "expiredLink": { "message": "만료된 링크" }, "pleaseRestartRegistrationOrTryLoggingIn": { - "message": "Please restart registration or try logging in." + "message": "등록 재시작 혹은 다시 로그인을 해주시길 바랍니다" }, "youMayAlreadyHaveAnAccount": { - "message": "You may already have an account" + "message": "계정을 이미 가지고 계실수도 있습니다." }, "logOutConfirmation": { "message": "정말 로그아웃하시겠습니까?" @@ -891,10 +888,10 @@ "message": "2단계 인증은 보안 키, 인증 앱, SMS, 전화 통화 등의 다른 기기로 사용자의 로그인 시도를 검증하여 사용자의 계정을 더욱 안전하게 만듭니다. 2단계 인증은 bitwarden.com 웹 보관함에서 활성화할 수 있습니다. 지금 웹 사이트를 방문하시겠습니까?" }, "twoStepLoginConfirmationContent": { - "message": "Make your account more secure by setting up two-step login in the Bitwarden web app." + "message": "Bitwarden 웹 앱에 2단계 인증을 설정하여, 당신의 계정을 좀 더 안전하게 만드세요." }, "twoStepLoginConfirmationTitle": { - "message": "Continue to web app?" + "message": "웹 앱으로 진행하나요?" }, "editedFolder": { "message": "폴더 편집함" @@ -981,7 +978,7 @@ "message": "로그인을 추가할 건지 물어보기" }, "vaultSaveOptionsTitle": { - "message": "Save to vault options" + "message": "보관함 옵션들을 저장하기" }, "addLoginNotificationDesc": { "message": "\"로그인 추가 알림\"을 사용하면 새 로그인을 사용할 때마다 보관함에 그 로그인을 추가할 것인지 물어봅니다." @@ -990,22 +987,25 @@ "message": "보관함에 항목이 없을 경우 추가하라는 메시지를 표시합니다. 모든 로그인된 계정에 적용됩니다." }, "showCardsInVaultView": { - "message": "Show cards as Autofill suggestions on Vault view" + "message": "보관함 보기에서 카드 자동완성 제안를 표시" }, "showCardsCurrentTab": { "message": "탭 페이지에 카드 표시" }, "showCardsCurrentTabDesc": { - "message": "List card items on the Tab page for easy autofill." + "message": "간편한 자동완성을 위해 탭에 카드 항목들을 나열" }, "showIdentitiesInVaultView": { - "message": "Show identities as Autofill suggestions on Vault view" + "message": "보관함 보기에서 신원들의 자동완성 제안을 표시" }, "showIdentitiesCurrentTab": { - "message": "Show identities on Tab page" + "message": "탭 페이지에 신원들을 표시" }, "showIdentitiesCurrentTabDesc": { - "message": "List identity items on the Tab page for easy autofill." + "message": "간편한 자동완성을 위해 탭에 신원 항목들을 나열" + }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" }, "clearClipboard": { "message": "클립보드 비우기", @@ -1043,7 +1043,7 @@ "message": "예, 지금 변경하겠습니다." }, "notificationUnlockDesc": { - "message": "Unlock your Bitwarden vault to complete the autofill request." + "message": "Bitwarden 보관함을 잠금 해제 하여 자동완성 요청을 완료하세요." }, "notificationUnlock": { "message": "잠금 해제" @@ -1052,13 +1052,13 @@ "message": "추가 옵션" }, "enableContextMenuItem": { - "message": "Show context menu options" + "message": "문맥 매뉴 옵션 표시" }, "contextMenuItemDesc": { - "message": "Use a secondary click to access password generation and matching logins for the website." + "message": "우클릭을 사용하여, 비밀번호 생성과 웹사이트 로그인 매칭에 접근하세요" }, "contextMenuItemDescAlt": { - "message": "Use a secondary click to access password generation and matching logins for the website. Applies to all logged in accounts." + "message": "우클릭을 사용하여, 웹사이트의 비밀번호 생성과 사용가능한 로그인들에 접근하세요. 모든 로그인 된 계정에 적용됩니다." }, "defaultUriMatchDetection": { "message": "기본 URI 일치 인식", @@ -1089,7 +1089,7 @@ "description": "'Solarized' is a noun and the name of a color scheme. It should not be translated." }, "exportFrom": { - "message": "Export from" + "message": "~(으)로부터 내보내기" }, "exportVault": { "message": "보관함 내보내기" @@ -1098,19 +1098,19 @@ "message": "파일 형식" }, "fileEncryptedExportWarningDesc": { - "message": "This file export will be password protected and require the file password to decrypt." + "message": "이 파일 내보내기는 비밀번호로 보호될 것이며, 파일을 해독하기 위해서는 파일 비밀번호가 필요합니다." }, "filePassword": { "message": "파일 비밀번호" }, "exportPasswordDescription": { - "message": "This password will be used to export and import this file" + "message": "이 비밀번호는 이 파일을 파일 내보내거나, 가져오는데 사용됩니다." }, "accountRestrictedOptionDescription": { - "message": "Use your account encryption key, derived from your account's username and Master Password, to encrypt the export and restrict import to only the current Bitwarden account." + "message": "계정의 사용자 이름과 마스터 비밀번호에서 파생된 계정 암호화 키를 사용하여 내보내기를 암호화하고, 현재 Bitwarden계정으로 가져오기를 제한해보세요. " }, "passwordProtectedOptionDescription": { - "message": "Set a file password to encrypt the export and import it to any Bitwarden account using the password for decryption." + "message": "파일 비밀번호를 설정하여, 내보내기를 암호화하고, 해독에 그 파일 비밀번호를 사용하는 Bitwarden계정에 가져오세요." }, "exportTypeHeading": { "message": "내보내기 유형" @@ -1119,14 +1119,14 @@ "message": "계정 제한됨" }, "filePasswordAndConfirmFilePasswordDoNotMatch": { - "message": "“File password” and “Confirm file password“ do not match." + "message": "파일 비밀번호와 파일 비밀번호 확인이 일치하지 않습니다." }, "warning": { "message": "경고", "description": "WARNING (should stay in capitalized letters if the language permits)" }, "warningCapitalized": { - "message": "Warning", + "message": "경고", "description": "Warning (should maintain locale-relevant capitalization)" }, "confirmVaultExport": { @@ -1148,14 +1148,11 @@ "message": "공유됨" }, "bitwardenForBusinessPageDesc": { - "message": "Bitwarden for Business allows you to share your vault items with others by using an organization. Learn more on the bitwarden.com website." + "message": "비지니스용 Bitwarden은 조직을 사용하여 보관함 항목들을 다른 사람과 공유할 수 있게 해줍니다. 자세한 내용은 bitwarden.com 사이트에서 확인해주세요" }, "moveToOrganization": { "message": "조직으로 이동하기" }, - "share": { - "message": "공유" - }, "movedItemToOrg": { "message": "$ITEMNAME$이(가) $ORGNAME$(으)로 이동됨", "placeholders": { @@ -1209,7 +1206,7 @@ "message": "파일" }, "fileToShare": { - "message": "File to share" + "message": "공유할 파일" }, "selectFile": { "message": "파일을 선택하세요." @@ -1221,7 +1218,7 @@ "message": "기능 사용할 수 없음" }, "encryptionKeyMigrationRequired": { - "message": "Encryption key migration required. Please login through the web vault to update your encryption key." + "message": "암호화 키 마이그레이션이 필요합니다. 웹 볼트를 통해 로그인하여 암호화 키를 업데이트하세요." }, "premiumMembership": { "message": "프리미엄 멤버십" @@ -1245,10 +1242,10 @@ "message": "1GB의 암호화된 파일 저장소." }, "premiumSignUpEmergency": { - "message": "Emergency access." + "message": "비상 접근" }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "YubiKey나 Duo와 같은 독점적인 2단계 로그인 옵션" }, "ppremiumSignUpReports": { "message": "보관함을 안전하게 유지하기 위한 암호 위생, 계정 상태, 데이터 유출 보고서" @@ -1269,7 +1266,7 @@ "message": "bitwarden.com 웹 보관함에서 프리미엄 멤버십을 구입할 수 있습니다. 지금 웹 사이트를 방문하시겠습니까?" }, "premiumPurchaseAlertV2": { - "message": "You can purchase Premium from your account settings on the Bitwarden web app." + "message": "Bitwarden 웹 앱의 계정 설정에서 프리미엄에 대한 결제를 할 수 있습니다." }, "premiumCurrentMember": { "message": "프리미엄 사용자입니다!" @@ -1278,7 +1275,7 @@ "message": "Bitwarden을 지원해 주셔서 감사합니다." }, "premiumFeatures": { - "message": "Upgrade to Premium and receive:" + "message": "프리미엄으로 업그래이드 하고 받기: " }, "premiumPrice": { "message": "이 모든 기능을 연 $PRICE$에 이용하실 수 있습니다!", @@ -1290,7 +1287,7 @@ } }, "premiumPriceV2": { - "message": "All for just $PRICE$ per year!", + "message": "이 모든 기능을 연 $PRICE$에 이용하실 수 있습니다!", "placeholders": { "price": { "content": "$1", @@ -1319,6 +1316,12 @@ "enterVerificationCodeApp": { "message": "인증 앱에서 6자리 인증 코드를 입력하세요." }, + "authenticationTimeout": { + "message": "인증 시간 초과" + }, + "authenticationSessionTimedOut": { + "message": "인증 세션 시간이 초과 되었습니다. 다시 로그인을 시작해주세요." + }, "enterVerificationCodeEmail": { "message": "$EMAIL$ 주소로 전송된 6자리 인증 코드를 입력하세요.", "placeholders": { @@ -1383,17 +1386,17 @@ "message": "인증 앱" }, "authenticatorAppDescV2": { - "message": "Enter a code generated by an authenticator app like Bitwarden Authenticator.", + "message": "Bitwarden 인증같은 인증 앱을 통해 코드를 생성하여 입력해주세요", "description": "'Bitwarden Authenticator' is a product name and should not be translated." }, "yubiKeyTitleV2": { - "message": "Yubico OTP Security Key" + "message": "YubiKey OTP 보안 키" }, "yubiKeyDesc": { "message": "YubiKey를 사용하여 사용자의 계정에 접근합니다. YubiKey 4, 4 Nano, 4C 및 NEO 기기를 사용할 수 있습니다." }, "duoDescV2": { - "message": "Enter a code generated by Duo Security.", + "message": "Duo Security에서 생성한 코드를 입력하세요", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { @@ -1410,7 +1413,7 @@ "message": "이메일" }, "emailDescV2": { - "message": "Enter a code sent to your email." + "message": "이메일로 전송된 코드를 입력하세요." }, "selfHostedEnvironment": { "message": "자체 호스팅 환경" @@ -1419,13 +1422,13 @@ "message": "온-프레미스 Bitwarden이 호스팅되고 있는 서버의 기본 URL을 지정하세요." }, "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이나 최소한 하나의 사용자 지정 환경을 추가해야 합니다." }, "customEnvironment": { "message": "사용자 지정 환경" @@ -1437,7 +1440,7 @@ "message": "서버 URL" }, "selfHostBaseUrl": { - "message": "Self-host server URL", + "message": "자체 호스트 서버 URL", "description": "Label for field requesting a self-hosted integration service URL" }, "apiUrl": { @@ -1463,28 +1466,28 @@ "description": "Represents the message for allowing the user to enable the autofill overlay" }, "autofillSuggestionsSectionTitle": { - "message": "Autofill suggestions" + "message": "자동 완성 제안" }, "showInlineMenuLabel": { - "message": "Show autofill suggestions on form fields" + "message": "양식 필드에 자동 완성 제안 표시" }, "showInlineMenuIdentitiesLabel": { - "message": "Display identities as suggestions" + "message": "신원를 제안으로 표시" }, "showInlineMenuCardsLabel": { - "message": "Display cards as suggestions" + "message": "카드를 제안으로 표시" }, "showInlineMenuOnIconSelectionLabel": { - "message": "Display suggestions when icon is selected" + "message": "아이콘을 선택하면 제안이 표시됩니다." }, "showInlineMenuOnFormFieldsDescAlt": { - "message": "Applies to all logged in accounts." + "message": "로그인한 모든 계정에 적용" }, "turnOffBrowserBuiltInPasswordManagerSettings": { "message": "충돌을 방지하기 위해 브라우저의 기본 암호 관리 설정을 해제합니다." }, "turnOffBrowserBuiltInPasswordManagerSettingsLink": { - "message": "Edit browser settings." + "message": "브라우저 설정 편집" }, "autofillOverlayVisibilityOff": { "message": "끄기", @@ -1499,7 +1502,7 @@ "description": "Overlay appearance select option for showing the field on click of the overlay icon" }, "enableAutoFillOnPageLoadSectionTitle": { - "message": "Autofill on page load" + "message": "페이지 로드 시 자동 완성" }, "enableAutoFillOnPageLoad": { "message": "페이지 로드 시 자동 완성 사용" @@ -1511,10 +1514,10 @@ "message": "취약하거나 신뢰할 수 없는 웹사이트 페이지 로드 시 자동 완성이 악용될 수 있습니다." }, "learnMoreAboutAutofillOnPageLoadLinkText": { - "message": "Learn more about risks" + "message": "위험에 대해 자세히 알아보기" }, "learnMoreAboutAutofill": { - "message": "Learn more about autofill" + "message": "자동 완정에 대해 자세히 할아보기" }, "defaultAutoFillOnPageLoad": { "message": "로그인 항목에 대한 기본 자동 완성 설정" @@ -1541,13 +1544,13 @@ "message": "사이드바에서 보관함 열기" }, "commandAutofillLoginDesc": { - "message": "Autofill the last used login for the current website" + "message": "현재 웹사이트에 마지막으로 사용된 로그인을 자동 채우기" }, "commandAutofillCardDesc": { - "message": "Autofill the last used card for the current website" + "message": "현재 웹사이트에 마지막으로 사용된 카드를 자동 채우기" }, "commandAutofillIdentityDesc": { - "message": "Autofill the last used identity for the current website" + "message": "현재 웹사이트에 마지막으로 사용된 신원을 자동 채우기" }, "commandGeneratePasswordDesc": { "message": "새 무작위 비밀번호를 만들고 클립보드에 복사합니다." @@ -1580,7 +1583,7 @@ "message": "참 / 거짓" }, "cfTypeCheckbox": { - "message": "Checkbox" + "message": "체크박스" }, "cfTypeLinked": { "message": "연결됨", @@ -1600,7 +1603,7 @@ "message": "웹사이트 아이콘 표시하기" }, "faviconDesc": { - "message": "Show a recognizable image next to each login." + "message": "로그인 정보 옆에 식별용 이미지를 표시합니다." }, "faviconDescAlt": { "message": "각 로그인 정보 옆에 인식할 수 있는 이미지를 표시합니다. 모든 로그인된 계정에 적용됩니다." @@ -1765,10 +1768,10 @@ "message": "신원" }, "typeSshKey": { - "message": "SSH key" + "message": "SSH 키" }, "newItemHeader": { - "message": "New $TYPE$", + "message": "새 $TYPE$", "placeholders": { "type": { "content": "$1", @@ -1777,7 +1780,7 @@ } }, "editItemHeader": { - "message": "Edit $TYPE$", + "message": "$TYPE$ 수정", "placeholders": { "type": { "content": "$1", @@ -1786,7 +1789,7 @@ } }, "viewItemHeader": { - "message": "View $TYPE$", + "message": "$TYPE$ 보기", "placeholders": { "type": { "content": "$1", @@ -1798,13 +1801,13 @@ "message": "비밀번호 변경 기록" }, "generatorHistory": { - "message": "Generator history" + "message": "생성기 기록" }, "clearGeneratorHistoryTitle": { - "message": "Clear generator history" + "message": "생성기 기록 지우기" }, "cleargGeneratorHistoryDescription": { - "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + "message": "계속하면 모든 항목이 생성기 기록에서 영구적으로 삭제됩니다. 계속하시겠습니까?" }, "back": { "message": "뒤로" @@ -1813,7 +1816,7 @@ "message": "컬렉션" }, "nCollections": { - "message": "$COUNT$ collections", + "message": "$COUNT$ 컬렉션", "placeholders": { "count": { "content": "$1", @@ -1843,7 +1846,7 @@ "message": "보안 메모" }, "sshKeys": { - "message": "SSH Keys" + "message": "SSH 키" }, "clear": { "message": "삭제", @@ -1869,7 +1872,7 @@ "description": "Domain name. Ex. website.com" }, "baseDomainOptionRecommended": { - "message": "Base domain (recommended)", + "message": "기본 도메인 (추천)", "description": "Domain name. Ex. website.com" }, "domainName": { @@ -1923,13 +1926,13 @@ "message": "비밀번호가 없습니다." }, "clearHistory": { - "message": "Clear history" + "message": "기록 지우기" }, "nothingToShow": { - "message": "Nothing to show" + "message": "항목 없음" }, "nothingGeneratedRecently": { - "message": "You haven't generated anything recently" + "message": "최근에 생성한 것이 없습니다" }, "remove": { "message": "제거" @@ -1990,16 +1993,16 @@ "message": "PIN 코드를 사용하여 잠금 해제" }, "setYourPinTitle": { - "message": "Set PIN" + "message": "PIN 설정" }, "setYourPinButton": { - "message": "Set PIN" + "message": "PIN 설정" }, "setYourPinCode": { "message": "Bitwarden 잠금해제에 사용될 PIN 코드를 설정합니다. 이 애플리케이션에서 완전히 로그아웃할 경우 PIN 설정이 초기화됩니다." }, "setYourPinCode1": { - "message": "Your PIN will be used to unlock Bitwarden instead of your master password. Your PIN will reset if you ever fully log out of Bitwarden." + "message": "PIN은 마스터 비밀번호 대신 Bitwarden 잠금해제에 사용됩니다. Bitwarden에서 완전히 로그아웃하면 PIN이 재설정됩니다." }, "pinRequired": { "message": "PIN 코드가 필요합니다." @@ -2014,7 +2017,7 @@ "message": "생체 인식을 사용하여 잠금 해제" }, "unlockWithMasterPassword": { - "message": "Unlock with master password" + "message": "마스터 비밀번호로 잠금 해제" }, "awaitDesktop": { "message": "데스크톱으로부터의 확인을 대기 중" @@ -2026,7 +2029,7 @@ "message": "브라우저 다시 시작 시 마스터 비밀번호로 잠금" }, "lockWithMasterPassOnRestart1": { - "message": "Require master password on browser restart" + "message": "브라우저 다시 시작 시 마스터 비밀번호가 필요합니다" }, "selectOneCollection": { "message": "반드시 하나 이상의 컬렉션을 선택해야 합니다." @@ -2037,37 +2040,34 @@ "clone": { "message": "복제" }, - "passwordGeneratorPolicyInEffect": { - "message": "하나 이상의 단체 정책이 생성기 규칙에 영항을 미치고 있습니다." - }, "passwordGenerator": { - "message": "Password generator" + "message": "비밀번호 생성기" }, "usernameGenerator": { - "message": "Username generator" + "message": "사용자 이름 생성기" }, "useThisPassword": { - "message": "Use this password" + "message": "이 비밀번호 사용" }, "useThisUsername": { - "message": "Use this username" + "message": "이 사용자 이름 사용" }, "securePasswordGenerated": { - "message": "Secure password generated! Don't forget to also update your password on the website." + "message": "보안 비밀번호가 생성되었습니다! 웹사이트에서 비밀번호를 업데이트하는 것도 잊지 마세요." }, "useGeneratorHelpTextPartOne": { - "message": "Use the generator", + "message": "생성기를 사용하여", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "useGeneratorHelpTextPartTwo": { - "message": "to create a strong unique password", + "message": "강력한 고유 비밀번호를 만드세요", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "vaultTimeoutAction": { "message": "보관함 시간 제한 초과시 동작" }, "vaultTimeoutAction1": { - "message": "Timeout action" + "message": "시간초과 시 행동" }, "lock": { "message": "잠금", @@ -2096,7 +2096,7 @@ "message": "복원된 항목" }, "alreadyHaveAccount": { - "message": "Already have an account?" + "message": "이미 계정이 있으신가요?" }, "vaultTimeoutLogOutConfirmation": { "message": "로그아웃하면 보관함에 대한 모든 접근이 제거되며 시간 제한을 초과하면 온라인 인증을 요구합니다. 정말로 이 설정을 사용하시겠습니까?" @@ -2108,7 +2108,7 @@ "message": "자동 완성 및 저장" }, "fillAndSave": { - "message": "Fill and save" + "message": "채우기 및 저장" }, "autoFillSuccessAndSavedUri": { "message": "항목을 자동 완성하고 URI를 저장함" @@ -2117,16 +2117,16 @@ "message": "항목을 자동 완성함" }, "insecurePageWarning": { - "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + "message": "경고: 이 페이지는 보안이 해제된 HTTP 페이지이며, 제출한 모든 정보는 다른 사람이 보고 변경할 수 있습니다. 이 로그인은 원래 보안(HTTPS) 페이지에 저장되었습니다." }, "insecurePageWarningFillPrompt": { - "message": "Do you still wish to fill this login?" + "message": "여전히 이 로그인을 채우시겠습니까?" }, "autofillIframeWarning": { - "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to autofill anyway, or Cancel to stop." + "message": "양식은 저장된 로그인의 URI가 아닌 다른 도메인에서 호스팅됩니다. 그래도 자동 완성을 사용하시려면 OK, 아니라면 취소 버튼을 선택해주세요." }, "autofillIframeWarningTip": { - "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "message": "향후 이 경고를 방지하려면 이 URI인 $HOSTNAME$(을)를 Bitwarden로그인 항목에 저장하세요.", "placeholders": { "hostname": { "content": "$1", @@ -2189,25 +2189,25 @@ "message": "새 마스터 비밀번호가 정책 요구 사항을 따르지 않습니다." }, "receiveMarketingEmailsV2": { - "message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox." + "message": "Email 받은 편지함을 통해 Bitwarden의 조언, 공지사항 및 연구 기회들을 얻어보세요" }, "unsubscribe": { - "message": "Unsubscribe" + "message": "구독 취소" }, "atAnyTime": { - "message": "at any time." + "message": "언제든지" }, "byContinuingYouAgreeToThe": { - "message": "By continuing, you agree to the" + "message": "계속하면 다음에 동의하게 됩니다" }, "and": { - "message": "and" + "message": "그리고" }, "acceptPolicies": { "message": "이 박스를 체크하면 다음에 동의하는 것으로 간주됩니다:" }, "acceptPoliciesRequired": { - "message": "Terms of Service and Privacy Policy have not been acknowledged." + "message": "서비스 약관 및 개인 정보 보호 정책을 확인하지 않았습니다." }, "termsOfService": { "message": "서비스 약관" @@ -2222,10 +2222,10 @@ "message": "확인" }, "errorRefreshingAccessToken": { - "message": "Access Token Refresh Error" + "message": "엑세스 토큰 새로고침 오류" }, "errorRefreshingAccessTokenDesc": { - "message": "No refresh token or API keys found. Please try logging out and logging back in." + "message": "새로 고침 토큰이나 API 키를 찾을 수 없습니다. 로그아웃하고 다시 로그인해 주세요" }, "desktopSyncVerificationTitle": { "message": "데스크톱과의 동기화 인증" @@ -2264,10 +2264,10 @@ "message": "계정이 일치하지 않음" }, "nativeMessagingWrongUserKeyTitle": { - "message": "Biometric key missmatch" + "message": "생체인식 키 불일치" }, "nativeMessagingWrongUserKeyDesc": { - "message": "Biometric unlock failed. The biometric secret key failed to unlock the vault. Please try to set up biometrics again." + "message": "생체 인식 잠금 해제에 실패했습니다. 생체 인식 비밀 키가 보관함 잠금 해제에 실패했습니다. 생체 인식을 다시 설정해 보세요." }, "biometricsNotEnabledTitle": { "message": "생체 인식이 활성화되지 않음" @@ -2282,22 +2282,22 @@ "message": "이 기기에서는 생체 인식이 지원되지 않습니다." }, "biometricsNotUnlockedTitle": { - "message": "User locked or logged out" + "message": "사용자 잠금 또는 로그아웃" }, "biometricsNotUnlockedDesc": { - "message": "Please unlock this user in the desktop application and try again." + "message": "데스크톱 애플리케이션에서 이 사용자의 잠금을 해제하고 다시 시도해 주세요." }, "biometricsNotAvailableTitle": { - "message": "Biometric unlock unavailable" + "message": "생체 인식 잠금 해제 사용 불가" }, "biometricsNotAvailableDesc": { - "message": "Biometric unlock is currently unavailable. Please try again later." + "message": "생체 인식 잠금 해제는 현재 사용할 수 없습니다. 나중에 다시 시도해 주세요." }, "biometricsFailedTitle": { - "message": "Biometrics failed" + "message": "생체 인식 실패" }, "biometricsFailedDesc": { - "message": "Biometrics cannot be completed, consider using a master password or logging out. If this persists, please contact Bitwarden support." + "message": "생체 인식을 완료할 수 없습니다. 마스터 비밀번호를 사용하거나 로그아웃하는 것을 고려하세요. 이 문제가 계속되면 Bitwarden 지원팀에 문의해 주세요." }, "nativeMessaginPermissionErrorTitle": { "message": "권한이 부여되지 않음" @@ -2318,10 +2318,10 @@ "message": "조직의 정책이 소유권 설정에 영향을 미치고 있습니다." }, "personalOwnershipPolicyInEffectImports": { - "message": "An organization policy has blocked importing items into your individual vault." + "message": "조직 정책으로 인해 개별 보관함으로 항목을 가져오는 것이 차단되었습니다." }, "domainsTitle": { - "message": "Domains", + "message": "도메인", "description": "A category title describing the concept of web domains" }, "excludedDomains": { @@ -2331,10 +2331,10 @@ "message": "Bitwarden은 이 도메인들에 대해 로그인 정보를 저장할 것인지 묻지 않습니다. 페이지를 새로고침해야 변경된 내용이 적용됩니다." }, "excludedDomainsDescAlt": { - "message": "Bitwarden will not ask to save login details for these domains for all logged in accounts. You must refresh the page for changes to take effect." + "message": "BItwarden은 로그인한 모든 계정에 대해 이러한 도메인에 대한 로그인 세부 정보를 저장하도록 요청하지 않습니다. 변경 사항을 적용하려면 페이지를 새로 고쳐야 합니다" }, "websiteItemLabel": { - "message": "Website $number$ (URI)", + "message": "웹사이트 $number$ (URI)", "placeholders": { "number": { "content": "$1", @@ -2352,17 +2352,17 @@ } }, "excludedDomainsSavedSuccess": { - "message": "Excluded domain changes saved" + "message": "제외된 도메인 변경 사항 저장됨" }, "limitSendViews": { - "message": "Limit views" + "message": "제한 보기" }, "limitSendViewsHint": { - "message": "No one can view this Send after the limit is reached.", + "message": "제한에 도달한 후에는 아무도 이 전송을 볼 수 없습니다.", "description": "Displayed under the limit views field on Send" }, "limitSendViewsCount": { - "message": "$ACCESSCOUNT$ views left", + "message": "남은 $ACCESSCOUNT$ 횟수", "description": "Displayed under the limit views field on Send", "placeholders": { "accessCount": { @@ -2372,26 +2372,18 @@ } }, "send": { - "message": "Send", + "message": "보내기", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDetails": { - "message": "Send details", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "searchSends": { - "message": "Send 검색", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Send 추가", + "message": "보내기 세부 정보", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTypeText": { "message": "텍스트" }, "sendTypeTextToShare": { - "message": "Text to share" + "message": "공유할 텍스트" }, "sendTypeFile": { "message": "파일" @@ -2401,26 +2393,19 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "hideTextByDefault": { - "message": "Hide text by default" - }, - "maxAccessCountReached": { - "message": "최대 접근 횟수 도달", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." + "message": "기본적으로 텍스트 숨기기" }, "expired": { "message": "만료됨" }, - "pendingDeletion": { - "message": "삭제 대기 중" - }, "passwordProtected": { "message": "비밀번호로 보호됨" }, "copyLink": { - "message": "Copy link" + "message": "링크 복사" }, "copySendLink": { - "message": "Send 링크 복사", + "message": " Send 링크 복사", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "removePassword": { @@ -2433,7 +2418,7 @@ "message": "비밀번호 제거함" }, "deletedSend": { - "message": "Send 삭제함", + "message": " Send 삭제함", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { @@ -2447,55 +2432,36 @@ "message": "비밀번호를 제거하시겠습니까?" }, "deleteSend": { - "message": "Send 삭제", + "message": " Send 삭제", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendConfirmation": { - "message": "정말 이 Send를 삭제하시겠습니까?", + "message": "정말 이 Send를 삭제하시겠습니까?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendPermanentConfirmation": { - "message": "Are you sure you want to permanently delete this Send?", + "message": "이 Send을 영구적으로 삭제하시겠습니까?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editSend": { - "message": "Send 편집", + "message": " Send 편집", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "어떤 유형의 Send인가요?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "이 Send의 이름", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "전송하려는 파일" - }, "deletionDate": { "message": "삭제 날짜" }, - "deletionDateDesc": { - "message": "이 Send가 정해진 일시에 영구적으로 삭제됩니다.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { - "message": "The Send will be permanently deleted on this date.", + "message": "이 Send가 이 날짜에 영구적으로 삭제됩니다.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "expirationDate": { "message": "만료 날짜" }, - "expirationDateDesc": { - "message": "설정할 경우, 이 Send에 대한 접근 권한이 정해진 일시에 만료됩니다.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1일" }, "days": { - "message": "$DAYS$일", + "message": "$DAYS$ 일", "placeholders": { "days": { "content": "$1", @@ -2506,43 +2472,10 @@ "custom": { "message": "사용자 지정" }, - "maximumAccessCount": { - "message": "최대 접근 횟수" - }, - "maximumAccessCountDesc": { - "message": "설정할 경우, 최대 접근 횟수에 도달할 때 이 Send에 접근할 수 없게 됩니다.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "이 Send에 접근하기 위해 암호를 입력하도록 선택적으로 요구합니다.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { - "message": "Add an optional password for recipients to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNotesDesc": { - "message": "이 Send에 대한 비공개 메모", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "이 Send를 비활성화하여 아무도 접근할 수 없게 합니다.", + "message": "수신자가 이 Send에 액세스할 수 있도록 비밀번호 옵션를 추가합니다.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendShareDesc": { - "message": "저장할 때 이 Send의 링크를 클립보드에 복사합니다.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "전송하려는 텍스트" - }, - "sendHideText": { - "message": "이 Send의 텍스트를 기본적으로 숨김", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "현재 접근 횟수" - }, "createSend": { "message": "새 Send 생성", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2563,15 +2496,15 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSendSuccessfully": { - "message": "Send created successfully!", + "message": "Send가 성공적으로 생성되었습니다!", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendExpiresInHoursSingle": { - "message": "The Send will be available to anyone with the link for the next 1 hour.", + "message": "이 Send는 링크가 있는 누구나 향후 1시간 동안 이용할 수 있습니다.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendExpiresInHours": { - "message": "The Send will be available to anyone with the link for the next $HOURS$ hours.", + "message": "이 전송은 링크가 있는 누구나 향후 $HOURS$ 시간 동안 이용할 수 있습니다.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.", "placeholders": { "hours": { @@ -2581,11 +2514,11 @@ } }, "sendExpiresInDaysSingle": { - "message": "The Send will be available to anyone with the link for the next 1 day.", + "message": "이 Send은 향후 1일 동안 링크가 있는 누구나 이용할 수 있습니다.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendExpiresInDays": { - "message": "The Send will be available to anyone with the link for the next $DAYS$ days.", + "message": "이 Send은 향후 $DAYS$일 동안 링크가 있는 누구나 이용할 수 있습니다.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.", "placeholders": { "days": { @@ -2595,19 +2528,19 @@ } }, "sendLinkCopied": { - "message": "Send link copied", + "message": "Send 링크 복사됨", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editedSend": { - "message": "Send 수정함", + "message": "Send 수정됨", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFilePopoutDialogText": { - "message": "Pop out extension?", + "message": "확장자를 새 창에서 열까요?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFilePopoutDialogDesc": { - "message": "To create a file Send, you need to pop out the extension to a new window.", + "message": "파일 Send를 만들려면, 새 창으로 확장자를 열어야 합니다.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinuxChromiumFileWarning": { @@ -2620,23 +2553,11 @@ "message": "Safari에서 파일을 선택할 경우, 이 배너를 클릭하여 확장 프로그램을 새 창에서 여세요." }, "popOut": { - "message": "Pop out" + "message": "새 창에서 열기" }, "sendFileCalloutHeader": { "message": "시작하기 전에" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "달력을 보고 날짜를 선택하려면", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "여기를 클릭하여", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "확장 프로그램을 새 창에서 여세요.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "제공된 만료 날짜가 유효하지 않습니다." }, @@ -2652,14 +2573,8 @@ "dateParsingError": { "message": "삭제 날짜와 만료 날짜를 저장하는 도중 오류가 발생했습니다." }, - "hideEmail": { - "message": "받는 사람으로부터 나의 이메일 주소 숨기기" - }, "hideYourEmail": { - "message": "Hide your email address from viewers." - }, - "sendOptionsPolicyInEffect": { - "message": "하나 이상의 단체 정책이 Send 설정에 영향을 미치고 있습니다." + "message": "사람들로부터 이메일 주소를 숨기세요." }, "passwordPrompt": { "message": "마스터 비밀번호 재확인" @@ -2674,7 +2589,7 @@ "message": "이메일 인증 필요함" }, "emailVerifiedV2": { - "message": "Email verified" + "message": "이메일 인증됨" }, "emailVerificationRequiredDesc": { "message": "이 기능을 사용하려면 이메일 인증이 필요합니다. 웹 보관함에서 이메일을 인증할 수 있습니다." @@ -2689,10 +2604,10 @@ "message": "최근에 조직 관리자가 마스터 비밀번호를 변경했습니다. 보관함에 액세스하려면 지금 업데이트해야 합니다. 계속하면 현재 세션에서 로그아웃되며 다시 로그인해야 합니다. 다른 장치의 활성 세션은 최대 1시간 동안 계속 활성 상태로 유지될 수 있습니다." }, "updateWeakMasterPasswordWarning": { - "message": "Your master password does not meet one or more of your organization policies. In order to access the vault, you must update your master password now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." + "message": "마스터 비밀번호가 조직 정책 중 하나 이상을 충족하지 못합니다. 보관함에 액세스하려면, 지금 마스터 비밀번호를 업데이트해야 합니다. 계속 진행하면 현재 세션에서 로그아웃되므로, 다시 로그인해야 합니다. 다른 장치에서 활성 세션은 최대 1시간 동안 계속 활성 상태로 유지될 수 있습니다." }, "tdeDisabledMasterPasswordRequired": { - "message": "Your organization has disabled trusted device encryption. Please set a master password to access your vault." + "message": "조직에서 신뢰할 수 있는 장치 암호화를 비활성화했습니다. 보관함에 접근하려면 마스터 비밀번호를 설정하세요." }, "resetPasswordPolicyAutoEnroll": { "message": "자동 등록" @@ -2708,15 +2623,15 @@ "description": "Used as a message within the notification bar when no folders are found" }, "orgPermissionsUpdatedMustSetPassword": { - "message": "Your organization permissions were updated, requiring you to set a master password.", + "message": "조직 권한이 업데이트되어 마스터 비밀번호를 설정해야 합니다.", "description": "Used as a card title description on the set password page to explain why the user is there" }, "orgRequiresYouToSetPassword": { - "message": "Your organization requires you to set a master password.", + "message": "당신의 조직은 마스터 비밀번호를 설정해야 합니다.", "description": "Used as a card title description on the set password page to explain why the user is there" }, "cardMetrics": { - "message": "out of $TOTAL$", + "message": "$TOTAL$ 중에서", "placeholders": { "total": { "content": "$1", @@ -2735,7 +2650,7 @@ "message": "분" }, "vaultTimeoutPolicyAffectingOptions": { - "message": "Enterprise policy requirements have been applied to your timeout options" + "message": "타임아웃 옵션에 기업의 정책 요구 사항이 적용되었습니다" }, "vaultTimeoutPolicyInEffect": { "message": "조직 정책이 보관함 제한 시간에 영향을 미치고 있습니다. 최대 허용 보관함 제한 시간은 $HOURS$시간 $MINUTES$분입니다", @@ -2751,7 +2666,7 @@ } }, "vaultTimeoutPolicyInEffect1": { - "message": "$HOURS$ hour(s) and $MINUTES$ minute(s) maximum.", + "message": "최대 $HOURS$시간 $MINUTES$분", "placeholders": { "hours": { "content": "$1", @@ -2764,7 +2679,7 @@ } }, "vaultTimeoutPolicyMaximumError": { - "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "message": "타임아웃이 조직에서 설정한 제한을 초과합니다: 최대 $HOURS$시간 $MINUTES$분", "placeholders": { "hours": { "content": "$1", @@ -2777,7 +2692,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s). Your vault timeout action is set to $ACTION$.", + "message": "조직 정책이 보관함 타임아웃에 영향을 미치고 있습니다. 최대 허용 보관함 타임아웃은 최대 $HOURS$시간 $MINUTES$분입니다. 보관함 타임아웃 작업은 $ACTION$으로 설정되어 있습니다.", "placeholders": { "hours": { "content": "$1", @@ -2794,7 +2709,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "Your organization policies have set your vault timeout action to $ACTION$.", + "message": "조직 정책에 따라 보관함 타임아웃 작업이 $ACTION$으로 설정되었습니다.", "placeholders": { "action": { "content": "$1", @@ -2803,7 +2718,7 @@ } }, "vaultTimeoutTooLarge": { - "message": "Your vault timeout exceeds the restrictions set by your organization." + "message": "보관함 시간 초과가 조직에서 설정한 제한을 초과합니다." }, "vaultExportDisabled": { "message": "보관함 내보내기 비활성화됨" @@ -2851,7 +2766,7 @@ "message": "개인 보관함을 내보내는 중" }, "exportingIndividualVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", + "message": "$EMAIL$과 관련된 개별 보관함 항목만 내보냅니다. 조직 보관함 항목은 포함되지 않습니다. 보관함 항목 정보만 내보내며 관련 첨부 파일은 포함되지 않습니다", "placeholders": { "email": { "content": "$1", @@ -2860,10 +2775,10 @@ } }, "exportingOrganizationVaultTitle": { - "message": "Exporting organization vault" + "message": "조직 보관함을 내보내는 중" }, "exportingOrganizationVaultDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", + "message": "$ORGANIZATION$ 조직과 연관된 조직 보관함만 내보내기됩니다. 개인 보관함이나 다른 조직의 항목은 포함되지 않습니다.", "placeholders": { "organization": { "content": "$1", @@ -2874,17 +2789,14 @@ "error": { "message": "오류" }, - "regenerateUsername": { - "message": "아이디 재생성" - }, "generateUsername": { "message": "아이디 생성" }, "generateEmail": { - "message": "Generate email" + "message": "이메일 생성" }, "spinboxBoundariesHint": { - "message": "Value must be between $MIN$ and $MAX$.", + "message": "값은 $MIN$과 $MAX$ 사이여야 합니다", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2898,7 +2810,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", + "message": " 강력한 비밀번호를 생성하려면 $RECOMMENDED$ 문자 이상을 사용하세요", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2908,7 +2820,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": " 강력한 암호를 생성하려면 $RECOMMENDED$ 단어 이상을 사용하세요.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2917,21 +2829,18 @@ } } }, - "usernameType": { - "message": "아이디 유형" - }, "plusAddressedEmail": { - "message": "Plus addressed email", + "message": "추가 이메일", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" }, "plusAddressedEmailDesc": { - "message": "Use your email provider's sub-addressing capabilities." + "message": "이메일 제공업체의 하위 주소 지정 기능을 사용하세요." }, "catchallEmail": { - "message": "Catch-all email" + "message": "Catch-all 이메일 (도메인 상의 어떤 주소로도 전송된 이메일을 받을 수 있는 주소)" }, "catchallEmailDesc": { - "message": "Use your domain's configured catch-all inbox." + "message": "catch-all이 설정된 내 도메인의 메일함을 사용하세요." }, "random": { "message": "무작위" @@ -2942,12 +2851,6 @@ "websiteName": { "message": "웹사이트 이름" }, - "whatWouldYouLikeToGenerate": { - "message": "무엇을 생성하실건가요?" - }, - "passwordType": { - "message": "비밀번호 유형" - }, "service": { "message": "서비스" }, @@ -2955,18 +2858,18 @@ "message": "포워딩된 이메일 별칭" }, "forwardedEmailDesc": { - "message": "Generate an email alias with an external forwarding service." + "message": "외부 포워딩 서비스를 사용해서 이메일 주소 별칭을 만들어보세요." }, "forwarderDomainName": { - "message": "Email domain", + "message": "이메일 도메인", "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { - "message": "Choose a domain that is supported by the selected service", + "message": "선택한 서비스에서 지원하는 도메인 선택", "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { - "message": "$SERVICENAME$ error: $ERRORMESSAGE$", + "message": "$SERVICENAME$ 오류: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", "placeholders": { "servicename": { @@ -2980,11 +2883,11 @@ } }, "forwarderGeneratedBy": { - "message": "Generated by Bitwarden.", + "message": "Bitwarden에서 생성됨", "description": "Displayed with the address on the forwarding service's configuration screen." }, "forwarderGeneratedByWithWebsite": { - "message": "Website: $WEBSITE$. Generated by Bitwarden.", + "message": "웹사이트: $WEBSITE$. Bitwarden에서 생성됨", "description": "Displayed with the address on the forwarding service's configuration screen.", "placeholders": { "WEBSITE": { @@ -2994,7 +2897,7 @@ } }, "forwaderInvalidToken": { - "message": "Invalid $SERVICENAME$ API token", + "message": "잘못된 $SERVICENAME$ API 토큰\n", "description": "Displayed when the user's API token is empty or rejected by the forwarding service.", "placeholders": { "servicename": { @@ -3004,7 +2907,7 @@ } }, "forwaderInvalidTokenWithMessage": { - "message": "Invalid $SERVICENAME$ API token: $ERRORMESSAGE$", + "message": "잘못된 $SERVICENAME$ API 토큰: $ERRORMESSAGE$", "description": "Displayed when the user's API token is rejected by the forwarding service with an error message.", "placeholders": { "servicename": { @@ -3018,7 +2921,7 @@ } }, "forwarderNoAccountId": { - "message": "Unable to obtain $SERVICENAME$ masked email account ID.", + "message": "$SERVICENAME$ 마스크된 이메일 계정 ID를 얻을 수 없습니다.", "description": "Displayed when the forwarding service fails to return an account ID.", "placeholders": { "servicename": { @@ -3028,7 +2931,7 @@ } }, "forwarderNoDomain": { - "message": "Invalid $SERVICENAME$ domain.", + "message": "잘못된 $SERVICENAME$ 도메인.", "description": "Displayed when the domain is empty or domain authorization failed at the forwarding service.", "placeholders": { "servicename": { @@ -3038,7 +2941,7 @@ } }, "forwarderNoUrl": { - "message": "Invalid $SERVICENAME$ url.", + "message": "잘못된 $SERVICENAME$ URL", "description": "Displayed when the url of the forwarding service wasn't supplied.", "placeholders": { "servicename": { @@ -3048,7 +2951,7 @@ } }, "forwarderUnknownError": { - "message": "Unknown $SERVICENAME$ error occurred.", + "message": "알 수 없는 $SERVICENAME$ 오류가 발생했습니다.", "description": "Displayed when the forwarding service failed due to an unknown error.", "placeholders": { "servicename": { @@ -3058,7 +2961,7 @@ } }, "forwarderUnknownForwarder": { - "message": "Unknown forwarder: '$SERVICENAME$'.", + "message": "알 수 없는 포워더: '$SERVICENAME$'.", "description": "Displayed when the forwarding service is not supported.", "placeholders": { "servicename": { @@ -3084,13 +2987,13 @@ "message": "프리미엄 구독이 필요합니다" }, "organizationIsDisabled": { - "message": "Organization suspended." + "message": "조직이 중지됨" }, "disabledOrganizationFilterError": { - "message": "Items in suspended Organizations cannot be accessed. Contact your Organization owner for assistance." + "message": "중단된 조직의 항목에 액세스할 수 없습니다. 조직 소유자에게 도움을 요청하세요." }, "loggingInTo": { - "message": "Logging in to $DOMAIN$", + "message": "$DOMAIN$(으)로 로그인", "placeholders": { "domain": { "content": "$1", @@ -3099,13 +3002,13 @@ } }, "settingsEdited": { - "message": "Settings have been edited" + "message": "설정이 편집되었습니다" }, "environmentEditedClick": { - "message": "Click here" + "message": "여기를 클릭하세요." }, "environmentEditedReset": { - "message": "to reset to pre-configured settings" + "message": "사전 구성된 설정으로 재설정하려면" }, "serverVersion": { "message": "서버 버전" @@ -3117,7 +3020,7 @@ "message": "제 3자" }, "thirdPartyServerMessage": { - "message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.", + "message": "제 3자 서버 구현에 연결되었습니다. $SERVERNAME$. 공식 서버를 사용하여 버그를 확인하거나 타사 서버에 보고해 주세요.", "placeholders": { "servername": { "content": "$1", @@ -3126,7 +3029,7 @@ } }, "lastSeenOn": { - "message": "last seen on: $DATE$", + "message": "확인된 날짜: $DATE$", "placeholders": { "date": { "content": "$1", @@ -3135,10 +3038,10 @@ } }, "loginWithMasterPassword": { - "message": "Log in with master password" + "message": "마스터 비밀번호로 로그인" }, "loggingInAs": { - "message": "Logging in as" + "message": "다음으로 로그인 중" }, "notYou": { "message": "본인이 아닌가요?" @@ -3150,67 +3053,67 @@ "message": "이메일 기억하기" }, "loginWithDevice": { - "message": "Log in with device" + "message": "기기로 로그인" }, "loginWithDeviceEnabledInfo": { - "message": "Log in with device must be set up in the settings of the Bitwarden app. Need another option?" + "message": "기기로 로그인하려면 Bitwarden 모바일 앱 설정에서 설정해야 합니다. 다른 방식이 필요하신가요?" }, "fingerprintPhraseHeader": { - "message": "Fingerprint phrase" + "message": "지문 구절" }, "fingerprintMatchInfo": { - "message": "Please make sure your vault is unlocked and the Fingerprint phrase matches on the other device." + "message": "반드시 보관함이 잠금 해제되었고, 지문 구절이 다른 기기에서 일치하는지 확인해주세요." }, "resendNotification": { - "message": "Resend notification" + "message": "알림 다시 보내기" }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "모든 로그인 방식 보기" }, - "viewAllLoginOptions": { - "message": "View all log in options" + "viewAllLoginOptionsV1": { + "message": "모든 로그인 옵션 보기" }, "notificationSentDevice": { - "message": "A notification has been sent to your device." + "message": "기기에 알림이 전송되었습니다." }, "aNotificationWasSentToYourDevice": { - "message": "A notification was sent to your device" + "message": "기기에 알림이 전송되었습니다." }, "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { - "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + "message": "반드시 계정이 잠금 해제되었고, 지문 구절이 다른 기기에서 일치하는지 확인해주세요." }, "youWillBeNotifiedOnceTheRequestIsApproved": { - "message": "You will be notified once the request is approved" + "message": "요청이 승인되면 알림을 받게 됩니다" }, "needAnotherOptionV1": { - "message": "Need another option?" + "message": "다른 옵션이 필요하신가요?" }, "loginInitiated": { - "message": "Login initiated" + "message": "로그인 시작" }, "exposedMasterPassword": { - "message": "Exposed Master Password" + "message": "노출된 마스터 비밀번호" }, "exposedMasterPasswordDesc": { - "message": "Password found in a data breach. Use a unique password to protect your account. Are you sure you want to use an exposed password?" + "message": "데이터 유출이 된 비밀번호임이 발견되었습니다. 계정을 보호하려면 고유한 비밀번호를 사용하세요. 노출된 비밀번호를 사용하시겠습니까?" }, "weakAndExposedMasterPassword": { - "message": "Weak and Exposed Master Password" + "message": "취약하고 노출된 마스터 비밀번호" }, "weakAndBreachedMasterPasswordDesc": { - "message": "Weak password identified and found in a data breach. Use a strong and unique password to protect your account. Are you sure you want to use this password?" + "message": "데이터 유출이 된 약한 비밀번호임이 발견되었습니다. 계정을 보호하려면 강력하고 고유한 비밀번호를 사용하세요. 이 비밀번호를 사용하시겠습니까?" }, "checkForBreaches": { - "message": "Check known data breaches for this password" + "message": "이 비밀번호에 대한 알려진 데이터 유출 확인\n" }, "important": { - "message": "Important:" + "message": "중요:" }, "masterPasswordHint": { - "message": "Your master password cannot be recovered if you forget it!" + "message": "마스터 비밀번호를 잊어버리면 복구할 수 없습니다!\n" }, "characterMinimum": { - "message": "$LENGTH$ character minimum", + "message": "최소 $LENGTH$ 문자", "placeholders": { "length": { "content": "$1", @@ -3219,13 +3122,13 @@ } }, "autofillPageLoadPolicyActivated": { - "message": "Your organization policies have turned on autofill on page load." + "message": "조직 정책에 따라, 페이지 로드 시 자동 완성 기능을 켰습니다." }, "howToAutofill": { - "message": "How to autofill" + "message": "자동 완성 사용법" }, "autofillSelectInfoWithCommand": { - "message": "Select an item from this screen, use the shortcut $COMMAND$, or explore other options in settings.", + "message": "이 화면에서 항목을 선택하거나, 바로 가기 $COMMAND$를 사용하거나, 설정의 다른 옵션을 탐색하세요.", "placeholders": { "command": { "content": "$1", @@ -3234,31 +3137,31 @@ } }, "autofillSelectInfoWithoutCommand": { - "message": "Select an item from this screen, or explore other options in settings." + "message": "이 화면에서 항목을 선택하거나 설정의 다른 옵션을 탐색하세요." }, "gotIt": { - "message": "Got it" + "message": "이해했습니다" }, "autofillSettings": { "message": "자동 완성 설정" }, "autofillKeyboardShortcutSectionTitle": { - "message": "Autofill shortcut" + "message": "자동 완성 바로가기" }, "autofillKeyboardShortcutUpdateLabel": { - "message": "Change shortcut" + "message": "바로가기 변경" }, "autofillKeyboardManagerShortcutsLabel": { - "message": "Manage shortcuts" + "message": "바로가기 관리" }, "autofillShortcut": { "message": "자동 완성 키보드 단축키" }, "autofillLoginShortcutNotSet": { - "message": "The autofill login shortcut is not set. Change this in the browser's settings." + "message": "자동 채우기 로그인 바로 가기가 설정되어 있지 않습니다. 브라우저 설정에서 이 항목을 변경해주세요." }, "autofillLoginShortcutText": { - "message": "The autofill login shortcut is $COMMAND$. Manage all shortcuts in the browser's settings.", + "message": "자동 채우기 로그인 바로 가기는 $COMMAND$입니다. 브라우저 설정의 모든 바로 가기를 관리하세요.", "placeholders": { "command": { "content": "$1", @@ -3267,7 +3170,7 @@ } }, "autofillShortcutTextSafari": { - "message": "Default autofill shortcut: $COMMAND$.", + "message": "기본 자동 완성 바로 가기: $COMMAND$.", "placeholders": { "command": { "content": "$1", @@ -3276,65 +3179,65 @@ } }, "opensInANewWindow": { - "message": "Opens in a new window" + "message": "새 창에서 열립니다" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "향후 로그인을 원활하게 하기 위해 이 기기 기억하기" }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "기기 승인이 필요합니다. 아래에서 승인 옵션을 선택하세요:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "기기 승인이 필요합니다." }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "아래에서 승인 옵션을 선택하세요" }, "rememberThisDevice": { "message": "이 기기 기억하기" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "공용 기기를 사용하는 경우 체크 해제" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "다른 장치에서 승인" }, "requestAdminApproval": { - "message": "관리자 승인 필요" + "message": "관리자 인증 필요" }, "approveWithMasterPassword": { - "message": "Approve with master password" + "message": "마스터 비밀번호로 승인" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "조직의 SSO 식별자가 필요합니다" }, "creatingAccountOn": { - "message": "Creating account on" + "message": "계정 만들기" }, "checkYourEmail": { - "message": "Check your email" + "message": "이메일을 확인해주세요" }, "followTheLinkInTheEmailSentTo": { - "message": "Follow the link in the email sent to" + "message": "이메일로 전송한 링크를 통해" }, "andContinueCreatingYourAccount": { - "message": "and continue creating your account." + "message": "계정을 계속 생성하세요." }, "noEmail": { - "message": "No email?" + "message": "이메일이 전송되지 않았나요?" }, "goBack": { - "message": "Go back" + "message": "뒤로 돌아가서" }, "toEditYourEmailAddress": { - "message": "to edit your email address." + "message": "이메일 주소를 수정하기" }, "eu": { "message": "EU", "description": "European Union" }, "accessDenied": { - "message": "Access denied. You do not have permission to view this page." + "message": "접근이 거부되었습니다. 이 페이지를 볼 권한이 없습니다." }, "general": { "message": "일반" @@ -3349,45 +3252,45 @@ "message": "관리자 승인 필요" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "요청이 관리자에게 전송되었습니다." }, "youWillBeNotifiedOnceApproved": { - "message": "You will be notified once approved." + "message": "승인되면 알림을 받게 됩니다." }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "로그인에 문제가 있나요?" }, "loginApproved": { - "message": "Login approved" + "message": "로그인 승인됨" }, "userEmailMissing": { - "message": "User email missing" + "message": "사용자 이메일 누락" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "활성화된 사용자의 이메일을 찾을 수 없습니다. 로그아웃합니다." }, "deviceTrusted": { - "message": "Device trusted" + "message": "신뢰할 수 있는 장치" }, "sendsNoItemsTitle": { - "message": "No active Sends", + "message": "활성화된 Send없음", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendsNoItemsMessage": { - "message": "Use Send to securely share encrypted information with anyone.", + "message": "Send를 사용하여 암호화된 정보를 어느 사람과도 안전하게 공유합니다.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "inputRequired": { - "message": "Input is required." + "message": "입력이 필요합니다." }, "required": { - "message": "required" + "message": "필수" }, "search": { "message": "검색" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "입력은 최소한 $COUNT$자 이상이어야 합니다.", "placeholders": { "count": { "content": "$1", @@ -3396,7 +3299,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "입력 길이는 $COUNT$자를 초과해서는 안 됩니다.", "placeholders": { "count": { "content": "$1", @@ -3405,7 +3308,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "다음 문자는 허용되지 않습니다: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -3414,7 +3317,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "입력 값은 최소 $MIN$자 이상이어야 합니다.", "placeholders": { "min": { "content": "$1", @@ -3423,7 +3326,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "입력 값은 $MAX$ 자를 초과해서는 안 됩니다.", "placeholders": { "max": { "content": "$1", @@ -3435,14 +3338,14 @@ "message": "하나 이상의 이메일이 유효하지 않습니다." }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "입력에는 공백만 포함해서는 안 됩니다.", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "입력이 이메일 주소가 아닙니다" }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "위의 $COUNT$ 필드에 주의가 필요합니다", "placeholders": { "count": { "content": "$1", @@ -3451,10 +3354,10 @@ } }, "singleFieldNeedsAttention": { - "message": "1 field needs your attention." + "message": "1개의 필드가 주의가 필요합니다." }, "multipleFieldsNeedAttention": { - "message": "$COUNT$ fields need your attention.", + "message": "$COUNT$ 개의 필드가 주의가 필요합니다.", "placeholders": { "count": { "content": "$1", @@ -3463,22 +3366,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- 선택 --" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "- 필터링할 유형 --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "옵션을 검색하는 중..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "항목을 찾을 수 없습니다" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "모두 지우기" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ $QUANTITY$개 더보기", "placeholders": { "quantity": { "content": "$1", @@ -3487,30 +3390,30 @@ } }, "submenu": { - "message": "Submenu" + "message": "하위 메뉴" }, "toggleCollapse": { - "message": "Toggle collapse", + "message": "토글이 붕괴됨", "description": "Toggling an expand/collapse state." }, "filelessImport": { - "message": "Import your data to Bitwarden?", + "message": "데이터를 Bitwarden으로 가져오시겠습니까?", "description": "Default notification title for triggering a fileless import." }, "lpFilelessImport": { - "message": "Protect your LastPass data and import to Bitwarden?", + "message": "LastPass 데이터를 보호하고 Bitwarden으로 가져오시겠습니까?", "description": "LastPass specific notification title for triggering a fileless import." }, "lpCancelFilelessImport": { - "message": "Save as unencrypted file", + "message": "암호화되지 않은 파일로 저장", "description": "LastPass specific notification button text for cancelling a fileless import." }, "startFilelessImport": { - "message": "Import to Bitwarden", + "message": "Bitwarden으로 가져오기", "description": "Notification button text for starting a fileless import." }, "importing": { - "message": "Importing...", + "message": "가져오는 중...", "description": "Notification message for when an import is in progress." }, "dataSuccessfullyImported": { @@ -3518,52 +3421,52 @@ "description": "Notification message for when an import has completed successfully." }, "dataImportFailed": { - "message": "Error importing. Check console for details.", + "message": "가져오는 중 오류가 발생했습니다. 자세한 내용은 콘솔을 확인하세요.", "description": "Notification message for when an import has failed." }, "importNetworkError": { - "message": "Network error encountered during import.", + "message": "가져오기 중에 네트워크 오류가 발생했습니다.", "description": "Notification message for when an import has failed due to a network error." }, "aliasDomain": { - "message": "Alias domain" + "message": "도메인 별칭" }, "passwordRepromptDisabledAutofillOnPageLoad": { - "message": "Items with master password re-prompt cannot be autofilled on page load. Autofill on page load turned off.", + "message": "마스터 비밀번호 재 요청이 있는 항목은 페이지 로드에서 자동으로 채울 수 없습니다. 페이지 로드의 자동 완성이 꺼졌습니다.", "description": "Toast message for describing that master password re-prompt cannot be autofilled on page load." }, "autofillOnPageLoadSetToDefault": { - "message": "Autofill on page load set to use default setting.", + "message": "페이지 로드 시 자동 완성이 기본 설정을 사용하도록 설정되었습니다.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "turnOffMasterPasswordPromptToEditField": { - "message": "Turn off master password re-prompt to edit this field", + "message": "마스터 암호 재 요청을 해제하여 이 필드를 편집합니다", "description": "Message appearing below the autofill on load message when master password reprompt is set for a vault item." }, "toggleSideNavigation": { - "message": "Toggle side navigation" + "message": "사이드 내비게이션 전환" }, "skipToContent": { - "message": "Skip to content" + "message": "콘텐츠로 건너뛰기" }, "bitwardenOverlayButton": { - "message": "Bitwarden autofill menu button", + "message": "Bitwarden 자동 완성 메뉴 버튼", "description": "Page title for the iframe containing the overlay button" }, "toggleBitwardenVaultOverlay": { - "message": "Toggle Bitwarden autofill menu", + "message": "Bitwarden 자동 완성메뉴 전환", "description": "Screen reader and tool tip label for the overlay button" }, "bitwardenVault": { - "message": "Bitwarden autofill menu", + "message": "Bitwarden 자동 완성 매뉴", "description": "Page title in overlay" }, "unlockYourAccountToViewMatchingLogins": { - "message": "Unlock your account to view matching logins", + "message": "일치하는 로그인을 보기위해 계정을 잠금해제하세요", "description": "Text to display in overlay when the account is locked." }, "unlockYourAccountToViewAutofillSuggestions": { - "message": "Unlock your account to view autofill suggestions", + "message": "계정 잠금을 해제하여 자동 채우기 제안 보기", "description": "Text to display in overlay when the account is locked." }, "unlockAccount": { @@ -3571,19 +3474,27 @@ "description": "Button text to display in overlay when the account is locked." }, "unlockAccountAria": { - "message": "Unlock your account, opens in a new window", + "message": "계정 잠금을 해제하기, 새 창에서 열립니다", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "TOTP 인증 코드", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "TOTP 만료까지 남은 시간", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { - "message": "Fill credentials for", + "message": "자격 증명 채우기", "description": "Screen reader text for when overlay item is in focused" }, "partialUsername": { - "message": "Partial username", + "message": "부분적인 사용자 이름", "description": "Screen reader text for when a login item is focused where a partial username is displayed. SR will announce this phrase before reading the text of the partial username" }, "noItemsToShow": { - "message": "No items to show", + "message": "표시할 항목 없음", "description": "Text to show in overlay if there are no matching items" }, "newItem": { @@ -3591,64 +3502,64 @@ "description": "Button text to display in overlay when there are no matching items" }, "addNewVaultItem": { - "message": "Add new vault item", + "message": "새 보관함 항목 추가", "description": "Screen reader text (aria-label) for new item button in overlay" }, "newLogin": { - "message": "New login", + "message": "새 로그인", "description": "Button text to display within inline menu when there are no matching items on a login field" }, "addNewLoginItemAria": { - "message": "Add new vault login item, opens in a new window", + "message": "새 보관함 로그인 항목 추가, 새 창에서 열립니다", "description": "Screen reader text (aria-label) for new login button within inline menu" }, "newCard": { - "message": "New card", + "message": "새 카드", "description": "Button text to display within inline menu when there are no matching items on a credit card field" }, "addNewCardItemAria": { - "message": "Add new vault card item, opens in a new window", + "message": "새 보관함 카드 항목 추가, 새 창에서 열립니다", "description": "Screen reader text (aria-label) for new card button within inline menu" }, "newIdentity": { - "message": "New identity", + "message": "신규 ID", "description": "Button text to display within inline menu when there are no matching items on an identity field" }, "addNewIdentityItemAria": { - "message": "Add new vault identity item, opens in a new window", + "message": "새 보관함 ID 항목 추가, 새 창에서 열립니다", "description": "Screen reader text (aria-label) for new identity button within inline menu" }, "bitwardenOverlayMenuAvailable": { - "message": "Bitwarden autofill menu available. Press the down arrow key to select.", + "message": "Bitwarden 자동 완성 메뉴를 사용할 수 있습니다. 아래쪽 화살표 키를 눌러 선택하세요.", "description": "Screen reader text for announcing when the overlay opens on the page" }, "turnOn": { - "message": "Turn on" + "message": "켜기" }, "ignore": { - "message": "Ignore" + "message": "무시하기" }, "importData": { - "message": "Import data", + "message": "데이터 가져오기", "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" }, "importError": { - "message": "Import error" + "message": "가져오기 오류" }, "importErrorDesc": { - "message": "There was a problem with the data you tried to import. Please resolve the errors listed below in your source file and try again." + "message": "가져오려고 하는 데이터에 문제가 있습니다. 아래에 표시된 파일의 오류를 해결한 뒤 다시 시도해 주세요." }, "resolveTheErrorsBelowAndTryAgain": { - "message": "Resolve the errors below and try again." + "message": "아래 오류를 해결하고 다시 시도하세요." }, "description": { - "message": "Description" + "message": "설명" }, "importSuccess": { - "message": "Data successfully imported" + "message": "데이터 가져오기 성공" }, "importSuccessNumberOfItems": { - "message": "A total of $AMOUNT$ items were imported.", + "message": "총 $AMOUNT$개의 항목을 가져왔습니다.", "placeholders": { "amount": { "content": "$1", @@ -3660,43 +3571,43 @@ "message": "다시 시도" }, "verificationRequiredForActionSetPinToContinue": { - "message": "Verification required for this action. Set a PIN to continue." + "message": "이 작업을 수행하려면 증명이 필요합니다. 계속하려면 PIN을 설정하세요." }, "setPin": { - "message": "Set PIN" + "message": "PIN 설정하기" }, "verifyWithBiometrics": { - "message": "Verify with biometrics" + "message": "생체 인식을 사용하여 증명하기" }, "awaitingConfirmation": { - "message": "Awaiting confirmation" + "message": "확인 대기 중" }, "couldNotCompleteBiometrics": { - "message": "Could not complete biometrics." + "message": "생체 인식을 완료할 수 없습니다." }, "needADifferentMethod": { - "message": "Need a different method?" + "message": "다른 방법이 필요하신가요?" }, "useMasterPassword": { - "message": "Use master password" + "message": "마스터 비밀번호를 사용하기" }, "usePin": { - "message": "Use PIN" + "message": "PIN 사용하기" }, "useBiometrics": { - "message": "Use biometrics" + "message": "생체 인식 사용하기" }, "enterVerificationCodeSentToEmail": { - "message": "Enter the verification code that was sent to your email." + "message": "이메일로 전송된 인증 코드를 입력해주세요" }, "resendCode": { - "message": "Resend code" + "message": "코드 재전송" }, "total": { - "message": "Total" + "message": "합계" }, "importWarning": { - "message": "You are importing data to $ORGANIZATION$. Your data may be shared with members of this organization. Do you want to proceed?", + "message": "데이터를 $ORGANIZATION$로 가져오고 있습니다. 데이터를 이 조직의 구성원들과 공유할 수 있습니다. 계속 진행하시겠습니까?", "placeholders": { "organization": { "content": "$1", @@ -3705,19 +3616,19 @@ } }, "duoHealthCheckResultsInNullAuthUrlError": { - "message": "Error connecting with the Duo service. Use a different two-step login method or contact Duo for assistance." + "message": "Duo 서비스 연결 중 오류가 발생했습니다. 다른 2단계 로그인 방법을 사용하거나 Duo에 문의하여 도움을 받으세요." }, "launchDuoAndFollowStepsToFinishLoggingIn": { - "message": "Launch Duo and follow the steps to finish logging in." + "message": "듀오를 실행하고 단계를 따라 로그인을 완료하세요" }, "duoRequiredForAccount": { - "message": "Duo two-step login is required for your account." + "message": "계정에 Duo 2단계 로그인이 필요합니다." }, "popoutTheExtensionToCompleteLogin": { - "message": "Popout the extension to complete login." + "message": "확장 프로그램을 실행하여 로그인을 완료합니다." }, "popoutExtension": { - "message": "Popout extension" + "message": "확장 프로그램을 새 창에서 열기" }, "launchDuo": { "message": "Duo 실행" @@ -3729,25 +3640,25 @@ "message": "아무것도 가져오지 못했습니다." }, "importEncKeyError": { - "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." + "message": "내보내려는 파일을 복호화하던 중 오류가 발생했습니다. 암호화 키가 내보내려는 데이터를 암호화한 키와 일치하지 않습니다." }, "invalidFilePassword": { - "message": "Invalid file password, please use the password you entered when you created the export file." + "message": "파일 비밀번호가 잘못되었습니다. 내보내기 파일을 만들 때 입력한 비밀번호를 사용해 주세요." }, "destination": { - "message": "Destination" + "message": "수신자" }, "learnAboutImportOptions": { - "message": "Learn about your import options" + "message": "가져오기 옵션 알아보기" }, "selectImportFolder": { - "message": "Select a folder" + "message": "폴더 선택" }, "selectImportCollection": { - "message": "Select a collection" + "message": "컬렉션 선택" }, "importTargetHint": { - "message": "Select this option if you want the imported file contents moved to a $DESTINATION$", + "message": "가져온 파일의 내용을 $DESTINATION$로 이동하려면 이 옵션을 선택하세요.", "description": "Located as a hint under the import target. Will be appended by either folder or collection, depending if the user is importing into an individual or an organizational vault.", "placeholders": { "destination": { @@ -3757,25 +3668,25 @@ } }, "importUnassignedItemsError": { - "message": "File contains unassigned items." + "message": "파일에 할당되지 않은 항목이 포함되어 있습니다." }, "selectFormat": { - "message": "Select the format of the import file" + "message": "불러올 파일의 포맷 선택" }, "selectImportFile": { - "message": "Select the import file" + "message": "불러올 파일 선택" }, "chooseFile": { - "message": "Choose File" + "message": "파일 선택" }, "noFileChosen": { - "message": "No file chosen" + "message": "선택된 파일 없음" }, "orCopyPasteFileContents": { - "message": "or copy/paste the import file contents" + "message": "또는 가져온 파일 내용 복사/붙여넣기" }, "instructionsFor": { - "message": "$NAME$ Instructions", + "message": "$NAME$ 지침", "description": "The title for the import tool instructions.", "placeholders": { "name": { @@ -3785,25 +3696,25 @@ } }, "confirmVaultImport": { - "message": "Confirm vault import" + "message": "보관함 가져오기 확인" }, "confirmVaultImportDesc": { - "message": "This file is password-protected. Please enter the file password to import data." + "message": "이 파일은 비밀번호로 보호받고 있습니다. 데이터를 가져오려면 파일 비밀번호를 입력하세요." }, "confirmFilePassword": { - "message": "Confirm file password" + "message": "파일 비밀번호 확인" }, "exportSuccess": { - "message": "Vault data exported" + "message": "보관함 데이터 내보내짐" }, "typePasskey": { "message": "패스키" }, "accessing": { - "message": "Accessing" + "message": "접근 중" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "로그인 완료!" }, "passkeyNotCopied": { "message": "패스키가 복사되지 않습니다" @@ -3815,7 +3726,7 @@ "message": "사이트에서 인증을 요구합니다. 이 기능은 비밀번호가 없는 계정에서는 아직 지원하지 않습니다." }, "logInWithPasskeyQuestion": { - "message": "Log in with passkey?" + "message": "패스키로 로그인하시겠어요?" }, "passkeyAlreadyExists": { "message": "이미 이 애플리케이션에 해당하는 패스키가 있습니다." @@ -3824,16 +3735,16 @@ "message": "이 애플리케이션에 대한 패스키를 찾을 수 없습니다." }, "noMatchingPasskeyLogin": { - "message": "사이트와 일치하는 로그인이 없습니다." + "message": "이 사이트와 일치하는 로그인이 없습니다." }, "noMatchingLoginsForSite": { - "message": "No matching logins for this site" + "message": "사이트와 일치하는 로그인 없음" }, "searchSavePasskeyNewLogin": { - "message": "Search or save passkey as new login" + "message": "패스키를 새 로그인으로 검색 또는 저장" }, "confirm": { - "message": "Confirm" + "message": "확인" }, "savePasskey": { "message": "패스키 저장" @@ -3842,10 +3753,10 @@ "message": "새 로그인으로 패스키 저장" }, "chooseCipherForPasskeySave": { - "message": "Choose a login to save this passkey to" + "message": "패스키를 저장할 로그인 선택하기" }, "chooseCipherForPasskeyAuth": { - "message": "Choose a passkey to log in with" + "message": "로그인할 패스키 선택" }, "passkeyItem": { "message": "패스키 항목" @@ -3857,128 +3768,128 @@ "message": "이 항목은 이미 패스키가 있습니다. 정말로 현재 패스키를 덮어쓰시겠어요?" }, "featureNotSupported": { - "message": "Feature not yet supported" + "message": "아직 지원되지 않는 기능" }, "yourPasskeyIsLocked": { "message": "패스키를 사용하려면 인증이 필요합니다. 인증을 진행해주세요." }, "multifactorAuthenticationCancelled": { - "message": "Multifactor authentication cancelled" + "message": "멀티팩터 인증이 취소되었습니다" }, "noLastPassDataFound": { - "message": "No LastPass data found" + "message": "LastPass 데이터를 찾을 수 없습니다" }, "incorrectUsernameOrPassword": { - "message": "Incorrect username or password" + "message": "잘못된 사용자 이름 또는 비밀번호 입니다." }, "incorrectPassword": { - "message": "Incorrect password" + "message": "잘못된 비밀번호입니다" }, "incorrectCode": { - "message": "Incorrect code" + "message": "잘못된 코드입니다." }, "incorrectPin": { - "message": "Incorrect PIN" + "message": "올바르지 않은 PIN입니다." }, "multifactorAuthenticationFailed": { - "message": "Multifactor authentication failed" + "message": "멀티팩터 인증 실패" }, "includeSharedFolders": { - "message": "Include shared folders" + "message": "공유 폴더 포함" }, "lastPassEmail": { - "message": "LastPass Email" + "message": "LastPass 이메일" }, "importingYourAccount": { - "message": "Importing your account..." + "message": "계정 가져오기 중..." }, "lastPassMFARequired": { - "message": "LastPass multifactor authentication required" + "message": "LastPass 멀티팩터 인증 필요" }, "lastPassMFADesc": { - "message": "Enter your one-time passcode from your authentication app" + "message": "인증 앱에서 일회용 비밀번호 입력하기" }, "lastPassOOBDesc": { - "message": "Approve the login request in your authentication app or enter a one-time passcode." + "message": "인증 앱에서 로그인 요청을 승인하거나 일회용 비밀번호를 입력하세요" }, "passcode": { - "message": "Passcode" + "message": "비밀번호" }, "lastPassMasterPassword": { - "message": "LastPass master password" + "message": "LastPass 마스터 비밀번호" }, "lastPassAuthRequired": { - "message": "LastPass authentication required" + "message": "LastPass 인증 필요" }, "awaitingSSO": { - "message": "Awaiting SSO authentication" + "message": "SSO 인증 대기 중" }, "awaitingSSODesc": { - "message": "Please continue to log in using your company credentials." + "message": "회사 자격 증명을 사용하여 계속 로그인해 주세요." }, "seeDetailedInstructions": { - "message": "See detailed instructions on our help site at", + "message": "도움말 사이트에서 자세한 지침을 확인하세요", "description": "This is followed a by a hyperlink to the help website." }, "importDirectlyFromLastPass": { - "message": "Import directly from LastPass" + "message": "LastPass에서 직접 가져오기" }, "importFromCSV": { - "message": "Import from CSV" + "message": "CSV에서 가져오기" }, "lastPassTryAgainCheckEmail": { - "message": "Try again or look for an email from LastPass to verify it's you." + "message": "다시 시도하거나 LastPass에서 이메일을 찾아 사용자임을 증명하세요." }, "collection": { - "message": "Collection" + "message": "컬렉션" }, "lastPassYubikeyDesc": { - "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + "message": "LastPass 계정과 연결된 YubiKey를 컴퓨터의 USB 포트에 삽입한 다음 버튼을 누릅니다." }, "switchAccount": { - "message": "Switch account" + "message": "계정 전환" }, "switchAccounts": { - "message": "Switch accounts" + "message": "계정 전환" }, "switchToAccount": { - "message": "Switch to account" + "message": "계정 전환" }, "activeAccount": { - "message": "Active account" + "message": "계정 활성화" }, "availableAccounts": { - "message": "Available accounts" + "message": "사용 가능한 계정" }, "accountLimitReached": { - "message": "Account limit reached. Log out of an account to add another." + "message": "계정 개수 제한에 도달했습니다. 추가로 로그인하려면 다른 계정을 로그아웃 해주세요." }, "active": { - "message": "active" + "message": "활성" }, "locked": { - "message": "locked" + "message": "잠김" }, "unlocked": { - "message": "unlocked" + "message": "잠금 해제됨" }, "server": { - "message": "server" + "message": "서버" }, "hostedAt": { - "message": "hosted at" + "message": "호스팅된" }, "useDeviceOrHardwareKey": { - "message": "Use your device or hardware key" + "message": "기기또는 하드웨어 키를 사용하세요" }, "justOnce": { - "message": "Just once" + "message": "한 번만 알림" }, "alwaysForThisSite": { - "message": "Always for this site" + "message": "항상 이 사이트에 대해" }, "domainAddedToExcludedDomains": { - "message": "$DOMAIN$ added to excluded domains.", + "message": "제외된 도메인에 $DOMAIN$이 추가되었습니다.", "placeholders": { "domain": { "content": "$1", @@ -3987,31 +3898,31 @@ } }, "commonImportFormats": { - "message": "Common formats", + "message": "일반적인 형식", "description": "Label indicating the most common import formats" }, "confirmContinueToBrowserSettingsTitle": { - "message": "Continue to browser settings?", + "message": "브라우저 설정으로 이동하시겠습니까?", "description": "Title for dialog which asks if the user wants to proceed to a relevant browser settings page" }, "confirmContinueToHelpCenter": { - "message": "Continue to Help Center?", + "message": "도움말 센터로 이동하시겠습니까?", "description": "Title for dialog which asks if the user wants to proceed to a relevant Help Center page" }, "confirmContinueToHelpCenterPasswordManagementContent": { - "message": "Change your browser's autofill and password management settings.", + "message": "브라우저의 자동 완성 및 비밀번호 관리 설정을 변경합니다.", "description": "Body content for dialog which asks if the user wants to proceed to the Help Center's page about browser password management settings" }, "confirmContinueToHelpCenterKeyboardShortcutsContent": { - "message": "You can view and set extension shortcuts in your browser's settings.", + "message": "브라우저 설정에서 확장 단축키를 보고, 설정할 수 있습니다.", "description": "Body content for dialog which asks if the user wants to proceed to the Help Center's page about browser keyboard shortcut settings" }, "confirmContinueToBrowserPasswordManagementSettingsContent": { - "message": "Change your browser's autofill and password management settings.", + "message": "브라우저의 자동 채우기 및 비밀번호 관리 설정을 변경합니다.", "description": "Body content for dialog which asks if the user wants to proceed to the browser's password management settings page" }, "confirmContinueToBrowserKeyboardShortcutSettingsContent": { - "message": "You can view and set extension shortcuts in your browser's settings.", + "message": "브라우저 설정에서 확장 단축키를 보고, 설정할 수 있습니다.", "description": "Body content for dialog which asks if the user wants to proceed to the browser's keyboard shortcut settings page" }, "overrideDefaultBrowserAutofillTitle": { @@ -4019,7 +3930,7 @@ "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" }, "overrideDefaultBrowserAutofillDescription": { - "message": "Ignoring this option may cause conflicts between Bitwarden autofill suggestions and your browser's.", + "message": "이 옵션을 무시하면 Bitwarden 자동 완성 제안과 브라우저 간에 충돌이 발생할 수 있습니다", "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" }, "overrideDefaultBrowserAutoFillSettings": { @@ -4027,39 +3938,39 @@ "description": "Label for the setting that allows overriding the default browser autofill settings" }, "privacyPermissionAdditionNotGrantedTitle": { - "message": "Unable to set Bitwarden as the default password manager", + "message": "Bitwarden을 기본 비밀번호 관리자로 설정할 수 없습니다", "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" }, "privacyPermissionAdditionNotGrantedDescription": { - "message": "You must grant browser privacy permissions to Bitwarden to set it as the default password manager.", + "message": "기본 비밀번호 관리자로 설정하려면 Bitwarden에게 브라우저 개인정보 보호 권한을 부여해야 합니다.", "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" }, "makeDefault": { - "message": "Make default", + "message": "기본값으로 만들기", "description": "Button text for the setting that allows overriding the default browser autofill settings" }, "saveCipherAttemptSuccess": { - "message": "Credentials saved successfully!", + "message": "자격 증명이 성공적으로 저장됨!", "description": "Notification message for when saving credentials has succeeded." }, "passwordSaved": { - "message": "Password saved!", + "message": "비밀번호 저장됨!", "description": "Notification message for when saving credentials has succeeded." }, "updateCipherAttemptSuccess": { - "message": "Credentials updated successfully!", + "message": "자격 증명이 성공적으로 업데이트됨!", "description": "Notification message for when updating credentials has succeeded." }, "passwordUpdated": { - "message": "Password updated!", + "message": "비밀번호 업데이트됨!", "description": "Notification message for when updating credentials has succeeded." }, "saveCipherAttemptFailed": { - "message": "Error saving credentials. Check console for details.", + "message": "자격 증명 저장 중 오류가 발생했습니다. 자세한 내용은 콘솔을 확인하세요.", "description": "Notification message for when saving credentials has failed." }, "success": { - "message": "Success" + "message": "성공" }, "removePasskey": { "message": "패스키 제거" @@ -4068,22 +3979,22 @@ "message": "패스키 제거됨" }, "autofillSuggestions": { - "message": "Autofill suggestions" + "message": "자동 완성 제안" }, "autofillSuggestionsTip": { - "message": "Save a login item for this site to autofill" + "message": "이 사이트에서 자동으로 작성할 로그인 항목 저장" }, "yourVaultIsEmpty": { - "message": "Your vault is empty" + "message": "당신의 보관함이 비어있습니다" }, "noItemsMatchSearch": { - "message": "No items match your search" + "message": "사이트와 일치하는 항목 없음" }, "clearFiltersOrTryAnother": { - "message": "Clear filters or try another search term" + "message": "필터 지우기 또는 다른 검색어 시도" }, "copyInfoTitle": { - "message": "Copy info - $ITEMNAME$", + "message": "정보 복사 - $ITEMNAME$", "description": "Title for a button that opens a menu with options to copy information from an item.", "placeholders": { "itemname": { @@ -4093,7 +4004,7 @@ } }, "copyNoteTitle": { - "message": "Copy Note - $ITEMNAME$", + "message": "메모 복사 - $ITEMNAME$", "description": "Title for a button copies a note to the clipboard.", "placeholders": { "itemname": { @@ -4103,7 +4014,7 @@ } }, "moreOptionsLabel": { - "message": "More options, $ITEMNAME$", + "message": "$ITEMNAME$ 의 다른 옵션", "description": "Aria label for a button that opens a menu with more options for an item.", "placeholders": { "itemname": { @@ -4113,7 +4024,7 @@ } }, "moreOptionsTitle": { - "message": "More options - $ITEMNAME$", + "message": "다른 옵션 - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", "placeholders": { "itemname": { @@ -4123,7 +4034,7 @@ } }, "viewItemTitle": { - "message": "View item - $ITEMNAME$", + "message": "항목 보기 - $ITEMNAME$", "description": "Title for a link that opens a view for an item.", "placeholders": { "itemname": { @@ -4133,7 +4044,7 @@ } }, "autofillTitle": { - "message": "Autofill - $ITEMNAME$", + "message": "자동 완성 - $ITEMNAME$", "description": "Title for a button that autofills a login item.", "placeholders": { "itemname": { @@ -4143,22 +4054,22 @@ } }, "noValuesToCopy": { - "message": "No values to copy" + "message": "복사할 값이 없습니다" }, "assignToCollections": { - "message": "Assign to collections" + "message": "컬렉션에 할당하기" }, "copyEmail": { - "message": "Copy email" + "message": "이메일 복사하기" }, "copyPhone": { - "message": "Copy phone" + "message": "전화번호 복사하기" }, "copyAddress": { - "message": "Copy address" + "message": "주소 복사하기" }, "adminConsole": { - "message": "Admin Console" + "message": "관리자 콘솔" }, "accountSecurity": { "message": "계정 보안" @@ -4170,13 +4081,13 @@ "message": "화면 스타일" }, "errorAssigningTargetCollection": { - "message": "Error assigning target collection." + "message": "대상 컬렉션을 할당하는 중 오류가 발생했습니다." }, "errorAssigningTargetFolder": { - "message": "Error assigning target folder." + "message": "대상 폴더를 할당하는 중 오류가 발생했습니다." }, "viewItemsIn": { - "message": "View items in $NAME$", + "message": "$NAME$에서 항목 보기", "description": "Button to view the contents of a folder or collection", "placeholders": { "name": { @@ -4186,7 +4097,7 @@ } }, "backTo": { - "message": "Back to $NAME$", + "message": "다시 $NAME$로 돌아가기", "description": "Navigate back to a previous folder or collection", "placeholders": { "name": { @@ -4196,10 +4107,10 @@ } }, "new": { - "message": "New" + "message": "새 항목" }, "removeItem": { - "message": "Remove $NAME$", + "message": "$NAME$ 제거", "description": "Remove a selected option, such as a folder or collection", "placeholders": { "name": { @@ -4209,16 +4120,16 @@ } }, "itemsWithNoFolder": { - "message": "Items with no folder" + "message": "폴더가 없는 항목" }, "itemDetails": { - "message": "Item details" + "message": "항목 세부사항" }, "itemName": { - "message": "Item name" + "message": "항목 이름" }, "cannotRemoveViewOnlyCollections": { - "message": "You cannot remove collections with View only permissions: $COLLECTIONS$", + "message": "보기 권한만 있는 컬렉션은 제거할 수 없습니다: $COLLECTIONS$", "placeholders": { "collections": { "content": "$1", @@ -4227,47 +4138,47 @@ } }, "organizationIsDeactivated": { - "message": "Organization is deactivated" + "message": "조직이 비활성화되었습니다" }, "owner": { - "message": "Owner" + "message": "소유자" }, "selfOwnershipLabel": { - "message": "You", + "message": "당신", "description": "Used as a label to indicate that the user is the owner of an item." }, "contactYourOrgAdmin": { - "message": "Items in deactivated organizations cannot be accessed. Contact your organization owner for assistance." + "message": "비활성화된 조직의 항목에 액세스할 수 없습니다. 조직 소유자에게 도움을 요청하세요." }, "additionalInformation": { - "message": "Additional information" + "message": "추가 정보" }, "itemHistory": { - "message": "Item history" + "message": "항목 기록" }, "lastEdited": { - "message": "Last edited" + "message": "최근 수정 날짜:" }, "ownerYou": { - "message": "Owner: You" + "message": "소유자: 당신" }, "linked": { - "message": "Linked" + "message": "연결됨" }, "copySuccessful": { - "message": "Copy Successful" + "message": "복사 성공" }, "upload": { - "message": "Upload" + "message": "업로드" }, "addAttachment": { - "message": "Add attachment" + "message": "첨부파일 추가" }, "maxFileSizeSansPunctuation": { - "message": "Maximum file size is 500 MB" + "message": "최대 파일 크기는 500MB입니다." }, "deleteAttachmentName": { - "message": "Delete attachment $NAME$", + "message": "첨부파일 $NAME$ 삭제", "placeholders": { "name": { "content": "$1", @@ -4276,7 +4187,7 @@ } }, "downloadAttachmentName": { - "message": "Download $NAME$", + "message": "$NAME$ 다운로드", "placeholders": { "name": { "content": "$1", @@ -4285,25 +4196,25 @@ } }, "permanentlyDeleteAttachmentConfirmation": { - "message": "Are you sure you want to permanently delete this attachment?" + "message": "정말로 이 첨부파일을 영구적으로 삭제하시겠습니까?" }, "premium": { - "message": "Premium" + "message": "프리미엄" }, "freeOrgsCannotUseAttachments": { - "message": "Free organizations cannot use attachments" + "message": "무료 조직에서는 첨부 파일을 사용할 수 없습니다." }, "filters": { - "message": "Filters" + "message": "필터" }, "filterVault": { - "message": "Filter vault" + "message": "보관함 필터링" }, "filterApplied": { - "message": "One filter applied" + "message": "필터 1개가 적용되었습니다" }, "filterAppliedPlural": { - "message": "$COUNT$ filters applied", + "message": "$COUNT$개의 필터가 적용되었습니다", "placeholders": { "count": { "content": "$1", @@ -4312,16 +4223,16 @@ } }, "personalDetails": { - "message": "Personal details" + "message": "개인 정보" }, "identification": { - "message": "Identification" + "message": "본인 확인" }, "contactInfo": { - "message": "Contact info" + "message": "연락처 정보" }, "downloadAttachment": { - "message": "Download - $ITEMNAME$", + "message": "다운로드 - $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -4330,23 +4241,23 @@ } }, "cardNumberEndsWith": { - "message": "card number ends with", + "message": "카드 번호는 다음으로 끝납니다", "description": "Used within the inline menu to provide an aria description when users are attempting to fill a card cipher." }, "loginCredentials": { - "message": "Login credentials" + "message": "로그인 정보" }, "authenticatorKey": { - "message": "Authenticator key" + "message": "인증 키" }, "autofillOptions": { - "message": "Autofill options" + "message": "자동 완성 옵션" }, "websiteUri": { - "message": "Website (URI)" + "message": "웹사이트 (URI)" }, "websiteUriCount": { - "message": "Website (URI) $COUNT$", + "message": "웹사이트 (URI) $COUNT$", "description": "Label for an input field that contains a website URI. The input field is part of a list of fields, and the count indicates the position of the field in the list.", "placeholders": { "count": { @@ -4356,16 +4267,16 @@ } }, "websiteAdded": { - "message": "Website added" + "message": "웹사이트 추가됨" }, "addWebsite": { - "message": "Add website" + "message": "웹사이트 추가" }, "deleteWebsite": { - "message": "Delete website" + "message": "웹사이트 삭제" }, "defaultLabel": { - "message": "Default ($VALUE$)", + "message": "기본값 ($VALUE$)", "description": "A label that indicates the default value for a field with the current default value in parentheses.", "placeholders": { "value": { @@ -4375,7 +4286,7 @@ } }, "showMatchDetection": { - "message": "Show match detection $WEBSITE$", + "message": "$WEBSITE$ 일치 인식 보이기", "placeholders": { "website": { "content": "$1", @@ -4384,7 +4295,7 @@ } }, "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", + "message": "$WEBSITE$ 일치 인식 숨기기", "placeholders": { "website": { "content": "$1", @@ -4393,19 +4304,19 @@ } }, "autoFillOnPageLoad": { - "message": "Autofill on page load?" + "message": "페이지 로드 시 자동 완성을 할까요?" }, "cardExpiredTitle": { - "message": "Expired card" + "message": "만료된 카드" }, "cardExpiredMessage": { - "message": "If you've renewed it, update the card's information" + "message": "갱신한 경우, 카드 정보를 업데이트합니다" }, "cardDetails": { - "message": "Card details" + "message": "카드 상세정보" }, "cardBrandDetails": { - "message": "$BRAND$ details", + "message": "$BRAND$ 상세정보", "placeholders": { "brand": { "content": "$1", @@ -4417,40 +4328,40 @@ "message": "애니메이션 활성화" }, "showAnimations": { - "message": "Show animations" + "message": "애니메이션 표시" }, "addAccount": { - "message": "Add account" + "message": "계정 추가" }, "loading": { - "message": "Loading" + "message": "불러오는 중" }, "data": { - "message": "Data" + "message": "데이터" }, "passkeys": { - "message": "Passkeys", + "message": "패스키", "description": "A section header for a list of passkeys." }, "passwords": { - "message": "Passwords", + "message": "비밀번호", "description": "A section header for a list of passwords." }, "logInWithPasskeyAriaLabel": { - "message": "Log in with passkey", + "message": "패스키로 로그인", "description": "ARIA label for the inline menu button that logs in with a passkey." }, "assign": { - "message": "Assign" + "message": "할당" }, "bulkCollectionAssignmentDialogDescriptionSingular": { - "message": "Only organization members with access to these collections will be able to see the item." + "message": "이 컬렉션에 액세스할 수 있는 조직 구성원만 해당 항목을 볼 수 있습니다." }, "bulkCollectionAssignmentDialogDescriptionPlural": { - "message": "Only organization members with access to these collections will be able to see the items." + "message": "이 컬렉션에 액세스할 수 있는 조직 구성원만 해당 항목들을 볼 수 있습니다." }, "bulkCollectionAssignmentWarning": { - "message": "You have selected $TOTAL_COUNT$ items. You cannot update $READONLY_COUNT$ of the items because you do not have edit permissions.", + "message": "$TOTAL_COUNT$ 항목들을 선택했습니다. 편집 권한이 없기 때문에 항목들의 $READONLY_COUNT$를 업데이트할 수 없습니다.", "placeholders": { "total_count": { "content": "$1", @@ -4462,37 +4373,37 @@ } }, "addField": { - "message": "Add field" + "message": "필드 추가" }, "add": { - "message": "Add" + "message": "추가" }, "fieldType": { - "message": "Field type" + "message": "필드 유형" }, "fieldLabel": { - "message": "Field label" + "message": "필드 레이블" }, "textHelpText": { - "message": "Use text fields for data like security questions" + "message": "보안 질문과 같은 데이터에 텍스트 필드를 사용하세요" }, "hiddenHelpText": { - "message": "Use hidden fields for sensitive data like a password" + "message": "비밀번호와 같은 중요한 데이터의 경우 숨겨진 필드를 사용하세요." }, "checkBoxHelpText": { - "message": "Use checkboxes if you'd like to autofill a form's checkbox, like a remember email" + "message": "이메일 기억과 같이 양식의 체크박스를 자동으로 채우려면 체크박스들을 사용하세요" }, "linkedHelpText": { - "message": "Use a linked field when you are experiencing autofill issues for a specific website." + "message": "특정 웹사이트에 대한 자동 채우기 문제가 발생할 때는, 연결 필드를 사용하세요" }, "linkedLabelHelpText": { - "message": "Enter the the field's html id, name, aria-label, or placeholder." + "message": "필드의 html ID, 이름, aria-label 또는 플레이스홀더를 입력하세요" }, "editField": { - "message": "Edit field" + "message": "필드 편집" }, "editFieldLabel": { - "message": "Edit $LABEL$", + "message": "$LABEL$ 편집", "placeholders": { "label": { "content": "$1", @@ -4501,7 +4412,7 @@ } }, "deleteCustomField": { - "message": "Delete $LABEL$", + "message": "$LABEL$ 삭제", "placeholders": { "label": { "content": "$1", @@ -4510,7 +4421,7 @@ } }, "fieldAdded": { - "message": "$LABEL$ added", + "message": "$LABEL$ 추가됨", "placeholders": { "label": { "content": "$1", @@ -4519,7 +4430,7 @@ } }, "reorderToggleButton": { - "message": "Reorder $LABEL$. Use arrow key to move item up or down.", + "message": "$LABEL$을 재정렬합니다. 화살표 키를 사용하여 항목을 위나 아래로 이동할 수 있습니다.", "placeholders": { "label": { "content": "$1", @@ -4528,7 +4439,7 @@ } }, "reorderFieldUp": { - "message": "$LABEL$ moved up, position $INDEX$ of $LENGTH$", + "message": "$LABEL$을 위로 이동했습니다. 위치: $INDEX$ / $LENGTH$", "placeholders": { "label": { "content": "$1", @@ -4545,13 +4456,13 @@ } }, "selectCollectionsToAssign": { - "message": "Select collections to assign" + "message": "할당할 컬렉션을 선택하세요" }, "personalItemTransferWarningSingular": { - "message": "1 item will be permanently transferred to the selected organization. You will no longer own this item." + "message": "1개 항목이 선택한 조직으로 영구적으로 전송됩니다. 더 이상 이 항목을 소유하지 않습니다." }, "personalItemsTransferWarningPlural": { - "message": "$PERSONAL_ITEMS_COUNT$ items will be permanently transferred to the selected organization. You will no longer own these items.", + "message": "$PERSONAL_ITEMS_COUNT$ 개 항목들이 선택한 조직으로 영구적으로 전송됩니다. 더 이상 이 항목들을 소유하지 않습니다.", "placeholders": { "personal_items_count": { "content": "$1", @@ -4560,7 +4471,7 @@ } }, "personalItemWithOrgTransferWarningSingular": { - "message": "1 item will be permanently transferred to $ORG$. You will no longer own this item.", + "message": "1개 항목이 $ORG$으로 영구적으로 전송됩니다. 더 이상 이 항목을 소유하지 않습니다.", "placeholders": { "org": { "content": "$1", @@ -4569,7 +4480,7 @@ } }, "personalItemsWithOrgTransferWarningPlural": { - "message": "$PERSONAL_ITEMS_COUNT$ items will be permanently transferred to $ORG$. You will no longer own these items.", + "message": "$PERSONAL_ITEMS_COUNT$ 개 항목들이 $ORG$으로 영구적으로 전송됩니다. 더 이상 이 항목들을 소유하지 않습니다.", "placeholders": { "personal_items_count": { "content": "$1", @@ -4582,13 +4493,13 @@ } }, "successfullyAssignedCollections": { - "message": "Successfully assigned collections" + "message": "성공적으로 컬렉션을 할당했습니다" }, "nothingSelected": { - "message": "You have not selected anything." + "message": "아무것도 선택하지 않았습니다." }, "movedItemsToOrg": { - "message": "Selected items moved to $ORGNAME$", + "message": "선택한 항목이 $ORGNAME$(으)로 이동됨", "placeholders": { "orgname": { "content": "$1", @@ -4597,7 +4508,7 @@ } }, "itemsMovedToOrg": { - "message": "Items moved to $ORGNAME$", + "message": "항목들이 $ORGNAME$로 이동했습니다", "placeholders": { "orgname": { "content": "$1", @@ -4606,7 +4517,7 @@ } }, "itemMovedToOrg": { - "message": "Item moved to $ORGNAME$", + "message": "항목이 $ORGNAME$로 이동했습니다", "placeholders": { "orgname": { "content": "$1", @@ -4615,7 +4526,7 @@ } }, "reorderFieldDown": { - "message": "$LABEL$ moved down, position $INDEX$ of $LENGTH$", + "message": "$LABEL$을 아래로 이동했습니다. 위치: $INDEX$ / $LENGTH$", "placeholders": { "label": { "content": "$1", @@ -4632,49 +4543,52 @@ } }, "itemLocation": { - "message": "Item Location" + "message": "항목 위치" }, "fileSend": { - "message": "File Send" + "message": "파일 Send" }, "fileSends": { - "message": "File Sends" + "message": "파일 Send" }, "textSend": { - "message": "Text Send" + "message": "텍스트 Send" }, "textSends": { - "message": "Text Sends" + "message": "텍스트 Send" }, "bitwardenNewLook": { - "message": "Bitwarden has a new look!" + "message": "Bitwarden이 새로운 모습으로 돌아왔습니다!" }, "bitwardenNewLookDesc": { - "message": "It's easier and more intuitive than ever to autofill and search from the Vault tab. Take a look around!" + "message": "보관함 탭에서 자동 완성하고 검색하는 것이 그 어느 때보다 쉽고 직관적입니다. 둘러보세요!" }, "accountActions": { - "message": "Account actions" + "message": "계정 작업" }, "showNumberOfAutofillSuggestions": { - "message": "Show number of login autofill suggestions on extension icon" + "message": "확장 아이콘에 로그인 자동 완성 제안 수 표시" + }, + "showQuickCopyActions": { + "message": "보관함에서 빠른 복사 기능 표시" }, "systemDefault": { - "message": "System default" + "message": "시스템 기본 설정" }, "enterprisePolicyRequirementsApplied": { - "message": "Enterprise policy requirements have been applied to this setting" + "message": "기업 정책에 따른 요구사항들이 옵션들에 적용되었습니다." }, "sshPrivateKey": { - "message": "Private key" + "message": "개인 키" }, "sshPublicKey": { - "message": "Public key" + "message": "공개 키" }, "sshFingerprint": { - "message": "Fingerprint" + "message": "지문" }, "sshKeyAlgorithm": { - "message": "Key type" + "message": "키 유형" }, "sshKeyAlgorithmED25519": { "message": "ED25519" @@ -4689,213 +4603,249 @@ "message": "RSA 4096-Bit" }, "retry": { - "message": "Retry" + "message": "재시도" }, "vaultCustomTimeoutMinimum": { - "message": "Minimum custom timeout is 1 minute." + "message": "최소 사용자 지정 시간 초과는 1분입니다." }, "additionalContentAvailable": { - "message": "Additional content is available" + "message": "추가 콘텐츠를 사용할 수 있습니다" }, "fileSavedToDevice": { - "message": "File saved to device. Manage from your device downloads." + "message": "파일을 장치에 저장했습니다. 장치 다운로드로 관리할 수 있습니다." }, "showCharacterCount": { - "message": "Show character count" + "message": "글자 수 표시하기" }, "hideCharacterCount": { - "message": "Hide character count" + "message": "글자 수 숨기기" }, "itemsInTrash": { - "message": "Items in trash" + "message": "휴지통에 있는 항목" }, "noItemsInTrash": { - "message": "No items in trash" + "message": "휴지통에 항목이 없습니다." }, "noItemsInTrashDesc": { - "message": "Items you delete will appear here and be permanently deleted after 30 days" + "message": "삭제한 항목은 여기에 표시되며 30일 후 영구적으로 삭제됩니다." }, "trashWarning": { - "message": "Items that have been in trash more than 30 days will automatically be deleted" + "message": "30일 이상 휴지통에 보관된 항목은 자동으로 삭제됩니다." }, "restore": { - "message": "Restore" + "message": "복원" }, "deleteForever": { - "message": "Delete forever" + "message": "영구 삭제하기" }, "noEditPermissions": { - "message": "You don't have permission to edit this item" + "message": "아이템을 수정할 권한이 없습니다." }, "authenticating": { - "message": "Authenticating" + "message": "인증 중" }, "fillGeneratedPassword": { - "message": "Fill generated password", + "message": "생성된 비밀번호를 입력하세요", "description": "Heading for the password generator within the inline menu" }, "passwordRegenerated": { - "message": "Password regenerated", + "message": "비밀번호가 재생성되었습니다.", "description": "Notification message for when a password has been regenerated" }, "saveLoginToBitwarden": { - "message": "Save login to Bitwarden?", + "message": "Bitwarden에 로그인을 저장하시겠습니까?", "description": "Confirmation message for saving a login to Bitwarden" }, "spaceCharacterDescriptor": { - "message": "Space", + "message": "스페이스", "description": "Represents the space key in screen reader content as a readable word" }, "tildeCharacterDescriptor": { - "message": "Tilde", + "message": "물결표(~)", "description": "Represents the ~ key in screen reader content as a readable word" }, "backtickCharacterDescriptor": { - "message": "Backtick", + "message": "백틱(`)", "description": "Represents the ` key in screen reader content as a readable word" }, "exclamationCharacterDescriptor": { - "message": "Exclamation mark", + "message": "느낌표 (!)", "description": "Represents the ! key in screen reader content as a readable word" }, "atSignCharacterDescriptor": { - "message": "At sign", + "message": "골뱅이표 (@)", "description": "Represents the @ key in screen reader content as a readable word" }, "hashSignCharacterDescriptor": { - "message": "Hash sign", + "message": "해시 기호 (#)", "description": "Represents the # key in screen reader content as a readable word" }, "dollarSignCharacterDescriptor": { - "message": "Dollar sign", + "message": "달러 기호 ($)", "description": "Represents the $ key in screen reader content as a readable word" }, "percentSignCharacterDescriptor": { - "message": "Percent sign", + "message": "퍼센트 기호 (%)", "description": "Represents the % key in screen reader content as a readable word" }, "caretCharacterDescriptor": { - "message": "Caret", + "message": "캐럿 기호 (^)", "description": "Represents the ^ key in screen reader content as a readable word" }, "ampersandCharacterDescriptor": { - "message": "Ampersand", + "message": "앰퍼샌드 기호 (&)", "description": "Represents the & key in screen reader content as a readable word" }, "asteriskCharacterDescriptor": { - "message": "Asterisk", + "message": "별표 (*)", "description": "Represents the * key in screen reader content as a readable word" }, "parenLeftCharacterDescriptor": { - "message": "Left parenthesis", + "message": "왼쪽 소괄호 ' ( '", "description": "Represents the ( key in screen reader content as a readable word" }, "parenRightCharacterDescriptor": { - "message": "Right parenthesis", + "message": "오른쪽 소괄호 ' ) '", "description": "Represents the ) key in screen reader content as a readable word" }, "hyphenCharacterDescriptor": { - "message": "Underscore", + "message": "밑줄( _ )", "description": "Represents the _ key in screen reader content as a readable word" }, "underscoreCharacterDescriptor": { - "message": "Hyphen", + "message": "붙임표 ( - )", "description": "Represents the - key in screen reader content as a readable word" }, "plusCharacterDescriptor": { - "message": "Plus", + "message": "더하기 기호 ( + )", "description": "Represents the + key in screen reader content as a readable word" }, "equalsCharacterDescriptor": { - "message": "Equals", + "message": "등호 ( = )", "description": "Represents the = key in screen reader content as a readable word" }, "braceLeftCharacterDescriptor": { - "message": "Left brace", + "message": "왼쪽 중괄호 ' { '", "description": "Represents the { key in screen reader content as a readable word" }, "braceRightCharacterDescriptor": { - "message": "Right brace", + "message": "오른쪽 중괄호 ' } '", "description": "Represents the } key in screen reader content as a readable word" }, "bracketLeftCharacterDescriptor": { - "message": "Left bracket", + "message": "왼쪽 대괄호 ' [ '", "description": "Represents the [ key in screen reader content as a readable word" }, "bracketRightCharacterDescriptor": { - "message": "Right bracket", + "message": "오른쪽 대괄호 ' ] '", "description": "Represents the ] key in screen reader content as a readable word" }, "pipeCharacterDescriptor": { - "message": "Pipe", + "message": "파이프 기호 ( | )", "description": "Represents the | key in screen reader content as a readable word" }, "backSlashCharacterDescriptor": { - "message": "Back slash", + "message": "백슬래시 ( \\ )", "description": "Represents the back slash key in screen reader content as a readable word" }, "colonCharacterDescriptor": { - "message": "Colon", + "message": "콜론 ( : )", "description": "Represents the : key in screen reader content as a readable word" }, "semicolonCharacterDescriptor": { - "message": "Semicolon", + "message": "세미콜론( ; )", "description": "Represents the ; key in screen reader content as a readable word" }, "doubleQuoteCharacterDescriptor": { - "message": "Double quote", + "message": "쌍 따옴표 ( \" )", "description": "Represents the double quote key in screen reader content as a readable word" }, "singleQuoteCharacterDescriptor": { - "message": "Single quote", + "message": "홑 따옴표 ( ' )", "description": "Represents the ' key in screen reader content as a readable word" }, "lessThanCharacterDescriptor": { - "message": "Less than", + "message": "보다 작음 ( < )", "description": "Represents the < key in screen reader content as a readable word" }, "greaterThanCharacterDescriptor": { - "message": "Greater than", + "message": "보다 큰 ( > )", "description": "Represents the > key in screen reader content as a readable word" }, "commaCharacterDescriptor": { - "message": "Comma", + "message": "쉼표( , )", "description": "Represents the , key in screen reader content as a readable word" }, "periodCharacterDescriptor": { - "message": "Period", + "message": "마침표 ( . )", "description": "Represents the . key in screen reader content as a readable word" }, "questionCharacterDescriptor": { - "message": "Question mark", + "message": "물음표 ( ? )", "description": "Represents the ? key in screen reader content as a readable word" }, "forwardSlashCharacterDescriptor": { - "message": "Forward slash", + "message": "슬래시 ( / )", "description": "Represents the / key in screen reader content as a readable word" }, "lowercaseAriaLabel": { - "message": "Lowercase" + "message": "소문자" }, "uppercaseAriaLabel": { - "message": "Uppercase" + "message": "대문자" }, "generatedPassword": { - "message": "Generated password" + "message": "비밀번호 생성" }, "compactMode": { - "message": "Compact mode" + "message": "컴팩트 모드\n" }, "beta": { - "message": "Beta" + "message": "베타" + }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" }, "extensionWidth": { - "message": "Extension width" + "message": "확장 폭" }, "wide": { - "message": "Wide" + "message": "넓게" }, "extraWide": { - "message": "Extra wide" + "message": "매우 넓게" } } diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index 8c2c12ec65b..eaf1cb9f9db 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Tapatybės automatinis užpildymas" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Sukurti slaptažodį (nukopijuotas)" }, @@ -447,9 +454,6 @@ "length": { "message": "Ilgis" }, - "passwordMinLength": { - "message": "Minimalus slaptažodžio ilgis" - }, "uppercase": { "message": "Didžiosiomis (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Mažiausiai simbolių" }, - "avoidAmbChar": { - "message": "Vengti dviprasmiškų simbolių", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Įvertinkite šį plėtinį" }, - "rateExtensionDesc": { - "message": "Apsvarstykite galimybę mums padėti palikdami gerą atsiliepimą!" - }, "browserNotSupportClipboard": { "message": "Jūsų žiniatinklio naršyklė nepalaiko automatinio kopijavimo. Vietoj to nukopijuokite rankiniu būdu." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Pateikti tapatybės elementų skirtuko puslapyje, kad būtų lengva automatiškai užpildyti." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Išvalyti iškarpinę", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Perkelti į organizaciją" }, - "share": { - "message": "Bendrinti" - }, "movedItemToOrg": { "message": "$ITEMNAME$ perkelta(s) į $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Klonuoti" }, - "passwordGeneratorPolicyInEffect": { - "message": "Viena ar daugiau organizacijos politikų turi įtakos Jūsų generatoriaus nustatymams." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Ieškoti „Sends“", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Pridėti „Send“", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Tekstas" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Pasiektas maksimalus prisijungimų skaičius", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Nebegalioja" }, - "pendingDeletion": { - "message": "Laukiama ištrynimo" - }, "passwordProtected": { "message": "Apsaugota slaptažodžiu" }, @@ -2468,24 +2447,9 @@ "message": "Redaguoti „Send“", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Kokio tai tipo „Send“?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Draugiškas pavadinimas, apibūdinantis šį „Send“.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Failas, kurį norite siųsti." - }, "deletionDate": { "message": "Ištrynimo data" }, - "deletionDateDesc": { - "message": "Nurodytos datos ir laiko metu „Send“ bus bus ištrinta visam laikui.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Galiojimo data" }, - "expirationDateDesc": { - "message": "Jei nustatyta, prieiga prie šio „Send“ nustos galioti nurodyta data ir laikui.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 d" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Pasirinktinis" }, - "maximumAccessCount": { - "message": "Maksimalus prisijungimų skaičius" - }, - "maximumAccessCountDesc": { - "message": "Jei nustatyta, vartotojai nebegalės pasiekti šio „Send“, kai bus pasiektas maksimalus prisijungimų skaičius.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Pasirinktinai reikalauti slaptažodžio, kad vartotojai galėtų pasiekti šį „Send“.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Asmeninės pastabos apie šį „Send“.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Išjunkite šį „Send“, kad niekas negalėtų jo pasiekti.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Išsaugant šį „Send“ nukopijuokite nuorodą į mainų sritį.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Tekstas, kurį norite siųsti." - }, - "sendHideText": { - "message": "Pagal numatytuosius nustatymus slėpti šį „Send“ tekstą.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Dabartinis prisijungimų skaičius" - }, "createSend": { "message": "Naujas Siuntinys", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Prieš pradedant" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Norint pasinaudoti kalendoriaus stiliaus datos pasirinkikliu", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "spauskite čia", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "iškelti atskirame lange.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Nurodytas galiojimo laikas negalioja." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Išsaugant jūsų ištrynimo ir galiojimo datas įvyko klaida." }, - "hideEmail": { - "message": "Slėpti mano el. pašto adresą nuo gavėjų." - }, "hideYourEmail": { "message": "Slėpkite savo el. pašto adresą nuo žiūrėtojų." }, - "sendOptionsPolicyInEffect": { - "message": "Viena ar daugiau organizacijos politikų turi įtakos Jūsų „Send“ nustatymams." - }, "passwordPrompt": { "message": "Iš naujo prašoma pagrindinio slaptažodžio" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Klaida" }, - "regenerateUsername": { - "message": "Pergeneruoti vartotojo vardą iš naujo" - }, "generateUsername": { "message": "Generuoti vartotojo vardą" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Vartotojo prisijungimo vardo tipas" - }, "plusAddressedEmail": { "message": "Plius adresuotas el. paštas", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Svetainės pavadinimas" }, - "whatWouldYouLikeToGenerate": { - "message": "Ką norėtumėte sugeneruoti?" - }, - "passwordType": { - "message": "Slaptažodžio tipas" - }, "service": { "message": "Paslauga" }, @@ -3580,6 +3477,14 @@ "message": "Atrakinti savo paskyrą, atidaromas naujame lange", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Užpildykite prisijungimo duomenis", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index e91d207ff20..2c2a9c3c69c 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Automātiski aizpildīt identitāti" }, + "fillVerificationCode": { + "message": "Aizpildīt apliecinājuma kodu" + }, + "fillVerificationCodeAria": { + "message": "Aizpildīt apliecinājuma kodu", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Izveidot paroli (tiks ievietota starpliktuvē)" }, @@ -447,9 +454,6 @@ "length": { "message": "Garums" }, - "passwordMinLength": { - "message": "Mazākais pieļaujamais paroles garums" - }, "uppercase": { "message": "Lielie burti (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Mazākais pieļaujamais īpašo rakstzīmju skaits" }, - "avoidAmbChar": { - "message": "Izvairīties no viegli sajaucamām rakstzīmēm", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Izvairīties no viegli sajaucamām rakstzīmēm", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Novērtēt paplašinājumu" }, - "rateExtensionDesc": { - "message": "Lūgums apsvērt palīdzēt mums ar labu atsauksmi." - }, "browserNotSupportClipboard": { "message": "Pārlūks neatbalsta vienkāršo ievietošanu starpliktuvē. Tā vietā tas jāievieto starpliktuvē pašrocīgi." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Attēlot identitātes ciļņu lapā vieglākai aizpildei." }, + "clickToAutofillOnVault": { + "message": "Glabātavas skatā jāklikšķina uz vienumiem, lai automātiski aizpildītu" + }, "clearClipboard": { "message": "Notīrīt starpliktuvi", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Pārvietot uz apvienību" }, - "share": { - "message": "Kopīgot" - }, "movedItemToOrg": { "message": "$ITEMNAME$ pārvietots uz $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Pavairot" }, - "passwordGeneratorPolicyInEffect": { - "message": "Viens vai vairāki apvienības nosacījumi ietekmē veidotāja iestatījumus." - }, "passwordGenerator": { "message": "Paroļu veidotājs" }, @@ -2385,14 +2379,6 @@ "message": "Informācija par Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Meklēt Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Pievienot Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Teksts" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Pēc noklusējuma paslēpt tekstu" }, - "maxAccessCountReached": { - "message": "Sasniegts lielākais pieļaujamais piekļuves reižu skaits", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Beidzies izmantošanas laiks" }, - "pendingDeletion": { - "message": "Gaida dzēšanu" - }, "passwordProtected": { "message": "Aizsargāts ar paroli" }, @@ -2468,24 +2447,9 @@ "message": "Labot Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Kāds ir šī Send veids?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Lasāms nosaukums, kas apraksta šo Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Datne, kuru ir vēlme nosūtīt." - }, "deletionDate": { "message": "Dzēšanas datums" }, - "deletionDateDesc": { - "message": "Send tiks neatgriezeniski izdzēsts norādītajā datumā un laikā.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Send šajā datumā tiks neatgriezeniski izdzēsts.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Derīguma beigu datums" }, - "expirationDateDesc": { - "message": "Ja iestatīts, piekļuve šim Send beigsies norādītajā datumā un laikā.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 diena" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Pielāgots" }, - "maximumAccessCount": { - "message": "Lielākais pieļaujamais piekļuves reižu skaits" - }, - "maximumAccessCountDesc": { - "message": "Ja iestatīts, lietotāji nevarēs piekļūt šim Send, kad tiks sasniegts lielākais pieļaujamais piekļūšanas reižu skaits.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Pēc izvēles pieprasīt paroli, lai lietotāji varētu piekļūt šim Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Pēc izvēles var pievienot paroli, lai saņēmēji varētu piekļūt šim Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Personīgas piezīmes par šo Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Izslēgt šo Send, lai neviens tam nevarētu piekļūt.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Saglabāšanas brīdī ievietot šī Send saiti starpliktuvē.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Teksts, kuru ir vēlme nosūtīt." - }, - "sendHideText": { - "message": "Pēc noklusējuma paslēpt šī Send tekstu.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Pašreizējais piekļuvju skaits" - }, "createSend": { "message": "Jauns Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Pirms sākšanas" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Lai izmantotu kalendāra veida datumu atlasītāju,", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "klikšķināt šeit", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": ", lai atvērtu jaunā logā.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Norādītais derīguma beigu datums nav derīgs." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Atgadījusies kļūda dzēšanas un derīguma beigu datumu saglabāšanā." }, - "hideEmail": { - "message": "Slēpt e-pasta adresi no saņēmējiem." - }, "hideYourEmail": { "message": "Paslēpt e-pasta adresi no apskatītājiem." }, - "sendOptionsPolicyInEffect": { - "message": "Viens vai vairāki apvienības nosacījumi ietekmē Send iespējas." - }, "passwordPrompt": { "message": "Galvenās paroles pārvaicāšana" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Kļūda" }, - "regenerateUsername": { - "message": "Pārizveidot lietotājvārdu" - }, "generateUsername": { "message": "Izveidot lietotājvārdu" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Lietotājvārda veids" - }, "plusAddressedEmail": { "message": "E-pasta adrese ar plusu", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Tīmekļvietnes nosaukums" }, - "whatWouldYouLikeToGenerate": { - "message": "Ko ir nepieciešams izveidot?" - }, - "passwordType": { - "message": "Paroles veids" - }, "service": { "message": "Pakalpojums" }, @@ -3174,7 +3071,7 @@ "message": "Skatīt visas pieteikšanās iespējas" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Skatīt visas pieteikšanās iespējas" }, "notificationSentDevice": { "message": "Uz ierīci ir nosūtīts paziņojums." @@ -3580,6 +3477,14 @@ "message": "Atslēgt savu kontu, tiks atvērts jaunā logā", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Laikā balstīts vienreizējas izmantošanas paroles apliecināšanas kods", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Atlikušais laiks, pirms beigsies pašreizējā TOTP derīgums", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Aizpildīt pieteikšanās datus", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Paplašinājuma ikonā rādīt pieteikšanās automātiskās aizpildes ieteikumu skaitu" }, + "showQuickCopyActions": { + "message": "Glabātavā rādīt ātrās kopēšanas darbības" + }, "systemDefault": { "message": "Sistēmas noklusējums" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Svarīgs paziņojums" + }, + "setupTwoStepLogin": { + "message": "Iestatīt divpakāpju pieteikšanos" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden, sākot ar 2025. gada februāri, nosūtīs kodu uz konta e-pasta adresi, lai apliecinātu pieteikšanos no jaunām ierīcēm." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Var iestatīt divpakāpju pieteikšanos kā citu veidu, kā aizsargāt savu kontu, vai iestatīt savu e-pasta adresi uz tādu, kurai ir piekļuve." + }, + "remindMeLater": { + "message": "Atgādināt man vēlāk" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Vai ir uzticama piekļuve savai e-pasta adresei $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nē, nav" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Jā, varu uzticami piekļūt savam e-pastam" + }, + "turnOnTwoStepLogin": { + "message": "Ieslēgt divpakāpju pieteikšanos" + }, + "changeAcctEmail": { + "message": "Mainīt konta e-pasta adresi" + }, "extensionWidth": { "message": "Paplašinājuma platums" }, diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index e031dfcbcbd..cd210c85ce1 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "പാസ്‌വേഡ് സൃഷ്ടിക്കുക (പകർത്തുക )" }, @@ -447,9 +454,6 @@ "length": { "message": "നീളം" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "കുറഞ്ഞ പ്രത്യേക പ്രതീകങ്ങൾ" }, - "avoidAmbChar": { - "message": "അവ്യക്തമായ പ്രതീകങ്ങൾ ഒഴിവാക്കുക", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "എക്സ്റ്റൻഷൻ റേറ്റ് ചെയ്യുക " }, - "rateExtensionDesc": { - "message": "ഒരു നല്ല അവലോകനത്തിന് ഞങ്ങളെ സഹായിക്കുന്നത് പരിഗണിക്കുക!" - }, "browserNotSupportClipboard": { "message": "നിങ്ങളുടെ ബ്രൌസർ എളുപ്പമുള്ള ക്ലിപ്പ്ബോർഡ് പകർത്തൽ പിന്തുണയ്ക്കത്തില്ല. പകരം അത് സ്വമേധയാ പകർക്കുക ." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "ക്ലിപ്ബോര്‍ഡ് മായ്ക്കുക", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organization" }, - "share": { - "message": "പങ്കിടുക" - }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "ക്ലോൺ" }, - "passwordGeneratorPolicyInEffect": { - "message": "ഒന്നോ അതിലധികമോ സംഘടന നയങ്ങൾ നിങ്ങളുടെ പാസ്സ്‌വേഡ് സൃഷ്ടാവിൻ്റെ ക്രമീകരണങ്ങളെ ബാധിക്കുന്നു" - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expired" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Expiration date" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 day" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Custom" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index dfb25015dc3..d156e6d6458 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -447,9 +454,6 @@ "length": { "message": "लांबी" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum special" }, - "avoidAmbChar": { - "message": "Avoid ambiguous characters", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "विस्तारकाचे मूल्यांकन करा" }, - "rateExtensionDesc": { - "message": "चांगला अभिप्राय देऊन आम्हाला मदत करा!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Clear clipboard", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organization" }, - "share": { - "message": "Share" - }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Clone" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expired" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Expiration date" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 day" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Custom" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 6aa17c1d7e3..6ab3755c8f4 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -447,9 +454,6 @@ "length": { "message": "Length" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum special" }, - "avoidAmbChar": { - "message": "Avoid ambiguous characters", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Clear clipboard", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organization" }, - "share": { - "message": "Share" - }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Clone" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expired" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Expiration date" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 day" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Custom" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 0a14e176891..16469051a0c 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Auto-utfyll identitet" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generer et passord (kopiert)" }, @@ -447,9 +454,6 @@ "length": { "message": "Lengde" }, - "passwordMinLength": { - "message": "Minimum passordlengde" - }, "uppercase": { "message": "Store bokstaver (A–Å)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minste antall spesialtegn" }, - "avoidAmbChar": { - "message": "Unngå tvetydige tegn", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Gi denne utvidelsen en vurdering" }, - "rateExtensionDesc": { - "message": "Tenk gjerne på om du vil skrive en anmeldelse om oss!" - }, "browserNotSupportClipboard": { "message": "Nettleseren din støtter ikke kopiering til utklippstavlen på noe enkelt vis. Prøv å kopiere det manuelt i stedet." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Vis identitetselementer på fanesiden for enkel auto-utfylling." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Tøm utklippstavlen", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Flytt til organisasjon" }, - "share": { - "message": "Del" - }, "movedItemToOrg": { "message": "$ITEMNAME$ flyttet til $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Klon" }, - "passwordGeneratorPolicyInEffect": { - "message": "En eller flere av virksomhetens regler påvirker generatorinnstillingene dine." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Søk i Send-ene", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Legg til Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Tekst" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Maksimalt antall tilganger nådd", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Utløpt" }, - "pendingDeletion": { - "message": "Venter på sletting" - }, "passwordProtected": { "message": "Passord beskyttet" }, @@ -2468,24 +2447,9 @@ "message": "Rediger Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Hvilken type Send er dette?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Et vennlig navn for å beskrive dette Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Filen du vil send." - }, "deletionDate": { "message": "Dato for sletting" }, - "deletionDateDesc": { - "message": "Send-en vil bli slettet permanent på den angitte dato og klokkeslett.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Utløpsdato" }, - "expirationDateDesc": { - "message": "Hvis satt, vil tilgang til denne Send gå ut på angitt dato og klokkeslett.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 dag" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Egendefinert" }, - "maximumAccessCount": { - "message": "Maksimal antall tilganger" - }, - "maximumAccessCountDesc": { - "message": "Hvis satt, vil ikke brukere lenger ha tilgang til dette Send når maksimal antall tilgang er nådd.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Eventuelt krever et passord for brukere å få tilgang til denne Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notater om denne Send-en.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deaktiver denne Send-en, slik at ingen får tilgang til den.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Kopier denne Send-ens lenke til utklippstavlen når den har blitt lagret.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Teksten du ønsker å sende." - }, - "sendHideText": { - "message": "Skjul denne Send-ens tekst som standard.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Antall nåværende tilganger" - }, "createSend": { "message": "Lag en ny Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Før du starter" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Hvis du vil bruke en kalenderstil-datovelger", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "kilkk her", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "å pope ut vinduet.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Utløpsdatoen angitt er ikke gyldig." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Det oppstod en feil ved lagring av slettingen og utløpsdatoene." }, - "hideEmail": { - "message": "Skjul min e-postadresse fra mottakere." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "En eller flere av virksomhetens regler påvirker generatorinnstillingene dine." - }, "passwordPrompt": { "message": "Forespørsel om hovedpassord på nytt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Feil" }, - "regenerateUsername": { - "message": "Regenerer brukernavn" - }, "generateUsername": { "message": "Generer brukernavn" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Brukernavntype" - }, "plusAddressedEmail": { "message": "Pluss-adressert e-post", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Navn på nettside" }, - "whatWouldYouLikeToGenerate": { - "message": "Hva vil du generere?" - }, - "passwordType": { - "message": "Passordtype" - }, "service": { "message": "Tjeneste" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "Systemforvalg" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 6aa17c1d7e3..6ab3755c8f4 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -447,9 +454,6 @@ "length": { "message": "Length" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum special" }, - "avoidAmbChar": { - "message": "Avoid ambiguous characters", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Clear clipboard", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organization" }, - "share": { - "message": "Share" - }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Clone" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expired" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Expiration date" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 day" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Custom" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 28cbea13382..7a252ee3ddb 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Identiteit automatisch invullen" }, + "fillVerificationCode": { + "message": "Verificatiecode invullen" + }, + "fillVerificationCodeAria": { + "message": "Verificatiecode invullen", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Wachtwoord genereren (op klembord)" }, @@ -447,9 +454,6 @@ "length": { "message": "Lengte" }, - "passwordMinLength": { - "message": "Minimale wachtwoordlengte" - }, "uppercase": { "message": "Hoofdletters (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum aantal speciale tekens" }, - "avoidAmbChar": { - "message": "Dubbelzinnige tekens vermijden", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Dubbelzinnige tekens vermijden", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Deze extensie beoordelen" }, - "rateExtensionDesc": { - "message": "Je kunt ons helpen door een goede recensie achter te laten!" - }, "browserNotSupportClipboard": { "message": "Je webbrowser ondersteunt kopiëren naar plakbord niet. Kopieer handmatig." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Identiteiten weergeven op de tabpagina voor gemakkelijk automatisch invullen." }, + "clickToAutofillOnVault": { + "message": "Klik op items om automatisch in te vullen op de kluisweergave" + }, "clearClipboard": { "message": "Klembord wissen", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Naar organisatie verplaatsen" }, - "share": { - "message": "Delen" - }, "movedItemToOrg": { "message": "$ITEMNAME$ verplaatst naar $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Dupliceren" }, - "passwordGeneratorPolicyInEffect": { - "message": "Een of meer organisatiebeleidseisen heeft invloed op de instellingen van je generator." - }, "passwordGenerator": { "message": "Wachtwoordgenerator" }, @@ -2385,14 +2379,6 @@ "message": "Send-details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Sends zoeken", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Send toevoegen", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Tekst" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Tekst standaard verbergen" }, - "maxAccessCountReached": { - "message": "Maximum aantal keren benaderd", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Verlopen" }, - "pendingDeletion": { - "message": "Wordt verwijderd" - }, "passwordProtected": { "message": "Beveiligd met wachtwoord" }, @@ -2468,24 +2447,9 @@ "message": "Send bewerken", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Wat voor soort Send is dit?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Een vriendelijke naam om deze Send te beschrijven.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Het bestand dat je wilt versturen." - }, "deletionDate": { "message": "Verwijderingsdatum" }, - "deletionDateDesc": { - "message": "Deze Send wordt op de aangegeven datum en tijd definitief verwijderd.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Op deze datum wordt de Send definitief verwijderd.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Vervaldatum" }, - "expirationDateDesc": { - "message": "Als dit is ingesteld verloopt deze Send op een specifieke datum en tijd.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 dag" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Aangepast" }, - "maximumAccessCount": { - "message": "Maximum toegangsaantal" - }, - "maximumAccessCountDesc": { - "message": "Als dit is ingesteld kunnen gebruikers deze Send niet meer benaderen zodra het maximale aantal toegang is bereikt.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Vereis optioneel een wachtwoord voor gebruikers om toegang te krijgen tot deze Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Voeg een optioneel wachtwoord toe voor ontvangers om toegang te krijgen tot deze Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Privénotities over deze Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Schakel deze Send uit zodat niemand hem kan benaderen.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Kopieer de link van deze Send bij het opslaan naar het klembord.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "De tekst die je wilt versturen." - }, - "sendHideText": { - "message": "De tekst van deze Send standaard verbergen.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Huidige toegangsaantal" - }, "createSend": { "message": "Nieuwe Send aanmaken", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Voor je begint" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Om een datumkiezer in kalenderstijl te gebruiken", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "klik hier", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "om een pop-up te openen.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "De opgegeven vervaldatum is niet geldig." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Er is een fout opgetreden bij het opslaan van je verwijder- en vervaldatum." }, - "hideEmail": { - "message": "Verberg mijn e-mailadres voor ontvangers." - }, "hideYourEmail": { "message": "Je e-mailadres voor ontvangers verbergen." }, - "sendOptionsPolicyInEffect": { - "message": "Een of meer organisatiebeleidseisen heeft invloed op de mogelijkheden van je Send." - }, "passwordPrompt": { "message": "Hoofdwachtwoord opnieuw vragen" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Fout" }, - "regenerateUsername": { - "message": "Gebruikersnaam opnieuw genereren" - }, "generateUsername": { "message": "Gebruikersnamen genereren" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Type gebruikersnaam" - }, "plusAddressedEmail": { "message": "E-mailadres-met-plus", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Websitenaam" }, - "whatWouldYouLikeToGenerate": { - "message": "Wat wil je genereren?" - }, - "passwordType": { - "message": "Type wachtwoord" - }, "service": { "message": "Dienst" }, @@ -3580,6 +3477,14 @@ "message": "Je account ontgrendelen, opent in een nieuw venster", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-gebaseerde eenmalige wachtwoord verificatiecode", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Resterende tijd voordat de huidige TOTP vervalt", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Inloggegevens invullen voor", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Aantal login-autofill-suggesties op het extensie-pictogram weergeven" }, + "showQuickCopyActions": { + "message": "Toon snelle kopieeracties in de kluis" + }, "systemDefault": { "message": "Systeemstandaard" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Belangrijke mededeling" + }, + "setupTwoStepLogin": { + "message": "Tweestapsaanmelding instellen" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Vanaf februari 2025 stuurt Bitwarden een code naar het e-mailadres van je account om inloggen op nieuwe apparaten te verifiëren." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Je kunt tweestapsaanmelding instellen als een alternatieve manier om je account te beschermen of je e-mailadres te veranderen naar een waar je toegang toe hebt." + }, + "remindMeLater": { + "message": "Herinner me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Heb je betrouwbare toegang tot je e-mail, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nee, dat heb ik niet" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ja, ik heb betrouwbare toegang tot mijn e-mail" + }, + "turnOnTwoStepLogin": { + "message": "Tweestapsaanmelding inschakelen" + }, + "changeAcctEmail": { + "message": "E-mailadres van het account veranderen" + }, "extensionWidth": { "message": "Extensiebreedte" }, diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 6aa17c1d7e3..6ab3755c8f4 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -447,9 +454,6 @@ "length": { "message": "Length" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum special" }, - "avoidAmbChar": { - "message": "Avoid ambiguous characters", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Clear clipboard", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organization" }, - "share": { - "message": "Share" - }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Clone" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expired" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Expiration date" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 day" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Custom" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 6aa17c1d7e3..6ab3755c8f4 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -447,9 +454,6 @@ "length": { "message": "Length" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum special" }, - "avoidAmbChar": { - "message": "Avoid ambiguous characters", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Clear clipboard", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organization" }, - "share": { - "message": "Share" - }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Clone" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expired" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Expiration date" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 day" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Custom" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index a49b5c52e3b..5d1cd22c9ef 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -23,19 +23,19 @@ "message": "New to Bitwarden?" }, "logInWithPasskey": { - "message": "Log in with passkey" + "message": "Zaloguj się używając passkey" }, "useSingleSignOn": { - "message": "Use single sign-on" + "message": "Użyj jednokrotnego logowania" }, "welcomeBack": { - "message": "Welcome back" + "message": "Witaj ponownie" }, "setAStrongPassword": { "message": "Ustaw silne hasło" }, "finishCreatingYourAccountBySettingAPassword": { - "message": "Ukończ tworzenie konta poprzez utworzenie hasła" + "message": "Ukończ tworzenie konta poprzez ustawienie hasła" }, "enterpriseSingleSignOn": { "message": "Logowanie jednokrotne" @@ -120,7 +120,7 @@ "message": "Kopiuj hasło" }, "copyPassphrase": { - "message": "Copy passphrase" + "message": "Kopiuj frazę bezpieczeństwa" }, "copyNote": { "message": "Kopiuj notatkę" @@ -153,13 +153,13 @@ "message": "Kopiuj numer licencji" }, "copyPrivateKey": { - "message": "Copy private key" + "message": "Skopiuj klucz prywatny" }, "copyPublicKey": { - "message": "Copy public key" + "message": "Skopiuj klucz publiczny" }, "copyFingerprint": { - "message": "Copy fingerprint" + "message": "Skopiuj odcisk palca" }, "copyCustomField": { "message": "Kopiuj $FIELD$", @@ -177,7 +177,7 @@ "message": "Kopiuj notatki" }, "fill": { - "message": "Fill", + "message": "Wypełnij", "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autouzupełnianie tożsamości" }, + "fillVerificationCode": { + "message": "Wypełnij kod weryfikacyjny" + }, + "fillVerificationCodeAria": { + "message": "Wypełnij kod weryfikacyjny", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Wygeneruj hasło (do schowka)" }, @@ -436,7 +443,7 @@ "message": "Wygeneruj hasło" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "Wygenruj frazę zabezpieczającą" }, "regeneratePassword": { "message": "Wygeneruj ponownie hasło" @@ -447,9 +454,6 @@ "length": { "message": "Długość" }, - "passwordMinLength": { - "message": "Minimalna długość hasła" - }, "uppercase": { "message": "Wielkie litery (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimalna liczba znaków specjalnych" }, - "avoidAmbChar": { - "message": "Unikaj niejednoznacznych znaków", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Unikaj niejednoznacznych znaków", "description": "Label for the avoid ambiguous characters checkbox." @@ -600,7 +600,7 @@ "message": "Otwórz stronę" }, "launchWebsiteName": { - "message": "Launch website $ITEMNAME$", + "message": "Otwórz stronę internetową $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -621,7 +621,7 @@ "message": "Inne" }, "unlockMethods": { - "message": "Odblokuj Opcje" + "message": "Opcje odblokowania" }, "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Ustaw metodę odblokowania, aby zmienić czas blokowania sejfu." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Oceń rozszerzenie" }, - "rateExtensionDesc": { - "message": "Wesprzyj nas pozytywną opinią!" - }, "browserNotSupportClipboard": { "message": "Przeglądarka nie obsługuje łatwego kopiowania schowka. Skopiuj element ręcznie." }, @@ -855,7 +852,7 @@ "message": "Zaloguj się" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "Zaloguj się do Bitwarden" }, "restartRegistration": { "message": "Zrestartuj rejestrację" @@ -981,7 +978,7 @@ "message": "Poproś o dodanie danych logowania" }, "vaultSaveOptionsTitle": { - "message": "Zapisz do ustawień sejfu" + "message": "Opcje zapisywania w sejfie" }, "addLoginNotificationDesc": { "message": "\"Dodaj powiadomienia logowania\" automatycznie wyświetla monit o zapisanie nowych danych logowania do sejfu przy każdym pierwszym logowaniu." @@ -999,7 +996,7 @@ "message": "Pokaż elementy karty na stronie głównej, aby ułatwić autouzupełnianie." }, "showIdentitiesInVaultView": { - "message": "Pokaż tożsamośći jako sugestie autouzupełniania w widoku sejfu" + "message": "Pokaż tożsamości jako sugestie autouzupełniania w widoku sejfu" }, "showIdentitiesCurrentTab": { "message": "Pokaż tożsamości na stronie głównej" @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Pokaż elementy tożsamości na stronie głównej, aby ułatwić autouzupełnianie." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Wyczyść schowek", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1126,7 +1126,7 @@ "description": "WARNING (should stay in capitalized letters if the language permits)" }, "warningCapitalized": { - "message": "Warning", + "message": "Ostrzeżenie", "description": "Warning (should maintain locale-relevant capitalization)" }, "confirmVaultExport": { @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Przenieś do organizacji" }, - "share": { - "message": "Udostępnij" - }, "movedItemToOrg": { "message": "Element $ITEMNAME$ został przeniesiony do organizacji $ORGNAME$", "placeholders": { @@ -1320,7 +1317,7 @@ "message": "Wpisz 6-cyfrowy kod weryfikacyjny z aplikacji uwierzytelniającej." }, "authenticationTimeout": { - "message": "Authentication timeout" + "message": "Limit czasu uwierzytelniania" }, "authenticationSessionTimedOut": { "message": "The authentication session timed out. Please restart the login process." @@ -1443,7 +1440,7 @@ "message": "Adres URL serwera" }, "selfHostBaseUrl": { - "message": "Self-host server URL", + "message": "URL samodzielnie hostowanego serwera", "description": "Label for field requesting a self-hosted integration service URL" }, "apiUrl": { @@ -1487,7 +1484,7 @@ "message": "Dotyczy wszystkich zalogowanych kont." }, "turnOffBrowserBuiltInPasswordManagerSettings": { - "message": "Wyłącz wbudowany w przeglądarkę menedżera haseł, aby uniknąć konfliktów." + "message": "Wyłącz wbudowany w przeglądarkę menedżer haseł, aby uniknąć konfliktów." }, "turnOffBrowserBuiltInPasswordManagerSettingsLink": { "message": "Edytuj ustawienia przeglądarki." @@ -1771,7 +1768,7 @@ "message": "Tożsamość" }, "typeSshKey": { - "message": "SSH key" + "message": "Klucz SSH" }, "newItemHeader": { "message": "Nowy $TYPE$", @@ -1804,13 +1801,13 @@ "message": "Historia hasła" }, "generatorHistory": { - "message": "Generator history" + "message": "Historia generatora" }, "clearGeneratorHistoryTitle": { - "message": "Clear generator history" + "message": "Wyczyść historię generatora" }, "cleargGeneratorHistoryDescription": { - "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + "message": "Jeśli zatwierdzisz, wszystkie wygenerowane hasła zostaną usunięte z historii generatora. Czy chcesz kontynuować mimo to?" }, "back": { "message": "Powrót" @@ -1849,7 +1846,7 @@ "message": "Bezpieczne notatki" }, "sshKeys": { - "message": "SSH Keys" + "message": "Klucze SSH" }, "clear": { "message": "Wyczyść", @@ -1932,10 +1929,10 @@ "message": "Wyczyść historię" }, "nothingToShow": { - "message": "Nothing to show" + "message": "Brak zawartości do pokazania" }, "nothingGeneratedRecently": { - "message": "You haven't generated anything recently" + "message": "Nic nie zostało wygenerowane przez ciebie w ostatnim czasie" }, "remove": { "message": "Usuń" @@ -2043,9 +2040,6 @@ "clone": { "message": "Klonuj" }, - "passwordGeneratorPolicyInEffect": { - "message": "Co najmniej jedna zasada organizacji wpływa na ustawienia generatora." - }, "passwordGenerator": { "message": "Generator hasła" }, @@ -2385,14 +2379,6 @@ "message": "Szczegóły Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Szukaj w wysyłkach", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Dodaj wysyłkę", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Tekst" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Domyślnie ukryj tekst" }, - "maxAccessCountReached": { - "message": "Maksymalna liczba dostępów została osiągnięta", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Wygasła" }, - "pendingDeletion": { - "message": "Oczekiwanie na usunięcie" - }, "passwordProtected": { "message": "Chroniona hasłem" }, @@ -2468,24 +2447,9 @@ "message": "Edytuj wysyłkę", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Jakiego typu jest to wysyłka?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Nazwa wysyłki.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Plik, który chcesz wysłać." - }, "deletionDate": { "message": "Data usunięcia" }, - "deletionDateDesc": { - "message": "Wysyłka zostanie trwale usunięta w określonym czasie.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Send zostanie trwale usunięte w tej dacie.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Data wygaśnięcia" }, - "expirationDateDesc": { - "message": "Jeśli funkcja jest włączona, dostęp do wysyłki wygaśnie po określonym czasie.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 dzień" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Niestandardowe" }, - "maximumAccessCount": { - "message": "Maksymalna liczba dostępów" - }, - "maximumAccessCountDesc": { - "message": "Jeśli funkcja jest włączona, po osiągnięciu maksymalnej liczby dostępów, użytkownicy nie będą mieli dostępu do tej wysyłki.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Opcjonalne hasło dla użytkownika, aby uzyskać dostęp do wysyłki.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { - "message": "Add an optional password for recipients to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNotesDesc": { - "message": "Prywatne notatki o tej wysyłce.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Wyłącz wysyłkę, aby nikt nie miał do niej dostępu.", + "message": "Zabezpiecz tę wiadomość hasłem, które będzie wymagane, aby uzyskać do niej dostęp.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendShareDesc": { - "message": "Po zapisaniu wysyłki, skopiuj link do schowka.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Tekst, który chcesz wysłać." - }, - "sendHideText": { - "message": "Ukryj domyślnie tekst wysyłki.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Obecna liczba dostępów" - }, "createSend": { "message": "Nowa wysyłka", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2609,7 +2536,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFilePopoutDialogText": { - "message": "Pop out extension?", + "message": "Otworzyć rozszerzenie w nowym oknie?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFilePopoutDialogDesc": { @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Zanim zaczniesz" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Aby wybrać datę, ", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "kliknij tutaj", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "w celu otwarcia rozszerzenia w oknie.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Data wygaśnięcia nie jest prawidłowa." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Wystąpił błąd podczas zapisywania dat usunięcia i wygaśnięcia." }, - "hideEmail": { - "message": "Ukryj mój adres e-mail przed odbiorcami." - }, "hideYourEmail": { "message": "Ukryj mój adres e-mail przed oglądającymi." }, - "sendOptionsPolicyInEffect": { - "message": "Co najmniej jedna zasada organizacji wpływa na ustawienia wysyłek." - }, "passwordPrompt": { "message": "Potwierdź hasłem głównym" }, @@ -2880,17 +2789,14 @@ "error": { "message": "Błąd" }, - "regenerateUsername": { - "message": "Wygeneruj ponownie nazwę użytkownika" - }, "generateUsername": { "message": "Wygeneruj nazwę użytkownika" }, "generateEmail": { - "message": "Generate email" + "message": "Wygeneruj e-mail" }, "spinboxBoundariesHint": { - "message": "Value must be between $MIN$ and $MAX$.", + "message": "Wartość musi być pomiędzy $MIN$ a $MAX$.", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -2904,7 +2810,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", + "message": " Użyj $RECOMMENDED$ znaków lub więcej, aby wygenerować silne hasło.", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2914,7 +2820,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": " Użyj $RECOMMENDED$ słów lub więcej, aby wygenerować silne hasło.", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Rodzaj nazwy użytkownika" - }, "plusAddressedEmail": { "message": "Adres e-mail z plusem", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Nazwa strony" }, - "whatWouldYouLikeToGenerate": { - "message": "Co chcesz wygenerować?" - }, - "passwordType": { - "message": "Rodzaj hasła" - }, "service": { "message": "Usługa" }, @@ -2964,11 +2861,11 @@ "message": "Wygeneruj alias adresu e-mail z zewnętrznej usługi przekierowania." }, "forwarderDomainName": { - "message": "Email domain", + "message": "Domena adresu e-mail", "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { - "message": "Choose a domain that is supported by the selected service", + "message": "Wybierz domenę, która jest obsługiwana przez wybraną usługę", "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { @@ -3171,16 +3068,16 @@ "message": "Wyślij ponownie powiadomienie" }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "Zobacz wszystkie sposoby logowania" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Zobacz wszystkie sposoby logowania" }, "notificationSentDevice": { "message": "Powiadomienie zostało wysłane na urządzenie." }, "aNotificationWasSentToYourDevice": { - "message": "A notification was sent to your device" + "message": "Powiadomienie zostało wysłane na twoje urządzenie" }, "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" @@ -3189,7 +3086,7 @@ "message": "You will be notified once the request is approved" }, "needAnotherOptionV1": { - "message": "Need another option?" + "message": "Potrzebujesz innego sposobu?" }, "loginInitiated": { "message": "Logowanie rozpoczęte" @@ -3285,16 +3182,16 @@ "message": "Otwiera w nowym oknie" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Zapamiętaj to urządzenie, aby przyszłe logowania były bezproblemowe" }, "deviceApprovalRequired": { "message": "Wymagane zatwierdzenie urządzenia. Wybierz opcję zatwierdzenia poniżej:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Wymagane zatwierdzenie urządzenia" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Wybierz opcję zatwierdzenia poniżej" }, "rememberThisDevice": { "message": "Zapamiętaj to urządzenie" @@ -3370,7 +3267,7 @@ "message": "Brak adresu e-mail użytkownika" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "Nie znaleziono aktywnego adresu e-mail. Trwa wylogowanie." }, "deviceTrusted": { "message": "Zaufano urządzeniu" @@ -3580,6 +3477,14 @@ "message": "Odblokuj swoje konto, otwiera się w nowym oknie", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Pozostały czas do wygaśnięcia bieżącego TOTP", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Wypełnij dane logowania dla", "description": "Screen reader text for when overlay item is in focused" @@ -3809,7 +3714,7 @@ "message": "Uzyskiwanie dostępu" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Zalogowano!" }, "passkeyNotCopied": { "message": "Passkey nie zostanie skopiowany" @@ -4303,13 +4208,13 @@ "message": "Filtry" }, "filterVault": { - "message": "Filter vault" + "message": "Filtruj sejf" }, "filterApplied": { - "message": "One filter applied" + "message": "Zastosowano jeden filtr" }, "filterAppliedPlural": { - "message": "$COUNT$ filters applied", + "message": "$COUNT$ filtrów zastosowanych", "placeholders": { "count": { "content": "$1", @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Pokaż liczbę sugestii autouzupełniania logowania na ikonie rozszerzenia" }, + "showQuickCopyActions": { + "message": "Pokaż akcje szybkiego kopiowania w Sejfie" + }, "systemDefault": { "message": "Domyślny systemu" }, @@ -4671,28 +4579,28 @@ "message": "Do tego ustalenia zastosowano wymogi polityki przedsiębiorstw" }, "sshPrivateKey": { - "message": "Private key" + "message": "Klucz prywatny" }, "sshPublicKey": { - "message": "Public key" + "message": "Klucz publiczny" }, "sshFingerprint": { - "message": "Fingerprint" + "message": "Odcisk palca" }, "sshKeyAlgorithm": { - "message": "Key type" + "message": "Typ klucza" }, "sshKeyAlgorithmED25519": { "message": "ED25519" }, "sshKeyAlgorithmRSA2048": { - "message": "RSA 2048-Bit" + "message": "RSA 2048-bitowy" }, "sshKeyAlgorithmRSA3072": { - "message": "RSA 3072-Bit" + "message": "RSA 3072-bitowy" }, "sshKeyAlgorithmRSA4096": { - "message": "RSA 4096-Bit" + "message": "RSA 4096-bitowy" }, "retry": { "message": "Powtórz" @@ -4737,23 +4645,23 @@ "message": "Uwierzytelnianie" }, "fillGeneratedPassword": { - "message": "Fill generated password", + "message": "Uzupełnij wygenerowanym hasłem", "description": "Heading for the password generator within the inline menu" }, "passwordRegenerated": { - "message": "Password regenerated", + "message": "Hasło zostało ponownie wygenerowane", "description": "Notification message for when a password has been regenerated" }, "saveLoginToBitwarden": { - "message": "Save login to Bitwarden?", + "message": "Zapisać dane logowania w Bitwarden?", "description": "Confirmation message for saving a login to Bitwarden" }, "spaceCharacterDescriptor": { - "message": "Space", + "message": "Spacja", "description": "Represents the space key in screen reader content as a readable word" }, "tildeCharacterDescriptor": { - "message": "Tilde", + "message": "Tylda", "description": "Represents the ~ key in screen reader content as a readable word" }, "backtickCharacterDescriptor": { @@ -4761,23 +4669,23 @@ "description": "Represents the ` key in screen reader content as a readable word" }, "exclamationCharacterDescriptor": { - "message": "Exclamation mark", + "message": "Wykrzyknik", "description": "Represents the ! key in screen reader content as a readable word" }, "atSignCharacterDescriptor": { - "message": "At sign", + "message": "Małpa", "description": "Represents the @ key in screen reader content as a readable word" }, "hashSignCharacterDescriptor": { - "message": "Hash sign", + "message": "Hashtag", "description": "Represents the # key in screen reader content as a readable word" }, "dollarSignCharacterDescriptor": { - "message": "Dollar sign", + "message": "Znak dolara", "description": "Represents the $ key in screen reader content as a readable word" }, "percentSignCharacterDescriptor": { - "message": "Percent sign", + "message": "Znak procenta", "description": "Represents the % key in screen reader content as a readable word" }, "caretCharacterDescriptor": { @@ -4789,23 +4697,23 @@ "description": "Represents the & key in screen reader content as a readable word" }, "asteriskCharacterDescriptor": { - "message": "Asterisk", + "message": "Gwiazdka", "description": "Represents the * key in screen reader content as a readable word" }, "parenLeftCharacterDescriptor": { - "message": "Left parenthesis", + "message": "Prawy nawias okrągły", "description": "Represents the ( key in screen reader content as a readable word" }, "parenRightCharacterDescriptor": { - "message": "Right parenthesis", + "message": "Prawy nawias okrągły", "description": "Represents the ) key in screen reader content as a readable word" }, "hyphenCharacterDescriptor": { - "message": "Underscore", + "message": "Znak podkreślenia", "description": "Represents the _ key in screen reader content as a readable word" }, "underscoreCharacterDescriptor": { - "message": "Hyphen", + "message": "Myślnik", "description": "Represents the - key in screen reader content as a readable word" }, "plusCharacterDescriptor": { @@ -4813,27 +4721,27 @@ "description": "Represents the + key in screen reader content as a readable word" }, "equalsCharacterDescriptor": { - "message": "Equals", + "message": "Znak równości", "description": "Represents the = key in screen reader content as a readable word" }, "braceLeftCharacterDescriptor": { - "message": "Left brace", + "message": "Lewy nawias klamrowy", "description": "Represents the { key in screen reader content as a readable word" }, "braceRightCharacterDescriptor": { - "message": "Right brace", + "message": "Prawy nawias klamrowy", "description": "Represents the } key in screen reader content as a readable word" }, "bracketLeftCharacterDescriptor": { - "message": "Left bracket", + "message": "Lewy nawias kwadratowy", "description": "Represents the [ key in screen reader content as a readable word" }, "bracketRightCharacterDescriptor": { - "message": "Right bracket", + "message": "Prawy nawias kwadratowy", "description": "Represents the ] key in screen reader content as a readable word" }, "pipeCharacterDescriptor": { - "message": "Pipe", + "message": "Pionowa kreska", "description": "Represents the | key in screen reader content as a readable word" }, "backSlashCharacterDescriptor": { @@ -4841,39 +4749,39 @@ "description": "Represents the back slash key in screen reader content as a readable word" }, "colonCharacterDescriptor": { - "message": "Colon", + "message": "Dwukropek", "description": "Represents the : key in screen reader content as a readable word" }, "semicolonCharacterDescriptor": { - "message": "Semicolon", + "message": "Średnik", "description": "Represents the ; key in screen reader content as a readable word" }, "doubleQuoteCharacterDescriptor": { - "message": "Double quote", + "message": "Cudzysłów", "description": "Represents the double quote key in screen reader content as a readable word" }, "singleQuoteCharacterDescriptor": { - "message": "Single quote", + "message": "Apostrof", "description": "Represents the ' key in screen reader content as a readable word" }, "lessThanCharacterDescriptor": { - "message": "Less than", + "message": "Mniejszy niż", "description": "Represents the < key in screen reader content as a readable word" }, "greaterThanCharacterDescriptor": { - "message": "Greater than", + "message": "Większy niż", "description": "Represents the > key in screen reader content as a readable word" }, "commaCharacterDescriptor": { - "message": "Comma", + "message": "Przecinek", "description": "Represents the , key in screen reader content as a readable word" }, "periodCharacterDescriptor": { - "message": "Period", + "message": "Kropka", "description": "Represents the . key in screen reader content as a readable word" }, "questionCharacterDescriptor": { - "message": "Question mark", + "message": "Znak zapytania", "description": "Represents the ? key in screen reader content as a readable word" }, "forwardSlashCharacterDescriptor": { @@ -4881,27 +4789,63 @@ "description": "Represents the / key in screen reader content as a readable word" }, "lowercaseAriaLabel": { - "message": "Lowercase" + "message": "Małe litery" }, "uppercaseAriaLabel": { - "message": "Uppercase" + "message": "Wielkie litery" }, "generatedPassword": { - "message": "Generated password" + "message": "Wygenerowane hasło" }, "compactMode": { - "message": "Compact mode" + "message": "Tryb kompaktowy" }, "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { - "message": "Extension width" + "message": "Szerokość rozszerzenia" }, "wide": { - "message": "Wide" + "message": "Szerokie" }, "extraWide": { - "message": "Extra wide" + "message": "Bardzo szerokie" } } diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index 0a34f5b9dd7..cd0c9979103 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -3,7 +3,7 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden Gerenciador de Senhas", + "message": "Gerenciador de senhas Bitwarden", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Preenchimento automático identidade" }, + "fillVerificationCode": { + "message": "Preencher o código de verificação" + }, + "fillVerificationCodeAria": { + "message": "Preencher o código de verificação", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Gerar Senha (copiada)" }, @@ -235,7 +242,7 @@ "message": "Correio eletrônico da conta" }, "requestHint": { - "message": "Pedir dica" + "message": "Solicitar dica" }, "requestPasswordHint": { "message": "Dica da senha mestra" @@ -289,7 +296,7 @@ "message": "Continuar na extensão da loja do navegador?" }, "continueToBrowserExtensionStoreDesc": { - "message": "Ajude outros a descobrir se o Bitwarden está certo para eles. Visite a loja de extensões do seu navegador e deixe uma classificação agora." + "message": "Ajude outras pessoas a descobrirem se o Bitwarden é o que elas estão procurando. Visite a loja de extensões do seu navegador e deixe uma classificação agora." }, "changeMasterPasswordOnWebConfirmation": { "message": "Você pode alterar a sua senha mestra no aplicativo web Bitwarden." @@ -333,7 +340,7 @@ "message": "Gerenciador de Segredos Bitwarden" }, "continueToSecretsManagerPageDesc": { - "message": "Armazene, gerencie e compartilhe segredos de desenvolvedor com o Gerenciador de segredos do Bitwarden. Saiba mais no site bitwarden.com." + "message": "Armazene, gerencie e compartilhe senhas de desenvolvedor com o Gerenciador de segredos do Bitwarden. Saiba mais no site bitwarden.com." }, "passwordlessDotDev": { "message": "Passwordless.dev" @@ -342,10 +349,10 @@ "message": "Crie experiências de login suaves e seguras, livres de senhas tradicionais com Passwordless.dev. Saiba mais no site bitwarden.com." }, "freeBitwardenFamilies": { - "message": "Famílias do Bitwarden Grátis" + "message": "Plano Familiar do Bitwarden Grátis" }, "freeBitwardenFamiliesPageDesc": { - "message": "Você é elegível para as Famílias do Bitwarden Grátis. Resgate esta oferta hoje no aplicativo web." + "message": "Você é elegível para o plano Familiar do Bitwarden Grátis. Resgate esta oferta hoje no aplicativo web." }, "version": { "message": "Versão" @@ -447,9 +454,6 @@ "length": { "message": "Comprimento" }, - "passwordMinLength": { - "message": "Tamanho mínimo da senha" - }, "uppercase": { "message": "Maiúsculas (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,12 +525,8 @@ "minSpecial": { "message": "Especiais Mínimos" }, - "avoidAmbChar": { - "message": "Evitar Caracteres Ambíguos", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { - "message": "Evitar Caracteres Ambíguos", + "message": "Evitar caracteres ambíguos", "description": "Label for the avoid ambiguous characters checkbox." }, "generatorPolicyInEffect": { @@ -641,9 +641,6 @@ "rateExtension": { "message": "Avaliar a Extensão" }, - "rateExtensionDesc": { - "message": "Por favor considere ajudar-nos com uma boa avaliação!" - }, "browserNotSupportClipboard": { "message": "O seu navegador web não suporta cópia para a área de transferência. Em alternativa, copie manualmente." }, @@ -894,7 +891,7 @@ "message": "Torne sua conta mais segura configurando o 'login' em duas etapas no aplicativo ‘web’ do Bitwarden." }, "twoStepLoginConfirmationTitle": { - "message": "Continuar para o aplicativo da ‘web’?" + "message": "Continuar para o aplicativo web?" }, "editedFolder": { "message": "Pasta Editada" @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Liste os itens de identidade na aba atual para facilitar preenchimento automático." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Limpar Área de Transferência", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1126,7 +1126,7 @@ "description": "WARNING (should stay in capitalized letters if the language permits)" }, "warningCapitalized": { - "message": "Warning", + "message": "Atenção", "description": "Warning (should maintain locale-relevant capitalization)" }, "confirmVaultExport": { @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Mover para a Organização" }, - "share": { - "message": "Compartilhar" - }, "movedItemToOrg": { "message": "$ITEMNAME$ movido para $ORGNAME$", "placeholders": { @@ -1221,7 +1218,7 @@ "message": "Funcionalidade Indisponível" }, "encryptionKeyMigrationRequired": { - "message": "Migração de chave de criptografia necessária. Faça login através do cofre web para atualizar sua chave de criptografia." + "message": "É necessário migrar sua chave de criptografia. Faça login através do cofre web para atualizar sua chave de criptografia." }, "premiumMembership": { "message": "Assinatura Premium" @@ -1320,10 +1317,10 @@ "message": "Insira o código de verificação de 6 dígitos do seu aplicativo de autenticação." }, "authenticationTimeout": { - "message": "Authentication timeout" + "message": "Tempo de autenticação esgotado" }, "authenticationSessionTimedOut": { - "message": "The authentication session timed out. Please restart the login process." + "message": "A sessão de autenticação expirou. Por favor, reinicie o processo de login." }, "enterVerificationCodeEmail": { "message": "Insira o código de verificação de 6 dígitos que foi enviado por e-mail para $EMAIL$.", @@ -1487,7 +1484,7 @@ "message": "Aplica-se a todas as contas conectadas." }, "turnOffBrowserBuiltInPasswordManagerSettings": { - "message": "Desative as configurações do gerenciador de senhas do seu navegador para evitar conflitos." + "message": "Desative o gerenciador de senhas padrão do seu navegador para evitar conflitos." }, "turnOffBrowserBuiltInPasswordManagerSettingsLink": { "message": "Editar configurações do navegador." @@ -1849,7 +1846,7 @@ "message": "Notas seguras" }, "sshKeys": { - "message": "SSH Keys" + "message": "Chaves SSH" }, "clear": { "message": "Limpar", @@ -2043,9 +2040,6 @@ "clone": { "message": "Clonar" }, - "passwordGeneratorPolicyInEffect": { - "message": "Uma ou mais políticas da organização estão afetando as suas configurações do gerador." - }, "passwordGenerator": { "message": "Gerador de Senha" }, @@ -2195,7 +2189,7 @@ "message": "A sua nova senha mestra não cumpre aos requisitos da política." }, "receiveMarketingEmailsV2": { - "message": "Obtenha conselhos, novidades, e oportunidades de pesquisa do Bitwarden em sua caixa de entrada." + "message": "Obtenha dicas, novidades e oportunidades de pesquisa do Bitwarden em sua caixa de entrada." }, "unsubscribe": { "message": "Cancelar subscrição" @@ -2385,14 +2379,6 @@ "message": "Enviar detalhes", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Pesquisar Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Adicionar Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Texto" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Ocultar texto por padrão" }, - "maxAccessCountReached": { - "message": "Número máximo de acessos atingido", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expirado" }, - "pendingDeletion": { - "message": "Exclusão pendente" - }, "passwordProtected": { "message": "Protegido por senha" }, @@ -2468,24 +2447,9 @@ "message": "Editar Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Que tipo de Send é este?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Um nome amigável para descrever este Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "O arquivo que você deseja enviar." - }, "deletionDate": { "message": "Data de Exclusão" }, - "deletionDateDesc": { - "message": "O Send será eliminado permanentemente na data e hora especificadas.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "O envio será eliminado permanentemente na data e hora especificadas.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Data de Validade" }, - "expirationDateDesc": { - "message": "Se definido, o acesso a este Send expirará na data e hora especificadas.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 dia" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Personalizado" }, - "maximumAccessCount": { - "message": "Contagem Máxima de Acessos" - }, - "maximumAccessCountDesc": { - "message": "Se atribuído, usuários não poderão mais acessar este Send assim que o número máximo de acessos for atingido.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Exigir opcionalmente uma senha para os usuários acessarem este Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Adicione uma senha opcional para os destinatários para acessar este Envio.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Notas privadas sobre esse Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Desative este Send para que ninguém possa acessá-lo.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copiar o link deste Send para área de transferência após salvar.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "O texto que você deseja enviar." - }, - "sendHideText": { - "message": "Ocultar o texto deste Send por padrão.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Contagem Atual de Acessos" - }, "createSend": { "message": "Criar Novo Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Antes de começar" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Para usar um seletor de data no estilo calendário", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "clique aqui", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "para abrir a sua janela.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "A data de validade fornecida não é válida." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Ocorreu um erro ao salvar as suas datas de exclusão e validade." }, - "hideEmail": { - "message": "Ocultar meu endereço de e-mail dos destinatários." - }, "hideYourEmail": { "message": "Ocultar meu endereço de correio eletrônico dos destinatários." }, - "sendOptionsPolicyInEffect": { - "message": "Uma ou mais políticas da organização estão afetando as suas opções de Send." - }, "passwordPrompt": { "message": "Solicitação nova de senha mestra" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Erro" }, - "regenerateUsername": { - "message": "Recriar Usuário" - }, "generateUsername": { "message": "Gerar Usuário" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Tipo de usuário" - }, "plusAddressedEmail": { "message": "E-mail alternativo (com um +)", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Nome do Site" }, - "whatWouldYouLikeToGenerate": { - "message": "O que você gostaria de gerar?" - }, - "passwordType": { - "message": "Categoria de Senha" - }, "service": { "message": "Serviço" }, @@ -3171,25 +3068,25 @@ "message": "Reenviar notificação" }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "Visualizar todas as opções de login" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Visualizar todas as opções de login" }, "notificationSentDevice": { "message": "Uma notificação foi enviada para seu dispositivo." }, "aNotificationWasSentToYourDevice": { - "message": "A notification was sent to your device" + "message": "Uma notificação foi enviada para o seu dispositivo" }, "makeSureYourAccountIsUnlockedAndTheFingerprintEtc": { - "message": "Make sure your account is unlocked and the fingerprint phrase matches on the other device" + "message": "Certifique-se que sua conta esteja desbloqueada e que a frase de identificação corresponda à do outro dispositivo" }, "youWillBeNotifiedOnceTheRequestIsApproved": { - "message": "You will be notified once the request is approved" + "message": "Você será notificado assim que a requisição for aprovada" }, "needAnotherOptionV1": { - "message": "Need another option?" + "message": "Precisa de outra opção?" }, "loginInitiated": { "message": "Login iniciado" @@ -3285,16 +3182,16 @@ "message": "Abrir em uma nova janela" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "Lembrar deste dispositivo para permanecer conectado" }, "deviceApprovalRequired": { "message": "Aprovação do dispositivo necessária. Selecione uma opção de aprovação abaixo:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "Aprovação do dispositivo necessária" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "Selecione uma opção de aprovação abaixo" }, "rememberThisDevice": { "message": "Lembrar deste dispositivo" @@ -3370,7 +3267,7 @@ "message": "E-mail do usuário ausente" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "E-mail de usuário ativo não encontrado. Desconectando." }, "deviceTrusted": { "message": "Dispositivo confiável" @@ -3580,6 +3477,14 @@ "message": "Desbloqueie sua conta, abra em uma nova janela", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Código de Verificação TOTP", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Tempo até expirar o código", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Preencha as credenciais para", "description": "Screen reader text for when overlay item is in focused" @@ -3809,7 +3714,7 @@ "message": "Acessando" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "Sessão Iniciada!" }, "passkeyNotCopied": { "message": "A chave de acesso não será copiada" @@ -4303,13 +4208,13 @@ "message": "Filtros" }, "filterVault": { - "message": "Filter vault" + "message": "Filtrar cofre" }, "filterApplied": { - "message": "One filter applied" + "message": "Um filtro aplicado" }, "filterAppliedPlural": { - "message": "$COUNT$ filters applied", + "message": "Foram aplicados $COUNT$ filtros", "placeholders": { "count": { "content": "$1", @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Mostrar o número de sugestões de preenchimento automático de login no ícone da extensão" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "Padrão do sistema" }, @@ -4895,13 +4803,49 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Aviso importante" + }, + "setupTwoStepLogin": { + "message": "Configurar login em duas etapas" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden enviará um código para o seu e-mail para verificar novos dispositivos a partir de fevereiro de 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Você pode configurar o login em duas etapas como uma forma alternativa de proteger sua conta ou mudar seu e-mail para um que você possa acessar." + }, + "remindMeLater": { + "message": "Lembre-me depois" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Você tem acesso ao seu e-mail, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Não tenho" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Sim, posso acessar meu e-mail de forma confiável" + }, + "turnOnTwoStepLogin": { + "message": "Ativar login em duas etapas" + }, + "changeAcctEmail": { + "message": "Alterar e-mail" + }, "extensionWidth": { - "message": "Extension width" + "message": "Largura da janela" }, "wide": { - "message": "Wide" + "message": "Grande" }, "extraWide": { - "message": "Extra wide" + "message": "Extra Grande" } } diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 6c85d72e6bf..24caa7e813d 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Preencher automaticamente identidade" }, + "fillVerificationCode": { + "message": "Preencher código de verificação" + }, + "fillVerificationCodeAria": { + "message": "Preencher código de verificação", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Gerar palavra-passe (copiada)" }, @@ -447,9 +454,6 @@ "length": { "message": "Comprimento" }, - "passwordMinLength": { - "message": "Comprimento mínimo da palavra-passe" - }, "uppercase": { "message": "Maiúsculas (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Mínimo de caracteres especiais" }, - "avoidAmbChar": { - "message": "Evitar caracteres ambíguos", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Evitar caracteres ambíguos", "description": "Label for the avoid ambiguous characters checkbox." @@ -561,7 +561,7 @@ "message": "Frase de acesso" }, "favorite": { - "message": "Favorito" + "message": "Adicionar aos favoritos" }, "unfavorite": { "message": "Remover dos favoritos" @@ -641,9 +641,6 @@ "rateExtension": { "message": "Avaliar a extensão" }, - "rateExtensionDesc": { - "message": "Por favor, considere ajudar-nos com uma boa avaliação!" - }, "browserNotSupportClipboard": { "message": "O seu navegador Web não suporta a cópia fácil da área de transferência. Em vez disso, copie manualmente." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Listar itens de identidades na página Separador para facilitar o preenchimento automático." }, + "clickToAutofillOnVault": { + "message": "Clique nos itens para preencher automaticamente na vista do cofre" + }, "clearClipboard": { "message": "Limpar área de transferência", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Mover para a organização" }, - "share": { - "message": "Partilhar" - }, "movedItemToOrg": { "message": "$ITEMNAME$ movido para $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Duplicar" }, - "passwordGeneratorPolicyInEffect": { - "message": "Uma ou mais políticas da organização estão a afetar as suas definições do gerador." - }, "passwordGenerator": { "message": "Gerador de palavras-passe" }, @@ -2385,14 +2379,6 @@ "message": "Detalhes do Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Procurar Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Adicionar Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Texto" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Ocultar texto por predefinição" }, - "maxAccessCountReached": { - "message": "Número máximo de acessos atingido", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expirado" }, - "pendingDeletion": { - "message": "Eliminação pendente" - }, "passwordProtected": { "message": "Protegido por palavra-passe" }, @@ -2468,24 +2447,9 @@ "message": "Editar Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Que tipo de Send é este?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Um nome simpático para descrever este Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "O ficheiro que deseja enviar." - }, "deletionDate": { "message": "Data de eliminação" }, - "deletionDateDesc": { - "message": "O Send será permanentemente eliminado na data e hora especificadas.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "O Send será permanentemente eliminado nesta data.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Prazo de validade" }, - "expirationDateDesc": { - "message": "Se definido, o acesso a este Send expirará na data e hora especificadas.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 dia" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Personalizado" }, - "maximumAccessCount": { - "message": "Número máximo de acessos" - }, - "maximumAccessCountDesc": { - "message": "Se definido, os utilizadores deixarão de poder aceder a este Send quando a contagem máxima de acessos for atingida.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Opcionalmente, exigir uma palavra-passe para os utilizadores acederem a este Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Adicione uma palavra-passe opcional para os destinatários acederem a este Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Notas privadas sobre este Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Desative este Send para que ninguém possa aceder ao mesmo.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copiar o link deste Send para a área de transferência ao guardar.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "O texto que deseja enviar." - }, - "sendHideText": { - "message": "Ocultar o texto deste Send por defeito.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Número de acessos atual" - }, "createSend": { "message": "Novo Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Antes de começar" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Para utilizar um seletor de datas do tipo calendário,", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "clique aqui", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "para abrir a janela.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "O prazo de validade fornecido não é válido." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Ocorreu um erro ao guardar as suas datas de eliminação e validade." }, - "hideEmail": { - "message": "Ocultar o meu endereço de e-mail dos destinatários." - }, "hideYourEmail": { "message": "Oculte o seu endereço de e-mail dos visualizadores." }, - "sendOptionsPolicyInEffect": { - "message": "Uma ou mais políticas da organização estão a afetar as suas opções do Send." - }, "passwordPrompt": { "message": "Pedir novamente a palavra-passe mestra" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Erro" }, - "regenerateUsername": { - "message": "Regenerar nome de utilizador" - }, "generateUsername": { "message": "Gerar nome de utilizador" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Tipo de nome de utilizador" - }, "plusAddressedEmail": { "message": "E-mail com subendereço", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Nome do site" }, - "whatWouldYouLikeToGenerate": { - "message": "O que é que gostaria de gerar?" - }, - "passwordType": { - "message": "Tipo de palavra-passe" - }, "service": { "message": "Serviço" }, @@ -3174,7 +3071,7 @@ "message": "Ver todas as opções de início de sessão" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Ver todas as opções de início de sessão" }, "notificationSentDevice": { "message": "Foi enviada uma notificação para o seu dispositivo." @@ -3580,6 +3477,14 @@ "message": "Desbloqueie a sua conta, abre numa nova janela", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Código de verificação de palavra-passe única com base no tempo", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Tempo restante antes da TOTP atual expirar", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Preencher as credenciais para", "description": "Screen reader text for when overlay item is in focused" @@ -3806,7 +3711,7 @@ "message": "Chave de acesso" }, "accessing": { - "message": "A aceder" + "message": "A aceder a" }, "loggedInExclamation": { "message": "Sessão iniciada!" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Mostrar o número de sugestões de preenchimento automático de credenciais no ícone da extensão" }, + "showQuickCopyActions": { + "message": "Mostrar ações de cópia rápida no cofre" + }, "systemDefault": { "message": "Predefinição do sistema" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Aviso importante" + }, + "setupTwoStepLogin": { + "message": "Definir a verificação de dois passos" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "O Bitwarden enviará um código para o e-mail da sua conta para verificar as credenciais de novos dispositivos a partir de fevereiro de 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Pode configurar a verificação de dois passos como forma alternativa de proteger a sua conta ou alterar o seu e-mail para um a que possa aceder." + }, + "remindMeLater": { + "message": "Lembrar-me mais tarde" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Tem um acesso fiável ao seu e-mail, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Não, não tenho" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Sim, consigo aceder de forma fiável ao meu e-mail" + }, + "turnOnTwoStepLogin": { + "message": "Ativar a verificação de dois passos" + }, + "changeAcctEmail": { + "message": "Alterar o e-mail da conta" + }, "extensionWidth": { "message": "Largura da extensão" }, diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 680d22feeb6..58c0a313f69 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autocompletare identitate" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generare parolă (s-a copiat)" }, @@ -447,9 +454,6 @@ "length": { "message": "Lungime" }, - "passwordMinLength": { - "message": "Lungimea minimă a parolei" - }, "uppercase": { "message": "Litere mari (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minim de caractere speciale" }, - "avoidAmbChar": { - "message": "Se evită caracterele ambigue", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Evaluare extensie" }, - "rateExtensionDesc": { - "message": "Vă rugăm să luați în considerare să ne ajutați cu o recenzie bună!" - }, "browserNotSupportClipboard": { "message": "Browserul dvs. nu acceptă copierea în clipboard. Transcrieți datele manual." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Listați elementelor de identitate de pe pagina Filă pentru a facilita completarea automată." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Golire clipboard", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Mutare la organizație" }, - "share": { - "message": "Partajare" - }, "movedItemToOrg": { "message": "$ITEMNAME$ mutat la $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Clonare" }, - "passwordGeneratorPolicyInEffect": { - "message": "Una sau mai multe politici organizaționale vă afectează setările generatorului." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Căutare Send-uri", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Adăugare Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "S-a atins numărul maxim de accesări", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expirat" }, - "pendingDeletion": { - "message": "Ștergere în așteptare" - }, "passwordProtected": { "message": "Protejat cu parolă" }, @@ -2468,24 +2447,9 @@ "message": "Editare Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Ce fel de Send este acesta?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Un nume prietenos pentru a descrie acest Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Fișierul pe care doriți să-l trimiteți." - }, "deletionDate": { "message": "Data ștergerii" }, - "deletionDateDesc": { - "message": "Send-ul va fi șters definitiv la data și ora specificate.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Data expirării" }, - "expirationDateDesc": { - "message": "Dacă este setat, accesul la acest Send va expira la data și ora specificate.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 zi" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Personalizat" }, - "maximumAccessCount": { - "message": "Număr maxim de accesări" - }, - "maximumAccessCountDesc": { - "message": "Dacă este configurat, utilizatorii nu vor mai putea accesa acest Send când a fost atins numărul maxim de accesări.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Opțional, este necesară o parolă pentru ca utilizatorii să acceseze acest Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Note private despre acest Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Dezactivați acest Send pentru ca nimeni să nu-l poată accesa.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copiază acest link de Send în clipboard la salvare.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Textul pe care doriți să-l trimiteți." - }, - "sendHideText": { - "message": "Ascunde în mod implicit textul acestui Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Numărul actual de accesări" - }, "createSend": { "message": "Nou Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Înainte de a începe" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Pentru a utiliza un selector de date în stil calendar,", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "faceți clic aici", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "pentru ca fereastra dvs să apară.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Data de expirare furnizată nu este validă." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "A survenit o eroare la salvarea datelor de ștergere și de expirare." }, - "hideEmail": { - "message": "Ascundeți adresa mea de e-mail de la destinatari." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "Una sau mai multe politici organizaționale vă afectează opțiunile Send-ului." - }, "passwordPrompt": { "message": "Re-solicitare parolă principală" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Eroare" }, - "regenerateUsername": { - "message": "Regenerare nume de utilizator" - }, "generateUsername": { "message": "Generare nume de utilizator" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Tip de nume de utilizator" - }, "plusAddressedEmail": { "message": "E-mail Plus adresat", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Nume website" }, - "whatWouldYouLikeToGenerate": { - "message": "Ce doriți să generați?" - }, - "passwordType": { - "message": "Tip de parolă" - }, "service": { "message": "Serviciu" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 526d77e009d..acdee563ceb 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Автозаполнение личности" }, + "fillVerificationCode": { + "message": "Заполнить код подтверждения" + }, + "fillVerificationCodeAria": { + "message": "Заполнить код подтверждения", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Сгенерировать пароль (с копированием)" }, @@ -447,9 +454,6 @@ "length": { "message": "Длина" }, - "passwordMinLength": { - "message": "Минимальная длина пароля" - }, "uppercase": { "message": "Прописные буквы (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Минимум символов" }, - "avoidAmbChar": { - "message": "Избегать неоднозначных символов", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Избегать неоднозначных символов", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Оценить расширение" }, - "rateExtensionDesc": { - "message": "Пожалуйста, подумайте о том, чтобы помочь нам хорошим отзывом!" - }, "browserNotSupportClipboard": { "message": "Ваш браузер не поддерживает копирование данных в буфер обмена. Скопируйте вручную." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Личности будут отображены на вкладке для удобного автозаполнения." }, + "clickToAutofillOnVault": { + "message": "Кликните элементы для автозаполнения в режиме просмотра хранилища" + }, "clearClipboard": { "message": "Очистить буфер обмена", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Переместить в организацию" }, - "share": { - "message": "Поделиться" - }, "movedItemToOrg": { "message": "$ITEMNAME$ перемещен в $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Клонировать" }, - "passwordGeneratorPolicyInEffect": { - "message": "На настройки генератора влияют одна или несколько политик организации." - }, "passwordGenerator": { "message": "Генератор паролей" }, @@ -2385,14 +2379,6 @@ "message": "Информация о Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Поиск Send’ов", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Добавить Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Текст" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Скрыть текст по умолчанию" }, - "maxAccessCountReached": { - "message": "Достигнут максимум обращений", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Срок истек" }, - "pendingDeletion": { - "message": "Ожидание удаления" - }, "passwordProtected": { "message": "Защищено паролем" }, @@ -2468,24 +2447,9 @@ "message": "Изменить Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Выберите тип Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Понятное имя для описания этой Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Файл, который вы хотите отправить." - }, "deletionDate": { "message": "Дата удаления" }, - "deletionDateDesc": { - "message": "Эта Send будет окончательно удалена в указанные дату и время.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "С этой даты Send будет удалена навсегда.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Дата истечения" }, - "expirationDateDesc": { - "message": "Если задано, доступ к этой Send истечет в указанные дату и время.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 день" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Пользовательский" }, - "maximumAccessCount": { - "message": "Максимум обращений" - }, - "maximumAccessCountDesc": { - "message": "Если задано, пользователи больше не смогут получить доступ к этой Send, как только будет достигнуто максимальное количество обращений.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "По возможности запрашивать у пользователей пароль для доступа к этой Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Добавьте опциональный пароль для доступа получателей к этой Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Личные заметки об этой Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Деактивировать эту Send, чтобы никто не мог получить к ней доступ.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Скопировать ссылку на эту Send в буфер обмена после сохранения.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Текст, который вы хотите отправить." - }, - "sendHideText": { - "message": "Скрыть текст этой Send по умолчанию.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Текущих обращений" - }, "createSend": { "message": "Новая Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Перед тем, как начать" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Для использования календарного стиля выбора даты,", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "нажмите здесь", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "для открытия в новом окне.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Срок истечения указан некорректно." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Произошла ошибка при сохранении данных о сроках удаления и истечения." }, - "hideEmail": { - "message": "Скрыть мой адрес email от получателей." - }, "hideYourEmail": { "message": "Скрыть ваш email от просматривающих." }, - "sendOptionsPolicyInEffect": { - "message": "На параметры Send влияют одна или несколько политик организации." - }, "passwordPrompt": { "message": "Повторный запрос мастер-пароля" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Ошибка" }, - "regenerateUsername": { - "message": "Пересоздать имя пользователя" - }, "generateUsername": { "message": "Создать имя пользователя" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Тип имени пользователя" - }, "plusAddressedEmail": { "message": "Субадресованные email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Название сайта" }, - "whatWouldYouLikeToGenerate": { - "message": "Что вы хотите сгенерировать?" - }, - "passwordType": { - "message": "Тип пароля" - }, "service": { "message": "Служба" }, @@ -3174,7 +3071,7 @@ "message": "Посмотреть все варианты авторизации" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Посмотреть все варианты авторизации" }, "notificationSentDevice": { "message": "На ваше устройство отправлено уведомление." @@ -3580,6 +3477,14 @@ "message": "Разблокируйте ваш аккаунт, откроется в новом окне", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Код подтверждения, основанный на времени", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Время, оставшееся до истечения срока действия текущего TOTP", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Заполнить учетные данные", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Показывать количество вариантов автозаполнения логина на значке расширения" }, + "showQuickCopyActions": { + "message": "Показать быстрые действия копирования в хранилище" + }, "systemDefault": { "message": "Системный" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Важное уведомление" + }, + "setupTwoStepLogin": { + "message": "Настроить двухэтапную аутентификацию" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Начиная с февраля 2025 года Bitwarden будет отправлять код на электронную почту вашего аккаунта для подтверждения авторизации с новых устройств." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "В качестве альтернативного способа защиты учетной записи вы можете настроить двухэтапную аутентификацию или сменить электронную почту на ту, к которой вы можете получить доступ." + }, + "remindMeLater": { + "message": "Напомнить позже" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Есть ли у вас надежный доступ к электронной почте $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Нет, не знаю" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Да, я имею надежный доступ к своей электронной почте" + }, + "turnOnTwoStepLogin": { + "message": "Включить двухэтапную аутентификацию" + }, + "changeAcctEmail": { + "message": "Изменить email аккаунта" + }, "extensionWidth": { "message": "Ширина расширения" }, diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 9ada4ff9281..b25c2fd30d5 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "මුරපදය ජනනය (පිටපත්)" }, @@ -447,9 +454,6 @@ "length": { "message": "දිග" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "අවම විශේෂ" }, - "avoidAmbChar": { - "message": "අපැහැදිලි චරිත වලින් වළකින්න", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "දිගුව අනුපාතය" }, - "rateExtensionDesc": { - "message": "කරුණාකර හොඳ සමාලෝචනයකින් අපට උදව් කිරීම ගැන සලකා බලන්න!" - }, "browserNotSupportClipboard": { "message": "ඔබේ වෙබ් බ්රව්සරය පහසු පසුරු පුවරුවක් පිටපත් කිරීමට සහාය නොදක්වයි. ඒ වෙනුවට එය අතින් පිටපත් කරන්න." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "පසුරු පුවරුවට පැහැදිලි", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "සංවිධානය වෙත ගෙනයන්න" }, - "share": { - "message": "බෙදාගන්න" - }, "movedItemToOrg": { "message": "$ITEMNAME$ $ORGNAME$වෙත ගෙන ගියේය", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "ක්ලෝන" }, - "passwordGeneratorPolicyInEffect": { - "message": "සංවිධාන ප්රතිපත්ති එකක් හෝ වැඩි ගණනක් ඔබේ උත්පාදක සැකසුම් වලට බලපායි." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "සෙවුම් යවයි", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "යවන්න එකතු කරන්න", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "පෙළ" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "මැක්ස් ප්රවේශ ගණන ළඟා", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "කල් ඉකුත්" }, - "pendingDeletion": { - "message": "මකාදැමීම" - }, "passwordProtected": { "message": "මුරපදය ආරක්ෂා" }, @@ -2468,24 +2447,9 @@ "message": "යැවීම සංස්කරණය", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "මෙය කුමන ආකාරයේ යවන්න ද?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "මෙම යවන්න විස්තර කිරීමට මිත්රශීලී නමක්.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "ඔබට යැවීමට අවශ්ය ගොනුව." - }, "deletionDate": { "message": "මකාදැමීමේ දිනය" }, - "deletionDateDesc": { - "message": "නියම කරන ලද දිනය හා වේලාව මත Send ස්ථිරවම මකා දමනු ලැබේ.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "කල් ඉකුත්වන දිනය" }, - "expirationDateDesc": { - "message": "සකසා ඇත්නම්, මෙම යවන්න වෙත ප්රවේශය නිශ්චිත දිනය හා වේලාව කල් ඉකුත් වනු ඇත.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "දින 1" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "අභිරුචි" }, - "maximumAccessCount": { - "message": "උපරිම ප්රවේශ ගණන්" - }, - "maximumAccessCountDesc": { - "message": "සකසා ඇත්නම්, උපරිම ප්රවේශ ගණන ළඟා වූ පසු පරිශීලකයින්ට මෙම Send වෙත ප්රවේශ වීමට තවදුරටත් නොහැකි වනු ඇත.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "විකල්පයක් ලෙස පරිශීලකයින්ට මෙම යවන්න වෙත ප්රවේශ වීමට මුරපදයක් අවශ්ය වේ.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "මේ ගැන පෞද්ගලික සටහන් යවන්න.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "මෙය අක්රීය කරන්න යවන්න එවිට කිසිවෙකුට එයට ප්රවේශ විය නොහැක.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "සුරකින්න මත මෙම යවන්න ගේ සබැඳිය පසුරු පුවරුවට පිටපත් කරන්න.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "ඔබට යැවීමට අවශ්ය පෙළ." - }, - "sendHideText": { - "message": "මෙම යවන්න පෙළ පෙරනිමියෙන් සඟවන්න.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "වත්මන් ප්රවේශ ගණන්" - }, "createSend": { "message": "නව යවන්න නිර්මාණය", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "ඔබ ආරම්භ කිරීමට පෙර" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "දින දර්ශනය ශෛලිය දිනය ජීව අත්බෝම්බයක් සමග භාවිතා කිරීමට", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "මෙහි ක්ලික් කරන්න", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "ඔබේ කවුළුව දිස්වේ.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "ලබා දී ඇති කල් ඉකුත්වන දිනය වලංගු නොවේ." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "ඔබගේ මකාදැමීම සහ කල් ඉකුත් වීමේ දිනයන් ඉතිරි කිරීමේ දෝෂයක් තිබුණි." }, - "hideEmail": { - "message": "ලබන්නන්ගෙන් මගේ විද්යුත් තැපැල් ලිපිනය සඟවන්න." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "සංවිධාන ප්රතිපත්ති එකක් හෝ කිහිපයක් ඔබගේ Send විකල්පයන්ට බලපායි." - }, "passwordPrompt": { "message": "ප්රධාන මුරපදය නැවත විමසුමක්" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index d1374c51cfe..5d11227f003 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Automatické vyplnenie identity" }, + "fillVerificationCode": { + "message": "Vyplniť overovací kód" + }, + "fillVerificationCodeAria": { + "message": "Vyplniť overovací kód", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Vygenerovať heslo (skopírované)" }, @@ -447,9 +454,6 @@ "length": { "message": "Dĺžka" }, - "passwordMinLength": { - "message": "Minimálna dĺžka hesla" - }, "uppercase": { "message": "Veľké písmená (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum špeciálnych znakov" }, - "avoidAmbChar": { - "message": "Vyhnúť sa zameniteľným znakom", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Vyhnúť sa zameniteľným znakom", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Ohodnotiť rozšírenie" }, - "rateExtensionDesc": { - "message": "Prosíme, zvážte napísanie pozitívnej recenzie!" - }, "browserNotSupportClipboard": { "message": "Váš webový prehliadač nepodporuje automatické kopírovanie do schránky. Kopírujte manuálne." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Zoznam položiek identity na stránke \"Aktuálna karta\" na jednoduché automatické vypĺňanie." }, + "clickToAutofillOnVault": { + "message": "Kliknutím na položku v trezore automaticky vyplniť" + }, "clearClipboard": { "message": "Vymazať schránku", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Presunúť do organizácie" }, - "share": { - "message": "Zdieľať" - }, "movedItemToOrg": { "message": "$ITEMNAME$ presunuté do $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Klonovať" }, - "passwordGeneratorPolicyInEffect": { - "message": "Jedno alebo viac nastavení organizácie ovplyvňujú vaše nastavenia generátora." - }, "passwordGenerator": { "message": "Generátor hesla" }, @@ -2385,14 +2379,6 @@ "message": "Podrobnosti o Sende", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Hľadať Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Pridať Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "V predvolenom nastavení skryť text" }, - "maxAccessCountReached": { - "message": "Bol dosiahnutý maximálny počet prístupov", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expirované" }, - "pendingDeletion": { - "message": "Čakajúce odstránenie" - }, "passwordProtected": { "message": "Chránené heslom" }, @@ -2468,24 +2447,9 @@ "message": "Upraviť Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Aký typ Sendu to je?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Priateľský názov pre popísanie tohto Sendu.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Súbor, ktorý chcete odoslať." - }, "deletionDate": { "message": "Dátum odstránenia" }, - "deletionDateDesc": { - "message": "Send bude natrvalo odstránený v zadaný dátum a čas.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Send bude natrvalo odstránený v tento deň.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Dátum exspirácie" }, - "expirationDateDesc": { - "message": "Ak je nastavené, prístup k tomuto Sendu vyprší v zadaný dátum a čas.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 deň" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Vlastné" }, - "maximumAccessCount": { - "message": "Maximálny počet prístupov" - }, - "maximumAccessCountDesc": { - "message": "Ak je nastavené, používatelia už nebudú mať prístup k tomuto Sendu po dosiahnutí maximálneho počtu prístupov.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Voliteľne môžete vyžadovať heslo pre používateľov na prístup k tomuto Sendu.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Pridajte voliteľné heslo pre príjemcov na prístup k tomuto Sendu.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Zabezpečená poznámka o tomto Sende.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Vypnúť tento Send, aby k nemu nikto nemal prístup.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Pri uložení kopírovať odkaz na Send do schránky.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Text, ktorý chcete odoslať." - }, - "sendHideText": { - "message": "Predvolene skryť text tohto Sendu.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Súčasný počet prístupov" - }, "createSend": { "message": "Vytvoriť nový Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Skôr než začnete" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Ak chcete použiť pre výber dátumu štýl kalendára", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "kliknite sem", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "a vysunie sa.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Uvedený dátum exspirácie nie je platný." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Pri ukladaní dátumov odstránenia a vypršania platnosti sa vyskytla chyba." }, - "hideEmail": { - "message": "Skryť moju emailovú adresu pred príjemcami." - }, "hideYourEmail": { "message": "Skryť moju e-mailovú adresu pri zobrazení." }, - "sendOptionsPolicyInEffect": { - "message": "Jedno alebo viac pravidiel organizácie ovplyvňujú vaše možnosti funkcie Send." - }, "passwordPrompt": { "message": "Znova zadajte hlavné heslo" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Chyba" }, - "regenerateUsername": { - "message": "Vygenerovať nové používateľské meno" - }, "generateUsername": { "message": "Vygenerovať používateľské meno" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Typ používateľského mena" - }, "plusAddressedEmail": { "message": "E-mail s plusovým aliasom", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Názov stránky" }, - "whatWouldYouLikeToGenerate": { - "message": "Čo by ste chceli vygenerovať?" - }, - "passwordType": { - "message": "Typ hesla" - }, "service": { "message": "Služba" }, @@ -3174,7 +3071,7 @@ "message": "Zobraziť všetky možnosti prihlásenia" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Zobraziť všetky možnosti prihlásenia" }, "notificationSentDevice": { "message": "Do vášho zariadenia bolo odoslané upozornenie." @@ -3580,6 +3477,14 @@ "message": "Odomknúť konto v novom okne", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Overovací kód TOTP", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Čas zostávajúci do vypršania aktuálneho TOTP", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Vyplňte prihlasovacie údaje pre", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Zobraziť počet odporúčaných prihlasovacích údajov na ikone rozšírenia" }, + "showQuickCopyActions": { + "message": "Zobraziť akcie rýchleho kopírovania v trezore" + }, "systemDefault": { "message": "Predvolené systémom" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Dôležité upozornenie" + }, + "setupTwoStepLogin": { + "message": "Nastavenie dvojstupňového prihlásenia" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden vám od februára 2025 pošle na e-mail vášho účtu kód na overenie prihlásenia z nových zariadení." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Ako alternatívny spôsob ochrany svojho účtu môžete nastaviť dvojstupňové prihlásenie alebo zmeniť e-mail na taký, ku ktorému máte prístup." + }, + "remindMeLater": { + "message": "Pripomenúť neskôr" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Máte spoľahlivý prístup k svojmu e-mailu, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nie, nemám" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Áno, mám spoľahlivý prístup k svojmu e-mailu" + }, + "turnOnTwoStepLogin": { + "message": "Zapnúť dvojstupňové prihlásenie" + }, + "changeAcctEmail": { + "message": "Zmeniť e-mail účtu" + }, "extensionWidth": { "message": "Šírka rozšírenia" }, diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index d155e1b1a76..490f991d252 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Samodejno izpolni identiteto" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generiraj geslo (kopirano)" }, @@ -447,9 +454,6 @@ "length": { "message": "Dolžina" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Velike črke (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimalno posebnih znakov" }, - "avoidAmbChar": { - "message": "Izogibaj se dvoumnim znakom", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Ocenite to razširitev" }, - "rateExtensionDesc": { - "message": "Premislite, ali bi nam želeli pomagati z dobro oceno!" - }, "browserNotSupportClipboard": { "message": "Vaš brskalnik ne podpira enostavnega kopiranja na odložišče. Prosimo, kopirajte ročno." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Na strani Zavihek prikaži elemente identitete za lažje samodejno izpolnjevanje." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Počisti odložišče", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Premakni v organizacijo" }, - "share": { - "message": "Deli" - }, "movedItemToOrg": { "message": "Element $ITEMNAME$ premaknjen v $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Podvoji" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Išči pošiljke", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Dodaj pošiljko", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Besedilo" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Poteklo" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Uredi pošiljko", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Kakšna vrsta pošiljke je to?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Prijazno ime, ki opisuje to pošiljko", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Datoteka, ki jo želite poslati" - }, "deletionDate": { "message": "Datum izbrisa" }, - "deletionDateDesc": { - "message": "Pošiljka bo trajno izbrisana ob izbranem času.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Datum poteka" }, - "expirationDateDesc": { - "message": "Če to nastavite, bo pošiljka potekla ob izbranem času.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 dan" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Po meri" }, - "maximumAccessCount": { - "message": "Največje dovoljeno število dostopov" - }, - "maximumAccessCountDesc": { - "message": "Če to nastavite, uporabniki po določenem številu dostopov ne bodo mogli več dostopati do pošiljke.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Za dostop do te pošiljke lahko nastavite geslo.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Zasebni zapiski o tej pošiljki.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Onemogoči to pošiljko, da nihče ne more dostopati do nje.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Kopiraj povezavo te pošiljke v odložišče, ko shranim.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Besedilo, ki ga želite poslati" - }, - "sendHideText": { - "message": "Privzeto skrij besedilo te pošiljke.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Trenutno število dstopov" - }, "createSend": { "message": "Nova pošiljka", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Preden pričnete" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Za vnos datuma s pomočjo koledarčka", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "kliknite tukaj", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "za prikaz v lastnem oknu.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Datum poteka ni veljaven." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Pri shranjevanju datumov poteka in izbrisa je prišlo do napake." }, - "hideEmail": { - "message": "Skrij moj e-naslov pred prejemniki." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "Nekatere nastavitve organizacije vplivajo na možnosti, ki jih imate v zvezi s pošiljkami." - }, "passwordPrompt": { "message": "Ponovno zahtevaj glavno geslo" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Napaka" }, - "regenerateUsername": { - "message": "Ponovno ustvari uporabniško ime" - }, "generateUsername": { "message": "Ustvari uporabniško ime" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Vrsta uporabniškega imena" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Ime spletne strani" }, - "whatWouldYouLikeToGenerate": { - "message": "Kaj želite generirati?" - }, - "passwordType": { - "message": "Vrsta gesla" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index f17bc4c97eb..1a1ebf69794 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Ауто-пуњење идентитета" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Генериши Лозинку (копирано)" }, @@ -447,9 +454,6 @@ "length": { "message": "Дужина" }, - "passwordMinLength": { - "message": "Минимална дужина лозинке" - }, "uppercase": { "message": "Велика слова (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Минимално специјалних знакова" }, - "avoidAmbChar": { - "message": "Избегавај двосмислене карактере", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Избегавај двосмислене карактере", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Оцени овај додатак" }, - "rateExtensionDesc": { - "message": "Молимо вас да размотрите да нам помогнете уз добру оцену!" - }, "browserNotSupportClipboard": { "message": "Ваш прегледач не подржава једноставно копирање у клипборду. Уместо тога копирајте га ручно." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Прикажи ставке идентитета на страници за лакше аутоматско допуњавање." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Обриши привремену меморију", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Премести у организацију" }, - "share": { - "message": "Подели" - }, "movedItemToOrg": { "message": "$ITEMNAME$ премештен у $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Клонирај" }, - "passwordGeneratorPolicyInEffect": { - "message": "Једна или више смерница организације утичу на поставке вашег генератора." - }, "passwordGenerator": { "message": "Генератор Лозинке" }, @@ -2385,14 +2379,6 @@ "message": "Детаљи Send-а", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Тражи „Send“", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Додај „Send“", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Текст" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Сакриј текст подразумевано" }, - "maxAccessCountReached": { - "message": "Достигнут максималан број приступа", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Истекло" }, - "pendingDeletion": { - "message": "Брисање на чекању" - }, "passwordProtected": { "message": "Заштићено лозинком" }, @@ -2468,24 +2447,9 @@ "message": "Уреди „Send“", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Који је ово тип „Send“-a?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Име да се опише овај „Send“.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Датотека коју желиш да пошаљеш." - }, "deletionDate": { "message": "Брисање после" }, - "deletionDateDesc": { - "message": "„Send“ ће бити трајно избрисан наведеног датума и времена.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Send ће бити трајно обрисано у наведени датум.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Рок употребе" }, - "expirationDateDesc": { - "message": "Ако је постављено, приступ овом „Send“ истиче на наведени датум и време.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 дан" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Друго" }, - "maximumAccessCount": { - "message": "Максималан број приступа" - }, - "maximumAccessCountDesc": { - "message": "Ако је постављено, корисници више неће моћи да приступе овом „Send“ када се достигне максимални број приступа.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Опционално захтевајте лозинку за приступ корисницима „Send“-у.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Додајте опционалну лозинку за примаоце да приступе овом Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Приватне белешке о овом „Send“.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Онемогућите овај „Send“ да нико не би могао да му приступи.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Након чувања, копирај УРЛ за овај „Send“ у привремену меморију.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Текст који желиш да пошаљеш." - }, - "sendHideText": { - "message": "Подразумевано сакриј текст за овај „Send“.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Тренутни број приступа" - }, "createSend": { "message": "Креирај нови „Send“", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Пре него што почнеш" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Да би користио бирање датума кроз календар", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "кликните овде", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "Да бисте приказали искачући прозор.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Наведени датум истека није исправан." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Појавила се грешка при чувању датума брисања и истека." }, - "hideEmail": { - "message": "Сакриј моју е-адресу од примаоца." - }, "hideYourEmail": { "message": "Сакријте свој имејл од гледалаца." }, - "sendOptionsPolicyInEffect": { - "message": "Једна или више смерница организације утичу на опције „Send“-а." - }, "passwordPrompt": { "message": "Поновно тражење главне лозинке" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Грешка" }, - "regenerateUsername": { - "message": "Поново генериши име" - }, "generateUsername": { "message": "Генериши име" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Тип имена" - }, "plusAddressedEmail": { "message": "Плус имејл адресе", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Име Вашег веб-сајта" }, - "whatWouldYouLikeToGenerate": { - "message": "Шта желите да генеришете?" - }, - "passwordType": { - "message": "Тип лозинке" - }, "service": { "message": "Сервис" }, @@ -3580,6 +3477,14 @@ "message": "Откључајте свој налог, отвара се у новом прозору", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Попунити акредитиве за", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Прикажи број предлога за ауто-попуњавање пријаве на икони додатка" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "Системски подразумевано" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Бета" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Ширина додатка" }, diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index dc8c1bed901..84de9bbfa05 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofyll identitet" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Skapa lösenord (kopierad)" }, @@ -447,9 +454,6 @@ "length": { "message": "Längd" }, - "passwordMinLength": { - "message": "Minsta tillåtna lösenordslängd" - }, "uppercase": { "message": "Versaler (A-Ö)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minsta antal speciella tecken" }, - "avoidAmbChar": { - "message": "Undvik tvetydiga tecken", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Undvik tvetydiga tecken", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Betygsätt tillägget" }, - "rateExtensionDesc": { - "message": "Överväg gärna att skriva en recension om oss!" - }, "browserNotSupportClipboard": { "message": "Din webbläsare har inte stöd för att enkelt kopiera till urklipp. Kopiera till urklipp manuellt istället." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Lista identitetsobjekt på fliksidan för enkel automatisk fyllning." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Rensa urklipp", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Flytta till organisation" }, - "share": { - "message": "Dela" - }, "movedItemToOrg": { "message": "$ITEMNAME$ flyttades till $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Klona" }, - "passwordGeneratorPolicyInEffect": { - "message": "En eller flera organisationspolicyer påverkar dina generatorinställningar." - }, "passwordGenerator": { "message": "Lösenordsgenerator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Sök bland Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Lägg till Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Det maximala antalet åtkomster har uppnåtts", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Utgången" }, - "pendingDeletion": { - "message": "Väntar på radering" - }, "passwordProtected": { "message": "Lösenordsskyddad" }, @@ -2468,24 +2447,9 @@ "message": "Redigera Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Vilken typ av Send är detta?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Ett vänligt namn som beskriver denna Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Filen du vill skicka." - }, "deletionDate": { "message": "Raderingsdatum" }, - "deletionDateDesc": { - "message": "Denna Send kommer att raderas permanent på angivet datum och klockslag.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Utgångsdatum" }, - "expirationDateDesc": { - "message": "Om angiven kommer åtkomst till denna Send att upphöra på angivet datum och tid.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 dag" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Anpassad" }, - "maximumAccessCount": { - "message": "Maximalt antal åtkomster" - }, - "maximumAccessCountDesc": { - "message": "Om angivet kommer användare inte längre kunna komma åt denna Send när det maximala antalet åtkomster har uppnåtts.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Kräv valfritt ett lösenord för att användare ska komma åt denna Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Privata anteckningar om denna Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Inaktivera denna Send så att ingen kan komma åt den.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Kopiera länken till denna Send vid sparande.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Texten du vill skicka." - }, - "sendHideText": { - "message": "Dölj texten för denna Send som standard", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Nuvarande antal åtkomster" - }, "createSend": { "message": "Ny Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Innan du börjar" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "För att använda en datumväljare med kalenderstil", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "klicka här", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "för att öppna ett nytt fönster.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Det angivna utgångsdatumet är inte giltigt." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Det gick inte att spara raderings- och utgångsdatum." }, - "hideEmail": { - "message": "Dölj min e-postadress för mottagare." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "En eller flera organisationsriktlinjer påverkar dina Send-inställningar." - }, "passwordPrompt": { "message": "Återupprepa huvudlösenord" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Fel" }, - "regenerateUsername": { - "message": "Återskapa användarnamn" - }, "generateUsername": { "message": "Generera användarnamn" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Typ av användarnamn" - }, "plusAddressedEmail": { "message": "Plusadresserad e-post", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Webbplatsnamn" }, - "whatWouldYouLikeToGenerate": { - "message": "Vad skulle du vilja generera?" - }, - "passwordType": { - "message": "Lösenordstyp" - }, "service": { "message": "Tjänst" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fyll i uppgifter för", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "Systemstandard" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 6aa17c1d7e3..6ab3755c8f4 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generate password (copied)" }, @@ -447,9 +454,6 @@ "length": { "message": "Length" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "Uppercase (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum special" }, - "avoidAmbChar": { - "message": "Avoid ambiguous characters", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Rate the extension" }, - "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" - }, "browserNotSupportClipboard": { "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "List identity items on the Tab page for easy autofill." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Clear clipboard", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Move to organization" }, - "share": { - "message": "Share" - }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Clone" }, - "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Search Sends", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Add Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Text" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expired" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "Edit Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Expiration date" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 day" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Custom" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "The text you want to send." - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 517ab17a489..d9ff1c3f076 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Autofill identity" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Generate Password (copied)" }, @@ -447,9 +454,6 @@ "length": { "message": "ความยาว" }, - "passwordMinLength": { - "message": "Minimum password length" - }, "uppercase": { "message": "ตัวพิมพ์ใหญ่ (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Minimum Special" }, - "avoidAmbChar": { - "message": "Avoid Ambiguous Characters", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Rate the Extension" }, - "rateExtensionDesc": { - "message": "โปรดพิจารณา ช่วยเราด้วยการตรวจสอบที่ดี!" - }, "browserNotSupportClipboard": { "message": "เว็บเบราว์เซอร์ของคุณไม่รองรับการคัดลอกคลิปบอร์ดอย่างง่าย คัดลอกด้วยตนเองแทน" }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "แสดงรายการข้อมูลประจำตัวในหน้าแท็บเพื่อให้ป้อนอัตโนมัติได้ง่าย" }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "ล้างคลิปบอร์ด", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "ย้ายไปยังแบบองค์กร" }, - "share": { - "message": "แชร์" - }, "movedItemToOrg": { "message": "ย้าย $ITEMNAME$ ไปยัง $ORGNAME$ แล้ว", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "โคลน" }, - "passwordGeneratorPolicyInEffect": { - "message": "นโยบายองค์กรอย่างน้อยหนึ่งนโยบายส่งผลต่อการตั้งค่าตัวสร้างของคุณ" - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "ค้นหาใน Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "เพิ่ม Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "ข้อความ" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Max access count reached", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Expired" }, - "pendingDeletion": { - "message": "Pending deletion" - }, "passwordProtected": { "message": "Password protected" }, @@ -2468,24 +2447,9 @@ "message": "แก้ไข Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "What type of Send is this?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "A friendly name to describe this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "The file you want to send." - }, "deletionDate": { "message": "Deletion date" }, - "deletionDateDesc": { - "message": "The Send will be permanently deleted on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Expiration date" }, - "expirationDateDesc": { - "message": "If set, access to this Send will expire on the specified date and time.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 วัน" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Custom" }, - "maximumAccessCount": { - "message": "Maximum Access Count" - }, - "maximumAccessCountDesc": { - "message": "If set, users will no longer be able to access this Send once the maximum access count is reached.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Optionally require a password for users to access this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Private notes about this Send.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Deactivate this Send so that no one can access it.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Copy this Send's link to clipboard upon save.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "ข้อความที่คุณต้องการส่ง" - }, - "sendHideText": { - "message": "Hide this Send's text by default.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Current access count" - }, "createSend": { "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Before you start" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "To use a calendar style date picker", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "click here", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "to pop out your window.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "The expiration date provided is not valid." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "There was an error saving your deletion and expiration dates." }, - "hideEmail": { - "message": "Hide my email address from recipients." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." - }, "passwordPrompt": { "message": "Master password re-prompt" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Error" }, - "regenerateUsername": { - "message": "Regenerate username" - }, "generateUsername": { "message": "Generate username" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Username type" - }, "plusAddressedEmail": { "message": "Plus addressed email", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Website name" }, - "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" - }, - "passwordType": { - "message": "Password type" - }, "service": { "message": "Service" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Fill credentials for", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index 496ce4eac76..a4c064e0394 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Kimliği otomatik doldur" }, + "fillVerificationCode": { + "message": "Doğrulama kodunu doldur" + }, + "fillVerificationCodeAria": { + "message": "Doğrulama kodunu doldur", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Parola oluştur (ve kopyala)" }, @@ -414,10 +421,10 @@ "message": "Son eşitleme:" }, "passGen": { - "message": "Parola üretici" + "message": "Parola üreteci" }, "generator": { - "message": "Oluşturucu", + "message": "Üreteç", "description": "Short for 'credential generator'." }, "passGenInfo": { @@ -447,9 +454,6 @@ "length": { "message": "Uzunluk" }, - "passwordMinLength": { - "message": "Minimum parola uzunluğu" - }, "uppercase": { "message": "Büyük harf (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "En az özel karakter" }, - "avoidAmbChar": { - "message": "Okurken karışabilecek karakterleri kullanma", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Okurken karışabilecek karakterleri kullanma", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Uzantıyı değerlendirin" }, - "rateExtensionDesc": { - "message": "İyi bir yorum yazarak bizi destekleyebilirsiniz." - }, "browserNotSupportClipboard": { "message": "Web tarayıcınız panoya kopyalamayı desteklemiyor. Parolayı elle kopyalayın." }, @@ -837,7 +834,7 @@ "message": "Bitwarden can store and fill 2-step verification codes. Select the camera icon to take a screenshot of this website's authenticator QR code, or copy and paste the key into this field." }, "learnMoreAboutAuthenticators": { - "message": "Learn more about authenticators" + "message": "Kimlik doğrulayıcılar hakkında bilgi alın" }, "copyTOTP": { "message": "Kimlik doğrulama anahtarını kopyala (TOTP)" @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Kolay otomatik doldurma için sekme sayfasında kimlikleri listele." }, + "clickToAutofillOnVault": { + "message": "Kasa görünümünde otomatik doldurmak istediğiniz kayıtlara tıklayın" + }, "clearClipboard": { "message": "Panoyu temizle", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Kuruluşa taşı" }, - "share": { - "message": "Paylaş" - }, "movedItemToOrg": { "message": "$ITEMNAME$ $ORGNAME$ kuruluşuna taşındı", "placeholders": { @@ -1320,10 +1317,10 @@ "message": "Kimlik doğrulama uygulamanızdaki 6 haneli doğrulama kodunu girin." }, "authenticationTimeout": { - "message": "Authentication timeout" + "message": "Kimlik doğrulama zaman aşımı" }, "authenticationSessionTimedOut": { - "message": "The authentication session timed out. Please restart the login process." + "message": "Kimlik doğrulama oturumu zaman aşımına uğradı. Lütfen giriş sürecini yeniden başlatın." }, "enterVerificationCodeEmail": { "message": "$EMAIL$ adresine e-postayla gönderdiğimiz 6 haneli doğrulama kodunu girin.", @@ -1428,7 +1425,7 @@ "message": "Specify the base URL of your on-premises hosted Bitwarden installation. Example: https://bitwarden.company.com" }, "selfHostedCustomEnvHeader": { - "message": "For advanced configuration, you can specify the base URL of each service independently." + "message": "İleri düzey yapılandırma için her hizmetin taban URL'sini bağımsız olarak belirleyebilirsiniz." }, "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." @@ -2043,9 +2040,6 @@ "clone": { "message": "Klonla" }, - "passwordGeneratorPolicyInEffect": { - "message": "Bir ya da daha fazla kuruluş ilkesi, oluşturucu ayarlarınızı etkiliyor." - }, "passwordGenerator": { "message": "Parola üreteci" }, @@ -2385,14 +2379,6 @@ "message": "Send ayrıntıları", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Send'lerde ara", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Send ekle", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Metin" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Metni varsayılan olarak gizle" }, - "maxAccessCountReached": { - "message": "Maksimum erişim sayısına ulaşıldı", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Süresi dolmuş" }, - "pendingDeletion": { - "message": "Silinmesi bekleniyor" - }, "passwordProtected": { "message": "Parola korumalı" }, @@ -2468,24 +2447,9 @@ "message": "Send'i düzenle", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Bu ne tür bir Send?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Bu Send'i açıklayan anlaşılır bir ad", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Göndermek istediğiniz dosya." - }, "deletionDate": { "message": "Silinme tarihi" }, - "deletionDateDesc": { - "message": "Bu Send belirtilen tarih ve saatte kalıcı olacak silinecek.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Bu Send belirtilen tarihte kalıcı olacak silinecek.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Son kullanma tarihi" }, - "expirationDateDesc": { - "message": "Bunu ayarlarsanız belirtilen tarih ve saatten sonra bu Send'e erişilemeyecektir.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 gün" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Özel" }, - "maximumAccessCount": { - "message": "Maksimum erişim sayısı" - }, - "maximumAccessCountDesc": { - "message": "Bunu ayarlarsanız maksimum erişim sayısına ulaşıldıktan sonra bu Send'e erişilemeyecektir.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Kullanıcıların bu Send'e erişmek için parola girmelerini isteyebilirsiniz.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Alıcıların bu Send'e erişmesi için isterseniz parola ekleyebilirsiniz.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Bu Send ile ilgili özel notlar.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Kimsenin erişememesi için bu Send'i devre dışı bırak.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Kaydettikten sonra bu Send'in linkini panoya kopyala.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Göndermek istediğiniz metin." - }, - "sendHideText": { - "message": "Bu Send'in metnini varsayılan olarak gizle.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Mevcut erişim sayısı" - }, "createSend": { "message": "Yeni Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Başlamadan önce" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Takvim tarzı tarih seçiyi kullanmak için", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "buraya tıklayarak", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "yeni bir pencere açın.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Belirtilen son kullanma tarihi geçersiz." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Silinme ve son kullanma tarihleriniz kaydedilirken bir hata oluştu." }, - "hideEmail": { - "message": "E-posta adresimi alıcılardan gizle." - }, "hideYourEmail": { "message": "E-posta adresimi Send'i görüntüleyenlerden gizle." }, - "sendOptionsPolicyInEffect": { - "message": "Bir veya daha fazla kuruluş ilkesi Send seçeneklerinizi etkiliyor." - }, "passwordPrompt": { "message": "Ana parolayı yeniden iste" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Hata" }, - "regenerateUsername": { - "message": "Kullanıcı adını yeniden oluştur" - }, "generateUsername": { "message": "Kullanıcı adı oluştur" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Kullanıcı adı türü" - }, "plusAddressedEmail": { "message": "Artı adresli e-posta", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Web sitesi adı" }, - "whatWouldYouLikeToGenerate": { - "message": "Ne oluşturmak istersiniz?" - }, - "passwordType": { - "message": "Parola türü" - }, "service": { "message": "Servis" }, @@ -3174,7 +3071,7 @@ "message": "Tüm giriş seçeneklerini gör" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Tüm giriş seçeneklerini gör" }, "notificationSentDevice": { "message": "Cihazınıza bir bildirim gönderildi." @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Zamana dayalı tek seferlik parola doğrulama kodu", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Geçerli TOTP için kalan süre", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Bilgileri doldur", "description": "Screen reader text for when overlay item is in focused" @@ -4303,13 +4208,13 @@ "message": "Filtreler" }, "filterVault": { - "message": "Filter vault" + "message": "Kasayı filtrele" }, "filterApplied": { - "message": "One filter applied" + "message": "1 filtre uygulandı" }, "filterAppliedPlural": { - "message": "$COUNT$ filters applied", + "message": "$COUNT$ filtre uygulandı", "placeholders": { "count": { "content": "$1", @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Otomatik öneri sayısını uzantı simgesinde göster" }, + "showQuickCopyActions": { + "message": "Kasada hızlı kopyalama komutlarını göster" + }, "systemDefault": { "message": "Sistem varsayılanı" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Önemli uyarı" + }, + "setupTwoStepLogin": { + "message": "İki adımlı girişi ayarla" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Şubat 2025 itibarıyla Bitwarden, yeni cihazlardan yeni girişleri doğrulamanız için e-posta adresinize bir kod gönderecektir." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Hesabınızı korumanın alternatif bir yolu olarak iki adımlı girişi etkinleştirebilirsiniz. Aksi halde e-posta adresinizin doğru olduğundan emin olmalısınız." + }, + "remindMeLater": { + "message": "Daha sonra hatırlat" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "$EMAIL$ adresinize sağlıklı bir şekilde erişebiliyor musunuz?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Hayır, erişemiyorum" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Evet, e-postalarıma sağlıklı bir şekilde erişebiliyorum" + }, + "turnOnTwoStepLogin": { + "message": "İki adımlı girişi etkinleştir" + }, + "changeAcctEmail": { + "message": "Hesap e-postasını değiştir" + }, "extensionWidth": { "message": "Uzantı genişliği" }, diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index d9a1c11bed5..c817b55dab3 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Автозаповнення посвідчень" }, + "fillVerificationCode": { + "message": "Заповнити код підтвердження" + }, + "fillVerificationCodeAria": { + "message": "Заповнити код підтвердження", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Генерувати пароль (з копіюванням)" }, @@ -447,9 +454,6 @@ "length": { "message": "Довжина" }, - "passwordMinLength": { - "message": "Мінімальна довжина пароля" - }, "uppercase": { "message": "Верхній регістр (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Мінімум спеціальних символів" }, - "avoidAmbChar": { - "message": "Уникати неоднозначних символів", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Уникати неоднозначних символів", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Оцінити розширення" }, - "rateExtensionDesc": { - "message": "Розкажіть іншим про свої враження, залишивши хороший відгук!" - }, "browserNotSupportClipboard": { "message": "Ваш браузер не підтримує копіювання даних в буфер обміну. Скопіюйте вручну." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Показувати список посвідчень на сторінці вкладки для легкого автозаповнення." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Очистити буфер обміну", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Перемістити до організації" }, - "share": { - "message": "Поділитися" - }, "movedItemToOrg": { "message": "$ITEMNAME$ переміщено до $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Клонувати" }, - "passwordGeneratorPolicyInEffect": { - "message": "На параметри генератора впливають одна чи декілька політик організації." - }, "passwordGenerator": { "message": "Генератор паролів" }, @@ -2385,14 +2379,6 @@ "message": "Надіслати подробиці", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Шукати відправлення", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Додати відправлення", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Текст" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Типово приховувати текст" }, - "maxAccessCountReached": { - "message": "Досягнуто максимальної кількості доступів", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Термін дії завершився" }, - "pendingDeletion": { - "message": "Очікується видалення" - }, "passwordProtected": { "message": "Захищено паролем" }, @@ -2468,24 +2447,9 @@ "message": "Редагування", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Який це тип відправлення?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Опис цього відправлення.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Файл, який ви хочете відправити." - }, "deletionDate": { "message": "Термін дії" }, - "deletionDateDesc": { - "message": "Відправлення буде остаточно видалено у вказаний час.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "Відправлення буде остаточно видалено у вказану дату.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Дата завершення" }, - "expirationDateDesc": { - "message": "Якщо встановлено, термін дії цього відправлення завершиться у вказаний час.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 день" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Власний" }, - "maximumAccessCount": { - "message": "Максимальна кількість доступів" - }, - "maximumAccessCountDesc": { - "message": "Якщо встановлено, користувачі більше не зможуть отримати доступ до цього відправлення після досягнення максимальної кількості доступів.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Ви можете встановити пароль для доступу до цього відправлення.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "За бажання додайте пароль для отримувачів цього відправлення.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Особисті нотатки про це відправлення.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Деактивувати це відправлення для скасування доступу до нього.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Копіювати посилання цього відправлення перед збереженням.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Текст, який ви хочете відправити." - }, - "sendHideText": { - "message": "Приховувати текст цього відправлення.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Поточна кількість доступів" - }, "createSend": { "message": "Нове відправлення", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Перед початком" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Щоб використовувати календарний стиль вибору дати", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "натисніть тут", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "щоб відкріпити ваше вікно.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Вказано недійсний термін дії." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "При збереженні дат видалення і терміну дії виникла помилка." }, - "hideEmail": { - "message": "Приховувати мою адресу електронної пошти від отримувачів." - }, "hideYourEmail": { "message": "Приховати адресу е-пошти від отримувачів." }, - "sendOptionsPolicyInEffect": { - "message": "На параметри відправлень впливають одна чи декілька політик організації." - }, "passwordPrompt": { "message": "Повторний запит головного пароля" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Помилка" }, - "regenerateUsername": { - "message": "Повторно генерувати ім'я користувача" - }, "generateUsername": { "message": "Генерувати ім'я користувача" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Тип імені користувача" - }, "plusAddressedEmail": { "message": "Адреса е-пошти з плюсом", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Назва вебсайту" }, - "whatWouldYouLikeToGenerate": { - "message": "Що ви бажаєте згенерувати?" - }, - "passwordType": { - "message": "Тип пароля" - }, "service": { "message": "Послуга" }, @@ -3174,7 +3071,7 @@ "message": "Переглянути всі варіанти входу" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "Переглянути всі варіанти входу" }, "notificationSentDevice": { "message": "Сповіщення було надіслано на ваш пристрій." @@ -3580,6 +3477,14 @@ "message": "Розблокування облікового запису – відкриється нове вікно", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Код підтвердження одноразового пароля", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Час, що залишився до завершення чинного TOTP", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Заповнити облікові дані для", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Показувати кількість пропозицій автозаповнення на піктограмі розширення" }, + "showQuickCopyActions": { + "message": "Показати дії швидкого копіювання у сховищі" + }, "systemDefault": { "message": "Типово (система)" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Бета" }, + "importantNotice": { + "message": "Важлива інформація" + }, + "setupTwoStepLogin": { + "message": "Налаштувати двоетапну перевірку" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden надсилатиме код підтвердження на електронну пошту вашого облікового запису під час входу з нових пристроїв, починаючи з лютого 2025 року." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Ви можете налаштувати двоетапну перевірку як альтернативний спосіб захисту свого облікового запису, або змінити електронну пошту на таку, до якої ви маєте доступ." + }, + "remindMeLater": { + "message": "Нагадати пізніше" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Ви маєте постійний доступ до своєї електронної пошти $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Ні, не маю" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Так, я маю постійний доступ до своєї електронної пошти" + }, + "turnOnTwoStepLogin": { + "message": "Увімкнути двоетапну перевірку" + }, + "changeAcctEmail": { + "message": "Змінити адресу е-пошти" + }, "extensionWidth": { "message": "Ширина вікна розширення" }, diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 317a2599be1..268b12a6254 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "Tự động điền danh tính" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "Tạo mật khẩu (đã sao chép)" }, @@ -447,9 +454,6 @@ "length": { "message": "Độ dài" }, - "passwordMinLength": { - "message": "Độ dài mật khẩu tối thiểu" - }, "uppercase": { "message": "Chữ in hoa (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "Số kí tự đặc biệt tối thiểu" }, - "avoidAmbChar": { - "message": "Tránh các ký tự không rõ ràng", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "Đánh giá tiện ích mở rộng" }, - "rateExtensionDesc": { - "message": "Xin hãy nhìn nhận và đánh giá tốt cho chúng tôi!" - }, "browserNotSupportClipboard": { "message": "Trình duyệt web của bạn không hỗ trợ dễ dàng sao chép bộ nhớ tạm. Bạn có thể sao chép nó theo cách thủ công để thay thế." }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "Liệt kê các mục danh tính trên trang Tab để dễ dàng tự động điền." }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "Dọn dẹp khay nhớ tạm", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "Di chuyển đến tổ chức" }, - "share": { - "message": "Chia sẻ" - }, "movedItemToOrg": { "message": "$ITEMNAME$ đã được di chuyển đến $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "Tạo bản sao" }, - "passwordGeneratorPolicyInEffect": { - "message": "Các chính sách của tổ chức đang ảnh hưởng đến cài đặt tạo mật khẩu của bạn." - }, "passwordGenerator": { "message": "Trình tạo mật khẩu" }, @@ -2385,14 +2379,6 @@ "message": "Send details", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "Tìm kiếm mục Gửi", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "Thêm mục Gửi", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "Văn bản" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "Hide text by default" }, - "maxAccessCountReached": { - "message": "Đã vượt số lần truy cập tối đa", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "Đã hết hạn" }, - "pendingDeletion": { - "message": "Đang chờ xóa" - }, "passwordProtected": { "message": "Mật khẩu đã được bảo vệ" }, @@ -2468,24 +2447,9 @@ "message": "Sửa mục Gửi", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "Đây là loại Send gì?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "Một cái tên thân thiện để mô tả về Send này.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "Tập tin bạn muốn gửi." - }, "deletionDate": { "message": "Ngày xóa" }, - "deletionDateDesc": { - "message": "Mục Gửi sẽ được xóa vĩnh viễn vào ngày và giờ chỉ định.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "The Send will be permanently deleted on this date.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "Ngày hết hạn" }, - "expirationDateDesc": { - "message": "Nếu được thiết lập, mục Gửi này sẽ hết hạn vào ngày và giờ được chỉ định.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 ngày" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "Tùy chỉnh" }, - "maximumAccessCount": { - "message": "Số lượng truy cập tối đa" - }, - "maximumAccessCountDesc": { - "message": "Nếu được thiết lập, khi đã đạt tới số lượng truy cập tối đa, người dùng sẽ không thể truy cập mục Gửi này nữa.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "Yêu cầu nhập mật khẩu khi người dùng truy cập vào phần Gửi này.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "Add an optional password for recipients to access this Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "Ghi chú riêng tư về Send này.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "Vô hiệu hoá mục Gửi này để không ai có thể truy cập nó.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "Sao chép liên kết của Send này vào khay nhớ tạm khi lưu.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "Văn bản bạn muốn gửi." - }, - "sendHideText": { - "message": "Ẩn văn bản của Send này theo mặc định.", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "Số lượng truy cập hiện tại" - }, "createSend": { "message": "Send mới", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "Trước khi bạn bắt đầu" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "Để dùng bộ chọn ngày dạng lịch", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "nhấn vào đây", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "để bật cửa sổ của bạn ra.", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "Ngày hết hạn bạn nhập không hợp lệ." }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "Đã xảy ra lỗi khi lưu ngày xoá và ngày hết hạn của bạn." }, - "hideEmail": { - "message": "Ẩn địa chỉ email của tôi khỏi người nhận." - }, "hideYourEmail": { "message": "Hide your email address from viewers." }, - "sendOptionsPolicyInEffect": { - "message": "Các chính sách của tổ chức đang ảnh hưởng đến tùy chọn Gửi của bạn." - }, "passwordPrompt": { "message": "Nhắc lại mật khẩu chính" }, @@ -2880,9 +2789,6 @@ "error": { "message": "Lỗi" }, - "regenerateUsername": { - "message": "Tạo lại tên người dùng" - }, "generateUsername": { "message": "Tạo tên người dùng" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "Loại tên người dùng" - }, "plusAddressedEmail": { "message": "Địa chỉ email có hậu tố", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "Tên website" }, - "whatWouldYouLikeToGenerate": { - "message": "Bạn muốn tạo gì?" - }, - "passwordType": { - "message": "Loại mật khẩu" - }, "service": { "message": "Dịch vụ" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "Điền thông tin đăng nhập cho", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Thông báo quan trọng" + }, + "setupTwoStepLogin": { + "message": "Thiết lập đăng nhập hai bước" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Nhắc sau" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Không, tôi không có" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Có, tôi có quyền truy cập email này" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Đổi email tài khoản" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index fb7f7dd7bdc..39a945360f8 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -50,7 +50,7 @@ "message": "提交" }, "emailAddress": { - "message": "电子邮件地址" + "message": "电子邮箱地址" }, "masterPass": { "message": "主密码" @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "自动填充身份" }, + "fillVerificationCode": { + "message": "填写验证码" + }, + "fillVerificationCodeAria": { + "message": "填写验证码", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "生成密码(并复制)" }, @@ -232,7 +239,7 @@ "message": "添加项目" }, "accountEmail": { - "message": "账户邮件地址" + "message": "账户电子邮箱" }, "requestHint": { "message": "请求提示" @@ -241,13 +248,13 @@ "message": "请求密码提示" }, "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { - "message": "输入您的账户电子邮件地址,您的密码提示将发送给您" + "message": "输入您的账户电子邮箱地址,您的密码提示将发送给您" }, "passwordHint": { "message": "密码提示" }, "enterEmailToGetHint": { - "message": "请输入您账户的电子邮件地址来接收主密码提示。" + "message": "请输入您的账户电子邮箱地址来接收主密码提示。" }, "getMasterPasswordHint": { "message": "获取主密码提示" @@ -345,7 +352,7 @@ "message": "免费 Bitwarden 家庭" }, "freeBitwardenFamiliesPageDesc": { - "message": "您有资格获得免费的 Bitwarden 家庭。立即在网页应用中兑换此优惠。" + "message": "您有资格获得免费的 Bitwarden 家庭。立即在网页 App 中兑换此优惠。" }, "version": { "message": "版本" @@ -372,7 +379,7 @@ "message": "文件夹名称" }, "folderHintText": { - "message": "通过在父文件夹名后面跟随一个「/」来嵌套文件夹。例如:Social/Forums" + "message": "通过在父文件夹名后面跟随「/」来嵌套文件夹。示例:Social/Forums" }, "noFoldersAdded": { "message": "未添加文件夹" @@ -447,9 +454,6 @@ "length": { "message": "长度" }, - "passwordMinLength": { - "message": "最小密码长度" - }, "uppercase": { "message": "大写 (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "符号最少个数" }, - "avoidAmbChar": { - "message": "避免易混淆的字符", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "避免易混淆的字符", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "为本扩展打分" }, - "rateExtensionDesc": { - "message": "请给我们好评!" - }, "browserNotSupportClipboard": { "message": "您的浏览器不支持剪贴板简单复制,请手动复制。" }, @@ -754,10 +751,10 @@ "message": "发生了一个错误" }, "emailRequired": { - "message": "必须填写电子邮件地址。" + "message": "必须填写电子邮箱地址。" }, "invalidEmail": { - "message": "无效的电子邮件地址。" + "message": "无效的电子邮箱地址。" }, "masterPasswordRequired": { "message": "必须填写主密码。" @@ -794,7 +791,7 @@ "message": "您可以关闭此窗口" }, "masterPassSent": { - "message": "我们已经为您发送了包含主密码提示的邮件。" + "message": "我们已经为您发送了包含主密码提示的电子邮件。" }, "verificationCodeRequired": { "message": "必须填写验证码。" @@ -816,7 +813,7 @@ } }, "autofillError": { - "message": "无法在此页面上自动填充所选项目。请改为手动复制并粘贴。" + "message": "无法在此页面上自动填充所选项目。请手动复制并粘贴。" }, "totpCaptureError": { "message": "无法从当前网页扫描二维码" @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "在标签页上列出身份项目,以便于自动填充。" }, + "clickToAutofillOnVault": { + "message": "在密码库视图中点击项目以自动填充" + }, "clearClipboard": { "message": "清空剪贴板", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "移动到组织" }, - "share": { - "message": "共享" - }, "movedItemToOrg": { "message": "$ITEMNAME$ 已移动到 $ORGNAME$", "placeholders": { @@ -1326,7 +1323,7 @@ "message": "身份验证会话超时。请重新启动登录过程。" }, "enterVerificationCodeEmail": { - "message": "请输入发送给电子邮件 $EMAIL$ 的 6 位数验证码。", + "message": "请输入发送给 $EMAIL$ 的 6 位数验证码。", "placeholders": { "email": { "content": "$1", @@ -1403,7 +1400,7 @@ "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { - "message": "为您的组织使用 Duo Security 的 Duo 移动应用、短信、电话或 U2F 安全钥匙来进行验证。", + "message": "为您的组织使用 Duo Security 的 Duo 移动 App、短信、电话或 U2F 安全钥匙来进行验证。", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "webAuthnTitle": { @@ -1413,7 +1410,7 @@ "message": "使用任何 WebAuthn 兼容的安全钥匙访问您的帐户。" }, "emailTitle": { - "message": "电子邮件" + "message": "电子邮箱" }, "emailDescV2": { "message": "输入发送到您的电子邮箱的代码。" @@ -1431,7 +1428,7 @@ "message": "对于高级配置,您可以单独指定每个服务的基础 URL。" }, "selfHostedEnvFormInvalid": { - "message": "您必须添加基础服务器 URL 或至少添加一个自定义环境。" + "message": "您必须添加基础服务器 URL 或至少一个自定义环境。" }, "customEnvironment": { "message": "自定义环境" @@ -1723,7 +1720,7 @@ "message": "许可证号码" }, "email": { - "message": "电子邮件" + "message": "电子邮箱" }, "phone": { "message": "电话" @@ -2043,9 +2040,6 @@ "clone": { "message": "克隆" }, - "passwordGeneratorPolicyInEffect": { - "message": "一个或多个组织策略正在影响您的生成器设置。" - }, "passwordGenerator": { "message": "密码生成器" }, @@ -2201,13 +2195,13 @@ "message": "取消订阅" }, "atAnyTime": { - "message": "随时" + "message": "随时。" }, "byContinuingYouAgreeToThe": { "message": "若继续,代表您同意" }, "and": { - "message": "以及" + "message": "和" }, "acceptPolicies": { "message": "选中此框表示您同意:" @@ -2364,7 +2358,7 @@ "message": "限制查看" }, "limitSendViewsHint": { - "message": "在达到限额后,任何人无法查看此 Send。", + "message": "达到限额后,任何人无法查看此 Send。", "description": "Displayed under the limit views field on Send" }, "limitSendViewsCount": { @@ -2385,14 +2379,6 @@ "message": "Send 详细信息", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "搜索 Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "添加 Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "文本" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "默认隐藏文本" }, - "maxAccessCountReached": { - "message": "已达最大访问次数", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "已过期" }, - "pendingDeletion": { - "message": "等待删除" - }, "passwordProtected": { "message": "密码保护" }, @@ -2468,24 +2447,9 @@ "message": "编辑 Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "这是什么类型的 Send?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "用于描述此 Send 的友好名称。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "您想要发送的文件。" - }, "deletionDate": { "message": "删除日期" }, - "deletionDateDesc": { - "message": "此 Send 将在指定的日期和时间后被永久删除。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "此 Send 将在此日期后被永久删除。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "过期日期" }, - "expirationDateDesc": { - "message": "设置后,对此 Send 的访问将在指定的日期和时间后过期。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 天" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "自定义" }, - "maximumAccessCount": { - "message": "最大访问次数" - }, - "maximumAccessCountDesc": { - "message": "设置后,当达到最大访问次数时用户将不再能访问此 Send。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "可选,用户需要提供密码才能访问此 Send。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "添加一个用于收件人访问此 Send 的可选密码。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "关于此 Send 的私密备注。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "停用此 Send 则任何人无法访问它。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "保存时复制此 Send 的链接到剪贴板。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "您想要发送的文本。" - }, - "sendHideText": { - "message": "默认隐藏此 Send 的文本。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "当前访问次数" - }, "createSend": { "message": "创建 Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "在开始之前" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "要使用日历样式的日期选择器", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "点击这里", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "来弹出窗口。", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "所提供的过期日期无效。" }, @@ -2658,14 +2573,8 @@ "dateParsingError": { "message": "保存您的删除和过期日期时出错。" }, - "hideEmail": { - "message": "对收件人隐藏我的电子邮件地址。" - }, "hideYourEmail": { - "message": "对查看者隐藏您的电子邮件地址。" - }, - "sendOptionsPolicyInEffect": { - "message": "一个或多个组织策略正在影响您的 Send 选项。" + "message": "对查看者隐藏您的电子邮箱地址。" }, "passwordPrompt": { "message": "主密码重新提示" @@ -2677,13 +2586,13 @@ "message": "此操作受到保护。若要继续,请重新输入您的主密码以验证您的身份。" }, "emailVerificationRequired": { - "message": "需要验证电子邮件" + "message": "需要验证电子邮箱" }, "emailVerifiedV2": { "message": "电子邮箱已验证" }, "emailVerificationRequiredDesc": { - "message": "您必须验证电子邮件才能使用此功能。您可以在网页密码库中验证您的电子邮件。" + "message": "您必须验证电子邮箱才能使用此功能。您可以在网页密码库中验证您的电子邮箱。" }, "updatedMasterPassword": { "message": "已更新主密码" @@ -2880,14 +2789,11 @@ "error": { "message": "错误" }, - "regenerateUsername": { - "message": "重新生成用户名" - }, "generateUsername": { "message": "生成用户名" }, "generateEmail": { - "message": "生成电子邮件地址" + "message": "生成电子邮箱" }, "spinboxBoundariesHint": { "message": "值必须在 $MIN$ 和 $MAX$ 之间。", @@ -2923,18 +2829,15 @@ } } }, - "usernameType": { - "message": "用户名类型" - }, "plusAddressedEmail": { - "message": "附加地址电子邮件", + "message": "附加地址电子邮箱", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" }, "plusAddressedEmailDesc": { - "message": "使用您的电子邮件供应商的子地址功能。" + "message": "使用您的电子邮箱提供商的子地址功能。" }, "catchallEmail": { - "message": "Catch-all 电子邮件" + "message": "Catch-all 电子邮箱" }, "catchallEmailDesc": { "message": "使用您的域名配置的 Catch-all 收件箱。" @@ -2948,23 +2851,17 @@ "websiteName": { "message": "网站名称" }, - "whatWouldYouLikeToGenerate": { - "message": "您想要生成什么?" - }, - "passwordType": { - "message": "密码类型" - }, "service": { "message": "服务" }, "forwardedEmail": { - "message": "转发的电子邮件别名" + "message": "转发的电子邮箱别名" }, "forwardedEmailDesc": { - "message": "使用外部转发服务生成一个电子邮件别名。" + "message": "使用外部转发服务生成一个电子邮箱别名。" }, "forwarderDomainName": { - "message": "电子邮件域名", + "message": "电子邮箱域名", "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { @@ -3024,7 +2921,7 @@ } }, "forwarderNoAccountId": { - "message": "无法获取 $SERVICENAME$ 电子邮件账户 ID。", + "message": "无法获取 $SERVICENAME$ 电子邮箱账户 ID。", "description": "Displayed when the forwarding service fails to return an account ID.", "placeholders": { "servicename": { @@ -3153,7 +3050,7 @@ "message": "初来乍到吗?" }, "rememberEmail": { - "message": "记住电子邮件地址" + "message": "记住电子邮箱" }, "loginWithDevice": { "message": "使用设备登录" @@ -3174,7 +3071,7 @@ "message": "查看所有登录选项" }, "viewAllLoginOptionsV1": { - "message": "View all log in options" + "message": "查看所有登录选项" }, "notificationSentDevice": { "message": "通知已发送到您的设备。" @@ -3315,7 +3212,7 @@ "message": "必须填写组织 SSO 标识符。" }, "creatingAccountOn": { - "message": "正创建账户于" + "message": "创建账户至" }, "checkYourEmail": { "message": "检查您的电子邮箱" @@ -3333,7 +3230,7 @@ "message": "返回" }, "toEditYourEmailAddress": { - "message": "编辑您的电子邮件地址。" + "message": "编辑您的电子邮箱地址。" }, "eu": { "message": "欧盟", @@ -3367,10 +3264,10 @@ "message": "登录已批准" }, "userEmailMissing": { - "message": "缺少用户电子邮件" + "message": "缺少用户电子邮箱" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "未找到活动的用户电子邮件。您将被注销。" + "message": "未找到活动的用户电子邮箱。您将被注销。" }, "deviceTrusted": { "message": "设备已信任" @@ -3438,14 +3335,14 @@ } }, "multipleInputEmails": { - "message": "一个或多个电子邮件地址无效" + "message": "一个或多个电子邮箱无效" }, "inputTrimValidator": { "message": "输入不能只包含空格。", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "输入的不是电子邮件地址。" + "message": "输入的不是电子邮箱地址。" }, "fieldsNeedAttention": { "message": "上面的 $COUNT$ 个字段需要您注意。", @@ -3550,7 +3447,7 @@ "message": "切换侧边导航" }, "skipToContent": { - "message": "跳转到正文" + "message": "跳转到内容" }, "bitwardenOverlayButton": { "message": "Bitwarden 自动填充菜单按钮", @@ -3580,6 +3477,14 @@ "message": "解锁您的账户(在新窗口中打开)", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "基于时间的一次性密码验证码", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "TOTP 到期前剩余时间", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "为其填写凭据", "description": "Screen reader text for when overlay item is in focused" @@ -3711,7 +3616,7 @@ } }, "duoHealthCheckResultsInNullAuthUrlError": { - "message": "与 Duo 服务连接时出错。请使用不同的两步登录方式或联系 Duo 寻求帮助。" + "message": "与 Duo 服务连接时出错。请使用不同的两步登录方式或联系 Duo 获取协助。" }, "launchDuoAndFollowStepsToFinishLoggingIn": { "message": "启动 DUO 并按照步骤完成登录。" @@ -4155,7 +4060,7 @@ "message": "分配到集合" }, "copyEmail": { - "message": "复制电子邮件地址" + "message": "复制电子邮箱" }, "copyPhone": { "message": "复制电话号码" @@ -4258,7 +4163,7 @@ "message": "所有者:您" }, "linked": { - "message": "已链接" + "message": "链接型" }, "copySuccessful": { "message": "复制成功" @@ -4486,10 +4391,10 @@ "message": "对于如密码之类的敏感数据,请使用隐藏型字段" }, "checkBoxHelpText": { - "message": "如果您想自动勾选表单复选框(例如记住电子邮件地址),请使用复选框" + "message": "如果您想自动勾选表单复选框(例如记住电子邮箱),请使用复选框型字段" }, "linkedHelpText": { - "message": "当您处理特定网站的自动填充问题时,请使用链接型字段。" + "message": "当您处理特定网站的自动填充问题时,请使用链接型字段" }, "linkedLabelHelpText": { "message": "输入字段的 html id、名称、aria-label 或占位符。" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "在扩展图标上显示自动填充建议的登录的数量" }, + "showQuickCopyActions": { + "message": "在密码库上显示快速复制操作" + }, "systemDefault": { "message": "跟随系统" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta 版" }, + "importantNotice": { + "message": "重要通知" + }, + "setupTwoStepLogin": { + "message": "设置两步登录" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "从 2025 年 02 月开始,Bitwarden 将向您的账户电子邮箱发送一个代码,以验证来自新设备的登录。" + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "您可以设置两步登录作为保护账户的替代方法,或将您的电子邮箱更改为您可以访问的电子邮箱。" + }, + "remindMeLater": { + "message": "稍后提醒我" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "您能可靠地访问您的电子邮箱 $EMAIL$ 吗?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "不,我不能" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "是的,我可以可靠地访问我的电子邮箱" + }, + "turnOnTwoStepLogin": { + "message": "开启两步登录" + }, + "changeAcctEmail": { + "message": "更改账户电子邮箱" + }, "extensionWidth": { "message": "扩展宽度" }, diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index 9d51dfedb53..4b215df6ba2 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -192,6 +192,13 @@ "autoFillIdentity": { "message": "自動填入身分資訊" }, + "fillVerificationCode": { + "message": "Fill verification code" + }, + "fillVerificationCodeAria": { + "message": "Fill Verification Code", + "description": "Aria label for the heading displayed the inline menu for totp code autofill" + }, "generatePasswordCopied": { "message": "產生及複製密碼" }, @@ -241,7 +248,7 @@ "message": "請求密碼提示" }, "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { - "message": "輸入您帳戶的電子郵件,您的密碼提示會傳送給您" + "message": "輸入您帳號的電子郵件,您的密碼提示會傳送給您" }, "passwordHint": { "message": "密碼提示" @@ -447,9 +454,6 @@ "length": { "message": "長度" }, - "passwordMinLength": { - "message": "最小密碼長度" - }, "uppercase": { "message": "大寫 (A-Z)", "description": "deprecated. Use uppercaseLabel instead." @@ -521,10 +525,6 @@ "minSpecial": { "message": "最少符號位數" }, - "avoidAmbChar": { - "message": "避免易混淆的字元", - "description": "deprecated. Use avoidAmbiguous instead." - }, "avoidAmbiguous": { "message": "Avoid ambiguous characters", "description": "Label for the avoid ambiguous characters checkbox." @@ -641,9 +641,6 @@ "rateExtension": { "message": "為本套件評分" }, - "rateExtensionDesc": { - "message": "請給予我們好評!" - }, "browserNotSupportClipboard": { "message": "您的瀏覽器不支援剪貼簿簡單複製,請手動複製。" }, @@ -1007,6 +1004,9 @@ "showIdentitiesCurrentTabDesc": { "message": "於分頁頁面顯示身分以便於自動填入。" }, + "clickToAutofillOnVault": { + "message": "Click items to autofill on Vault view" + }, "clearClipboard": { "message": "清除剪貼簿", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." @@ -1153,9 +1153,6 @@ "moveToOrganization": { "message": "移動至組織 " }, - "share": { - "message": "共用" - }, "movedItemToOrg": { "message": "已將 $ITEMNAME$ 移動至 $ORGNAME$", "placeholders": { @@ -2043,9 +2040,6 @@ "clone": { "message": "克隆" }, - "passwordGeneratorPolicyInEffect": { - "message": "一個或多個組織原則正影響密碼產生器設定。" - }, "passwordGenerator": { "message": "Password generator" }, @@ -2385,14 +2379,6 @@ "message": "Send 詳細資訊", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "searchSends": { - "message": "搜尋 Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "addSend": { - "message": "新增 Send", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendTypeText": { "message": "文字" }, @@ -2409,16 +2395,9 @@ "hideTextByDefault": { "message": "默認隱藏文字" }, - "maxAccessCountReached": { - "message": "已達最大存取次數", - "description": "This text will be displayed after a Send has been accessed the maximum amount of times." - }, "expired": { "message": "已逾期" }, - "pendingDeletion": { - "message": "等待刪除" - }, "passwordProtected": { "message": "密碼保護" }, @@ -2468,24 +2447,9 @@ "message": "編輯 Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendTypeHeader": { - "message": "這是什麽類型的 Send?", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendNameDesc": { - "message": "用於描述此 Send 的易記名稱。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendFileDesc": { - "message": "您想要傳送的檔案。" - }, "deletionDate": { "message": "刪除日期" }, - "deletionDateDesc": { - "message": "此 Send 將在指定的日期和時間後被永久刪除。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "deletionDateDescV2": { "message": "此 Send 將在指定的日期後被永久刪除。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2493,10 +2457,6 @@ "expirationDate": { "message": "逾期日期" }, - "expirationDateDesc": { - "message": "如果設定此選項,對此 Send 的存取將在指定的日期和時間後逾期。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "oneDay": { "message": "1 天" }, @@ -2512,43 +2472,10 @@ "custom": { "message": "自訂" }, - "maximumAccessCount": { - "message": "最大存取次數" - }, - "maximumAccessCountDesc": { - "message": "如果設定此選項,當達到最大存取次數時,使用者將無法再次存取此 Send。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendPasswordDesc": { - "message": "選用功能。使用者需提供密碼才能存取此 Send。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, "sendPasswordDescV3": { "message": "新增一個用於收件人存取此 Send 的可選密碼。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, - "sendNotesDesc": { - "message": "關於此 Send 的私人備註。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendDisableDesc": { - "message": "停用此 Send 以阻止任何人存取。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendShareDesc": { - "message": "儲存時複製此 Send 的連結至剪貼簿。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "sendTextDesc": { - "message": "您想要傳送的文字。" - }, - "sendHideText": { - "message": "預設隱藏此 Send 的文字。", - "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." - }, - "currentAccessCount": { - "message": "目前存取次數" - }, "createSend": { "message": "建立新 Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2631,18 +2558,6 @@ "sendFileCalloutHeader": { "message": "在開始之前" }, - "sendFirefoxCustomDatePopoutMessage1": { - "message": "使用行事曆樣式的日期選擇器", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**To use a calendar style date picker ** click here to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage2": { - "message": "點選此處", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker **click here** to pop out your window.'" - }, - "sendFirefoxCustomDatePopoutMessage3": { - "message": "要彈出至視窗。", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'To use a calendar style date picker click here **to pop out your window.**'" - }, "expirationDateIsInvalid": { "message": "指定的逾期日期無效。" }, @@ -2658,15 +2573,9 @@ "dateParsingError": { "message": "儲存刪除日期和逾期日期時發生錯誤。" }, - "hideEmail": { - "message": "對收件人隱藏我的電子郵件地址。" - }, "hideYourEmail": { "message": "對查看者隱藏您的電子郵件地址。" }, - "sendOptionsPolicyInEffect": { - "message": "一個或多個組織原則正影響您的 Send 選項。" - }, "passwordPrompt": { "message": "重新詢問主密碼" }, @@ -2880,9 +2789,6 @@ "error": { "message": "錯誤" }, - "regenerateUsername": { - "message": "重新產生使用者名稱" - }, "generateUsername": { "message": "產生使用者名稱" }, @@ -2923,9 +2829,6 @@ } } }, - "usernameType": { - "message": "使用者名稱類型" - }, "plusAddressedEmail": { "message": "加號地址電子郵件", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" @@ -2948,12 +2851,6 @@ "websiteName": { "message": "網站名稱" }, - "whatWouldYouLikeToGenerate": { - "message": "您想要產生什麼?" - }, - "passwordType": { - "message": "密碼類型" - }, "service": { "message": "服務" }, @@ -3580,6 +3477,14 @@ "message": "Unlock your account, opens in a new window", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, + "totpCodeAria": { + "message": "Time-based One-Time Password Verification Code", + "description": "Aria label for the totp code displayed in the inline menu for autofill" + }, + "totpSecondsSpanAria": { + "message": "Time remaining before current TOTP expires", + "description": "Aria label for the totp seconds displayed in the inline menu for autofill" + }, "fillCredentialsFor": { "message": "填入登入資訊給", "description": "Screen reader text for when overlay item is in focused" @@ -4664,6 +4569,9 @@ "showNumberOfAutofillSuggestions": { "message": "Show number of login autofill suggestions on extension icon" }, + "showQuickCopyActions": { + "message": "Show quick copy actions on Vault" + }, "systemDefault": { "message": "System default" }, @@ -4895,6 +4803,42 @@ "beta": { "message": "Beta" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, "extensionWidth": { "message": "Extension width" }, diff --git a/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.html b/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.html index 2589a08da19..8893697da17 100644 --- a/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.html +++ b/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.html @@ -20,6 +20,7 @@ [showReadonlyHostname]="showReadonlyHostname" [hideLogo]="true" [maxWidth]="maxWidth" + [hideFooter]="hideFooter" > diff --git a/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts b/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts index 568ced70027..10ef65d0654 100644 --- a/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts +++ b/apps/browser/src/auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.ts @@ -25,6 +25,7 @@ export interface ExtensionAnonLayoutWrapperData extends AnonLayoutWrapperData { showAcctSwitcher?: boolean; showBackButton?: boolean; showLogo?: boolean; + hideFooter?: boolean; } @Component({ @@ -54,6 +55,7 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy { protected showReadonlyHostname: boolean; protected maxWidth: "md" | "3xl"; protected hasLoggedInAccount: boolean = false; + protected hideFooter: boolean; protected theme: string; protected logo = ExtensionBitwardenLogo; @@ -112,6 +114,7 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy { this.pageIcon = firstChildRouteData["pageIcon"]; } + this.hideFooter = Boolean(firstChildRouteData["hideFooter"]); this.showReadonlyHostname = Boolean(firstChildRouteData["showReadonlyHostname"]); this.maxWidth = firstChildRouteData["maxWidth"]; @@ -158,6 +161,10 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy { this.pageIcon = data.pageIcon !== null ? data.pageIcon : null; } + if (data.hideFooter !== undefined) { + this.hideFooter = data.hideFooter !== null ? data.hideFooter : null; + } + if (data.showReadonlyHostname !== undefined) { this.showReadonlyHostname = data.showReadonlyHostname; } @@ -194,6 +201,7 @@ export class ExtensionAnonLayoutWrapperComponent implements OnInit, OnDestroy { this.showBackButton = null; this.showLogo = null; this.maxWidth = null; + this.hideFooter = null; } ngOnDestroy() { diff --git a/apps/browser/src/auth/popup/lock.component.html b/apps/browser/src/auth/popup/lock.component.html deleted file mode 100644 index fb1b09de49c..00000000000 --- a/apps/browser/src/auth/popup/lock.component.html +++ /dev/null @@ -1,100 +0,0 @@ -
- -
-

- {{ "verifyIdentity" | i18n }} -

-
- -
-
-
- -
-
-
-
- - -
-
- - -
-
- -
-
-
- -
-
- -
-

- -

- {{ biometricError }} -

- {{ "awaitDesktop" | i18n }} -

- - -
-
-
diff --git a/apps/browser/src/auth/popup/lock.component.ts b/apps/browser/src/auth/popup/lock.component.ts deleted file mode 100644 index 66de3fb89d2..00000000000 --- a/apps/browser/src/auth/popup/lock.component.ts +++ /dev/null @@ -1,185 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, NgZone, OnInit } from "@angular/core"; -import { Router } from "@angular/router"; -import { firstValueFrom } from "rxjs"; - -import { LockComponent as BaseLockComponent } from "@bitwarden/angular/auth/components/lock.component"; -import { PinServiceAbstraction } from "@bitwarden/auth/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; -import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; -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 { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; -import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; -import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; -import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; -import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { DialogService, ToastService } from "@bitwarden/components"; -import { - KdfConfigService, - KeyService, - BiometricsService, - BiometricStateService, -} from "@bitwarden/key-management"; - -import { BiometricErrors, BiometricErrorTypes } from "../../models/biometricErrors"; -import { BrowserRouterService } from "../../platform/popup/services/browser-router.service"; -import { fido2PopoutSessionData$ } from "../../vault/popup/utils/fido2-popout-session-data"; - -@Component({ - selector: "app-lock", - templateUrl: "lock.component.html", -}) -export class LockComponent extends BaseLockComponent implements OnInit { - private isInitialLockScreen: boolean; - - biometricError: string; - pendingBiometric = false; - fido2PopoutSessionData$ = fido2PopoutSessionData$(); - - constructor( - masterPasswordService: InternalMasterPasswordServiceAbstraction, - router: Router, - i18nService: I18nService, - platformUtilsService: PlatformUtilsService, - messagingService: MessagingService, - keyService: KeyService, - vaultTimeoutService: VaultTimeoutService, - vaultTimeoutSettingsService: VaultTimeoutSettingsService, - environmentService: EnvironmentService, - stateService: StateService, - apiService: ApiService, - logService: LogService, - ngZone: NgZone, - policyApiService: PolicyApiServiceAbstraction, - policyService: InternalPolicyService, - passwordStrengthService: PasswordStrengthServiceAbstraction, - authService: AuthService, - dialogService: DialogService, - deviceTrustService: DeviceTrustServiceAbstraction, - userVerificationService: UserVerificationService, - pinService: PinServiceAbstraction, - private routerService: BrowserRouterService, - biometricStateService: BiometricStateService, - biometricsService: BiometricsService, - accountService: AccountService, - kdfConfigService: KdfConfigService, - syncService: SyncService, - toastService: ToastService, - ) { - super( - masterPasswordService, - router, - i18nService, - platformUtilsService, - messagingService, - keyService, - vaultTimeoutService, - vaultTimeoutSettingsService, - environmentService, - stateService, - apiService, - logService, - ngZone, - policyApiService, - policyService, - passwordStrengthService, - dialogService, - deviceTrustService, - userVerificationService, - pinService, - biometricStateService, - biometricsService, - accountService, - authService, - kdfConfigService, - syncService, - toastService, - ); - this.successRoute = "/tabs/current"; - this.isInitialLockScreen = (window as any).previousPopupUrl == null; - - this.onSuccessfulSubmit = async () => { - const previousUrl = this.routerService.getPreviousUrl(); - if (previousUrl) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigateByUrl(previousUrl); - } else { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate([this.successRoute]); - } - }; - } - - async ngOnInit() { - await super.ngOnInit(); - const autoBiometricsPrompt = await firstValueFrom( - this.biometricStateService.promptAutomatically$, - ); - - window.setTimeout(async () => { - document.getElementById(this.pinEnabled ? "pin" : "masterPassword")?.focus(); - if ( - this.biometricLock && - autoBiometricsPrompt && - this.isInitialLockScreen && - (await this.authService.getAuthStatus()) === AuthenticationStatus.Locked - ) { - await this.unlockBiometric(true); - } - }, 100); - } - - override async unlockBiometric(automaticPrompt: boolean = false): Promise { - if (!this.biometricLock) { - return; - } - - this.biometricError = null; - - let success; - try { - const available = await super.isBiometricUnlockAvailable(); - if (!available) { - if (!automaticPrompt) { - await this.dialogService.openSimpleDialog({ - type: "warning", - title: { key: "biometricsNotAvailableTitle" }, - content: { key: "biometricsNotAvailableDesc" }, - acceptButtonText: { key: "ok" }, - cancelButtonText: null, - }); - } - } else { - this.pendingBiometric = true; - success = await super.unlockBiometric(); - } - } catch (e) { - const error = BiometricErrors[e?.message as BiometricErrorTypes]; - - if (error == null) { - this.logService.error("Unknown error: " + e); - return false; - } - - this.biometricError = this.i18nService.t(error.description); - } finally { - this.pendingBiometric = false; - } - - return success; - } -} diff --git a/apps/browser/src/autofill/background/notification.background.spec.ts b/apps/browser/src/autofill/background/notification.background.spec.ts index e043dbfdd2e..37c05a55a3a 100644 --- a/apps/browser/src/autofill/background/notification.background.spec.ts +++ b/apps/browser/src/autofill/background/notification.background.spec.ts @@ -60,10 +60,18 @@ describe("NotificationBackground", () => { const configService = mock(); const accountService = mock(); + const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>({ + id: "testId" as UserId, + email: "test@example.com", + emailVerified: true, + name: "Test User", + }); + beforeEach(() => { activeAccountStatusMock$ = new BehaviorSubject(AuthenticationStatus.Locked); authService = mock(); authService.activeAccountStatus$ = activeAccountStatusMock$; + accountService.activeAccount$ = activeAccountSubject; notificationBackground = new NotificationBackground( autofillService, cipherService, @@ -683,13 +691,6 @@ describe("NotificationBackground", () => { }); describe("saveOrUpdateCredentials", () => { - const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>({ - id: "testId" as UserId, - email: "test@example.com", - emailVerified: true, - name: "Test User", - }); - let getDecryptedCipherByIdSpy: jest.SpyInstance; let getAllDecryptedForUrlSpy: jest.SpyInstance; let updatePasswordSpy: jest.SpyInstance; diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts index 0947ce1e1da..5c6ff3c2c8c 100644 --- a/apps/browser/src/autofill/background/notification.background.ts +++ b/apps/browser/src/autofill/background/notification.background.ts @@ -83,6 +83,8 @@ export default class NotificationBackground { getWebVaultUrlForNotification: () => this.getWebVaultUrl(), }; + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + constructor( private autofillService: AutofillService, private cipherService: CipherService, @@ -569,9 +571,7 @@ export default class NotificationBackground { return; } - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); + const activeUserId = await firstValueFrom(this.activeUserId$); const cipher = await this.cipherService.encrypt(newCipher, activeUserId); try { @@ -611,10 +611,7 @@ export default class NotificationBackground { return; } - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); - + const activeUserId = await firstValueFrom(this.activeUserId$); const cipher = await this.cipherService.encrypt(cipherView, activeUserId); try { // We've only updated the password, no need to broadcast editedCipher message @@ -647,17 +644,15 @@ export default class NotificationBackground { if (Utils.isNullOrWhitespace(folderId) || folderId === "null") { return false; } - - const folders = await firstValueFrom(this.folderService.folderViews$); + const activeUserId = await firstValueFrom(this.activeUserId$); + const folders = await firstValueFrom(this.folderService.folderViews$(activeUserId)); return folders.some((x) => x.id === folderId); } private async getDecryptedCipherById(cipherId: string) { const cipher = await this.cipherService.get(cipherId); if (cipher != null && cipher.type === CipherType.Login) { - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); + const activeUserId = await firstValueFrom(this.activeUserId$); return await cipher.decrypt( await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId), @@ -697,7 +692,8 @@ export default class NotificationBackground { * Returns the first value found from the folder service's folderViews$ observable. */ private async getFolderData() { - return await firstValueFrom(this.folderService.folderViews$); + const activeUserId = await firstValueFrom(this.activeUserId$); + return await firstValueFrom(this.folderService.folderViews$(activeUserId)); } private async getWebVaultUrl(): Promise { diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index fd16bfcf16a..8b577ccccf5 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -2275,6 +2275,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { card, identity, sender, + addNewCipherType, }: CurrentAddNewItemData) { const cipherView: CipherView = this.buildNewVaultItemCipherView({ login, @@ -2294,7 +2295,10 @@ export class OverlayBackground implements OverlayBackgroundInterface { collectionIds: cipherView.collectionIds, }); - await this.openAddEditVaultItemPopout(sender.tab, { cipherId: cipherView.id }); + await this.openAddEditVaultItemPopout(sender.tab, { + cipherId: cipherView.id, + cipherType: addNewCipherType, + }); await BrowserApi.sendMessage("inlineAutofillMenuRefreshAddEditCipher"); } catch (error) { this.logService.error("Error building cipher and opening add/edit vault item popout", error); diff --git a/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts b/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts index 99ed4619954..144af0c0a35 100644 --- a/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts +++ b/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts @@ -25,6 +25,7 @@ import { BrowserScriptInjectorService } from "../../../platform/services/browser import { AbortManager } from "../../../vault/background/abort-manager"; import { Fido2ContentScript, Fido2ContentScriptId } from "../enums/fido2-content-script.enum"; import { Fido2PortName } from "../enums/fido2-port-name.enum"; +import { BrowserFido2ParentWindowReference } from "../services/browser-fido2-user-interface.service"; import { Fido2ExtensionMessage } from "./abstractions/fido2.background"; import { Fido2Background } from "./fido2.background"; @@ -56,7 +57,7 @@ describe("Fido2Background", () => { let senderMock!: MockProxy; let logService!: MockProxy; let fido2ActiveRequestManager: MockProxy; - let fido2ClientService!: MockProxy; + let fido2ClientService!: MockProxy>; let vaultSettingsService!: MockProxy; let scriptInjectorServiceMock!: MockProxy; let configServiceMock!: MockProxy; @@ -73,7 +74,7 @@ describe("Fido2Background", () => { }); senderMock = mock({ id: "1", tab: tabMock }); logService = mock(); - fido2ClientService = mock(); + fido2ClientService = mock>(); vaultSettingsService = mock(); abortManagerMock = mock(); abortController = mock(); diff --git a/apps/browser/src/autofill/fido2/background/fido2.background.ts b/apps/browser/src/autofill/fido2/background/fido2.background.ts index f84b7d29a66..e20a0584d20 100644 --- a/apps/browser/src/autofill/fido2/background/fido2.background.ts +++ b/apps/browser/src/autofill/fido2/background/fido2.background.ts @@ -23,10 +23,11 @@ import { ScriptInjectorService } from "../../../platform/services/abstractions/s import { AbortManager } from "../../../vault/background/abort-manager"; import { Fido2ContentScript, Fido2ContentScriptId } from "../enums/fido2-content-script.enum"; import { Fido2PortName } from "../enums/fido2-port-name.enum"; +import { BrowserFido2ParentWindowReference } from "../services/browser-fido2-user-interface.service"; import { - Fido2Background as Fido2BackgroundInterface, Fido2BackgroundExtensionMessageHandlers, + Fido2Background as Fido2BackgroundInterface, Fido2ExtensionMessage, SharedFido2ScriptInjectionDetails, SharedFido2ScriptRegistrationOptions, @@ -56,7 +57,7 @@ export class Fido2Background implements Fido2BackgroundInterface { constructor( private logService: LogService, private fido2ActiveRequestManager: Fido2ActiveRequestManager, - private fido2ClientService: Fido2ClientService, + private fido2ClientService: Fido2ClientService, private vaultSettingsService: VaultSettingsService, private scriptInjectorService: ScriptInjectorService, private configService: ConfigService, diff --git a/apps/browser/src/autofill/fido2/services/browser-fido2-user-interface.service.ts b/apps/browser/src/autofill/fido2/services/browser-fido2-user-interface.service.ts index 872bb1bb52a..04b09a7df32 100644 --- a/apps/browser/src/autofill/fido2/services/browser-fido2-user-interface.service.ts +++ b/apps/browser/src/autofill/fido2/services/browser-fido2-user-interface.service.ts @@ -111,11 +111,15 @@ export type BrowserFido2Message = { sessionId: string } & ( } ); +export type BrowserFido2ParentWindowReference = chrome.tabs.Tab; + /** * Browser implementation of the {@link Fido2UserInterfaceService}. * The user interface is implemented as a popout and the service uses the browser's messaging API to communicate with it. */ -export class BrowserFido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction { +export class BrowserFido2UserInterfaceService + implements Fido2UserInterfaceServiceAbstraction +{ constructor(private authService: AuthService) {} async newSession( diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap b/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap index 785cadb5510..acd06fb8c65 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/__snapshots__/autofill-inline-menu-list.spec.ts.snap @@ -681,6 +681,7 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f class="cipher-container" > + + + + + +`; + +exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers for an authenticated user creates the views for a list of card ciphers 1`] = `
@@ -1153,6 +1235,7 @@ exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers f class="cipher-container" > - -
- -
  • -
    - - -
    -
  • - - -`; - -exports[`AutofillInlineMenuList initAutofillInlineMenuList the list of ciphers for an authenticated user creates the views for a list of identity ciphers 1`] = ` -
    -
      -
    • -
      - - -
      -
    • -
    • -
      - - -
      -
    • -
    • -
      - - -
      -
    • -
    • -
      - - -
      -
    • -
    • -
      - + +
      +
    • +
    • +
      + + +
      +
    • +
    +
    +`; + exports[`AutofillInlineMenuList initAutofillInlineMenuList the locked inline menu for an unauthenticated user creates the views for the locked inline menu 1`] = `
    { expect(autofillInlineMenuList["inlineMenuListContainer"]).toMatchSnapshot(); }); - it("creates the view for a totp field", () => { + it("creates the view for a totp field", async () => { postWindowMessage( createInitAutofillInlineMenuListMessageMock({ inlineMenuFillType: CipherType.Login, ciphers: [ - createAutofillOverlayCipherDataMock(5, { + createAutofillOverlayCipherDataMock(1, { type: CipherType.Login, login: { totp: "123456", @@ -156,6 +156,8 @@ describe("AutofillInlineMenuList", () => { }), ); + await flushPromises(); + const cipherSubtitleElement = autofillInlineMenuList[ "inlineMenuListContainer" ].querySelector('[data-testid="totp-code"]'); @@ -165,6 +167,47 @@ describe("AutofillInlineMenuList", () => { expect(cipherSubtitleElement.textContent).toBe("123 456"); }); + it("renders correctly when there are multiple TOTP elements with username displayed", async () => { + const totpCipher1 = createAutofillOverlayCipherDataMock(1, { + type: CipherType.Login, + login: { + totp: "123456", + totpField: true, + username: "user1", + }, + }); + + const totpCipher2 = createAutofillOverlayCipherDataMock(2, { + type: CipherType.Login, + login: { + totp: "654321", + totpField: true, + username: "user2", + }, + }); + + postWindowMessage( + createInitAutofillInlineMenuListMessageMock({ + inlineMenuFillType: CipherType.Login, + ciphers: [totpCipher1, totpCipher2], + }), + ); + + await flushPromises(); + const checkSubtitleElement = (username: string) => { + const subtitleElement = autofillInlineMenuList["inlineMenuListContainer"].querySelector( + `span.cipher-subtitle[title="${username}"]`, + ); + expect(subtitleElement).not.toBeNull(); + expect(subtitleElement.textContent).toBe(username); + }; + + checkSubtitleElement("user1"); + checkSubtitleElement("user2"); + + expect(autofillInlineMenuList["inlineMenuListContainer"]).toMatchSnapshot(); + }); + it("creates the views for a list of card ciphers", () => { postWindowMessage( createInitAutofillInlineMenuListMessageMock({ diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts index 6cf390d0a29..acb01594cc6 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/autofill-inline-menu-list.ts @@ -5,7 +5,7 @@ import "lit/polyfill-support.js"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { EVENTS, UPDATE_PASSKEYS_HEADINGS_ON_SCROLL } from "@bitwarden/common/autofill/constants"; -import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums"; import { InlineMenuCipherData } from "../../../../background/abstractions/overlay.background"; import { InlineMenuFillTypes } from "../../../../enums/autofill-overlay.enum"; @@ -412,6 +412,29 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { ); } + /** + * Filters the ciphers to include only TOTP-related ones if the field is a TOTP field. + * If the field is a TOTP field but no TOTP is present, it returns an empty array. + * + * @param ciphers - The list of ciphers to filter. + * @returns The filtered list of ciphers or an empty list if no valid TOTP ciphers are present. + */ + private getFilteredCiphersForTotpField(ciphers: InlineMenuCipherData[]): InlineMenuCipherData[] { + if (!ciphers?.length) { + return []; + } + + const isTotpField = + this.inlineMenuFillType === CipherType.Login && + ciphers.some((cipher) => cipher.login?.totpField); + + if (isTotpField) { + return ciphers.filter((cipher) => cipher.login?.totp); + } + + return ciphers; + } + /** * Updates the list items with the passed ciphers. * If no ciphers are passed, the no results inline menu is built. @@ -427,12 +450,12 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { return; } - this.ciphers = ciphers; + this.ciphers = this.getFilteredCiphersForTotpField(ciphers); this.currentCipherIndex = 0; this.showInlineMenuAccountCreation = showInlineMenuAccountCreation; this.resetInlineMenuContainer(); - if (!ciphers?.length) { + if (!this.ciphers?.length) { this.buildNoResultsInlineMenuList(); return; } @@ -1163,7 +1186,7 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { } if (cipher.login?.totpField && cipher.login?.totp) { - return this.buildTotpElement(cipher.login?.totp); + return this.buildTotpElement(cipher.login?.totp, cipher.login?.username, cipher.reprompt); } const subTitleText = this.getSubTitleText(cipher); const cipherSubtitleElement = this.buildCipherSubtitleElement(subTitleText); @@ -1174,13 +1197,28 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { return cipherDetailsElement; } + /** + * Checks if there is more than one TOTP element being displayed. + * + * @returns {boolean} - Returns true if more than one TOTP element is displayed, otherwise false. + */ + private multipleTotpElements(): boolean { + return ( + this.ciphers.filter((cipher) => cipher.login?.totpField && cipher.login?.totp).length > 1 + ); + } + /** * Builds a TOTP element for a given TOTP code. * * @param totp - The TOTP code to display. */ - private buildTotpElement(totpCode: string): HTMLDivElement | null { + private buildTotpElement( + totpCode: string, + username: string, + reprompt: CipherRepromptType, + ): HTMLDivElement | null { if (!totpCode) { return null; } @@ -1196,12 +1234,18 @@ export class AutofillInlineMenuList extends AutofillInlineMenuPageElement { containerElement.appendChild(totpHeading); - const subtitleElement = document.createElement("span"); - subtitleElement.classList.add("cipher-subtitle"); - subtitleElement.textContent = formattedTotpCode; - subtitleElement.setAttribute("aria-label", this.getTranslation("totpCodeAria")); - subtitleElement.setAttribute("data-testid", "totp-code"); - containerElement.appendChild(subtitleElement); + if (this.multipleTotpElements() && username) { + const usernameSubtitle = this.buildCipherSubtitleElement(username); + containerElement.appendChild(usernameSubtitle); + } + + const totpCodeSpan = document.createElement("span"); + totpCodeSpan.classList.toggle("cipher-subtitle"); + totpCodeSpan.classList.toggle("masked-totp", !!reprompt); + totpCodeSpan.textContent = reprompt ? "●●●●●●" : formattedTotpCode; + totpCodeSpan.setAttribute("aria-label", this.getTranslation("totpCodeAria")); + totpCodeSpan.setAttribute("data-testid", "totp-code"); + containerElement.appendChild(totpCodeSpan); return containerElement; } diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss b/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss index 0d1bf3358cd..d0875cfe427 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/list/list.scss @@ -404,6 +404,11 @@ body * { color: themed("mutedTextColor"); } + &.masked-totp { + font-size: 0.875rem; + letter-spacing: 0.2rem; + } + &--passkey { display: flex; align-content: center; diff --git a/apps/browser/src/autofill/popup/settings/autofill.component.html b/apps/browser/src/autofill/popup/settings/autofill.component.html index 18c6f515337..e8882cf7bbb 100644 --- a/apps/browser/src/autofill/popup/settings/autofill.component.html +++ b/apps/browser/src/autofill/popup/settings/autofill.component.html @@ -120,7 +120,7 @@

    {{ "autofillSuggestionsSectionTitle" | i18n }}

    /> {{ "showCardsInVaultView" | i18n }} - + {{ "autofillSuggestionsSectionTitle" | i18n }} {{ "showIdentitiesInVaultView" | i18n }} + + + + {{ "clickToAutofillOnVault" | i18n }} + + diff --git a/apps/browser/src/autofill/popup/settings/autofill.component.ts b/apps/browser/src/autofill/popup/settings/autofill.component.ts index 828b45e7b37..da997f550b3 100644 --- a/apps/browser/src/autofill/popup/settings/autofill.component.ts +++ b/apps/browser/src/autofill/popup/settings/autofill.component.ts @@ -110,6 +110,7 @@ export class AutofillComponent implements OnInit { uriMatchOptions: { name: string; value: UriMatchStrategySetting }[]; showCardsCurrentTab: boolean = true; showIdentitiesCurrentTab: boolean = true; + clickItemsVaultView: boolean = false; autofillKeyboardHelperText: string; accountSwitcherEnabled: boolean = false; @@ -207,6 +208,10 @@ export class AutofillComponent implements OnInit { this.showIdentitiesCurrentTab = await firstValueFrom( this.vaultSettingsService.showIdentitiesCurrentTab$, ); + + this.clickItemsVaultView = await firstValueFrom( + this.vaultSettingsService.clickItemsToAutofillVaultView$, + ); } async updateInlineMenuVisibility() { @@ -413,4 +418,8 @@ export class AutofillComponent implements OnInit { async updateShowInlineMenuIdentities() { await this.autofillSettingsService.setShowInlineMenuIdentities(this.showInlineMenuIdentities); } + + async updateClickItemsVaultView() { + await this.vaultSettingsService.setClickItemsToAutofillVaultView(this.clickItemsVaultView); + } } diff --git a/apps/browser/src/autofill/popup/settings/notifications.component.html b/apps/browser/src/autofill/popup/settings/notifications.component.html index 86fe4923df8..c6446012d0c 100644 --- a/apps/browser/src/autofill/popup/settings/notifications.component.html +++ b/apps/browser/src/autofill/popup/settings/notifications.component.html @@ -50,7 +50,7 @@

    {{ "vaultSaveOptionsTitle" | i18n }}

    {{ "excludedDomains" | i18n }} - +
    diff --git a/apps/browser/src/autofill/services/autofill.service.spec.ts b/apps/browser/src/autofill/services/autofill.service.spec.ts index 812891930b8..91f926440a0 100644 --- a/apps/browser/src/autofill/services/autofill.service.spec.ts +++ b/apps/browser/src/autofill/services/autofill.service.spec.ts @@ -2937,7 +2937,9 @@ describe("AutofillService", () => { const expectedDateFormats = [ ["mm/yyyy", "05/2024"], + ["mm/YYYY", "05/2024"], ["mm/yy", "05/24"], + ["MM/YY", "05/24"], ["yyyy/mm", "2024/05"], ["yy/mm", "24/05"], ["mm-yyyy", "05-2024"], @@ -2946,6 +2948,7 @@ describe("AutofillService", () => { ["yy-mm", "24-05"], ["yyyymm", "202405"], ["yymm", "2405"], + ["YYMM", "2405"], ["mmyyyy", "052024"], ["mmyy", "0524"], ]; @@ -3875,7 +3878,7 @@ describe("AutofillService", () => { }); describe("given a autofill field value that indicates the field is a `select` input", () => { - it("will not add an autofil action to the fill script if the dataValue cannot be found in the select options", () => { + it("will not add an autofill action to the fill script if the dataValue cannot be found in the select options", () => { const dataValue = "username"; const selectField = createAutofillFieldMock({ opid: "username-field", diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index d6524f7e70d..093f4bfb638 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -1514,7 +1514,7 @@ export default class AutofillService implements AutofillServiceInterface { ); return CreditCardAutoFillConstants.CardAttributesExtended.find((attributeName) => { - const fieldAttributeValue = field[attributeName]; + const fieldAttributeValue = field[attributeName]?.toLocaleLowerCase(); const fieldAttributeMatch = fieldAttributeValue?.match(dateFormatPattern); // break find as soon as a match is found diff --git a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.spec.ts b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.spec.ts index 6e267e25c3f..dc59e05a18b 100644 --- a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.spec.ts +++ b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.spec.ts @@ -21,8 +21,9 @@ describe("InlineMenuFieldQualificationService", () => { }); describe("isFieldForLoginForm", () => { - it("does not disqualify totp fields with flag set to true", () => { + it("does not disqualify totp fields for premium users with flag set to true", () => { inlineMenuFieldQualificationService["inlineMenuTotpFeatureFlag"] = true; + inlineMenuFieldQualificationService["premiumEnabled"] = true; const field = mock({ type: "text", autoCompleteType: "one-time-code", @@ -36,8 +37,25 @@ describe("InlineMenuFieldQualificationService", () => { ); }); - it("disqualify totp fields with flag set to false", () => { + it("disqualifies totp fields for premium users with flag set to false", () => { inlineMenuFieldQualificationService["inlineMenuTotpFeatureFlag"] = false; + inlineMenuFieldQualificationService["inlineMenuTotpFeatureFlag"] = true; + const field = mock({ + type: "text", + autoCompleteType: "one-time-code", + htmlName: "totp", + htmlID: "totp", + placeholder: "totp", + }); + + expect(inlineMenuFieldQualificationService.isFieldForLoginForm(field, pageDetails)).toBe( + false, + ); + }); + + it("disqualifies totp fields for non-premium users with flag set to true", () => { + inlineMenuFieldQualificationService["inlineMenuTotpFeatureFlag"] = true; + inlineMenuFieldQualificationService["premiumEnabled"] = false; const field = mock({ type: "text", autoCompleteType: "one-time-code", diff --git a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts index 32e55988a7a..43efd338c6e 100644 --- a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts +++ b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts @@ -151,14 +151,17 @@ export class InlineMenuFieldQualificationService private totpFieldAutocompleteValue = "one-time-code"; private inlineMenuFieldQualificationFlagSet = false; private inlineMenuTotpFeatureFlag = false; + private premiumEnabled = false; constructor() { void Promise.all([ sendExtensionMessage("getInlineMenuFieldQualificationFeatureFlag"), sendExtensionMessage("getInlineMenuTotpFeatureFlag"), - ]).then(([fieldQualificationFlag, totpFeatureFlag]) => { + sendExtensionMessage("getUserPremiumStatus"), + ]).then(([fieldQualificationFlag, totpFeatureFlag, premiumStatus]) => { this.inlineMenuFieldQualificationFlagSet = !!fieldQualificationFlag?.result; this.inlineMenuTotpFeatureFlag = !!totpFeatureFlag?.result; + this.premiumEnabled = !!premiumStatus?.result; }); } @@ -174,10 +177,11 @@ export class InlineMenuFieldQualificationService } /** - * Autofill does not fill password type totp input fields + * Totp inline menu is available only for premium users. */ - if (this.inlineMenuTotpFeatureFlag) { + if (this.inlineMenuTotpFeatureFlag && this.premiumEnabled) { const isTotpField = this.isTotpField(field); + // Autofill does not fill totp inputs with a "password" `type` attribute value const passwordType = field.type === "password"; if (isTotpField && !passwordType) { return true; diff --git a/apps/browser/src/autofill/spec/autofill-mocks.ts b/apps/browser/src/autofill/spec/autofill-mocks.ts index 3a78e7f2c59..6e175906d30 100644 --- a/apps/browser/src/autofill/spec/autofill-mocks.ts +++ b/apps/browser/src/autofill/spec/autofill-mocks.ts @@ -241,7 +241,7 @@ export function createInitAutofillInlineMenuListMessageMock( createAutofillOverlayCipherDataMock(4, { icon: { imageEnabled: false, image: "", fallbackImage: "", icon: "" }, }), - createAutofillOverlayCipherDataMock(5, { login: { totp: "123456", totpField: true } }), + createAutofillOverlayCipherDataMock(5), createAutofillOverlayCipherDataMock(6), createAutofillOverlayCipherDataMock(7), createAutofillOverlayCipherDataMock(8), diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 019be2923b6..555e3a13fa0 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -201,11 +201,11 @@ import { ImportServiceAbstraction, } from "@bitwarden/importer/core"; import { - DefaultKdfConfigService, - KdfConfigService, BiometricStateService, BiometricsService, DefaultBiometricStateService, + DefaultKdfConfigService, + KdfConfigService, KeyService as KeyServiceAbstraction, } from "@bitwarden/key-management"; import { @@ -232,7 +232,10 @@ import { MainContextMenuHandler } from "../autofill/browser/main-context-menu-ha import LegacyOverlayBackground from "../autofill/deprecated/background/overlay.background.deprecated"; import { Fido2Background as Fido2BackgroundAbstraction } from "../autofill/fido2/background/abstractions/fido2.background"; import { Fido2Background } from "../autofill/fido2/background/fido2.background"; -import { BrowserFido2UserInterfaceService } from "../autofill/fido2/services/browser-fido2-user-interface.service"; +import { + BrowserFido2ParentWindowReference, + BrowserFido2UserInterfaceService, +} from "../autofill/fido2/services/browser-fido2-user-interface.service"; import { AutofillService as AutofillServiceAbstraction } from "../autofill/services/abstractions/autofill.service"; import AutofillService from "../autofill/services/autofill.service"; import { InlineMenuFieldQualificationService } from "../autofill/services/inline-menu-field-qualification.service"; @@ -337,10 +340,10 @@ export default class MainBackground { policyApiService: PolicyApiServiceAbstraction; sendApiService: SendApiServiceAbstraction; userVerificationApiService: UserVerificationApiServiceAbstraction; - fido2UserInterfaceService: Fido2UserInterfaceServiceAbstraction; - fido2AuthenticatorService: Fido2AuthenticatorServiceAbstraction; + fido2UserInterfaceService: Fido2UserInterfaceServiceAbstraction; + fido2AuthenticatorService: Fido2AuthenticatorServiceAbstraction; fido2ActiveRequestManager: Fido2ActiveRequestManagerAbstraction; - fido2ClientService: Fido2ClientServiceAbstraction; + fido2ClientService: Fido2ClientServiceAbstraction; avatarService: AvatarServiceAbstraction; mainContextMenuHandler: MainContextMenuHandler; cipherContextMenuHandler: CipherContextMenuHandler; @@ -736,7 +739,6 @@ export default class MainBackground { this.accountService, this.kdfConfigService, this.keyService, - this.apiService, ); this.passwordStrengthService = new PasswordStrengthService(); @@ -1102,6 +1104,7 @@ export default class MainBackground { messageListener, this.accountService, lockService, + this.billingAccountProfileStateService, ); this.nativeMessagingBackground = new NativeMessagingBackground( this.keyService, @@ -1310,27 +1313,10 @@ export default class MainBackground { await this.initOverlayAndTabsBackground(); - if (flagEnabled("sdk")) { - // Warn if the SDK for some reason can't be initialized - let supported = false; - let error: Error; - try { - supported = await firstValueFrom(this.sdkService.supported$); - } catch (e) { - error = e; - } - - if (!supported) { - this.sdkService - .failedToInitialize("background", error) - .catch((e) => this.logService.error(e)); - } - } - return new Promise((resolve) => { setTimeout(async () => { await this.refreshBadge(); - await this.fullSync(true); + await this.fullSync(false); this.taskSchedulerService.setInterval( ScheduledTaskNames.scheduleNextSyncInterval, 5 * 60 * 1000, // check every 5 minutes diff --git a/apps/browser/src/background/runtime.background.ts b/apps/browser/src/background/runtime.background.ts index f91b6983a08..56ad7909e61 100644 --- a/apps/browser/src/background/runtime.background.ts +++ b/apps/browser/src/background/runtime.background.ts @@ -7,6 +7,7 @@ import { NotificationsService } from "@bitwarden/common/abstractions/notificatio import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AutofillOverlayVisibility, ExtensionCommand } from "@bitwarden/common/autofill/constants"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; @@ -50,6 +51,7 @@ export default class RuntimeBackground { private messageListener: MessageListener, private accountService: AccountService, private readonly lockService: LockService, + private billingAccountProfileStateService: BillingAccountProfileStateService, ) { // onInstalled listener must be wired up before anything else, so we do it in the ctor chrome.runtime.onInstalled.addListener((details: any) => { @@ -74,6 +76,7 @@ export default class RuntimeBackground { "getUseTreeWalkerApiForPageDetailsCollectionFeatureFlag", "getInlineMenuFieldQualificationFeatureFlag", "getInlineMenuTotpFeatureFlag", + "getUserPremiumStatus", ]; if (messagesWithResponse.includes(msg.command)) { @@ -198,6 +201,12 @@ export default class RuntimeBackground { case "getInlineMenuFieldQualificationFeatureFlag": { return await this.configService.getFeatureFlag(FeatureFlag.InlineMenuFieldQualification); } + case "getUserPremiumStatus": { + const result = await firstValueFrom( + this.billingAccountProfileStateService.hasPremiumFromAnySource$, + ); + return result; + } case "getInlineMenuTotpFeatureFlag": { return await this.configService.getFeatureFlag(FeatureFlag.InlineMenuTotp); } diff --git a/apps/browser/src/billing/popup/settings/premium-v2.component.ts b/apps/browser/src/billing/popup/settings/premium-v2.component.ts index f24de0633ac..c17adcd52fe 100644 --- a/apps/browser/src/billing/popup/settings/premium-v2.component.ts +++ b/apps/browser/src/billing/popup/settings/premium-v2.component.ts @@ -21,7 +21,6 @@ import { SectionComponent, } from "@bitwarden/components"; -import { CurrentAccountComponent } from "../../../auth/popup/account-switching/current-account.component"; import { PopOutComponent } from "../../../platform/popup/components/pop-out.component"; import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component"; @@ -34,7 +33,6 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co ButtonModule, CardComponent, CommonModule, - CurrentAccountComponent, ItemModule, JslibModule, PopupPageComponent, diff --git a/apps/browser/src/billing/popup/settings/premium.component.html b/apps/browser/src/billing/popup/settings/premium.component.html deleted file mode 100644 index a8f9855e62d..00000000000 --- a/apps/browser/src/billing/popup/settings/premium.component.html +++ /dev/null @@ -1,72 +0,0 @@ -
    -
    - -
    -

    - {{ "premiumMembership" | i18n }} -

    -
    -
    -
    -
    - -

    {{ "premiumNotCurrentMember" | i18n }}

    -

    {{ "premiumSignUpAndGet" | i18n }}

    -
      -
    • - - {{ "ppremiumSignUpStorage" | i18n }} -
    • -
    • - - {{ "premiumSignUpTwoStepOptions" | i18n }} -
    • -
    • - - {{ "ppremiumSignUpReports" | i18n }} -
    • -
    • - - {{ "ppremiumSignUpTotp" | i18n }} -
    • -
    • - - {{ "ppremiumSignUpSupport" | i18n }} -
    • -
    • - - {{ "ppremiumSignUpFuture" | i18n }} -
    • -
    -

    {{ priceString }}

    - - -
    - -

    {{ "premiumCurrentMember" | i18n }}

    -

    {{ "premiumCurrentMemberThanks" | i18n }}

    - -
    -
    -
    diff --git a/apps/browser/src/billing/popup/settings/premium.component.ts b/apps/browser/src/billing/popup/settings/premium.component.ts deleted file mode 100644 index 70c8667646c..00000000000 --- a/apps/browser/src/billing/popup/settings/premium.component.ts +++ /dev/null @@ -1,61 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { CurrencyPipe, Location } from "@angular/common"; -import { Component } from "@angular/core"; - -import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/vault/components/premium.component"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService } from "@bitwarden/components"; - -@Component({ - selector: "app-premium", - templateUrl: "premium.component.html", -}) -export class PremiumComponent extends BasePremiumComponent { - priceString: string; - - constructor( - i18nService: I18nService, - platformUtilsService: PlatformUtilsService, - apiService: ApiService, - configService: ConfigService, - logService: LogService, - private location: Location, - private currencyPipe: CurrencyPipe, - dialogService: DialogService, - environmentService: EnvironmentService, - billingAccountProfileStateService: BillingAccountProfileStateService, - ) { - super( - i18nService, - platformUtilsService, - apiService, - configService, - logService, - dialogService, - environmentService, - billingAccountProfileStateService, - ); - - // Support old price string. Can be removed in future once all translations are properly updated. - const thePrice = this.currencyPipe.transform(this.price, "$"); - // Safari extension crashes due to $1 appearing in the price string ($10.00). Escape the $ to fix. - const formattedPrice = this.platformUtilsService.isSafari() - ? thePrice.replace("$", "$$$") - : thePrice; - this.priceString = i18nService.t("premiumPrice", formattedPrice); - if (this.priceString.indexOf("%price%") > -1) { - this.priceString = this.priceString.replace("%price%", thePrice); - } - } - - goBack() { - this.location.back(); - } -} diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index 0ccc75cd5da..9e4eb78291a 100644 --- a/apps/browser/src/manifest.json +++ b/apps/browser/src/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2024.12.0", + "version": "2025.1.0", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index 32f58b0cc52..ef7ede6b056 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -3,7 +3,7 @@ "minimum_chrome_version": "102.0", "name": "__MSG_extName__", "short_name": "__MSG_appName__", - "version": "2024.12.0", + "version": "2025.1.0", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/models/browserSendComponentState.ts b/apps/browser/src/models/browserSendComponentState.ts deleted file mode 100644 index 204595df272..00000000000 --- a/apps/browser/src/models/browserSendComponentState.ts +++ /dev/null @@ -1,20 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; -import { DeepJsonify } from "@bitwarden/common/types/deep-jsonify"; - -import { BrowserComponentState } from "./browserComponentState"; - -export class BrowserSendComponentState extends BrowserComponentState { - sends: SendView[]; - - static fromJSON(json: DeepJsonify) { - if (json == null) { - return null; - } - - return Object.assign(new BrowserSendComponentState(), json, { - sends: json.sends?.map((s) => SendView.fromJSON(s)), - }); - } -} diff --git a/apps/browser/src/platform/browser/browser-api.ts b/apps/browser/src/platform/browser/browser-api.ts index 05ea3c84e9e..1d8ff65c17d 100644 --- a/apps/browser/src/platform/browser/browser-api.ts +++ b/apps/browser/src/platform/browser/browser-api.ts @@ -165,6 +165,21 @@ export class BrowserApi { }); } + /** + * Fetch the currently open browser tab + */ + static async getCurrentTab(): Promise | null { + if (BrowserApi.isManifestVersion(3)) { + return await chrome.tabs.getCurrent(); + } + + return new Promise((resolve) => + chrome.tabs.getCurrent((tab) => { + resolve(tab); + }), + ); + } + static async tabsQuery(options: chrome.tabs.QueryInfo): Promise { return new Promise((resolve) => { chrome.tabs.query(options, (tabs) => { @@ -443,7 +458,7 @@ export class BrowserApi { // and that prompts us to show a new tab, this apparently doesn't happen on sideloaded // extensions and only shows itself production scenarios. See: https://bitwarden.atlassian.net/browse/PM-12298 if (this.isSafariApi) { - self.location.reload(); + return self.location.reload(); } return chrome.runtime.reload(); } diff --git a/apps/browser/src/platform/popup/browser-popup-utils.ts b/apps/browser/src/platform/popup/browser-popup-utils.ts index 35fd2361014..33a1ff4016d 100644 --- a/apps/browser/src/platform/popup/browser-popup-utils.ts +++ b/apps/browser/src/platform/popup/browser-popup-utils.ts @@ -3,7 +3,7 @@ import { BrowserApi } from "../browser/browser-api"; import { ScrollOptions } from "./abstractions/browser-popup-utils.abstractions"; -import { PopupWidthOptions } from "./layout/popup-width.service"; +import { PopupWidthOptions } from "./layout/popup-size.service"; class BrowserPopupUtils { /** @@ -24,6 +24,22 @@ class BrowserPopupUtils { return BrowserPopupUtils.urlContainsSearchParams(win, "uilocation", "popout"); } + /** + * Check if the current popup view is open inside of the current browser tab + * (it is possible in Chrome to open the extension in a tab) + */ + static async isInTab() { + const tabId = (await BrowserApi.getCurrentTab())?.id; + + if (tabId === undefined || tabId === null) { + return false; + } + + const result = BrowserApi.getExtensionViews({ tabId, type: "tab" }); + + return result.length > 0; + } + /** * Identifies if the popup is within the single action popout. * diff --git a/apps/browser/src/platform/popup/layout/popup-layout.mdx b/apps/browser/src/platform/popup/layout/popup-layout.mdx index aa11b4099a9..5723bef44b1 100644 --- a/apps/browser/src/platform/popup/layout/popup-layout.mdx +++ b/apps/browser/src/platform/popup/layout/popup-layout.mdx @@ -44,6 +44,9 @@ page looks nice when the extension is popped out. - `above-scroll-area` - When the page content overflows, this content will be "stuck" to the top of the page upon scrolling. +- `full-width-notice` + - Similar to `above-scroll-area`, this content will display before `above-scroll-area` without + container margin or padding. - default - Whatever content you want in `main`. @@ -108,6 +111,30 @@ Common interactive elements to insert into the `end` slot are: - "Add" button: this can be accomplished with the Button component and any custom functionality for that particular page +### Notice + + + + + +Common interactive elements to insert into the `full-width-notice` slot are: + +- `bit-banner`: shows a full-width notice + +Usage example: + +```html + + + + This is an important note about these ciphers + + + + + +``` + ## Popup footer Popup footer should be used when the page displays action buttons. It functions similarly to the diff --git a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts index 11f2d34df50..c1ac8823261 100644 --- a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts +++ b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts @@ -11,6 +11,7 @@ import { SendService } from "@bitwarden/common/tools/send/services/send.service. import { AvatarModule, BadgeModule, + BannerModule, ButtonModule, I18nMockService, IconButtonModule, @@ -40,7 +41,7 @@ class ExtensionContainerComponent {} @Component({ selector: "vault-placeholder", - template: ` + template: /*html*/ ` @@ -52,7 +53,7 @@ class ExtensionContainerComponent {} - + @@ -125,6 +126,18 @@ class MockCurrentAccountComponent {} }) class MockSearchComponent {} +@Component({ + selector: "mock-banner", + template: ` + + This is an important note about these ciphers + + `, + standalone: true, + imports: [BannerModule], +}) +class MockBannerComponent {} + @Component({ selector: "mock-vault-page", template: ` @@ -288,6 +301,12 @@ class MockVaultSubpageComponent {} export default { title: "Browser/Popup Layout", component: PopupPageComponent, + parameters: { + chromatic: { + // Disable tests while we troubleshoot their flaky-ness + disableSnapshot: true, + }, + }, decorators: [ moduleMetadata({ imports: [ @@ -298,6 +317,8 @@ export default { CommonModule, RouterModule, ExtensionContainerComponent, + MockBannerComponent, + MockSearchComponent, MockVaultSubpageComponent, MockVaultPageComponent, MockSendPageComponent, @@ -517,6 +538,22 @@ export const TransparentHeader: Story = { }), }; +export const Notice: Story = { + render: (args) => ({ + props: args, + template: /* HTML */ ` + + + + + + + + + `, + }), +}; + export const WidthOptions: Story = { render: (args) => ({ props: args, diff --git a/apps/browser/src/platform/popup/layout/popup-page.component.html b/apps/browser/src/platform/popup/layout/popup-page.component.html index 8eefc14ab55..ba9a108a504 100644 --- a/apps/browser/src/platform/popup/layout/popup-page.component.html +++ b/apps/browser/src/platform/popup/layout/popup-page.component.html @@ -1,5 +1,6 @@
    +
    (POPUP_STYLE_DISK }); /** - * Updates the extension popup width based on a user setting + * Handles sizing the popup based on available width/height, which can be affected by + * user default zoom level. + * Updates the extension popup width based on a user setting. **/ @Injectable({ providedIn: "root" }) -export class PopupWidthService { +export class PopupSizeService { private static readonly LocalStorageKey = "bw-popup-width"; private readonly state = inject(GlobalStateProvider).get(POPUP_WIDTH_KEY_DEF); @@ -41,15 +45,31 @@ export class PopupWidthService { } /** Begin listening for state changes */ - init() { + async init() { this.width$.subscribe((width: PopupWidthOption) => { - PopupWidthService.setStyle(width); - localStorage.setItem(PopupWidthService.LocalStorageKey, width); + PopupSizeService.setStyle(width); + localStorage.setItem(PopupSizeService.LocalStorageKey, width); }); + + const isInChromeTab = await BrowserPopupUtils.isInTab(); + + if (!BrowserPopupUtils.inPopup(window) || isInChromeTab) { + window.document.body.classList.add("body-full"); + } else if (window.innerHeight < 400) { + window.document.body.classList.add("body-xxs"); + } else if (window.innerHeight < 500) { + window.document.body.classList.add("body-xs"); + } else if (window.innerHeight < 600) { + window.document.body.classList.add("body-sm"); + } } private static setStyle(width: PopupWidthOption) { + if (!BrowserPopupUtils.inPopup(window)) { + return; + } const pxWidth = PopupWidthOptions[width] ?? PopupWidthOptions.default; + document.body.style.minWidth = `${pxWidth}px`; } @@ -57,7 +77,7 @@ export class PopupWidthService { * To keep the popup size from flickering on bootstrap, we store the width in `localStorage` so we can quickly & synchronously reference it. **/ static initBodyWidthFromLocalStorage() { - const storedValue = localStorage.getItem(PopupWidthService.LocalStorageKey); + const storedValue = localStorage.getItem(PopupSizeService.LocalStorageKey); this.setStyle(storedValue as any); } } diff --git a/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts b/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts index c9d5d726a6a..a2f0c78cd52 100644 --- a/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts +++ b/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts @@ -2,7 +2,6 @@ // @ts-strict-ignore import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory"; -import { RecoverableSDKError } from "@bitwarden/common/platform/services/sdk/default-sdk.service"; import type { BitwardenClient } from "@bitwarden/sdk-internal"; import { BrowserApi } from "../../browser/browser-api"; @@ -72,42 +71,14 @@ export class BrowserSdkClientFactory implements SdkClientFactory { ...args: ConstructorParameters ): Promise { const startTime = performance.now(); - try { - await loadWithTimeout(); - } catch (error) { - throw new Error(`Failed to load: ${error.message}`); - } + await load(); const endTime = performance.now(); - const elapsed = Math.round((endTime - startTime) / 1000); const instance = (globalThis as any).init_sdk(...args); this.logService.info("WASM SDK loaded in", Math.round(endTime - startTime), "ms"); - // If it takes 3 seconds or more to load, we want to capture it. - if (elapsed >= 3) { - throw new RecoverableSDKError(instance, elapsed); - } - return instance; } } - -const loadWithTimeout = async () => { - return new Promise((resolve, reject) => { - const timer = setTimeout(() => { - reject(new Error("Operation timed out after 10 second")); - }, 10000); - - load() - .then(() => { - clearTimeout(timer); - resolve(); - }) - .catch((error) => { - clearTimeout(timer); - reject(error); - }); - }); -}; diff --git a/apps/browser/src/platform/sync/foreground-sync.service.spec.ts b/apps/browser/src/platform/sync/foreground-sync.service.spec.ts index e1e921cc3a3..f5daff93815 100644 --- a/apps/browser/src/platform/sync/foreground-sync.service.spec.ts +++ b/apps/browser/src/platform/sync/foreground-sync.service.spec.ts @@ -3,7 +3,6 @@ import { Subject } from "rxjs"; import { CollectionService } from "@bitwarden/admin-console/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; @@ -30,12 +29,12 @@ describe("ForegroundSyncService", () => { const cipherService = mock(); const collectionService = mock(); const apiService = mock(); - const accountService = mock(); + const accountService = mockAccountServiceWith(userId); const authService = mock(); const sendService = mock(); const sendApiService = mock(); const messageListener = mock(); - const stateProvider = new FakeStateProvider(mockAccountServiceWith(userId)); + const stateProvider = new FakeStateProvider(accountService); const sut = new ForegroundSyncService( stateService, diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 38071a9e5c2..b7bc5643ac4 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -17,15 +17,15 @@ import { unauthGuardFn, } from "@bitwarden/angular/auth/guards"; import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; -import { extensionRefreshRedirect } from "@bitwarden/angular/utils/extension-refresh-redirect"; import { extensionRefreshSwap } from "@bitwarden/angular/utils/extension-refresh-swap"; +import { NewDeviceVerificationNoticeGuard } from "@bitwarden/angular/vault/guards"; import { AnonLayoutWrapperComponent, AnonLayoutWrapperData, LoginComponent, LoginSecondaryContentComponent, LockIcon, - LockV2Component, + LockComponent, LoginViaAuthRequestComponent, PasswordHintComponent, RegistrationFinishComponent, @@ -43,6 +43,11 @@ import { TwoFactorTimeoutIcon, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { + NewDeviceVerificationNoticePageOneComponent, + NewDeviceVerificationNoticePageTwoComponent, + VaultIcons, +} from "@bitwarden/vault"; import { twofactorRefactorSwap } from "../../../../libs/angular/src/utils/two-factor-component-refactor-route-swap"; import { fido2AuthGuard } from "../auth/guards/fido2-auth.guard"; @@ -54,7 +59,6 @@ import { } from "../auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component"; import { HintComponent } from "../auth/popup/hint.component"; import { HomeComponent } from "../auth/popup/home.component"; -import { LockComponent } from "../auth/popup/lock.component"; import { LoginDecryptionOptionsComponentV1 } from "../auth/popup/login-decryption-options/login-decryption-options-v1.component"; import { LoginComponentV1 } from "../auth/popup/login-v1.component"; import { LoginViaAuthRequestComponentV1 } from "../auth/popup/login-via-auth-request-v1.component"; @@ -77,37 +81,24 @@ import { ExcludedDomainsComponent } from "../autofill/popup/settings/excluded-do import { NotificationsSettingsV1Component } from "../autofill/popup/settings/notifications-v1.component"; import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component"; import { PremiumV2Component } from "../billing/popup/settings/premium-v2.component"; -import { PremiumComponent } from "../billing/popup/settings/premium.component"; import BrowserPopupUtils from "../platform/popup/browser-popup-utils"; import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service"; import { CredentialGeneratorHistoryComponent } from "../tools/popup/generator/credential-generator-history.component"; import { CredentialGeneratorComponent } from "../tools/popup/generator/credential-generator.component"; -import { GeneratorComponent } from "../tools/popup/generator/generator.component"; -import { PasswordGeneratorHistoryComponent } from "../tools/popup/generator/password-generator-history.component"; -import { SendAddEditComponent } from "../tools/popup/send/send-add-edit.component"; -import { SendGroupingsComponent } from "../tools/popup/send/send-groupings.component"; -import { SendTypeComponent } from "../tools/popup/send/send-type.component"; import { SendAddEditComponent as SendAddEditV2Component } from "../tools/popup/send-v2/add-edit/send-add-edit.component"; import { SendCreatedComponent } from "../tools/popup/send-v2/send-created/send-created.component"; import { SendV2Component } from "../tools/popup/send-v2/send-v2.component"; import { AboutPageV2Component } from "../tools/popup/settings/about-page/about-page-v2.component"; -import { AboutPageComponent } from "../tools/popup/settings/about-page/about-page.component"; import { MoreFromBitwardenPageV2Component } from "../tools/popup/settings/about-page/more-from-bitwarden-page-v2.component"; -import { MoreFromBitwardenPageComponent } from "../tools/popup/settings/about-page/more-from-bitwarden-page.component"; import { ExportBrowserV2Component } from "../tools/popup/settings/export/export-browser-v2.component"; -import { ExportBrowserComponent } from "../tools/popup/settings/export/export-browser.component"; import { ImportBrowserV2Component } from "../tools/popup/settings/import/import-browser-v2.component"; -import { ImportBrowserComponent } from "../tools/popup/settings/import/import-browser.component"; import { SettingsV2Component } from "../tools/popup/settings/settings-v2.component"; -import { SettingsComponent } from "../tools/popup/settings/settings.component"; import { clearVaultStateGuard } from "../vault/guards/clear-vault-state.guard"; import { AddEditComponent } from "../vault/popup/components/vault/add-edit.component"; import { AttachmentsComponent } from "../vault/popup/components/vault/attachments.component"; import { CollectionsComponent } from "../vault/popup/components/vault/collections.component"; -import { CurrentTabComponent } from "../vault/popup/components/vault/current-tab.component"; import { PasswordHistoryComponent } from "../vault/popup/components/vault/password-history.component"; import { ShareComponent } from "../vault/popup/components/vault/share.component"; -import { VaultFilterComponent } from "../vault/popup/components/vault/vault-filter.component"; import { VaultItemsComponent } from "../vault/popup/components/vault/vault-items.component"; import { VaultV2Component } from "../vault/popup/components/vault/vault-v2.component"; import { ViewComponent } from "../vault/popup/components/vault/view.component"; @@ -129,7 +120,6 @@ import { VaultSettingsComponent } from "../vault/popup/settings/vault-settings.c import { RouteElevation } from "./app-routing.animations"; import { debounceNavigationGuard } from "./services/debounce-navigation.service"; import { TabsV2Component } from "./tabs-v2.component"; -import { TabsComponent } from "./tabs.component"; /** * Data properties acceptable for use in extension route objects @@ -175,13 +165,6 @@ const routes: Routes = [ canActivate: [fido2AuthGuard], data: { elevation: 1 } satisfies RouteDataProperties, }), - { - path: "lock", - component: LockComponent, - canActivate: [lockGuard()], - canMatch: [extensionRefreshRedirect("/lockV2")], - data: { elevation: 1, doNotSaveUrl: true } satisfies RouteDataProperties, - }, ...twofactorRefactorSwap( TwoFactorComponent, AnonLayoutWrapperComponent, @@ -341,25 +324,28 @@ const routes: Routes = [ }), { path: "generator", - component: GeneratorComponent, + component: CredentialGeneratorComponent, canActivate: [authGuard], data: { elevation: 0 } satisfies RouteDataProperties, }, - ...extensionRefreshSwap(PasswordGeneratorHistoryComponent, CredentialGeneratorHistoryComponent, { + { path: "generator-history", + component: CredentialGeneratorHistoryComponent, canActivate: [authGuard], data: { elevation: 1 } satisfies RouteDataProperties, - }), - ...extensionRefreshSwap(ImportBrowserComponent, ImportBrowserV2Component, { + }, + { path: "import", + component: ImportBrowserV2Component, canActivate: [authGuard], data: { elevation: 1 } satisfies RouteDataProperties, - }), - ...extensionRefreshSwap(ExportBrowserComponent, ExportBrowserV2Component, { + }, + { path: "export", + component: ExportBrowserV2Component, canActivate: [authGuard], data: { elevation: 2 } satisfies RouteDataProperties, - }), + }, ...extensionRefreshSwap(AutofillV1Component, AutofillComponent, { path: "autofill", canActivate: [authGuard], @@ -408,12 +394,12 @@ const routes: Routes = [ canActivate: [authGuard], data: { elevation: 2 } satisfies RouteDataProperties, }), - ...extensionRefreshSwap(PremiumComponent, PremiumV2Component, { + { path: "premium", - component: PremiumComponent, + component: PremiumV2Component, canActivate: [authGuard], data: { elevation: 1 } satisfies RouteDataProperties, - }), + }, ...extensionRefreshSwap(AppearanceComponent, AppearanceV2Component, { path: "appearance", canActivate: [authGuard], @@ -425,21 +411,17 @@ const routes: Routes = [ data: { elevation: 1 } satisfies RouteDataProperties, }), { - path: "send-type", - component: SendTypeComponent, - canActivate: [authGuard], - data: { elevation: 1 } satisfies RouteDataProperties, - }, - ...extensionRefreshSwap(SendAddEditComponent, SendAddEditV2Component, { path: "add-send", + component: SendAddEditV2Component, canActivate: [authGuard], data: { elevation: 1 } satisfies RouteDataProperties, - }), - ...extensionRefreshSwap(SendAddEditComponent, SendAddEditV2Component, { + }, + { path: "edit-send", + component: SendAddEditV2Component, canActivate: [authGuard], data: { elevation: 1 } satisfies RouteDataProperties, - }), + }, { path: "send-created", component: SendCreatedComponent, @@ -650,8 +632,8 @@ const routes: Routes = [ ], }, { - path: "lockV2", - canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh), lockGuard()], + path: "lock", + canActivate: [lockGuard()], data: { pageIcon: LockIcon, pageTitle: { @@ -662,15 +644,19 @@ const routes: Routes = [ elevation: 1, /** * This ensures that in a passkey flow the `/fido2?` URL does not get - * overwritten in the `BrowserRouterService` by the `/lockV2` route. This way, after + * overwritten in the `BrowserRouterService` by the `/lock` route. This way, after * unlocking, the user can be redirected back to the `/fido2?` URL. + * + * Also, this prevents a routing loop when using biometrics to unlock the vault in MV2 (Firefox), + * locking up the browser (https://bitwarden.atlassian.net/browse/PM-16116). This involves the + * `popup-router-cache.service` pushing the `lock` route to the history. */ doNotSaveUrl: true, } satisfies ExtensionAnonLayoutWrapperData & RouteDataProperties, children: [ { path: "", - component: LockV2Component, + component: LockComponent, }, ], }, @@ -702,18 +688,49 @@ const routes: Routes = [ canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh, true, "/")], data: { elevation: 1 } satisfies RouteDataProperties, }, - ...extensionRefreshSwap(AboutPageComponent, AboutPageV2Component, { + { path: "about", + component: AboutPageV2Component, canActivate: [authGuard], data: { elevation: 1 } satisfies RouteDataProperties, - }), - ...extensionRefreshSwap(MoreFromBitwardenPageComponent, MoreFromBitwardenPageV2Component, { + }, + { path: "more-from-bitwarden", + component: MoreFromBitwardenPageV2Component, canActivate: [authGuard], data: { elevation: 2 } satisfies RouteDataProperties, - }), - ...extensionRefreshSwap(TabsComponent, TabsV2Component, { + }, + { + path: "new-device-notice", + component: ExtensionAnonLayoutWrapperComponent, + canActivate: [], + children: [ + { + path: "", + component: NewDeviceVerificationNoticePageOneComponent, + data: { + pageIcon: VaultIcons.ExclamationTriangle, + pageTitle: { + key: "importantNotice", + }, + hideFooter: true, + }, + }, + { + path: "setup", + component: NewDeviceVerificationNoticePageTwoComponent, + data: { + pageIcon: VaultIcons.UserLock, + pageTitle: { + key: "setupTwoStepLogin", + }, + }, + }, + ], + }, + { path: "tabs", + component: TabsV2Component, data: { elevation: 0 } satisfies RouteDataProperties, children: [ { @@ -723,35 +740,35 @@ const routes: Routes = [ }, { path: "current", - component: CurrentTabComponent, - canActivate: [authGuard], - canMatch: [extensionRefreshRedirect("/tabs/vault")], - data: { elevation: 0 } satisfies RouteDataProperties, - runGuardsAndResolvers: "always", + redirectTo: "/tabs/vault", }, - ...extensionRefreshSwap(VaultFilterComponent, VaultV2Component, { + { path: "vault", - canActivate: [authGuard], + component: VaultV2Component, + canActivate: [authGuard, NewDeviceVerificationNoticeGuard], canDeactivate: [clearVaultStateGuard], data: { elevation: 0 } satisfies RouteDataProperties, - }), - ...extensionRefreshSwap(GeneratorComponent, CredentialGeneratorComponent, { + }, + { path: "generator", + component: CredentialGeneratorComponent, canActivate: [authGuard], data: { elevation: 0 } satisfies RouteDataProperties, - }), - ...extensionRefreshSwap(SettingsComponent, SettingsV2Component, { + }, + { path: "settings", + component: SettingsV2Component, canActivate: [authGuard], data: { elevation: 0 } satisfies RouteDataProperties, - }), - ...extensionRefreshSwap(SendGroupingsComponent, SendV2Component, { + }, + { path: "send", + component: SendV2Component, canActivate: [authGuard], data: { elevation: 0 } satisfies RouteDataProperties, - }), + }, ], - }), + }, { path: "account-switcher", component: AccountSwitcherComponent, diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index cbdbed51db7..8e51152be2e 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -1,7 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit, inject } from "@angular/core"; -import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { NavigationEnd, Router, RouterOutlet } from "@angular/router"; import { Subject, takeUntil, firstValueFrom, concatMap, filter, tap } from "rxjs"; @@ -11,9 +10,7 @@ import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AnimationControlService } from "@bitwarden/common/platform/abstractions/animation-control.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { MessageListener } from "@bitwarden/common/platform/messaging"; import { UserId } from "@bitwarden/common/types/guid"; @@ -25,12 +22,9 @@ import { ToastService, } from "@bitwarden/components"; -import { flagEnabled } from "../platform/flags"; import { PopupCompactModeService } from "../platform/popup/layout/popup-compact-mode.service"; -import { PopupWidthService } from "../platform/popup/layout/popup-width.service"; import { PopupViewCacheService } from "../platform/popup/view-cache/popup-view-cache.service"; import { initPopupClosedListener } from "../platform/services/popup-view-cache-background.service"; -import { BrowserSendStateService } from "../tools/popup/services/browser-send-state.service"; import { VaultBrowserStateService } from "../vault/services/vault-browser-state.service"; import { routerTransition } from "./app-routing.animations"; @@ -47,7 +41,6 @@ import { DesktopSyncVerificationDialogComponent } from "./components/desktop-syn export class AppComponent implements OnInit, OnDestroy { private viewCacheService = inject(PopupViewCacheService); private compactModeService = inject(PopupCompactModeService); - private widthService = inject(PopupWidthService); private lastActivity: Date; private activeUserId: UserId; @@ -61,7 +54,6 @@ export class AppComponent implements OnInit, OnDestroy { private i18nService: I18nService, private router: Router, private stateService: StateService, - private browserSendStateService: BrowserSendStateService, private vaultBrowserStateService: VaultBrowserStateService, private cipherService: CipherService, private changeDetectorRef: ChangeDetectorRef, @@ -72,38 +64,13 @@ export class AppComponent implements OnInit, OnDestroy { private toastService: ToastService, private accountService: AccountService, private animationControlService: AnimationControlService, - private logService: LogService, - private sdkService: SdkService, - ) { - if (flagEnabled("sdk")) { - // Warn if the SDK for some reason can't be initialized - this.sdkService.supported$.pipe(takeUntilDestroyed()).subscribe({ - next: (supported) => { - if (!supported) { - this.logService.debug("SDK is not supported"); - this.sdkService - .failedToInitialize("popup", undefined) - .catch((e) => this.logService.error(e)); - } else { - this.logService.debug("SDK is supported"); - } - }, - error: (e: unknown) => { - this.sdkService - .failedToInitialize("popup", e as Error) - .catch((e) => this.logService.error(e)); - this.logService.error(e); - }, - }); - } - } + ) {} async ngOnInit() { initPopupClosedListener(); await this.viewCacheService.init(); this.compactModeService.init(); - this.widthService.init(); // Component states must not persist between closing and reopening the popup, otherwise they become dead objects // Clear them aggressively to make sure this doesn't occur @@ -271,8 +238,6 @@ export class AppComponent implements OnInit, OnDestroy { await Promise.all([ this.vaultBrowserStateService.setBrowserGroupingsComponentState(null), this.vaultBrowserStateService.setBrowserVaultItemsComponentState(null), - this.browserSendStateService.setBrowserSendComponentState(null), - this.browserSendStateService.setBrowserSendTypeComponentState(null), ]); } diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index 760b43a879c..04d681812fe 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -23,7 +23,6 @@ import { EnvironmentComponent } from "../auth/popup/environment.component"; import { ExtensionAnonLayoutWrapperComponent } from "../auth/popup/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component"; import { HintComponent } from "../auth/popup/hint.component"; import { HomeComponent } from "../auth/popup/home.component"; -import { LockComponent } from "../auth/popup/lock.component"; import { LoginDecryptionOptionsComponentV1 } from "../auth/popup/login-decryption-options/login-decryption-options-v1.component"; import { LoginComponentV1 } from "../auth/popup/login-v1.component"; import { LoginViaAuthRequestComponentV1 } from "../auth/popup/login-via-auth-request-v1.component"; @@ -49,7 +48,6 @@ import { ExcludedDomainsV1Component } from "../autofill/popup/settings/excluded- import { ExcludedDomainsComponent } from "../autofill/popup/settings/excluded-domains.component"; import { NotificationsSettingsV1Component } from "../autofill/popup/settings/notifications-v1.component"; import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component"; -import { PremiumComponent } from "../billing/popup/settings/premium.component"; import { PopOutComponent } from "../platform/popup/components/pop-out.component"; import { HeaderComponent } from "../platform/popup/header.component"; import { PopupFooterComponent } from "../platform/popup/layout/popup-footer.component"; @@ -57,13 +55,6 @@ import { PopupHeaderComponent } from "../platform/popup/layout/popup-header.comp import { PopupPageComponent } from "../platform/popup/layout/popup-page.component"; import { PopupTabNavigationComponent } from "../platform/popup/layout/popup-tab-navigation.component"; import { FilePopoutCalloutComponent } from "../tools/popup/components/file-popout-callout.component"; -import { GeneratorComponent } from "../tools/popup/generator/generator.component"; -import { PasswordGeneratorHistoryComponent } from "../tools/popup/generator/password-generator-history.component"; -import { SendListComponent } from "../tools/popup/send/components/send-list.component"; -import { SendAddEditComponent } from "../tools/popup/send/send-add-edit.component"; -import { SendGroupingsComponent } from "../tools/popup/send/send-groupings.component"; -import { SendTypeComponent } from "../tools/popup/send/send-type.component"; -import { SettingsComponent } from "../tools/popup/settings/settings.component"; import { ActionButtonsComponent } from "../vault/popup/components/action-buttons.component"; import { CipherRowComponent } from "../vault/popup/components/cipher-row.component"; import { AddEditCustomFieldsComponent } from "../vault/popup/components/vault/add-edit-custom-fields.component"; @@ -89,7 +80,6 @@ import { AppComponent } from "./app.component"; import { UserVerificationComponent } from "./components/user-verification.component"; import { ServicesModule } from "./services/services.module"; import { TabsV2Component } from "./tabs-v2.component"; -import { TabsComponent } from "./tabs.component"; // Register the locales for the application import "../platform/popup/locales"; @@ -158,28 +148,18 @@ import "../platform/popup/locales"; VaultFilterComponent, HintComponent, HomeComponent, - LockComponent, LoginViaAuthRequestComponentV1, LoginComponentV1, LoginDecryptionOptionsComponentV1, NotificationsSettingsV1Component, AppearanceComponent, - GeneratorComponent, - PasswordGeneratorHistoryComponent, PasswordHistoryComponent, - PremiumComponent, RegisterComponent, - SendAddEditComponent, - SendGroupingsComponent, - SendListComponent, - SendTypeComponent, SetPasswordComponent, - SettingsComponent, VaultSettingsComponent, ShareComponent, SsoComponentV1, SyncComponent, - TabsComponent, TabsV2Component, TwoFactorComponent, TwoFactorOptionsComponent, diff --git a/apps/browser/src/popup/main.ts b/apps/browser/src/popup/main.ts index db634ea2e2c..8ffe0743bf6 100644 --- a/apps/browser/src/popup/main.ts +++ b/apps/browser/src/popup/main.ts @@ -1,7 +1,7 @@ import { enableProdMode } from "@angular/core"; import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; -import { PopupWidthService } from "../platform/popup/layout/popup-width.service"; +import { PopupSizeService } from "../platform/popup/layout/popup-size.service"; import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service"; require("./scss/popup.scss"); @@ -10,7 +10,7 @@ require("./scss/tailwind.css"); import { AppModule } from "./app.module"; // We put these first to minimize the delay in window changing. -PopupWidthService.initBodyWidthFromLocalStorage(); +PopupSizeService.initBodyWidthFromLocalStorage(); // Should be removed once we deprecate support for Safari 16.0 and older. See Jira ticket [PM-1861] if (BrowserPlatformUtilsService.shouldApplySafariHeightFix(window)) { document.documentElement.classList.add("safari_height_fix"); diff --git a/apps/browser/src/popup/scss/base.scss b/apps/browser/src/popup/scss/base.scss index 49847dd7778..554396fa038 100644 --- a/apps/browser/src/popup/scss/base.scss +++ b/apps/browser/src/popup/scss/base.scss @@ -19,8 +19,8 @@ body { } body { - min-width: 380px; - height: 600px !important; + width: 380px; + height: 600px; position: relative; min-height: 100vh; overflow: hidden; @@ -33,18 +33,20 @@ body { } &.body-sm { - width: 375px !important; - height: 500px !important; + height: 500px; } &.body-xs { - width: 375px !important; - height: 300px !important; + height: 400px; + } + + &.body-xxs { + height: 300px; } &.body-full { - width: 100% !important; - height: 100% !important; + width: 100%; + height: 100%; } } diff --git a/apps/browser/src/popup/services/init.service.ts b/apps/browser/src/popup/services/init.service.ts index 9e6471eaf28..24661438495 100644 --- a/apps/browser/src/popup/services/init.service.ts +++ b/apps/browser/src/popup/services/init.service.ts @@ -1,5 +1,5 @@ import { DOCUMENT } from "@angular/common"; -import { Inject, Injectable } from "@angular/core"; +import { inject, Inject, Injectable } from "@angular/core"; import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; @@ -10,8 +10,11 @@ import { StateService } from "@bitwarden/common/platform/abstractions/state.serv import { BrowserApi } from "../../platform/browser/browser-api"; import BrowserPopupUtils from "../../platform/popup/browser-popup-utils"; +import { PopupSizeService } from "../../platform/popup/layout/popup-size.service"; @Injectable() export class InitService { + private sizeService = inject(PopupSizeService); + constructor( private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, @@ -28,13 +31,7 @@ export class InitService { await this.i18nService.init(); this.twoFactorService.init(); - if (!BrowserPopupUtils.inPopup(window)) { - window.document.body.classList.add("body-full"); - } else if (window.screen.availHeight < 600) { - window.document.body.classList.add("body-xs"); - } else if (window.screen.availHeight <= 800) { - window.document.body.classList.add("body-sm"); - } + await this.sizeService.init(); const htmlEl = window.document.documentElement; this.themingService.applyThemeChangesTo(this.document); diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 7014d908ac3..5b27833636f 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -152,7 +152,6 @@ import { ForegroundSyncService } from "../../platform/sync/foreground-sync.servi import { fromChromeRuntimeMessaging } from "../../platform/utils/from-chrome-runtime-messaging"; import { ExtensionLockComponentService } from "../../services/extension-lock-component.service"; import { ForegroundVaultTimeoutService } from "../../services/vault-timeout/foreground-vault-timeout.service"; -import { BrowserSendStateService } from "../../tools/popup/services/browser-send-state.service"; import { FilePopoutUtilsService } from "../../tools/popup/services/file-popout-utils.service"; import { Fido2UserVerificationService } from "../../vault/services/fido2-user-verification.service"; import { VaultBrowserStateService } from "../../vault/services/vault-browser-state.service"; @@ -473,11 +472,6 @@ const safeProviders: SafeProvider[] = [ useClass: UserNotificationSettingsService, deps: [StateProvider], }), - safeProvider({ - provide: BrowserSendStateService, - useClass: BrowserSendStateService, - deps: [StateProvider], - }), safeProvider({ provide: MessageListener, useFactory: (subject: Subject>>, ngZone: NgZone) => diff --git a/apps/browser/src/popup/tabs.component.html b/apps/browser/src/popup/tabs.component.html deleted file mode 100644 index fd04967b914..00000000000 --- a/apps/browser/src/popup/tabs.component.html +++ /dev/null @@ -1,57 +0,0 @@ -
    - - -
    diff --git a/apps/browser/src/popup/tabs.component.ts b/apps/browser/src/popup/tabs.component.ts deleted file mode 100644 index 7546c9ca13b..00000000000 --- a/apps/browser/src/popup/tabs.component.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Component, OnInit } from "@angular/core"; - -import BrowserPopupUtils from "../platform/popup/browser-popup-utils"; - -@Component({ - selector: "app-tabs", - templateUrl: "tabs.component.html", -}) -export class TabsComponent implements OnInit { - showCurrentTab = true; - - ngOnInit() { - this.showCurrentTab = !BrowserPopupUtils.inPopout(window); - } -} diff --git a/apps/browser/src/tools/popup/generator/generator.component.html b/apps/browser/src/tools/popup/generator/generator.component.html deleted file mode 100644 index d92d32a5623..00000000000 --- a/apps/browser/src/tools/popup/generator/generator.component.html +++ /dev/null @@ -1,588 +0,0 @@ - -
    - - -
    -

    - {{ "generator" | i18n }} -

    -
    - -
    -
    -
    - - {{ "passwordGeneratorPolicyInEffect" | i18n }} - -
    -
    -
    - - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    - -
    - - -
    -
    -
    -
    - -
    -

    - {{ "options" | i18n }} -

    -
    -
    - -
    - - -
    -
    -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - -
    -
    - {{ "passwordMinLength" | i18n }} - - {{ passwordOptionsMinLengthForReader$ | async }} - - {{ passwordOptions.minLength }} -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    - - -
    -
    -
    -
    - -
    - -
    -

    - {{ "options" | i18n }} -

    -
    -
    - -
    - - -
    -
    -
    -
    -
    -
    -
    - - -
    - -
    - - -
    -
    - - -
    -
    - -
    - - -
    -
    - -
    - - -
    -
    - - -
    -
    - - -
    -
    - -
    - - -
    -
    - -
    - - -
    -
    - -
    - - -
    -
    - - -
    -
    -
    -
    -
    -
    -
    - - -
    -
    - -
    - - -
    -
    -
    - - -
    -
    -
    -
    -
    -
    - - -
    -
    - -
    - - -
    -
    -
    - - -
    -
    -
    -
    -
    -
    - - -
    -
    - - -
    -
    -
    -
    -
    diff --git a/apps/browser/src/tools/popup/generator/generator.component.ts b/apps/browser/src/tools/popup/generator/generator.component.ts deleted file mode 100644 index d744c58f4d1..00000000000 --- a/apps/browser/src/tools/popup/generator/generator.component.ts +++ /dev/null @@ -1,88 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Location } from "@angular/common"; -import { Component, NgZone, OnInit } from "@angular/core"; -import { ActivatedRoute } from "@angular/router"; -import { firstValueFrom } from "rxjs"; - -import { GeneratorComponent as BaseGeneratorComponent } from "@bitwarden/angular/tools/generator/components/generator.component"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { AddEditCipherInfo } from "@bitwarden/common/vault/types/add-edit-cipher-info"; -import { ToastService } from "@bitwarden/components"; -import { - PasswordGenerationServiceAbstraction, - UsernameGenerationServiceAbstraction, -} from "@bitwarden/generator-legacy"; - -@Component({ - selector: "app-generator", - templateUrl: "generator.component.html", -}) -export class GeneratorComponent extends BaseGeneratorComponent implements OnInit { - private addEditCipherInfo: AddEditCipherInfo; - private cipherState: CipherView; - private cipherService: CipherService; - - constructor( - passwordGenerationService: PasswordGenerationServiceAbstraction, - usernameGenerationService: UsernameGenerationServiceAbstraction, - platformUtilsService: PlatformUtilsService, - i18nService: I18nService, - accountService: AccountService, - cipherService: CipherService, - route: ActivatedRoute, - logService: LogService, - ngZone: NgZone, - private location: Location, - toastService: ToastService, - ) { - super( - passwordGenerationService, - usernameGenerationService, - platformUtilsService, - accountService, - i18nService, - logService, - route, - ngZone, - window, - toastService, - ); - this.cipherService = cipherService; - } - - async ngOnInit() { - this.addEditCipherInfo = await firstValueFrom(this.cipherService.addEditCipherInfo$); - if (this.addEditCipherInfo != null) { - this.cipherState = this.addEditCipherInfo.cipher; - } - this.comingFromAddEdit = this.cipherState != null; - if (this.cipherState?.login?.hasUris) { - this.usernameWebsite = this.cipherState.login.uris[0].hostname; - } - await super.ngOnInit(); - } - - select() { - super.select(); - if (this.type === "password") { - this.cipherState.login.password = this.password; - } else if (this.type === "username") { - this.cipherState.login.username = this.username; - } - this.addEditCipherInfo.cipher = this.cipherState; - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.cipherService.setAddEditCipherInfo(this.addEditCipherInfo); - this.close(); - } - - close() { - this.location.back(); - } -} diff --git a/apps/browser/src/tools/popup/generator/password-generator-history.component.html b/apps/browser/src/tools/popup/generator/password-generator-history.component.html deleted file mode 100644 index 8f4a246fc5e..00000000000 --- a/apps/browser/src/tools/popup/generator/password-generator-history.component.html +++ /dev/null @@ -1,48 +0,0 @@ -
    -
    - -
    -

    - {{ "passwordHistory" | i18n }} -

    -
    - -
    -
    -
    -
    -
    -
    -
    -
    -
    - {{ h.date | date: "medium" }} -
    -
    -
    - -
    -
    -
    -
    -
    -

    {{ "noPasswordsInList" | i18n }}

    -
    -
    diff --git a/apps/browser/src/tools/popup/generator/password-generator-history.component.ts b/apps/browser/src/tools/popup/generator/password-generator-history.component.ts deleted file mode 100644 index 2436ae51a7d..00000000000 --- a/apps/browser/src/tools/popup/generator/password-generator-history.component.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Location } from "@angular/common"; -import { Component } from "@angular/core"; - -import { PasswordGeneratorHistoryComponent as BasePasswordGeneratorHistoryComponent } from "@bitwarden/angular/tools/generator/components/password-generator-history.component"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { ToastService } from "@bitwarden/components"; -import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; - -@Component({ - selector: "app-password-generator-history", - templateUrl: "password-generator-history.component.html", -}) -export class PasswordGeneratorHistoryComponent extends BasePasswordGeneratorHistoryComponent { - constructor( - passwordGenerationService: PasswordGenerationServiceAbstraction, - platformUtilsService: PlatformUtilsService, - i18nService: I18nService, - private location: Location, - toastService: ToastService, - ) { - super(passwordGenerationService, platformUtilsService, i18nService, window, toastService); - } - - close() { - this.location.back(); - } -} diff --git a/apps/browser/src/tools/popup/send/components/send-list.component.html b/apps/browser/src/tools/popup/send/components/send-list.component.html deleted file mode 100644 index 05c8e3e3754..00000000000 --- a/apps/browser/src/tools/popup/send/components/send-list.component.html +++ /dev/null @@ -1,98 +0,0 @@ -
    - -
    - - - -
    -
    diff --git a/apps/browser/src/tools/popup/send/components/send-list.component.ts b/apps/browser/src/tools/popup/send/components/send-list.component.ts deleted file mode 100644 index bab818cce6a..00000000000 --- a/apps/browser/src/tools/popup/send/components/send-list.component.ts +++ /dev/null @@ -1,38 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, EventEmitter, Input, Output } from "@angular/core"; - -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; -import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; - -@Component({ - selector: "app-send-list", - templateUrl: "send-list.component.html", -}) -export class SendListComponent { - @Input() sends: SendView[]; - @Input() title: string; - @Input() disabledByPolicy = false; - @Output() onSelected = new EventEmitter(); - @Output() onCopySendLink = new EventEmitter(); - @Output() onRemovePassword = new EventEmitter(); - @Output() onDeleteSend = new EventEmitter(); - - sendType = SendType; - - selectSend(s: SendView) { - this.onSelected.emit(s); - } - - copySendLink(s: SendView) { - this.onCopySendLink.emit(s); - } - - removePassword(s: SendView) { - this.onRemovePassword.emit(s); - } - - delete(s: SendView) { - this.onDeleteSend.emit(s); - } -} diff --git a/apps/browser/src/tools/popup/send/send-add-edit.component.html b/apps/browser/src/tools/popup/send/send-add-edit.component.html deleted file mode 100644 index 38c8a8175bb..00000000000 --- a/apps/browser/src/tools/popup/send/send-add-edit.component.html +++ /dev/null @@ -1,411 +0,0 @@ -
    -
    -
    - -
    -

    - {{ title }} -

    -
    - -
    -
    -
    - - - {{ "sendDisabledWarning" | i18n }} - - - {{ "sendOptionsPolicyInEffect" | i18n }} - - - - -
    -
    -
    - - -
    -
    - -
    - -
    -
    -
    - -
    - - -
    -
    -
    -
    - -
    -
    -
    - -
    {{ send.file.fileName }} ({{ send.file.sizeName }})
    -
    -
    - - -
    -
    - -
    - -
    -
    -
    - - -
    -
    - -
    -
    - - -
    -
    -
    - -
    -

    - {{ "share" | i18n }} -

    -
    - -
    - - -
    -
    -
    - -
    -

    - -

    -
    -
    - -
    -
    - -
    - - -
    -
    - -
    -
    -
    - - -
    -
    - -
    - - -
    -
    - -
    - - -
    -
    - -
    -
    -
    -
    - - -
    - -
    -
    - -
    - -
    -
    -
    - - -
    -
    - -
    - -
    -
    -
    - - -
    -
    -
    - -
    -
    -
    -
    - - - -
    -
    - -
    -
    -
    - -
    - -
    -
    -
    - - -
    -
    - -
    - -
    -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    -
    - -
    -
    - -
    -
    -
    -
    diff --git a/apps/browser/src/tools/popup/send/send-add-edit.component.ts b/apps/browser/src/tools/popup/send/send-add-edit.component.ts deleted file mode 100644 index 66b0355bb76..00000000000 --- a/apps/browser/src/tools/popup/send/send-add-edit.component.ts +++ /dev/null @@ -1,140 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { DatePipe, Location } from "@angular/common"; -import { Component, OnInit } from "@angular/core"; -import { FormBuilder } from "@angular/forms"; -import { ActivatedRoute, Router } from "@angular/router"; -import { first } from "rxjs/operators"; - -import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/tools/send/add-edit.component"; -import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; -import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { DialogService, ToastService } from "@bitwarden/components"; - -import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils"; -import { FilePopoutUtilsService } from "../services/file-popout-utils.service"; - -@Component({ - selector: "app-send-add-edit", - templateUrl: "send-add-edit.component.html", -}) -// eslint-disable-next-line rxjs-angular/prefer-takeuntil -export class SendAddEditComponent extends BaseAddEditComponent implements OnInit { - // Options header - showOptions = false; - // File visibility - isFirefox = false; - inPopout = false; - showFileSelector = false; - - constructor( - i18nService: I18nService, - platformUtilsService: PlatformUtilsService, - stateService: StateService, - messagingService: MessagingService, - policyService: PolicyService, - environmentService: EnvironmentService, - datePipe: DatePipe, - sendService: SendService, - private route: ActivatedRoute, - private router: Router, - private location: Location, - logService: LogService, - sendApiService: SendApiService, - dialogService: DialogService, - formBuilder: FormBuilder, - private filePopoutUtilsService: FilePopoutUtilsService, - billingAccountProfileStateService: BillingAccountProfileStateService, - accountService: AccountService, - toastService: ToastService, - ) { - super( - i18nService, - platformUtilsService, - environmentService, - datePipe, - sendService, - messagingService, - policyService, - logService, - stateService, - sendApiService, - dialogService, - formBuilder, - billingAccountProfileStateService, - accountService, - toastService, - ); - } - - popOutWindow() { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - BrowserPopupUtils.openCurrentPagePopout(window); - } - - async ngOnInit() { - // File visibility - this.showFileSelector = - !this.editMode && !this.filePopoutUtilsService.showFilePopoutMessage(window); - this.inPopout = BrowserPopupUtils.inPopout(window); - this.isFirefox = this.platformUtilsService.isFirefox(); - - // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe - this.route.queryParams.pipe(first()).subscribe(async (params) => { - if (params.sendId) { - this.sendId = params.sendId; - } - if (params.type) { - const type = parseInt(params.type, null); - this.type = type; - } - await super.ngOnInit(); - }); - - window.setTimeout(() => { - if (!this.editMode) { - document.getElementById("name").focus(); - } - }, 200); - } - - async submit(): Promise { - if (await super.submit()) { - this.cancel(); - return true; - } - - return false; - } - - async delete(): Promise { - if (await super.delete()) { - this.cancel(); - return true; - } - - return false; - } - - cancel() { - // If true, the window was pop'd out on the add-send page. location.back will not work - const isPopup = (window as any)?.previousPopupUrl?.startsWith("/add-send") ?? false; - if (!isPopup) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["tabs/send"]); - } else { - this.location.back(); - } - } -} diff --git a/apps/browser/src/tools/popup/send/send-groupings.component.html b/apps/browser/src/tools/popup/send/send-groupings.component.html deleted file mode 100644 index 213afdfa227..00000000000 --- a/apps/browser/src/tools/popup/send/send-groupings.component.html +++ /dev/null @@ -1,119 +0,0 @@ - -
    - -
    -

    {{ "send" | i18n }}

    - -
    - -
    -
    -
    - - {{ "sendDisabledWarning" | i18n }} - -
    - - - -

    {{ "noItemsInList" | i18n }}

    - -
    -
    - -
    -

    - {{ "types" | i18n }} -

    -
    - - -
    -
    -
    -

    - {{ "allSends" | i18n }} -
    {{ sends.length }}
    -

    -
    - -
    -
    -
    - -
    -

    {{ "noItemsInList" | i18n }}

    -
    -
    -
    - - -
    -
    -
    -
    diff --git a/apps/browser/src/tools/popup/send/send-groupings.component.ts b/apps/browser/src/tools/popup/send/send-groupings.component.ts deleted file mode 100644 index 80c2de1cdac..00000000000 --- a/apps/browser/src/tools/popup/send/send-groupings.component.ts +++ /dev/null @@ -1,203 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core"; -import { Router } from "@angular/router"; - -import { SendComponent as BaseSendComponent } from "@bitwarden/angular/tools/send/send.component"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; -import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; -import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; -import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; -import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { DialogService, ToastService } from "@bitwarden/components"; - -import { BrowserSendComponentState } from "../../../models/browserSendComponentState"; -import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils"; -import { BrowserSendStateService } from "../services/browser-send-state.service"; - -const ComponentId = "SendComponent"; - -@Component({ - selector: "app-send-groupings", - templateUrl: "send-groupings.component.html", -}) -export class SendGroupingsComponent extends BaseSendComponent implements OnInit, OnDestroy { - // Header - showLeftHeader = true; - // State Handling - state: BrowserSendComponentState; - private loadedTimeout: number; - - constructor( - sendService: SendService, - i18nService: I18nService, - platformUtilsService: PlatformUtilsService, - environmentService: EnvironmentService, - ngZone: NgZone, - policyService: PolicyService, - searchService: SearchService, - private stateService: BrowserSendStateService, - private router: Router, - private syncService: SyncService, - private changeDetectorRef: ChangeDetectorRef, - private broadcasterService: BroadcasterService, - logService: LogService, - sendApiService: SendApiService, - dialogService: DialogService, - toastService: ToastService, - ) { - super( - sendService, - i18nService, - platformUtilsService, - environmentService, - ngZone, - searchService, - policyService, - logService, - sendApiService, - dialogService, - toastService, - ); - this.onSuccessfulLoad = async () => { - this.selectAll(); - }; - } - - async ngOnInit() { - // Determine Header details - this.showLeftHeader = !( - BrowserPopupUtils.inSidebar(window) && this.platformUtilsService.isFirefox() - ); - // Clear state of Send Type Component - await this.stateService.setBrowserSendTypeComponentState(null); - // Let super class finish - await super.ngOnInit(); - // Handle State Restore if necessary - const restoredScopeState = await this.restoreState(); - if (this.state?.searchText != null) { - this.searchText = this.state.searchText; - } - - if (!this.syncService.syncInProgress) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.load(); - } else { - this.loadedTimeout = window.setTimeout(() => { - if (!this.loaded) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.load(); - } - }, 5000); - } - - if (!this.syncService.syncInProgress || restoredScopeState) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - BrowserPopupUtils.setContentScrollY(window, this.state?.scrollY); - } - - // Load all sends if sync completed in background - this.broadcasterService.subscribe(ComponentId, (message: any) => { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.ngZone.run(async () => { - switch (message.command) { - case "syncCompleted": - window.setTimeout(() => { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.load(); - }, 500); - break; - default: - break; - } - - this.changeDetectorRef.detectChanges(); - }); - }); - } - - ngOnDestroy() { - // Remove timeout - if (this.loadedTimeout != null) { - window.clearTimeout(this.loadedTimeout); - } - // Save state - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.saveState(); - // Unsubscribe - this.broadcasterService.unsubscribe(ComponentId); - } - - async selectType(type: SendType) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/send-type"], { queryParams: { type: type } }); - } - - async selectSend(s: SendView) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/edit-send"], { queryParams: { sendId: s.id } }); - } - - async addSend() { - if (this.disableSend) { - return; - } - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/add-send"]); - } - - async removePassword(s: SendView): Promise { - if (this.disableSend) { - return; - } - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - super.removePassword(s); - } - - showSearching() { - return this.hasSearched || (!this.searchPending && this.isSearchable); - } - - getSendCount(sends: SendView[], type: SendType): number { - return sends.filter((s) => s.type === type).length; - } - - private async saveState() { - this.state = Object.assign(new BrowserSendComponentState(), { - scrollY: BrowserPopupUtils.getContentScrollY(window), - searchText: this.searchText, - sends: this.sends, - }); - await this.stateService.setBrowserSendComponentState(this.state); - } - - private async restoreState(): Promise { - this.state = await this.stateService.getBrowserSendComponentState(); - if (this.state == null) { - return false; - } - - if (this.state.sends != null) { - this.sends = this.state.sends; - } - - return true; - } -} diff --git a/apps/browser/src/tools/popup/send/send-type.component.html b/apps/browser/src/tools/popup/send/send-type.component.html deleted file mode 100644 index 0ad6bed2881..00000000000 --- a/apps/browser/src/tools/popup/send/send-type.component.html +++ /dev/null @@ -1,68 +0,0 @@ -
    -
    - -
    -

    {{ "send" | i18n }}

    - -
    - -
    -
    -
    - - {{ "sendDisabledWarning" | i18n }} - -
    - - -

    {{ "noItemsInList" | i18n }}

    - -
    -
    -
    -

    - {{ groupingTitle }} - {{ filteredSends.length }} -

    -
    - - -
    -
    -
    diff --git a/apps/browser/src/tools/popup/send/send-type.component.ts b/apps/browser/src/tools/popup/send/send-type.component.ts deleted file mode 100644 index bcc24b443fc..00000000000 --- a/apps/browser/src/tools/popup/send/send-type.component.ts +++ /dev/null @@ -1,190 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Location } from "@angular/common"; -import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit } from "@angular/core"; -import { ActivatedRoute, Router } from "@angular/router"; -import { first } from "rxjs/operators"; - -import { SendComponent as BaseSendComponent } from "@bitwarden/angular/tools/send/send.component"; -import { SearchService } from "@bitwarden/common/abstractions/search.service"; -import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; -import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; -import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; -import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; -import { DialogService, ToastService } from "@bitwarden/components"; - -import { BrowserComponentState } from "../../../models/browserComponentState"; -import BrowserPopupUtils from "../../../platform/popup/browser-popup-utils"; -import { BrowserSendStateService } from "../services/browser-send-state.service"; - -const ComponentId = "SendTypeComponent"; - -@Component({ - selector: "app-send-type", - templateUrl: "send-type.component.html", -}) -export class SendTypeComponent extends BaseSendComponent implements OnInit, OnDestroy { - groupingTitle: string; - // State Handling - state: BrowserComponentState; - private refreshTimeout: number; - private applySavedState = true; - - constructor( - sendService: SendService, - i18nService: I18nService, - platformUtilsService: PlatformUtilsService, - environmentService: EnvironmentService, - ngZone: NgZone, - policyService: PolicyService, - searchService: SearchService, - private stateService: BrowserSendStateService, - private route: ActivatedRoute, - private location: Location, - private changeDetectorRef: ChangeDetectorRef, - private broadcasterService: BroadcasterService, - private router: Router, - logService: LogService, - sendApiService: SendApiService, - dialogService: DialogService, - toastService: ToastService, - ) { - super( - sendService, - i18nService, - platformUtilsService, - environmentService, - ngZone, - searchService, - policyService, - logService, - sendApiService, - dialogService, - toastService, - ); - this.onSuccessfulLoad = async () => { - this.selectType(this.type); - }; - this.applySavedState = - (window as any).previousPopupUrl != null && - !(window as any).previousPopupUrl.startsWith("/send-type"); - } - - async ngOnInit() { - // Let super class finish - await super.ngOnInit(); - // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe - this.route.queryParams.pipe(first()).subscribe(async (params) => { - if (this.applySavedState) { - this.state = await this.stateService.getBrowserSendTypeComponentState(); - if (this.state?.searchText != null) { - this.searchText = this.state.searchText; - } - } - - if (params.type != null) { - this.type = parseInt(params.type, null); - switch (this.type) { - case SendType.Text: - this.groupingTitle = this.i18nService.t("sendTypeText"); - break; - case SendType.File: - this.groupingTitle = this.i18nService.t("sendTypeFile"); - break; - default: - break; - } - await this.load((s) => s.type === this.type); - } - - // Restore state and remove reference - if (this.applySavedState && this.state != null) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - BrowserPopupUtils.setContentScrollY(window, this.state?.scrollY); - } - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.stateService.setBrowserSendTypeComponentState(null); - }); - - // Refresh Send list if sync completed in background - this.broadcasterService.subscribe(ComponentId, (message: any) => { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.ngZone.run(async () => { - switch (message.command) { - case "syncCompleted": - if (message.successfully) { - this.refreshTimeout = window.setTimeout(() => { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.refresh(); - }, 500); - } - break; - default: - break; - } - - this.changeDetectorRef.detectChanges(); - }); - }); - } - - ngOnDestroy() { - // Remove timeout - if (this.refreshTimeout != null) { - window.clearTimeout(this.refreshTimeout); - } - // Save state - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.saveState(); - // Unsubscribe - this.broadcasterService.unsubscribe(ComponentId); - } - - async selectSend(s: SendView) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/edit-send"], { queryParams: { sendId: s.id } }); - } - - async addSend() { - if (this.disableSend) { - return; - } - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/add-send"], { queryParams: { type: this.type } }); - } - - async removePassword(s: SendView): Promise { - if (this.disableSend) { - return; - } - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - super.removePassword(s); - } - - back() { - (window as any).routeDirection = "b"; - this.location.back(); - } - - private async saveState() { - this.state = { - scrollY: BrowserPopupUtils.getContentScrollY(window), - searchText: this.searchText, - }; - await this.stateService.setBrowserSendTypeComponentState(this.state); - } -} diff --git a/apps/browser/src/tools/popup/services/browser-send-state.service.spec.ts b/apps/browser/src/tools/popup/services/browser-send-state.service.spec.ts deleted file mode 100644 index 6f0ae1455ad..00000000000 --- a/apps/browser/src/tools/popup/services/browser-send-state.service.spec.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - FakeAccountService, - mockAccountServiceWith, -} from "@bitwarden/common/../spec/fake-account-service"; -import { FakeStateProvider } from "@bitwarden/common/../spec/fake-state-provider"; -import { awaitAsync } from "@bitwarden/common/../spec/utils"; - -import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { UserId } from "@bitwarden/common/types/guid"; - -import { BrowserComponentState } from "../../../models/browserComponentState"; -import { BrowserSendComponentState } from "../../../models/browserSendComponentState"; - -import { BrowserSendStateService } from "./browser-send-state.service"; - -describe("Browser Send State Service", () => { - let stateProvider: FakeStateProvider; - - let accountService: FakeAccountService; - let stateService: BrowserSendStateService; - const mockUserId = Utils.newGuid() as UserId; - - beforeEach(() => { - accountService = mockAccountServiceWith(mockUserId); - stateProvider = new FakeStateProvider(accountService); - - stateService = new BrowserSendStateService(stateProvider); - }); - - describe("getBrowserSendComponentState", () => { - it("should return BrowserSendComponentState", async () => { - const state = new BrowserSendComponentState(); - state.scrollY = 0; - state.searchText = "test"; - - await stateService.setBrowserSendComponentState(state); - - await awaitAsync(); - - const actual = await stateService.getBrowserSendComponentState(); - expect(actual).toStrictEqual(state); - }); - }); - - describe("getBrowserSendTypeComponentState", () => { - it("should return BrowserComponentState", async () => { - const state = new BrowserComponentState(); - state.scrollY = 0; - state.searchText = "test"; - - await stateService.setBrowserSendTypeComponentState(state); - - await awaitAsync(); - - const actual = await stateService.getBrowserSendTypeComponentState(); - expect(actual).toStrictEqual(state); - }); - }); -}); diff --git a/apps/browser/src/tools/popup/services/browser-send-state.service.ts b/apps/browser/src/tools/popup/services/browser-send-state.service.ts deleted file mode 100644 index d6c5ae4fd10..00000000000 --- a/apps/browser/src/tools/popup/services/browser-send-state.service.ts +++ /dev/null @@ -1,71 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Observable, firstValueFrom } from "rxjs"; - -import { ActiveUserState, StateProvider } from "@bitwarden/common/platform/state"; - -import { BrowserComponentState } from "../../../models/browserComponentState"; -import { BrowserSendComponentState } from "../../../models/browserSendComponentState"; - -import { BROWSER_SEND_COMPONENT, BROWSER_SEND_TYPE_COMPONENT } from "./key-definitions"; - -/** Get or set the active user's component state for the Send browser component - */ -export class BrowserSendStateService { - /** Observable that contains the current state for active user Sends including the send data and type counts - * along with the search text and scroll position - */ - browserSendComponentState$: Observable; - - /** Observable that contains the current state for active user Sends that only includes the search text - * and scroll position - */ - browserSendTypeComponentState$: Observable; - - private activeUserBrowserSendComponentState: ActiveUserState; - private activeUserBrowserSendTypeComponentState: ActiveUserState; - - constructor(protected stateProvider: StateProvider) { - this.activeUserBrowserSendComponentState = this.stateProvider.getActive(BROWSER_SEND_COMPONENT); - this.browserSendComponentState$ = this.activeUserBrowserSendComponentState.state$; - - this.activeUserBrowserSendTypeComponentState = this.stateProvider.getActive( - BROWSER_SEND_TYPE_COMPONENT, - ); - this.browserSendTypeComponentState$ = this.activeUserBrowserSendTypeComponentState.state$; - } - - /** Get the active user's browser send component state - * @returns { BrowserSendComponentState } contains the sends and type counts along with the scroll position and search text for the - * send component on the browser - */ - async getBrowserSendComponentState(): Promise { - return await firstValueFrom(this.browserSendComponentState$); - } - - /** Set the active user's browser send component state - * @param { BrowserSendComponentState } value sets the sends along with the scroll position and search text for - * the send component on the browser - */ - async setBrowserSendComponentState(value: BrowserSendComponentState): Promise { - await this.activeUserBrowserSendComponentState.update(() => value, { - shouldUpdate: (current) => !(current == null && value == null), - }); - } - - /** Get the active user's browser component state - * @returns { BrowserComponentState } contains the scroll position and search text for the sends menu on the browser - */ - async getBrowserSendTypeComponentState(): Promise { - return await firstValueFrom(this.browserSendTypeComponentState$); - } - - /** Set the active user's browser component state - * @param { BrowserComponentState } value set the scroll position and search text for the send component on the browser - */ - async setBrowserSendTypeComponentState(value: BrowserComponentState): Promise { - await this.activeUserBrowserSendTypeComponentState.update(() => value, { - shouldUpdate: (current) => !(current == null && value == null), - }); - } -} diff --git a/apps/browser/src/tools/popup/services/key-definitions.spec.ts b/apps/browser/src/tools/popup/services/key-definitions.spec.ts deleted file mode 100644 index 7517771669c..00000000000 --- a/apps/browser/src/tools/popup/services/key-definitions.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Jsonify } from "type-fest"; - -import { BrowserSendComponentState } from "../../../models/browserSendComponentState"; - -import { BROWSER_SEND_COMPONENT, BROWSER_SEND_TYPE_COMPONENT } from "./key-definitions"; - -describe("Key definitions", () => { - describe("BROWSER_SEND_COMPONENT", () => { - it("should deserialize BrowserSendComponentState", () => { - const keyDef = BROWSER_SEND_COMPONENT; - - const expectedState = { - scrollY: 0, - searchText: "test", - }; - - const result = keyDef.deserializer( - JSON.parse(JSON.stringify(expectedState)) as Jsonify, - ); - - expect(result).toEqual(expectedState); - }); - }); - - describe("BROWSER_SEND_TYPE_COMPONENT", () => { - it("should deserialize BrowserComponentState", () => { - const keyDef = BROWSER_SEND_TYPE_COMPONENT; - - const expectedState = { - scrollY: 0, - searchText: "test", - }; - - const result = keyDef.deserializer(JSON.parse(JSON.stringify(expectedState))); - - expect(result).toEqual(expectedState); - }); - }); -}); diff --git a/apps/browser/src/tools/popup/services/key-definitions.ts b/apps/browser/src/tools/popup/services/key-definitions.ts deleted file mode 100644 index 87c6da126cb..00000000000 --- a/apps/browser/src/tools/popup/services/key-definitions.ts +++ /dev/null @@ -1,27 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Jsonify } from "type-fest"; - -import { BROWSER_SEND_MEMORY, UserKeyDefinition } from "@bitwarden/common/platform/state"; - -import { BrowserComponentState } from "../../../models/browserComponentState"; -import { BrowserSendComponentState } from "../../../models/browserSendComponentState"; - -export const BROWSER_SEND_COMPONENT = new UserKeyDefinition( - BROWSER_SEND_MEMORY, - "browser_send_component", - { - deserializer: (obj: Jsonify) => - BrowserSendComponentState.fromJSON(obj), - clearOn: ["logout", "lock"], - }, -); - -export const BROWSER_SEND_TYPE_COMPONENT = new UserKeyDefinition( - BROWSER_SEND_MEMORY, - "browser_send_type_component", - { - deserializer: (obj: Jsonify) => BrowserComponentState.fromJSON(obj), - clearOn: ["logout", "lock"], - }, -); diff --git a/apps/browser/src/tools/popup/settings/about-page/about-page.component.html b/apps/browser/src/tools/popup/settings/about-page/about-page.component.html deleted file mode 100644 index 7537c75bd9e..00000000000 --- a/apps/browser/src/tools/popup/settings/about-page/about-page.component.html +++ /dev/null @@ -1,63 +0,0 @@ -
    -
    - -
    -

    - {{ "about" | i18n }} -

    -
    - -
    -
    -
    -
    -
    - - - - - -
    -
    -
    diff --git a/apps/browser/src/tools/popup/settings/about-page/about-page.component.ts b/apps/browser/src/tools/popup/settings/about-page/about-page.component.ts deleted file mode 100644 index 7c3e87a92fb..00000000000 --- a/apps/browser/src/tools/popup/settings/about-page/about-page.component.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { CommonModule } from "@angular/common"; -import { Component } from "@angular/core"; -import { RouterModule } from "@angular/router"; -import { firstValueFrom } from "rxjs"; - -import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { DeviceType } from "@bitwarden/common/enums"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService } from "@bitwarden/components"; - -import { BrowserApi } from "../../../../platform/browser/browser-api"; -import { PopOutComponent } from "../../../../platform/popup/components/pop-out.component"; -import { AboutDialogComponent } from "../about-dialog/about-dialog.component"; - -const RateUrls = { - [DeviceType.ChromeExtension]: - "https://chromewebstore.google.com/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb/reviews", - [DeviceType.FirefoxExtension]: - "https://addons.mozilla.org/en-US/firefox/addon/bitwarden-password-manager/#reviews", - [DeviceType.OperaExtension]: - "https://addons.opera.com/en/extensions/details/bitwarden-free-password-manager/#feedback-container", - [DeviceType.EdgeExtension]: - "https://microsoftedge.microsoft.com/addons/detail/jbkfoedolllekgbhcbcoahefnbanhhlh", - [DeviceType.VivaldiExtension]: - "https://chromewebstore.google.com/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb/reviews", - [DeviceType.SafariExtension]: "https://apps.apple.com/app/bitwarden/id1352778147", -}; - -@Component({ - templateUrl: "about-page.component.html", - standalone: true, - imports: [CommonModule, JslibModule, RouterModule, PopOutComponent], -}) -export class AboutPageComponent { - constructor( - private dialogService: DialogService, - private environmentService: EnvironmentService, - private platformUtilsService: PlatformUtilsService, - ) {} - - about() { - this.dialogService.open(AboutDialogComponent); - } - - async launchHelp() { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "continueToHelpCenter" }, - content: { key: "continueToHelpCenterDesc" }, - type: "info", - acceptButtonText: { key: "continue" }, - }); - if (confirmed) { - await BrowserApi.createNewTab("https://bitwarden.com/help/"); - } - } - - async openWebVault() { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "continueToWebApp" }, - content: { key: "continueToWebAppDesc" }, - type: "info", - acceptButtonText: { key: "continue" }, - }); - if (confirmed) { - const env = await firstValueFrom(this.environmentService.environment$); - const url = env.getWebVaultUrl(); - await BrowserApi.createNewTab(url); - } - } - - async rate() { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "continueToBrowserExtensionStore" }, - content: { key: "continueToBrowserExtensionStoreDesc" }, - type: "info", - acceptButtonText: { key: "continue" }, - }); - if (confirmed) { - const deviceType = this.platformUtilsService.getDevice(); - await BrowserApi.createNewTab((RateUrls as any)[deviceType]); - } - } -} diff --git a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page.component.html b/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page.component.html deleted file mode 100644 index 8e7b3495365..00000000000 --- a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page.component.html +++ /dev/null @@ -1,76 +0,0 @@ -
    -
    - -
    -

    - {{ "moreFromBitwarden" | i18n }} -

    -
    - -
    -
    -
    -
    -
    -
    - -
    - - - - - -
    -
    -
    diff --git a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page.component.ts b/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page.component.ts deleted file mode 100644 index 1f26d40b349..00000000000 --- a/apps/browser/src/tools/popup/settings/about-page/more-from-bitwarden-page.component.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { CommonModule } from "@angular/common"; -import { Component } from "@angular/core"; -import { RouterModule } from "@angular/router"; -import { Observable, firstValueFrom } from "rxjs"; - -import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { DialogService } from "@bitwarden/components"; - -import { BrowserApi } from "../../../../platform/browser/browser-api"; -import { PopOutComponent } from "../../../../platform/popup/components/pop-out.component"; -import { FamiliesPolicyService } from "../../../../services/families-policy.service"; - -@Component({ - templateUrl: "more-from-bitwarden-page.component.html", - standalone: true, - imports: [CommonModule, JslibModule, RouterModule, PopOutComponent], -}) -export class MoreFromBitwardenPageComponent { - canAccessPremium$: Observable; - protected isFreeFamilyPolicyEnabled$: Observable; - protected hasSingleEnterpriseOrg$: Observable; - - constructor( - private dialogService: DialogService, - private billingAccountProfileStateService: BillingAccountProfileStateService, - private environmentService: EnvironmentService, - private familiesPolicyService: FamiliesPolicyService, - ) { - this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$; - this.hasSingleEnterpriseOrg$ = this.familiesPolicyService.hasSingleEnterpriseOrg$(); - this.isFreeFamilyPolicyEnabled$ = this.familiesPolicyService.isFreeFamilyPolicyEnabled$(); - } - - async openFreeBitwardenFamiliesPage() { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "continueToWebApp" }, - content: { key: "freeBitwardenFamiliesPageDesc" }, - type: "info", - acceptButtonText: { key: "continue" }, - }); - if (confirmed) { - const env = await firstValueFrom(this.environmentService.environment$); - const url = env.getWebVaultUrl(); - await BrowserApi.createNewTab(url + "/#/settings/sponsored-families"); - } - } - - async openBitwardenForBusinessPage() { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "continueToBitwardenDotCom" }, - content: { key: "bitwardenForBusinessPageDesc" }, - type: "info", - acceptButtonText: { key: "continue" }, - }); - if (confirmed) { - await BrowserApi.createNewTab("https://bitwarden.com/products/business/"); - } - } - - async openAuthenticatorPage() { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "continueToBitwardenDotCom" }, - content: { key: "continueToAuthenticatorPageDesc" }, - type: "info", - acceptButtonText: { key: "continue" }, - }); - if (confirmed) { - await BrowserApi.createNewTab("https://bitwarden.com/products/authenticator"); - } - } - - async openSecretsManagerPage() { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "continueToBitwardenDotCom" }, - content: { key: "continueToSecretsManagerPageDesc" }, - type: "info", - acceptButtonText: { key: "continue" }, - }); - if (confirmed) { - await BrowserApi.createNewTab("https://bitwarden.com/products/secrets-manager"); - } - } - - async openPasswordlessDotDevPage() { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "continueToBitwardenDotCom" }, - content: { key: "continueToPasswordlessDotDevPageDesc" }, - type: "info", - acceptButtonText: { key: "continue" }, - }); - if (confirmed) { - await BrowserApi.createNewTab("https://bitwarden.com/products/passwordless"); - } - } -} diff --git a/apps/browser/src/tools/popup/settings/export/export-browser-v2.component.ts b/apps/browser/src/tools/popup/settings/export/export-browser-v2.component.ts index cbb66cbcf5a..86131176a6e 100644 --- a/apps/browser/src/tools/popup/settings/export/export-browser-v2.component.ts +++ b/apps/browser/src/tools/popup/settings/export/export-browser-v2.component.ts @@ -1,6 +1,6 @@ import { CommonModule } from "@angular/common"; import { Component } from "@angular/core"; -import { Router, RouterLink } from "@angular/router"; +import { Router } from "@angular/router"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AsyncActionsModule, ButtonModule, DialogModule } from "@bitwarden/components"; @@ -16,7 +16,6 @@ import { PopupPageComponent } from "../../../../platform/popup/layout/popup-page standalone: true, imports: [ CommonModule, - RouterLink, JslibModule, DialogModule, AsyncActionsModule, diff --git a/apps/browser/src/tools/popup/settings/export/export-browser.component.html b/apps/browser/src/tools/popup/settings/export/export-browser.component.html deleted file mode 100644 index bccde32a68d..00000000000 --- a/apps/browser/src/tools/popup/settings/export/export-browser.component.html +++ /dev/null @@ -1,26 +0,0 @@ -
    -
    - -
    -

    - {{ "exportVault" | i18n }} -

    -
    - -
    -
    -
    -
    - -
    -
    diff --git a/apps/browser/src/tools/popup/settings/export/export-browser.component.ts b/apps/browser/src/tools/popup/settings/export/export-browser.component.ts deleted file mode 100644 index 3125e0a2934..00000000000 --- a/apps/browser/src/tools/popup/settings/export/export-browser.component.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { CommonModule } from "@angular/common"; -import { Component } from "@angular/core"; -import { Router, RouterLink } from "@angular/router"; - -import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { AsyncActionsModule, ButtonModule, DialogModule } from "@bitwarden/components"; -import { ExportComponent } from "@bitwarden/vault-export-ui"; - -@Component({ - templateUrl: "export-browser.component.html", - standalone: true, - imports: [ - CommonModule, - RouterLink, - JslibModule, - DialogModule, - AsyncActionsModule, - ButtonModule, - ExportComponent, - ], -}) -export class ExportBrowserComponent { - /** - * Used to control the disabled state of the Submit button - * Gets set indirectly by the disabled state being emitted from the sub-form when thier form gets disabled or the submit button is clicked - */ - protected disabled = false; - - /** - * Used to control the disabled state of the Submit button - * Gets set indirectly by the loading state being emitted from the sub-form when their form is loading or finished loading - */ - protected loading = false; - - constructor(private router: Router) {} - - protected async onSuccessfulExport(organizationId: string): Promise { - await this.router.navigate(["/vault-settings"]); - } -} diff --git a/apps/browser/src/tools/popup/settings/import/import-browser-v2.component.ts b/apps/browser/src/tools/popup/settings/import/import-browser-v2.component.ts index 16759057ed5..66cb5c62f48 100644 --- a/apps/browser/src/tools/popup/settings/import/import-browser-v2.component.ts +++ b/apps/browser/src/tools/popup/settings/import/import-browser-v2.component.ts @@ -1,6 +1,6 @@ import { CommonModule } from "@angular/common"; import { Component } from "@angular/core"; -import { Router, RouterLink } from "@angular/router"; +import { Router } from "@angular/router"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AsyncActionsModule, ButtonModule, DialogModule } from "@bitwarden/components"; @@ -16,7 +16,6 @@ import { PopupPageComponent } from "../../../../platform/popup/layout/popup-page standalone: true, imports: [ CommonModule, - RouterLink, JslibModule, DialogModule, AsyncActionsModule, diff --git a/apps/browser/src/tools/popup/settings/import/import-browser.component.html b/apps/browser/src/tools/popup/settings/import/import-browser.component.html deleted file mode 100644 index 67b5eb348ae..00000000000 --- a/apps/browser/src/tools/popup/settings/import/import-browser.component.html +++ /dev/null @@ -1,26 +0,0 @@ -
    -
    - -
    -

    - {{ "importData" | i18n }} -

    -
    - -
    -
    -
    -
    - -
    -
    diff --git a/apps/browser/src/tools/popup/settings/import/import-browser.component.ts b/apps/browser/src/tools/popup/settings/import/import-browser.component.ts deleted file mode 100644 index 7ee4877ce1a..00000000000 --- a/apps/browser/src/tools/popup/settings/import/import-browser.component.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { CommonModule } from "@angular/common"; -import { Component } from "@angular/core"; -import { Router, RouterLink } from "@angular/router"; - -import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { AsyncActionsModule, ButtonModule, DialogModule } from "@bitwarden/components"; -import { ImportComponent } from "@bitwarden/importer/ui"; - -@Component({ - templateUrl: "import-browser.component.html", - standalone: true, - imports: [ - CommonModule, - RouterLink, - JslibModule, - DialogModule, - AsyncActionsModule, - ButtonModule, - ImportComponent, - ], -}) -export class ImportBrowserComponent { - protected disabled = false; - protected loading = false; - - constructor(private router: Router) {} - - protected async onSuccessfulImport(organizationId: string): Promise { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["/tabs/settings"]); - } -} diff --git a/apps/browser/src/tools/popup/settings/settings.component.html b/apps/browser/src/tools/popup/settings/settings.component.html deleted file mode 100644 index c547229653e..00000000000 --- a/apps/browser/src/tools/popup/settings/settings.component.html +++ /dev/null @@ -1,63 +0,0 @@ - -
    -

    - {{ "settings" | i18n }} -

    -
    - -
    -
    -
    -
    -
    - - - - - - -
    -
    -
    diff --git a/apps/browser/src/tools/popup/settings/settings.component.ts b/apps/browser/src/tools/popup/settings/settings.component.ts deleted file mode 100644 index 973efc72038..00000000000 --- a/apps/browser/src/tools/popup/settings/settings.component.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Component } from "@angular/core"; - -@Component({ - selector: "tools-settings", - templateUrl: "settings.component.html", -}) -export class SettingsComponent { - constructor() {} -} diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.spec.ts index 4e222a554f7..cbec7903031 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.spec.ts @@ -171,7 +171,7 @@ describe("AddEditFolderDialogComponent", () => { it("deletes the folder", async () => { await component.deleteFolder(); - expect(deleteFolder).toHaveBeenCalledWith(folderView.id); + expect(deleteFolder).toHaveBeenCalledWith(folderView.id, ""); expect(showToast).toHaveBeenCalledWith({ variant: "success", title: null, diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.ts index 5fbd54d9d78..a50403cea2d 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit-folder-dialog/add-edit-folder-dialog.component.ts @@ -13,7 +13,7 @@ import { } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -67,6 +67,7 @@ export class AddEditFolderDialogComponent implements AfterViewInit, OnInit { name: ["", Validators.required], }); + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); private destroyRef = inject(DestroyRef); constructor( @@ -114,10 +115,10 @@ export class AddEditFolderDialogComponent implements AfterViewInit, OnInit { this.folder.name = this.folderForm.controls.name.value; try { - const activeUserId = await firstValueFrom(this.accountService.activeAccount$); - const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId.id); + const activeUserId = await firstValueFrom(this.activeUserId$); + const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId); const folder = await this.folderService.encrypt(this.folder, userKey); - await this.folderApiService.save(folder); + await this.folderApiService.save(folder, activeUserId); this.toastService.showToast({ variant: "success", @@ -144,7 +145,8 @@ export class AddEditFolderDialogComponent implements AfterViewInit, OnInit { } try { - await this.folderApiService.delete(this.folder.id); + const activeUserId = await firstValueFrom(this.activeUserId$); + await this.folderApiService.delete(this.folder.id, activeUserId); this.toastService.showToast({ variant: "success", title: null, 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 a2b2d1ce581..2d8c4857c1c 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 @@ -326,15 +326,15 @@ export class AddEditV2Component implements OnInit { switch (type) { case CipherType.Login: - return this.i18nService.t(partOne, this.i18nService.t("typeLogin").toLocaleLowerCase()); + return this.i18nService.t(partOne, this.i18nService.t("typeLogin")); case CipherType.Card: - return this.i18nService.t(partOne, this.i18nService.t("typeCard").toLocaleLowerCase()); + return this.i18nService.t(partOne, this.i18nService.t("typeCard")); case CipherType.Identity: - return this.i18nService.t(partOne, this.i18nService.t("typeIdentity").toLocaleLowerCase()); + return this.i18nService.t(partOne, this.i18nService.t("typeIdentity")); case CipherType.SecureNote: - return this.i18nService.t(partOne, this.i18nService.t("note").toLocaleLowerCase()); + return this.i18nService.t(partOne, this.i18nService.t("note")); case CipherType.SshKey: - return this.i18nService.t(partOne, this.i18nService.t("typeSshKey").toLocaleLowerCase()); + return this.i18nService.t(partOne, this.i18nService.t("typeSshKey")); } } } diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.html b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.html index 52ae387e8b9..7c4ea3e5b46 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.html @@ -6,4 +6,5 @@ (onRefresh)="refreshCurrentTab()" [description]="(showEmptyAutofillTip$ | async) ? ('autofillSuggestionsTip' | i18n) : null" showAutofillButton + [primaryActionAutofill]="clickItemsToAutofillVaultView" > diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts index 8e72d84053d..deba204bd71 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-vault-list-items/autofill-vault-list-items.component.ts @@ -1,8 +1,9 @@ import { CommonModule } from "@angular/common"; -import { Component } from "@angular/core"; -import { combineLatest, map, Observable } from "rxjs"; +import { Component, OnInit } from "@angular/core"; +import { combineLatest, firstValueFrom, map, Observable } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { IconButtonModule, @@ -31,7 +32,7 @@ import { VaultListItemsContainerComponent } from "../vault-list-items-container/ selector: "app-autofill-vault-list-items", templateUrl: "autofill-vault-list-items.component.html", }) -export class AutofillVaultListItemsComponent { +export class AutofillVaultListItemsComponent implements OnInit { /** * The list of ciphers that can be used to autofill the current page. * @protected @@ -45,6 +46,8 @@ export class AutofillVaultListItemsComponent { */ protected showRefresh: boolean = BrowserPopupUtils.inSidebar(window); + clickItemsToAutofillVaultView = false; + /** * Observable that determines whether the empty autofill tip should be shown. * The tip is shown when there are no login ciphers to autofill, no filter is applied, and autofill is allowed in @@ -65,10 +68,17 @@ export class AutofillVaultListItemsComponent { constructor( private vaultPopupItemsService: VaultPopupItemsService, private vaultPopupAutofillService: VaultPopupAutofillService, + private vaultSettingsService: VaultSettingsService, ) { // TODO: Migrate logic to show Autofill policy toast PM-8144 } + async ngOnInit() { + this.clickItemsToAutofillVaultView = await firstValueFrom( + this.vaultSettingsService.clickItemsToAutofillVaultView$, + ); + } + /** * Refreshes the current tab to re-populate the autofill ciphers. * @protected diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.html b/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.html index 973b1f9f1a4..fbfebe8efff 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.html @@ -1,53 +1,117 @@ - - - - - - - - + + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts index 9dca0eec7c2..5d3dee9018e 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts @@ -46,6 +46,13 @@ export class ItemMoreOptionsComponent implements OnInit { return this._cipher$.value; } + /** + * Flag to show view item menu option. Used when something else is + * assigned as the primary action for the item, such as autofill. + */ + @Input({ transform: booleanAttribute }) + showViewOption: boolean; + /** * Flag to hide the autofill menu options. Used for items that are * already in the autofill list suggestion. @@ -111,6 +118,16 @@ export class ItemMoreOptionsComponent implements OnInit { await this.vaultPopupAutofillService.doAutofillAndSave(this.cipher, false); } + async onView() { + const repromptPassed = await this.passwordRepromptService.passwordRepromptCheck(this.cipher); + if (!repromptPassed) { + return; + } + await this.router.navigate(["/view-cipher"], { + queryParams: { cipherId: this.cipher.id, type: this.cipher.type }, + }); + } + /** * Toggles the favorite status of the cipher and updates it on the server. */ diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html index a493ea2171c..72ac590c779 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-list-items-container/vault-list-items-container.component.html @@ -27,9 +27,11 @@

    - +

    diff --git a/apps/web/src/app/billing/individual/premium/premium-v2.component.ts b/apps/web/src/app/billing/individual/premium/premium-v2.component.ts index 72dc5a30d5f..2abab57b7e0 100644 --- a/apps/web/src/app/billing/individual/premium/premium-v2.component.ts +++ b/apps/web/src/app/billing/individual/premium/premium-v2.component.ts @@ -5,10 +5,13 @@ import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormControl, FormGroup, Validators } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { combineLatest, concatMap, from, Observable, of } from "rxjs"; +import { debounceTime } from "rxjs/operators"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; +import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; +import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; @@ -44,6 +47,7 @@ export class PremiumV2Component { FeatureFlag.PM11901_RefactorSelfHostingLicenseUploader, ); + protected estimatedTax: number = 0; protected readonly familyPlanMaxUserCount = 6; protected readonly premiumPrice = 10; protected readonly storageGBPrice = 4; @@ -60,6 +64,7 @@ export class PremiumV2Component { private syncService: SyncService, private toastService: ToastService, private tokenService: TokenService, + private taxService: TaxServiceAbstraction, ) { this.isSelfHost = this.platformUtilsService.isSelfHost(); @@ -82,6 +87,12 @@ export class PremiumV2Component { }), ) .subscribe(); + + this.addOnFormGroup.controls.additionalStorage.valueChanges + .pipe(debounceTime(1000), takeUntilDestroyed()) + .subscribe(() => { + this.refreshSalesTax(); + }); } finalizeUpgrade = async () => { @@ -158,12 +169,6 @@ export class PremiumV2Component { return this.storageGBPrice * this.addOnFormGroup.value.additionalStorage; } - protected get estimatedTax(): number { - return this.taxInfoComponent?.taxRate != null - ? (this.taxInfoComponent.taxRate / 100) * this.subtotal - : 0; - } - protected get premiumURL(): string { return `${this.cloudWebVaultURL}/#/settings/subscription/premium`; } @@ -179,4 +184,36 @@ export class PremiumV2Component { protected async onLicenseFileSelectedChanged(): Promise { await this.postFinalizeUpgrade(); } + + private refreshSalesTax(): void { + if (!this.taxInfoComponent.country || !this.taxInfoComponent.postalCode) { + return; + } + const request: PreviewIndividualInvoiceRequest = { + passwordManager: { + additionalStorage: this.addOnFormGroup.value.additionalStorage, + }, + taxInformation: { + postalCode: this.taxInfoComponent.postalCode, + country: this.taxInfoComponent.country, + }, + }; + + this.taxService + .previewIndividualInvoice(request) + .then((invoice) => { + this.estimatedTax = invoice.taxAmount; + }) + .catch((error) => { + this.toastService.showToast({ + title: "", + variant: "error", + message: this.i18nService.t(error.message), + }); + }); + } + + protected onTaxInformationChanged(): void { + this.refreshSalesTax(); + } } diff --git a/apps/web/src/app/billing/individual/premium/premium.component.html b/apps/web/src/app/billing/individual/premium/premium.component.html index 8b848b48dab..12b6932d0f5 100644 --- a/apps/web/src/app/billing/individual/premium/premium.component.html +++ b/apps/web/src/app/billing/individual/premium/premium.component.html @@ -122,7 +122,7 @@

    {{ "summary" | i18n }}

    {{ "paymentInformation" | i18n }}

    - +
    {{ "planPrice" | i18n }}: {{ subtotal | currency: "USD $" }} diff --git a/apps/web/src/app/billing/individual/premium/premium.component.ts b/apps/web/src/app/billing/individual/premium/premium.component.ts index 9e2be6dcab1..76ca25c8cc6 100644 --- a/apps/web/src/app/billing/individual/premium/premium.component.ts +++ b/apps/web/src/app/billing/individual/premium/premium.component.ts @@ -1,13 +1,17 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Component, OnInit, ViewChild } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormControl, FormGroup, Validators } from "@angular/forms"; import { Router } from "@angular/router"; import { firstValueFrom, Observable } from "rxjs"; +import { debounceTime } from "rxjs/operators"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; +import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; @@ -39,6 +43,9 @@ export class PremiumComponent implements OnInit { protected addonForm = new FormGroup({ additionalStorage: new FormControl(0, [Validators.max(99), Validators.min(0)]), }); + + private estimatedTax: number = 0; + constructor( private apiService: ApiService, private i18nService: I18nService, @@ -50,9 +57,16 @@ export class PremiumComponent implements OnInit { private environmentService: EnvironmentService, private billingAccountProfileStateService: BillingAccountProfileStateService, private toastService: ToastService, + private taxService: TaxServiceAbstraction, ) { this.selfHosted = platformUtilsService.isSelfHost(); this.canAccessPremium$ = billingAccountProfileStateService.hasPremiumFromAnySource$; + + this.addonForm.controls.additionalStorage.valueChanges + .pipe(debounceTime(1000), takeUntilDestroyed()) + .subscribe(() => { + this.refreshSalesTax(); + }); } protected setSelectedFile(event: Event) { const fileInputEl = event.target; @@ -154,12 +168,42 @@ export class PremiumComponent implements OnInit { } get taxCharges(): number { - return this.taxInfoComponent != null && this.taxInfoComponent.taxRate != null - ? (this.taxInfoComponent.taxRate / 100) * this.subtotal - : 0; + return this.estimatedTax; } get total(): number { return this.subtotal + this.taxCharges || 0; } + + private refreshSalesTax(): void { + if (!this.taxInfoComponent.country || !this.taxInfoComponent.postalCode) { + return; + } + const request: PreviewIndividualInvoiceRequest = { + passwordManager: { + additionalStorage: this.addonForm.value.additionalStorage, + }, + taxInformation: { + postalCode: this.taxInfoComponent.postalCode, + country: this.taxInfoComponent.country, + }, + }; + + this.taxService + .previewIndividualInvoice(request) + .then((invoice) => { + this.estimatedTax = invoice.taxAmount; + }) + .catch((error) => { + this.toastService.showToast({ + title: "", + variant: "error", + message: this.i18nService.t(error.message), + }); + }); + } + + protected onTaxInformationChanged(): void { + this.refreshSalesTax(); + } } diff --git a/apps/web/src/app/billing/organizations/adjust-subscription.component.ts b/apps/web/src/app/billing/organizations/adjust-subscription.component.ts index 73dbb0a0026..0166560007e 100644 --- a/apps/web/src/app/billing/organizations/adjust-subscription.component.ts +++ b/apps/web/src/app/billing/organizations/adjust-subscription.component.ts @@ -5,6 +5,8 @@ import { FormBuilder, Validators } from "@angular/forms"; import { Subject, takeUntil } from "rxjs"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { OrganizationData } from "@bitwarden/common/admin-console/models/data/organization.data"; import { OrganizationSubscriptionUpdateRequest } from "@bitwarden/common/billing/models/request/organization-subscription-update.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ToastService } from "@bitwarden/components"; @@ -34,6 +36,7 @@ export class AdjustSubscription implements OnInit, OnDestroy { private organizationApiService: OrganizationApiServiceAbstraction, private formBuilder: FormBuilder, private toastService: ToastService, + private internalOrganizationService: InternalOrganizationServiceAbstraction, ) {} ngOnInit() { @@ -64,7 +67,20 @@ export class AdjustSubscription implements OnInit, OnDestroy { this.additionalSeatCount, this.adjustSubscriptionForm.value.newMaxSeats, ); - await this.organizationApiService.updatePasswordManagerSeats(this.organizationId, request); + + const response = await this.organizationApiService.updatePasswordManagerSeats( + this.organizationId, + request, + ); + + const organization = await this.internalOrganizationService.get(this.organizationId); + + const organizationData = new OrganizationData(response, { + isMember: organization.isMember, + isProviderUser: organization.isProviderUser, + }); + + await this.internalOrganizationService.upsert(organizationData); this.toastService.showToast({ variant: "success", diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html index 93751f0ef72..78005275f12 100644 --- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.html +++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.html @@ -344,25 +344,14 @@

    {{ "paymentMethod" | i18n }}

    - - - - + + + + +

    {{ "paymentMethod" | i18n }}

    +
    + +

    + + {{ "estimatedTax" | i18n }} + + + {{ estimatedTax | currency: "USD" : "$" }} + +

    +
    +

    (); + protected taxInformation: TaxInformation; + constructor( @Inject(DIALOG_DATA) private dialogParams: ChangePlanDialogParams, private dialogRef: DialogRef, @@ -189,6 +195,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { private organizationApiService: OrganizationApiServiceAbstraction, private configService: ConfigService, private billingApiService: BillingApiServiceAbstraction, + private taxService: TaxServiceAbstraction, ) {} async ngOnInit(): Promise { @@ -267,6 +274,11 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { this.setInitialPlanSelection(); this.loading = false; + + const taxInfo = await this.organizationApiService.getTaxInfo(this.organizationId); + this.taxInformation = TaxInformation.from(taxInfo); + + this.refreshSalesTax(); } setInitialPlanSelection() { @@ -402,6 +414,12 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { } this.selectedPlan = plan; this.formGroup.patchValue({ productTier: plan.productTier }); + + try { + this.refreshSalesTax(); + } catch { + this.estimatedTax = 0; + } } ngOnDestroy() { @@ -567,12 +585,6 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { ); } - get taxCharges() { - return this.taxComponent != null && this.taxComponent.taxRate != null - ? (this.taxComponent.taxRate / 100) * this.passwordManagerSubtotal - : 0; - } - get passwordManagerSeats() { if (this.selectedPlan.productTier === ProductTierType.Families) { return this.selectedPlan.PasswordManager.baseSeats; @@ -584,15 +596,15 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { if (this.organization.useSecretsManager) { return ( this.passwordManagerSubtotal + - this.additionalStorageTotal(this.selectedPlan) + - this.secretsManagerSubtotal + - this.taxCharges || 0 + this.additionalStorageTotal(this.selectedPlan) + + this.secretsManagerSubtotal + + this.estimatedTax ); } return ( this.passwordManagerSubtotal + - this.additionalStorageTotal(this.selectedPlan) + - this.taxCharges || 0 + this.additionalStorageTotal(this.selectedPlan) + + this.estimatedTax ); } @@ -645,8 +657,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { } changedCountry() { - if (this.deprecateStripeSourcesAPI && this.paymentV2Component && this.taxComponent) { - this.paymentV2Component.showBankAccount = this.taxComponent.country === "US"; + if (this.deprecateStripeSourcesAPI && this.paymentV2Component) { + this.paymentV2Component.showBankAccount = this.taxInformation.country === "US"; if ( !this.paymentV2Component.showBankAccount && @@ -654,8 +666,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { ) { this.paymentV2Component.select(PaymentMethodType.Card); } - } else if (this.paymentComponent && this.taxComponent) { - this.paymentComponent!.hideBank = this.taxComponent?.taxFormGroup?.value.country !== "US"; + } else if (this.paymentComponent && this.taxInformation) { + this.paymentComponent!.hideBank = this.taxInformation.country !== "US"; // Bank Account payments are only available for US customers if ( this.paymentComponent.hideBank && @@ -667,9 +679,14 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { } } + protected taxInformationChanged(event: TaxInformation): void { + this.taxInformation = event; + this.changedCountry(); + this.refreshSalesTax(); + } + submit = async () => { - if (!this.taxComponent?.taxFormGroup.valid && this.taxComponent?.taxFormGroup.touched) { - this.taxComponent?.taxFormGroup.markAllAsTouched(); + if (this.taxComponent !== undefined && !this.taxComponent.validate()) { return; } @@ -723,8 +740,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { this.formGroup.controls.premiumAccessAddon.value; request.planType = this.selectedPlan.type; if (this.showPayment) { - request.billingAddressCountry = this.taxComponent.taxFormGroup?.value.country; - request.billingAddressPostalCode = this.taxComponent.taxFormGroup?.value.postalCode; + request.billingAddressCountry = this.taxInformation.country; + request.billingAddressPostalCode = this.taxInformation.postalCode; } // Secrets Manager @@ -735,15 +752,9 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { const tokenizedPaymentSource = await this.paymentV2Component.tokenize(); const updatePaymentMethodRequest = new UpdatePaymentMethodRequest(); updatePaymentMethodRequest.paymentSource = tokenizedPaymentSource; - updatePaymentMethodRequest.taxInformation = { - country: this.taxComponent.country, - postalCode: this.taxComponent.postalCode, - taxId: this.taxComponent.taxId, - line1: this.taxComponent.line1, - line2: this.taxComponent.line2, - city: this.taxComponent.city, - state: this.taxComponent.state, - }; + updatePaymentMethodRequest.taxInformation = ExpandedTaxInfoUpdateRequest.From( + this.taxInformation, + ); await this.billingApiService.updateOrganizationPaymentMethod( this.organizationId, @@ -754,8 +765,8 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { const paymentRequest = new PaymentRequest(); paymentRequest.paymentToken = tokenResult[0]; paymentRequest.paymentMethodType = tokenResult[1]; - paymentRequest.country = this.taxComponent.taxFormGroup?.value.country; - paymentRequest.postalCode = this.taxComponent.taxFormGroup?.value.postalCode; + paymentRequest.country = this.taxInformation.country; + paymentRequest.postalCode = this.taxInformation.postalCode; await this.organizationApiService.updatePayment(this.organizationId, paymentRequest); } } @@ -944,4 +955,48 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { manageSelectableProduct(index: number) { return index; } + + private refreshSalesTax(): void { + if (!this.taxInformation.country || !this.taxInformation.postalCode) { + return; + } + + const request: PreviewOrganizationInvoiceRequest = { + organizationId: this.organizationId, + passwordManager: { + additionalStorage: 0, + plan: this.selectedPlan?.type, + seats: this.sub.seats, + }, + taxInformation: { + postalCode: this.taxInformation.postalCode, + country: this.taxInformation.country, + taxId: this.taxInformation.taxId, + }, + }; + + if (this.organization.useSecretsManager) { + request.secretsManager = { + seats: this.sub.smSeats, + additionalMachineAccounts: this.sub.smServiceAccounts, + }; + } + + this.taxService + .previewOrganizationInvoice(request) + .then((invoice) => { + this.estimatedTax = invoice.taxAmount; + }) + .catch((error) => { + this.toastService.showToast({ + title: "", + variant: "error", + message: this.i18nService.t(error.message), + }); + }); + } + + protected canUpdatePaymentInformation(): boolean { + return this.upgradeRequiresPaymentMethod || this.showPayment || this.isPaymentSourceEmpty(); + } } diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.html b/apps/web/src/app/billing/organizations/organization-plans.component.html index e1b74abea71..d37f95e3aa2 100644 --- a/apps/web/src/app/billing/organizations/organization-plans.component.html +++ b/apps/web/src/app/billing/organizations/organization-plans.component.html @@ -335,7 +335,7 @@

    {{ "summary" | i18n }}

    >{{ "additionalUsers" | i18n }}: {{ "users" | i18n }}: - {{ formGroup.controls["additionalSeats"].value || 0 }} × + {{ formGroup.controls.additionalSeats.value || 0 }} × {{ (selectablePlan.isAnnual ? selectablePlan.PasswordManager.seatPrice / 12 @@ -355,7 +355,7 @@

    {{ "summary" | i18n }}

    *ngIf="selectablePlan.PasswordManager.hasAdditionalStorageOption" > {{ "additionalStorageGb" | i18n }}: - {{ formGroup.controls["additionalStorage"].value || 0 }} × + {{ formGroup.controls.additionalStorage.value || 0 }} × {{ (selectablePlan.isAnnual ? selectablePlan.PasswordManager.additionalStoragePricePerGb / 12 @@ -388,7 +388,7 @@

    {{ "summary" | i18n }}

    >{{ "additionalUsers" | i18n }}: {{ "users" | i18n }}: - {{ formGroup.controls["additionalSeats"].value || 0 }} × + {{ formGroup.controls.additionalSeats.value || 0 }} × {{ selectablePlan.PasswordManager.seatPrice | currency: "$" }} {{ "monthAbbr" | i18n }} = {{ @@ -403,7 +403,7 @@

    {{ "summary" | i18n }}

    *ngIf="selectablePlan.PasswordManager.hasAdditionalStorageOption" > {{ "additionalStorageGb" | i18n }}: - {{ formGroup.controls["additionalStorage"].value || 0 }} × + {{ formGroup.controls.additionalStorage.value || 0 }} × {{ selectablePlan.PasswordManager.additionalStoragePricePerGb | currency: "$" }} {{ "monthAbbr" | i18n }} = {{ additionalStorageTotal(selectablePlan) | currency: "$" }} /{{ "month" | i18n }} @@ -440,7 +440,12 @@

    - +
    {{ "passwordManagerPlanPrice" | i18n }}: {{ passwordManagerSubtotal | currency: "USD $" }} @@ -450,7 +455,7 @@


    - {{ "estimatedTax" | i18n }}: {{ taxCharges | currency: "USD $" }} + {{ "estimatedTax" | i18n }}: {{ estimatedTax | currency: "USD $" }}


    diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.ts b/apps/web/src/app/billing/organizations/organization-plans.component.ts index e7a011792ae..4592f8de894 100644 --- a/apps/web/src/app/billing/organizations/organization-plans.component.ts +++ b/apps/web/src/app/billing/organizations/organization-plans.component.ts @@ -12,7 +12,9 @@ import { import { FormBuilder, Validators } from "@angular/forms"; import { Router } from "@angular/router"; import { Subject, takeUntil } from "rxjs"; +import { debounceTime } from "rxjs/operators"; +import { ManageTaxInformationComponent } from "@bitwarden/angular/billing/components"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; @@ -26,9 +28,12 @@ import { OrganizationUpgradeRequest } from "@bitwarden/common/admin-console/mode import { ProviderOrganizationCreateRequest } from "@bitwarden/common/admin-console/models/request/provider/provider-organization-create.request"; import { ProviderResponse } from "@bitwarden/common/admin-console/models/response/provider/provider.response"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; +import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; import { PaymentMethodType, PlanType, ProductTierType } from "@bitwarden/common/billing/enums"; +import { TaxInformation } from "@bitwarden/common/billing/models/domain"; import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request"; import { PaymentRequest } from "@bitwarden/common/billing/models/request/payment.request"; +import { PreviewOrganizationInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-organization-invoice.request"; import { UpdatePaymentMethodRequest } from "@bitwarden/common/billing/models/request/update-payment-method.request"; import { BillingResponse } from "@bitwarden/common/billing/models/response/billing.response"; import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response"; @@ -50,7 +55,6 @@ import { OrganizationCreateModule } from "../../admin-console/organizations/crea import { BillingSharedModule, secretsManagerSubscribeFormFactory } from "../shared"; import { PaymentV2Component } from "../shared/payment/payment-v2.component"; import { PaymentComponent } from "../shared/payment/payment.component"; -import { TaxInfoComponent } from "../shared/tax-info.component"; interface OnSuccessArgs { organizationId: string; @@ -72,13 +76,14 @@ const Allowed2020PlansForLegacyProviders = [ export class OrganizationPlansComponent implements OnInit, OnDestroy { @ViewChild(PaymentComponent) paymentComponent: PaymentComponent; @ViewChild(PaymentV2Component) paymentV2Component: PaymentV2Component; - @ViewChild(TaxInfoComponent) taxComponent: TaxInfoComponent; + @ViewChild(ManageTaxInformationComponent) taxComponent: ManageTaxInformationComponent; - @Input() organizationId: string; + @Input() organizationId?: string; @Input() showFree = true; @Input() showCancel = false; @Input() acceptingSponsorship = false; @Input() currentPlan: PlanResponse; + selectedFile: File; @Input() @@ -93,6 +98,8 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { private _productTier = ProductTierType.Free; + protected taxInformation: TaxInformation; + @Input() get plan(): PlanType { return this._plan; @@ -149,7 +156,10 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { billing: BillingResponse; provider: ProviderResponse; - private destroy$ = new Subject(); + protected estimatedTax: number = 0; + protected total: number = 0; + + private destroy$: Subject = new Subject(); constructor( private apiService: ApiService, @@ -168,6 +178,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { private toastService: ToastService, private configService: ConfigService, private billingApiService: BillingApiServiceAbstraction, + private taxService: TaxServiceAbstraction, ) { this.selfHosted = this.platformUtilsService.isSelfHost(); } @@ -181,6 +192,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.organization = await this.organizationService.get(this.organizationId); this.billing = await this.organizationApiService.getBilling(this.organizationId); this.sub = await this.organizationApiService.getSubscription(this.organizationId); + this.taxInformation = await this.organizationApiService.getTaxInfo(this.organizationId); + } else { + this.taxInformation = await this.apiService.getTaxInfo(); } if (!this.selfHosted) { @@ -241,6 +255,16 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { } this.loading = false; + + this.formGroup.valueChanges.pipe(debounceTime(1000), takeUntil(this.destroy$)).subscribe(() => { + this.refreshSalesTax(); + }); + + this.secretsManagerForm.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) + .subscribe(() => { + this.refreshSalesTax(); + }); } ngOnDestroy() { @@ -438,17 +462,6 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { return this.selectedPlan.trialPeriodDays != null; } - get taxCharges() { - return this.taxComponent != null && this.taxComponent.taxRate != null - ? (this.taxComponent.taxRate / 100) * - (this.passwordManagerSubtotal + this.secretsManagerSubtotal) - : 0; - } - - get total() { - return this.passwordManagerSubtotal + this.secretsManagerSubtotal + this.taxCharges || 0; - } - get paymentDesc() { if (this.acceptingSponsorship) { return this.i18nService.t("paymentSponsored"); @@ -554,9 +567,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.changedProduct(); } - changedCountry() { + protected changedCountry(): void { if (this.deprecateStripeSourcesAPI) { - this.paymentV2Component.showBankAccount = this.taxComponent.country === "US"; + this.paymentV2Component.showBankAccount = this.taxInformation?.country === "US"; if ( !this.paymentV2Component.showBankAccount && this.paymentV2Component.selected === PaymentMethodType.BankAccount @@ -564,7 +577,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.paymentV2Component.select(PaymentMethodType.Card); } } else { - this.paymentComponent.hideBank = this.taxComponent.taxFormGroup?.value.country !== "US"; + this.paymentComponent.hideBank = this.taxInformation?.country !== "US"; if ( this.paymentComponent.hideBank && this.paymentComponent.method === PaymentMethodType.BankAccount @@ -575,28 +588,31 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { } } - cancel() { + protected onTaxInformationChanged(event: TaxInformation): void { + this.taxInformation = event; + this.changedCountry(); + this.refreshSalesTax(); + } + + protected cancel(): void { this.onCanceled.emit(); } - setSelectedFile(event: Event) { + protected setSelectedFile(event: Event): void { const fileInputEl = event.target; this.selectedFile = fileInputEl.files.length > 0 ? fileInputEl.files[0] : null; } submit = async () => { - if (this.taxComponent) { - if (!this.taxComponent?.taxFormGroup.valid) { - this.taxComponent?.taxFormGroup.markAllAsTouched(); - return; - } + if (this.taxComponent && !this.taxComponent.validate()) { + return; } if (this.singleOrgPolicyBlock) { return; } const doSubmit = async (): Promise => { - let orgId: string = null; + let orgId: string; if (this.createOrganization) { const orgKey = await this.keyService.makeOrgKey(); const key = orgKey[0].encryptedString; @@ -607,11 +623,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { const collectionCt = collection.encryptedString; const orgKeys = await this.keyService.makeKeyPair(orgKey[1]); - if (this.selfHosted) { - orgId = await this.createSelfHosted(key, collectionCt, orgKeys); - } else { - orgId = await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]); - } + orgId = this.selfHosted + ? await this.createSelfHosted(key, collectionCt, orgKeys) + : await this.createCloudHosted(key, collectionCt, orgKeys, orgKey[1]); this.toastService.showToast({ variant: "success", @@ -619,7 +633,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { message: this.i18nService.t("organizationReadyToGo"), }); } else { - orgId = await this.updateOrganization(orgId); + orgId = await this.updateOrganization(); this.toastService.showToast({ variant: "success", title: null, @@ -653,7 +667,63 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.messagingService.send("organizationCreated", { organizationId }); }; - private async updateOrganization(orgId: string) { + protected get showTaxIdField(): boolean { + switch (this.formGroup.controls.productTier.value) { + case ProductTierType.Free: + case ProductTierType.Families: + return false; + default: + return true; + } + } + + private refreshSalesTax(): void { + if (this.formGroup.controls.plan.value == PlanType.Free) { + this.estimatedTax = 0; + return; + } + + if (!this.taxComponent.validate()) { + return; + } + + const request: PreviewOrganizationInvoiceRequest = { + organizationId: this.organizationId, + passwordManager: { + additionalStorage: this.formGroup.controls.additionalStorage.value, + plan: this.formGroup.controls.plan.value, + seats: this.formGroup.controls.additionalSeats.value, + }, + taxInformation: { + postalCode: this.taxInformation.postalCode, + country: this.taxInformation.country, + taxId: this.taxInformation.taxId, + }, + }; + + if (this.secretsManagerForm.controls.enabled.value === true) { + request.secretsManager = { + seats: this.secretsManagerForm.controls.userSeats.value, + additionalMachineAccounts: this.secretsManagerForm.controls.additionalServiceAccounts.value, + }; + } + + this.taxService + .previewOrganizationInvoice(request) + .then((invoice) => { + this.estimatedTax = invoice.taxAmount; + this.total = invoice.totalAmount; + }) + .catch((error) => { + this.toastService.showToast({ + title: "", + variant: "error", + message: this.i18nService.t(error.message), + }); + }); + } + + private async updateOrganization() { const request = new OrganizationUpgradeRequest(); request.additionalSeats = this.formGroup.controls.additionalSeats.value; request.additionalStorageGb = this.formGroup.controls.additionalStorage.value; @@ -661,8 +731,8 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.selectedPlan.PasswordManager.hasPremiumAccessOption && this.formGroup.controls.premiumAccessAddon.value; request.planType = this.selectedPlan.type; - request.billingAddressCountry = this.taxComponent.taxFormGroup?.value.country; - request.billingAddressPostalCode = this.taxComponent.taxFormGroup?.value.postalCode; + request.billingAddressCountry = this.taxInformation?.country; + request.billingAddressPostalCode = this.taxInformation?.postalCode; // Secrets Manager this.buildSecretsManagerRequest(request); @@ -671,10 +741,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { if (this.deprecateStripeSourcesAPI) { const updatePaymentMethodRequest = new UpdatePaymentMethodRequest(); updatePaymentMethodRequest.paymentSource = await this.paymentV2Component.tokenize(); - const expandedTaxInfoUpdateRequest = new ExpandedTaxInfoUpdateRequest(); - expandedTaxInfoUpdateRequest.country = this.taxComponent.country; - expandedTaxInfoUpdateRequest.postalCode = this.taxComponent.postalCode; - updatePaymentMethodRequest.taxInformation = expandedTaxInfoUpdateRequest; + updatePaymentMethodRequest.taxInformation = ExpandedTaxInfoUpdateRequest.From( + this.taxInformation, + ); await this.billingApiService.updateOrganizationPaymentMethod( this.organizationId, updatePaymentMethodRequest, @@ -684,8 +753,8 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { const paymentRequest = new PaymentRequest(); paymentRequest.paymentToken = paymentToken; paymentRequest.paymentMethodType = paymentMethodType; - paymentRequest.country = this.taxComponent.taxFormGroup?.value.country; - paymentRequest.postalCode = this.taxComponent.taxFormGroup?.value.postalCode; + paymentRequest.country = this.taxInformation?.country; + paymentRequest.postalCode = this.taxInformation?.postalCode; await this.organizationApiService.updatePayment(this.organizationId, paymentRequest); } } @@ -709,7 +778,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { collectionCt: string, orgKeys: [string, EncString], orgKey: SymmetricCryptoKey, - ) { + ): Promise { const request = new OrganizationCreateRequest(); request.key = key; request.collectionName = collectionCt; @@ -738,15 +807,13 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { this.selectedPlan.PasswordManager.hasPremiumAccessOption && this.formGroup.controls.premiumAccessAddon.value; request.planType = this.selectedPlan.type; - request.billingAddressPostalCode = this.taxComponent.taxFormGroup?.value.postalCode; - request.billingAddressCountry = this.taxComponent.taxFormGroup?.value.country; - if (this.taxComponent.taxFormGroup?.value.includeTaxId) { - request.taxIdNumber = this.taxComponent.taxFormGroup?.value.taxId; - request.billingAddressLine1 = this.taxComponent.taxFormGroup?.value.line1; - request.billingAddressLine2 = this.taxComponent.taxFormGroup?.value.line2; - request.billingAddressCity = this.taxComponent.taxFormGroup?.value.city; - request.billingAddressState = this.taxComponent.taxFormGroup?.value.state; - } + request.billingAddressPostalCode = this.taxInformation?.postalCode; + request.billingAddressCountry = this.taxInformation?.country; + request.taxIdNumber = this.taxInformation?.taxId; + request.billingAddressLine1 = this.taxInformation?.line1; + request.billingAddressLine2 = this.taxInformation?.line2; + request.billingAddressCity = this.taxInformation?.city; + request.billingAddressState = this.taxInformation?.state; } // Secrets Manager diff --git a/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.html b/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.html index 78f9955d31a..2c2dba938bc 100644 --- a/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.html +++ b/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.html @@ -63,20 +63,5 @@

    {{ "paymentMethod" | i18n }}

    {{ "paymentChargedWithUnpaidSubscription" | i18n }}

    - - -

    {{ "taxInformation" | i18n }}

    -

    {{ "taxInformationDesc" | i18n }}

    - - -
    diff --git a/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts b/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts index 4ed35461c72..270ba54f70d 100644 --- a/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts +++ b/apps/web/src/app/billing/organizations/payment-method/organization-payment-method.component.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Location } from "@angular/common"; -import { Component, OnDestroy, ViewChild } from "@angular/core"; +import { Component, OnDestroy } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute, Router } from "@angular/router"; import { from, lastValueFrom, switchMap } from "rxjs"; @@ -11,7 +11,6 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { PaymentMethodType } from "@bitwarden/common/billing/enums"; -import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request"; import { VerifyBankAccountRequest } from "@bitwarden/common/billing/models/request/verify-bank-account.request"; import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/models/response/organization-subscription.response"; import { PaymentSourceResponse } from "@bitwarden/common/billing/models/response/payment-source.response"; @@ -22,7 +21,6 @@ import { DialogService, ToastService } from "@bitwarden/components"; import { FreeTrial } from "../../../core/types/free-trial"; import { TrialFlowService } from "../../services/trial-flow.service"; -import { TaxInfoComponent } from "../../shared"; import { AddCreditDialogResult, openAddCreditDialog, @@ -36,8 +34,6 @@ import { templateUrl: "./organization-payment-method.component.html", }) export class OrganizationPaymentMethodComponent implements OnDestroy { - @ViewChild(TaxInfoComponent) taxInfoComponent: TaxInfoComponent; - organizationId: string; isUnpaid = false; accountCredit: number; @@ -155,6 +151,7 @@ export class OrganizationPaymentMethodComponent implements OnDestroy { data: { initialPaymentMethod: this.paymentSource?.type, organizationId: this.organizationId, + productTier: this.organization?.productTierType, }, }); @@ -170,6 +167,7 @@ export class OrganizationPaymentMethodComponent implements OnDestroy { data: { initialPaymentMethod: this.paymentSource?.type, organizationId: this.organizationId, + productTier: this.organization?.productTierType, }, }); const result = await lastValueFrom(dialogRef.closed); @@ -183,32 +181,6 @@ export class OrganizationPaymentMethodComponent implements OnDestroy { } }; - protected updateTaxInformation = async (): Promise => { - this.taxInfoComponent.taxFormGroup.updateValueAndValidity(); - this.taxInfoComponent.taxFormGroup.markAllAsTouched(); - - if (this.taxInfoComponent.taxFormGroup.invalid) { - return; - } - - const request = new ExpandedTaxInfoUpdateRequest(); - request.country = this.taxInfoComponent.country; - request.postalCode = this.taxInfoComponent.postalCode; - request.taxId = this.taxInfoComponent.taxId; - request.line1 = this.taxInfoComponent.line1; - request.line2 = this.taxInfoComponent.line2; - request.city = this.taxInfoComponent.city; - request.state = this.taxInfoComponent.state; - - await this.billingApiService.updateOrganizationTaxInformation(this.organizationId, request); - - this.toastService.showToast({ - variant: "success", - title: null, - message: this.i18nService.t("taxInfoUpdated"), - }); - }; - protected verifyBankAccount = async (request: VerifyBankAccountRequest): Promise => { await this.billingApiService.verifyOrganizationBankAccount(this.organizationId, request); this.toastService.showToast({ diff --git a/apps/web/src/app/billing/organizations/sm-adjust-subscription.component.ts b/apps/web/src/app/billing/organizations/sm-adjust-subscription.component.ts index 4a4f309c68b..fc7a188f967 100644 --- a/apps/web/src/app/billing/organizations/sm-adjust-subscription.component.ts +++ b/apps/web/src/app/billing/organizations/sm-adjust-subscription.component.ts @@ -5,6 +5,8 @@ import { FormBuilder, Validators } from "@angular/forms"; import { Subject, takeUntil } from "rxjs"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { InternalOrganizationServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { OrganizationData } from "@bitwarden/common/admin-console/models/data/organization.data"; import { OrganizationSmSubscriptionUpdateRequest } from "@bitwarden/common/billing/models/request/organization-sm-subscription-update.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -104,6 +106,7 @@ export class SecretsManagerAdjustSubscriptionComponent implements OnInit, OnDest private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, private toastService: ToastService, + private internalOrganizationService: InternalOrganizationServiceAbstraction, ) {} ngOnInit() { @@ -157,11 +160,20 @@ export class SecretsManagerAdjustSubscriptionComponent implements OnInit, OnDest ? this.formGroup.value.maxAutoscaleServiceAccounts : null; - await this.organizationApiService.updateSecretsManagerSubscription( + const response = await this.organizationApiService.updateSecretsManagerSubscription( this.organizationId, request, ); + const organization = await this.internalOrganizationService.get(this.organizationId); + + const organizationData = new OrganizationData(response, { + isMember: organization.isMember, + isProviderUser: organization.isProviderUser, + }); + + await this.internalOrganizationService.upsert(organizationData); + this.toastService.showToast({ variant: "success", title: null, diff --git a/apps/web/src/app/billing/services/reseller-warning.service.ts b/apps/web/src/app/billing/services/reseller-warning.service.ts new file mode 100644 index 00000000000..bfd5be3233a --- /dev/null +++ b/apps/web/src/app/billing/services/reseller-warning.service.ts @@ -0,0 +1,142 @@ +import { Injectable } from "@angular/core"; + +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { OrganizationBillingMetadataResponse } from "@bitwarden/common/billing/models/response/organization-billing-metadata.response"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + +export interface ResellerWarning { + type: "info" | "warning"; + message: string; +} + +@Injectable({ providedIn: "root" }) +export class ResellerWarningService { + private readonly RENEWAL_WARNING_DAYS = 14; + private readonly GRACE_PERIOD_DAYS = 30; + + constructor(private i18nService: I18nService) {} + + getWarning( + organization: Organization, + organizationBillingMetadata: OrganizationBillingMetadataResponse, + ): ResellerWarning | null { + if (!organization.hasReseller) { + return null; // If no reseller, return null immediately + } + + // Check for past due warning first (highest priority) + if (this.shouldShowPastDueWarning(organizationBillingMetadata)) { + const gracePeriodEnd = this.getGracePeriodEndDate(organizationBillingMetadata.invoiceDueDate); + if (!gracePeriodEnd) { + return null; + } + return { + type: "warning", + message: this.i18nService.t( + "resellerPastDueWarning", + organization.providerName, + this.formatDate(gracePeriodEnd), + ), + } as ResellerWarning; + } + + // Check for open invoice warning + if (this.shouldShowInvoiceWarning(organizationBillingMetadata)) { + const invoiceCreatedDate = organizationBillingMetadata.invoiceCreatedDate; + const invoiceDueDate = organizationBillingMetadata.invoiceDueDate; + if (!invoiceCreatedDate || !invoiceDueDate) { + return null; + } + return { + type: "info", + message: this.i18nService.t( + "resellerOpenInvoiceWarning", + organization.providerName, + this.formatDate(organizationBillingMetadata.invoiceCreatedDate), + this.formatDate(organizationBillingMetadata.invoiceDueDate), + ), + } as ResellerWarning; + } + + // Check for renewal warning + if (this.shouldShowRenewalWarning(organizationBillingMetadata)) { + const subPeriodEndDate = organizationBillingMetadata.subPeriodEndDate; + if (!subPeriodEndDate) { + return null; + } + + return { + type: "info", + message: this.i18nService.t( + "resellerRenewalWarning", + organization.providerName, + this.formatDate(organizationBillingMetadata.subPeriodEndDate), + ), + } as ResellerWarning; + } + + return null; + } + + private shouldShowRenewalWarning( + organizationBillingMetadata: OrganizationBillingMetadataResponse, + ): boolean { + if ( + !organizationBillingMetadata.hasSubscription || + !organizationBillingMetadata.subPeriodEndDate + ) { + return false; + } + const renewalDate = new Date(organizationBillingMetadata.subPeriodEndDate); + const daysUntilRenewal = Math.ceil( + (renewalDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24), + ); + return daysUntilRenewal <= this.RENEWAL_WARNING_DAYS; + } + + private shouldShowInvoiceWarning( + organizationBillingMetadata: OrganizationBillingMetadataResponse, + ): boolean { + if ( + !organizationBillingMetadata.hasOpenInvoice || + !organizationBillingMetadata.invoiceDueDate + ) { + return false; + } + const invoiceDueDate = new Date(organizationBillingMetadata.invoiceDueDate); + return invoiceDueDate > new Date(); + } + + private shouldShowPastDueWarning( + organizationBillingMetadata: OrganizationBillingMetadataResponse, + ): boolean { + if ( + !organizationBillingMetadata.hasOpenInvoice || + !organizationBillingMetadata.invoiceDueDate + ) { + return false; + } + const invoiceDueDate = new Date(organizationBillingMetadata.invoiceDueDate); + return invoiceDueDate <= new Date() && !organizationBillingMetadata.isSubscriptionUnpaid; + } + + private getGracePeriodEndDate(dueDate: Date | null): Date | null { + if (!dueDate) { + return null; + } + const gracePeriodEnd = new Date(dueDate); + gracePeriodEnd.setDate(gracePeriodEnd.getDate() + this.GRACE_PERIOD_DAYS); + return gracePeriodEnd; + } + + private formatDate(date: Date | null): string { + if (!date) { + return "N/A"; + } + return new Date(date).toLocaleDateString("en-US", { + month: "short", + day: "2-digit", + year: "numeric", + }); + } +} diff --git a/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog-v2.component.html b/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog-v2.component.html index e41d3d961cd..bb06f87ca03 100644 --- a/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog-v2.component.html +++ b/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog-v2.component.html @@ -5,7 +5,12 @@ [showBankAccount]="!!organizationId" [initialPaymentMethod]="initialPaymentMethod" > - + - - diff --git a/apps/web/src/app/billing/shared/payment-method.component.ts b/apps/web/src/app/billing/shared/payment-method.component.ts index 298573f0852..149b4adf520 100644 --- a/apps/web/src/app/billing/shared/payment-method.component.ts +++ b/apps/web/src/app/billing/shared/payment-method.component.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Location } from "@angular/common"; -import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; +import { Component, OnDestroy, OnInit } from "@angular/core"; import { FormBuilder, FormControl, Validators } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { lastValueFrom } from "rxjs"; @@ -16,7 +16,6 @@ import { OrganizationSubscriptionResponse } from "@bitwarden/common/billing/mode import { SubscriptionResponse } from "@bitwarden/common/billing/models/response/subscription.response"; import { VerifyBankRequest } from "@bitwarden/common/models/request/verify-bank.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SyncService } from "@bitwarden/common/platform/sync"; import { DialogService, ToastService } from "@bitwarden/components"; @@ -29,15 +28,12 @@ import { AdjustPaymentDialogResult, openAdjustPaymentDialog, } from "./adjust-payment-dialog/adjust-payment-dialog.component"; -import { TaxInfoComponent } from "./tax-info.component"; @Component({ templateUrl: "payment-method.component.html", }) // eslint-disable-next-line rxjs-angular/prefer-takeuntil export class PaymentMethodComponent implements OnInit, OnDestroy { - @ViewChild(TaxInfoComponent) taxInfo: TaxInfoComponent; - loading = false; firstLoaded = false; billing: BillingPaymentResponse; @@ -61,7 +57,6 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { ]), }); - taxForm = this.formBuilder.group({}); launchPaymentModalAutomatically = false; protected freeTrialData: FreeTrial; @@ -72,7 +67,6 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { protected platformUtilsService: PlatformUtilsService, private router: Router, private location: Location, - private logService: LogService, private route: ActivatedRoute, private formBuilder: FormBuilder, private dialogService: DialogService, @@ -198,15 +192,6 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { await this.load(); }; - submitTaxInfo = async () => { - await this.taxInfo.submitTaxInfo(); - this.toastService.showToast({ - variant: "success", - title: null, - message: this.i18nService.t("taxInfoUpdated"), - }); - }; - determineOrgsWithUpcomingPaymentIssues() { this.freeTrialData = this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues( this.organization, @@ -231,10 +216,6 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { return this.organizationId != null; } - get headerClass() { - return this.forOrganization ? ["page-header"] : ["tabbed-header"]; - } - get paymentSourceClasses() { if (this.paymentSource == null) { return []; diff --git a/apps/web/src/app/billing/shared/tax-info.component.html b/apps/web/src/app/billing/shared/tax-info.component.html index 82d5104a53a..4a42c0c1109 100644 --- a/apps/web/src/app/billing/shared/tax-info.component.html +++ b/apps/web/src/app/billing/shared/tax-info.component.html @@ -13,51 +13,41 @@
    -
    +
    {{ "zipPostalCode" | i18n }}
    -
    - - - {{ "includeVAT" | i18n }} - -
    -
    -
    -
    - - {{ "taxIdNumber" | i18n }} - - -
    -
    -
    -
    +
    {{ "address1" | i18n }}
    -
    +
    {{ "address2" | i18n }}
    -
    +
    {{ "cityTown" | i18n }}
    -
    +
    {{ "stateProvince" | i18n }}
    +
    + + {{ "taxIdNumber" | i18n }} + + +
    diff --git a/apps/web/src/app/billing/shared/tax-info.component.ts b/apps/web/src/app/billing/shared/tax-info.component.ts index 8ebec5e1dfe..214364e4cf2 100644 --- a/apps/web/src/app/billing/shared/tax-info.component.ts +++ b/apps/web/src/app/billing/shared/tax-info.component.ts @@ -1,31 +1,20 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core"; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core"; import { FormControl, FormGroup, Validators } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; import { Subject, takeUntil } from "rxjs"; +import { debounceTime } from "rxjs/operators"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; +import { CountryListItem } from "@bitwarden/common/billing/models/domain"; import { ExpandedTaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/expanded-tax-info-update.request"; -import { TaxInfoUpdateRequest } from "@bitwarden/common/billing/models/request/tax-info-update.request"; -import { TaxInfoResponse } from "@bitwarden/common/billing/models/response/tax-info.response"; -import { TaxRateResponse } from "@bitwarden/common/billing/models/response/tax-rate.response"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { SharedModule } from "../../shared"; -type TaxInfoView = Omit & { - includeTaxId: boolean; - [key: string]: unknown; -}; - -type CountryList = { - name: string; - value: string; - disabled: boolean; -}; - @Component({ selector: "app-tax-info", templateUrl: "tax-info.component.html", @@ -33,359 +22,68 @@ type CountryList = { imports: [SharedModule], }) // eslint-disable-next-line rxjs-angular/prefer-takeuntil -export class TaxInfoComponent implements OnInit { - @Input() trialFlow = false; - @Output() onCountryChanged = new EventEmitter(); +export class TaxInfoComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); + @Input() trialFlow = false; + @Output() countryChanged = new EventEmitter(); + @Output() taxInformationChanged: EventEmitter = new EventEmitter(); + taxFormGroup = new FormGroup({ - country: new FormControl(null, [Validators.required]), - postalCode: new FormControl(null), - includeTaxId: new FormControl(null), - taxId: new FormControl(null), - line1: new FormControl(null), - line2: new FormControl(null), - city: new FormControl(null), - state: new FormControl(null), + country: new FormControl(null, [Validators.required]), + postalCode: new FormControl(null, [Validators.required]), + taxId: new FormControl(null), + line1: new FormControl(null), + line2: new FormControl(null), + city: new FormControl(null), + state: new FormControl(null), }); + protected isTaxSupported: boolean; + loading = true; organizationId: string; providerId: string; - taxInfo: TaxInfoView = { - taxId: null, - line1: null, - line2: null, - city: null, - state: null, - postalCode: null, - country: "US", - includeTaxId: false, - }; - countryList: CountryList[] = [ - { name: "-- Select --", value: "", disabled: false }, - { name: "United States", value: "US", disabled: false }, - { name: "China", value: "CN", disabled: false }, - { name: "France", value: "FR", disabled: false }, - { name: "Germany", value: "DE", disabled: false }, - { name: "Canada", value: "CA", disabled: false }, - { name: "United Kingdom", value: "GB", disabled: false }, - { name: "Australia", value: "AU", disabled: false }, - { name: "India", value: "IN", disabled: false }, - { name: "", value: "-", disabled: true }, - { name: "Afghanistan", value: "AF", disabled: false }, - { name: "Åland Islands", value: "AX", disabled: false }, - { name: "Albania", value: "AL", disabled: false }, - { name: "Algeria", value: "DZ", disabled: false }, - { name: "American Samoa", value: "AS", disabled: false }, - { name: "Andorra", value: "AD", disabled: false }, - { name: "Angola", value: "AO", disabled: false }, - { name: "Anguilla", value: "AI", disabled: false }, - { name: "Antarctica", value: "AQ", disabled: false }, - { name: "Antigua and Barbuda", value: "AG", disabled: false }, - { name: "Argentina", value: "AR", disabled: false }, - { name: "Armenia", value: "AM", disabled: false }, - { name: "Aruba", value: "AW", disabled: false }, - { name: "Austria", value: "AT", disabled: false }, - { name: "Azerbaijan", value: "AZ", disabled: false }, - { name: "Bahamas", value: "BS", disabled: false }, - { name: "Bahrain", value: "BH", disabled: false }, - { name: "Bangladesh", value: "BD", disabled: false }, - { name: "Barbados", value: "BB", disabled: false }, - { name: "Belarus", value: "BY", disabled: false }, - { name: "Belgium", value: "BE", disabled: false }, - { name: "Belize", value: "BZ", disabled: false }, - { name: "Benin", value: "BJ", disabled: false }, - { name: "Bermuda", value: "BM", disabled: false }, - { name: "Bhutan", value: "BT", disabled: false }, - { name: "Bolivia, Plurinational State of", value: "BO", disabled: false }, - { name: "Bonaire, Sint Eustatius and Saba", value: "BQ", disabled: false }, - { name: "Bosnia and Herzegovina", value: "BA", disabled: false }, - { name: "Botswana", value: "BW", disabled: false }, - { name: "Bouvet Island", value: "BV", disabled: false }, - { name: "Brazil", value: "BR", disabled: false }, - { name: "British Indian Ocean Territory", value: "IO", disabled: false }, - { name: "Brunei Darussalam", value: "BN", disabled: false }, - { name: "Bulgaria", value: "BG", disabled: false }, - { name: "Burkina Faso", value: "BF", disabled: false }, - { name: "Burundi", value: "BI", disabled: false }, - { name: "Cambodia", value: "KH", disabled: false }, - { name: "Cameroon", value: "CM", disabled: false }, - { name: "Cape Verde", value: "CV", disabled: false }, - { name: "Cayman Islands", value: "KY", disabled: false }, - { name: "Central African Republic", value: "CF", disabled: false }, - { name: "Chad", value: "TD", disabled: false }, - { name: "Chile", value: "CL", disabled: false }, - { name: "Christmas Island", value: "CX", disabled: false }, - { name: "Cocos (Keeling) Islands", value: "CC", disabled: false }, - { name: "Colombia", value: "CO", disabled: false }, - { name: "Comoros", value: "KM", disabled: false }, - { name: "Congo", value: "CG", disabled: false }, - { name: "Congo, the Democratic Republic of the", value: "CD", disabled: false }, - { name: "Cook Islands", value: "CK", disabled: false }, - { name: "Costa Rica", value: "CR", disabled: false }, - { name: "Côte d'Ivoire", value: "CI", disabled: false }, - { name: "Croatia", value: "HR", disabled: false }, - { name: "Cuba", value: "CU", disabled: false }, - { name: "Curaçao", value: "CW", disabled: false }, - { name: "Cyprus", value: "CY", disabled: false }, - { name: "Czech Republic", value: "CZ", disabled: false }, - { name: "Denmark", value: "DK", disabled: false }, - { name: "Djibouti", value: "DJ", disabled: false }, - { name: "Dominica", value: "DM", disabled: false }, - { name: "Dominican Republic", value: "DO", disabled: false }, - { name: "Ecuador", value: "EC", disabled: false }, - { name: "Egypt", value: "EG", disabled: false }, - { name: "El Salvador", value: "SV", disabled: false }, - { name: "Equatorial Guinea", value: "GQ", disabled: false }, - { name: "Eritrea", value: "ER", disabled: false }, - { name: "Estonia", value: "EE", disabled: false }, - { name: "Ethiopia", value: "ET", disabled: false }, - { name: "Falkland Islands (Malvinas)", value: "FK", disabled: false }, - { name: "Faroe Islands", value: "FO", disabled: false }, - { name: "Fiji", value: "FJ", disabled: false }, - { name: "Finland", value: "FI", disabled: false }, - { name: "French Guiana", value: "GF", disabled: false }, - { name: "French Polynesia", value: "PF", disabled: false }, - { name: "French Southern Territories", value: "TF", disabled: false }, - { name: "Gabon", value: "GA", disabled: false }, - { name: "Gambia", value: "GM", disabled: false }, - { name: "Georgia", value: "GE", disabled: false }, - { name: "Ghana", value: "GH", disabled: false }, - { name: "Gibraltar", value: "GI", disabled: false }, - { name: "Greece", value: "GR", disabled: false }, - { name: "Greenland", value: "GL", disabled: false }, - { name: "Grenada", value: "GD", disabled: false }, - { name: "Guadeloupe", value: "GP", disabled: false }, - { name: "Guam", value: "GU", disabled: false }, - { name: "Guatemala", value: "GT", disabled: false }, - { name: "Guernsey", value: "GG", disabled: false }, - { name: "Guinea", value: "GN", disabled: false }, - { name: "Guinea-Bissau", value: "GW", disabled: false }, - { name: "Guyana", value: "GY", disabled: false }, - { name: "Haiti", value: "HT", disabled: false }, - { name: "Heard Island and McDonald Islands", value: "HM", disabled: false }, - { name: "Holy See (Vatican City State)", value: "VA", disabled: false }, - { name: "Honduras", value: "HN", disabled: false }, - { name: "Hong Kong", value: "HK", disabled: false }, - { name: "Hungary", value: "HU", disabled: false }, - { name: "Iceland", value: "IS", disabled: false }, - { name: "Indonesia", value: "ID", disabled: false }, - { name: "Iran, Islamic Republic of", value: "IR", disabled: false }, - { name: "Iraq", value: "IQ", disabled: false }, - { name: "Ireland", value: "IE", disabled: false }, - { name: "Isle of Man", value: "IM", disabled: false }, - { name: "Israel", value: "IL", disabled: false }, - { name: "Italy", value: "IT", disabled: false }, - { name: "Jamaica", value: "JM", disabled: false }, - { name: "Japan", value: "JP", disabled: false }, - { name: "Jersey", value: "JE", disabled: false }, - { name: "Jordan", value: "JO", disabled: false }, - { name: "Kazakhstan", value: "KZ", disabled: false }, - { name: "Kenya", value: "KE", disabled: false }, - { name: "Kiribati", value: "KI", disabled: false }, - { name: "Korea, Democratic People's Republic of", value: "KP", disabled: false }, - { name: "Korea, Republic of", value: "KR", disabled: false }, - { name: "Kuwait", value: "KW", disabled: false }, - { name: "Kyrgyzstan", value: "KG", disabled: false }, - { name: "Lao People's Democratic Republic", value: "LA", disabled: false }, - { name: "Latvia", value: "LV", disabled: false }, - { name: "Lebanon", value: "LB", disabled: false }, - { name: "Lesotho", value: "LS", disabled: false }, - { name: "Liberia", value: "LR", disabled: false }, - { name: "Libya", value: "LY", disabled: false }, - { name: "Liechtenstein", value: "LI", disabled: false }, - { name: "Lithuania", value: "LT", disabled: false }, - { name: "Luxembourg", value: "LU", disabled: false }, - { name: "Macao", value: "MO", disabled: false }, - { name: "Macedonia, the former Yugoslav Republic of", value: "MK", disabled: false }, - { name: "Madagascar", value: "MG", disabled: false }, - { name: "Malawi", value: "MW", disabled: false }, - { name: "Malaysia", value: "MY", disabled: false }, - { name: "Maldives", value: "MV", disabled: false }, - { name: "Mali", value: "ML", disabled: false }, - { name: "Malta", value: "MT", disabled: false }, - { name: "Marshall Islands", value: "MH", disabled: false }, - { name: "Martinique", value: "MQ", disabled: false }, - { name: "Mauritania", value: "MR", disabled: false }, - { name: "Mauritius", value: "MU", disabled: false }, - { name: "Mayotte", value: "YT", disabled: false }, - { name: "Mexico", value: "MX", disabled: false }, - { name: "Micronesia, Federated States of", value: "FM", disabled: false }, - { name: "Moldova, Republic of", value: "MD", disabled: false }, - { name: "Monaco", value: "MC", disabled: false }, - { name: "Mongolia", value: "MN", disabled: false }, - { name: "Montenegro", value: "ME", disabled: false }, - { name: "Montserrat", value: "MS", disabled: false }, - { name: "Morocco", value: "MA", disabled: false }, - { name: "Mozambique", value: "MZ", disabled: false }, - { name: "Myanmar", value: "MM", disabled: false }, - { name: "Namibia", value: "NA", disabled: false }, - { name: "Nauru", value: "NR", disabled: false }, - { name: "Nepal", value: "NP", disabled: false }, - { name: "Netherlands", value: "NL", disabled: false }, - { name: "New Caledonia", value: "NC", disabled: false }, - { name: "New Zealand", value: "NZ", disabled: false }, - { name: "Nicaragua", value: "NI", disabled: false }, - { name: "Niger", value: "NE", disabled: false }, - { name: "Nigeria", value: "NG", disabled: false }, - { name: "Niue", value: "NU", disabled: false }, - { name: "Norfolk Island", value: "NF", disabled: false }, - { name: "Northern Mariana Islands", value: "MP", disabled: false }, - { name: "Norway", value: "NO", disabled: false }, - { name: "Oman", value: "OM", disabled: false }, - { name: "Pakistan", value: "PK", disabled: false }, - { name: "Palau", value: "PW", disabled: false }, - { name: "Palestinian Territory, Occupied", value: "PS", disabled: false }, - { name: "Panama", value: "PA", disabled: false }, - { name: "Papua New Guinea", value: "PG", disabled: false }, - { name: "Paraguay", value: "PY", disabled: false }, - { name: "Peru", value: "PE", disabled: false }, - { name: "Philippines", value: "PH", disabled: false }, - { name: "Pitcairn", value: "PN", disabled: false }, - { name: "Poland", value: "PL", disabled: false }, - { name: "Portugal", value: "PT", disabled: false }, - { name: "Puerto Rico", value: "PR", disabled: false }, - { name: "Qatar", value: "QA", disabled: false }, - { name: "Réunion", value: "RE", disabled: false }, - { name: "Romania", value: "RO", disabled: false }, - { name: "Russian Federation", value: "RU", disabled: false }, - { name: "Rwanda", value: "RW", disabled: false }, - { name: "Saint Barthélemy", value: "BL", disabled: false }, - { name: "Saint Helena, Ascension and Tristan da Cunha", value: "SH", disabled: false }, - { name: "Saint Kitts and Nevis", value: "KN", disabled: false }, - { name: "Saint Lucia", value: "LC", disabled: false }, - { name: "Saint Martin (French part)", value: "MF", disabled: false }, - { name: "Saint Pierre and Miquelon", value: "PM", disabled: false }, - { name: "Saint Vincent and the Grenadines", value: "VC", disabled: false }, - { name: "Samoa", value: "WS", disabled: false }, - { name: "San Marino", value: "SM", disabled: false }, - { name: "Sao Tome and Principe", value: "ST", disabled: false }, - { name: "Saudi Arabia", value: "SA", disabled: false }, - { name: "Senegal", value: "SN", disabled: false }, - { name: "Serbia", value: "RS", disabled: false }, - { name: "Seychelles", value: "SC", disabled: false }, - { name: "Sierra Leone", value: "SL", disabled: false }, - { name: "Singapore", value: "SG", disabled: false }, - { name: "Sint Maarten (Dutch part)", value: "SX", disabled: false }, - { name: "Slovakia", value: "SK", disabled: false }, - { name: "Slovenia", value: "SI", disabled: false }, - { name: "Solomon Islands", value: "SB", disabled: false }, - { name: "Somalia", value: "SO", disabled: false }, - { name: "South Africa", value: "ZA", disabled: false }, - { name: "South Georgia and the South Sandwich Islands", value: "GS", disabled: false }, - { name: "South Sudan", value: "SS", disabled: false }, - { name: "Spain", value: "ES", disabled: false }, - { name: "Sri Lanka", value: "LK", disabled: false }, - { name: "Sudan", value: "SD", disabled: false }, - { name: "Suriname", value: "SR", disabled: false }, - { name: "Svalbard and Jan Mayen", value: "SJ", disabled: false }, - { name: "Swaziland", value: "SZ", disabled: false }, - { name: "Sweden", value: "SE", disabled: false }, - { name: "Switzerland", value: "CH", disabled: false }, - { name: "Syrian Arab Republic", value: "SY", disabled: false }, - { name: "Taiwan", value: "TW", disabled: false }, - { name: "Tajikistan", value: "TJ", disabled: false }, - { name: "Tanzania, United Republic of", value: "TZ", disabled: false }, - { name: "Thailand", value: "TH", disabled: false }, - { name: "Timor-Leste", value: "TL", disabled: false }, - { name: "Togo", value: "TG", disabled: false }, - { name: "Tokelau", value: "TK", disabled: false }, - { name: "Tonga", value: "TO", disabled: false }, - { name: "Trinidad and Tobago", value: "TT", disabled: false }, - { name: "Tunisia", value: "TN", disabled: false }, - { name: "Turkey", value: "TR", disabled: false }, - { name: "Turkmenistan", value: "TM", disabled: false }, - { name: "Turks and Caicos Islands", value: "TC", disabled: false }, - { name: "Tuvalu", value: "TV", disabled: false }, - { name: "Uganda", value: "UG", disabled: false }, - { name: "Ukraine", value: "UA", disabled: false }, - { name: "United Arab Emirates", value: "AE", disabled: false }, - { name: "United States Minor Outlying Islands", value: "UM", disabled: false }, - { name: "Uruguay", value: "UY", disabled: false }, - { name: "Uzbekistan", value: "UZ", disabled: false }, - { name: "Vanuatu", value: "VU", disabled: false }, - { name: "Venezuela, Bolivarian Republic of", value: "VE", disabled: false }, - { name: "Viet Nam", value: "VN", disabled: false }, - { name: "Virgin Islands, British", value: "VG", disabled: false }, - { name: "Virgin Islands, U.S.", value: "VI", disabled: false }, - { name: "Wallis and Futuna", value: "WF", disabled: false }, - { name: "Western Sahara", value: "EH", disabled: false }, - { name: "Yemen", value: "YE", disabled: false }, - { name: "Zambia", value: "ZM", disabled: false }, - { name: "Zimbabwe", value: "ZW", disabled: false }, - ]; - taxRates: TaxRateResponse[]; + countryList: CountryListItem[] = this.taxService.getCountries(); constructor( private apiService: ApiService, private route: ActivatedRoute, private logService: LogService, private organizationApiService: OrganizationApiServiceAbstraction, + private taxService: TaxServiceAbstraction, ) {} get country(): string { - return this.taxFormGroup.get("country").value; - } - - set country(country: string) { - this.taxFormGroup.get("country").setValue(country); + return this.taxFormGroup.controls.country.value; } get postalCode(): string { - return this.taxFormGroup.get("postalCode").value; - } - - set postalCode(postalCode: string) { - this.taxFormGroup.get("postalCode").setValue(postalCode); - } - - get includeTaxId(): boolean { - return this.taxFormGroup.get("includeTaxId").value; - } - - set includeTaxId(includeTaxId: boolean) { - this.taxFormGroup.get("includeTaxId").setValue(includeTaxId); + return this.taxFormGroup.controls.postalCode.value; } get taxId(): string { - return this.taxFormGroup.get("taxId").value; - } - - set taxId(taxId: string) { - this.taxFormGroup.get("taxId").setValue(taxId); + return this.taxFormGroup.controls.taxId.value; } get line1(): string { - return this.taxFormGroup.get("line1").value; - } - - set line1(line1: string) { - this.taxFormGroup.get("line1").setValue(line1); + return this.taxFormGroup.controls.line1.value; } get line2(): string { - return this.taxFormGroup.get("line2").value; - } - - set line2(line2: string) { - this.taxFormGroup.get("line2").setValue(line2); + return this.taxFormGroup.controls.line2.value; } get city(): string { - return this.taxFormGroup.get("city").value; - } - - set city(city: string) { - this.taxFormGroup.get("city").setValue(city); + return this.taxFormGroup.controls.city.value; } get state(): string { - return this.taxFormGroup.get("state").value; + return this.taxFormGroup.controls.state.value; } - set state(state: string) { - this.taxFormGroup.get("state").setValue(state); + get showTaxIdField(): boolean { + return !!this.organizationId; } async ngOnInit() { @@ -402,22 +100,13 @@ export class TaxInfoComponent implements OnInit { try { const taxInfo = await this.organizationApiService.getTaxInfo(this.organizationId); if (taxInfo) { - this.taxId = taxInfo.taxId; - this.state = taxInfo.state; - this.line1 = taxInfo.line1; - this.line2 = taxInfo.line2; - this.city = taxInfo.city; - this.state = taxInfo.state; - this.postalCode = taxInfo.postalCode; - this.country = taxInfo.country || "US"; - this.includeTaxId = - this.countrySupportsTax(this.country) && - (!!taxInfo.taxId || - !!taxInfo.line1 || - !!taxInfo.line2 || - !!taxInfo.city || - !!taxInfo.state); - this.setTaxInfoObject(); + this.taxFormGroup.controls.taxId.setValue(taxInfo.taxId); + this.taxFormGroup.controls.state.setValue(taxInfo.state); + this.taxFormGroup.controls.line1.setValue(taxInfo.line1); + this.taxFormGroup.controls.line2.setValue(taxInfo.line2); + this.taxFormGroup.controls.city.setValue(taxInfo.city); + this.taxFormGroup.controls.postalCode.setValue(taxInfo.postalCode); + this.taxFormGroup.controls.country.setValue(taxInfo.country); } } catch (e) { this.logService.error(e); @@ -426,119 +115,79 @@ export class TaxInfoComponent implements OnInit { try { const taxInfo = await this.apiService.getTaxInfo(); if (taxInfo) { - this.postalCode = taxInfo.postalCode; - this.country = taxInfo.country || "US"; + this.taxFormGroup.controls.postalCode.setValue(taxInfo.postalCode); + this.taxFormGroup.controls.country.setValue(taxInfo.country); } - this.setTaxInfoObject(); } catch (e) { this.logService.error(e); } } - if (this.country === "US") { - this.taxFormGroup.get("postalCode").setValidators([Validators.required]); - this.taxFormGroup.get("postalCode").updateValueAndValidity(); - } + this.isTaxSupported = await this.taxService.isCountrySupported( + this.taxFormGroup.controls.country.value, + ); - if (this.country !== "US") { - this.onCountryChanged.emit(); - } + this.countryChanged.emit(); }); - this.taxFormGroup - .get("country") - .valueChanges.pipe(takeUntil(this.destroy$)) + this.taxFormGroup.controls.country.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) .subscribe((value) => { - if (value === "US") { - this.taxFormGroup.get("postalCode").setValidators([Validators.required]); - } else { - this.taxFormGroup.get("postalCode").clearValidators(); - } - this.taxFormGroup.get("postalCode").updateValueAndValidity(); - this.setTaxInfoObject(); - this.changeCountry(); + this.taxService + .isCountrySupported(this.taxFormGroup.controls.country.value) + .then((isSupported) => { + this.isTaxSupported = isSupported; + }) + .catch(() => { + this.isTaxSupported = false; + }) + .finally(() => { + if (!this.isTaxSupported) { + this.taxFormGroup.controls.taxId.setValue(null); + this.taxFormGroup.controls.line1.setValue(null); + this.taxFormGroup.controls.line2.setValue(null); + this.taxFormGroup.controls.city.setValue(null); + this.taxFormGroup.controls.state.setValue(null); + } + + this.countryChanged.emit(); + }); + this.taxInformationChanged.emit(); }); - try { - const taxRates = await this.apiService.getTaxRates(); - if (taxRates) { - this.taxRates = taxRates.data; - } - } catch (e) { - this.logService.error(e); - } finally { - this.loading = false; - } - } - - get taxRate() { - if (this.taxRates != null) { - const localTaxRate = this.taxRates.find( - (x) => x.country === this.country && x.postalCode === this.postalCode, - ); - return localTaxRate?.rate ?? null; - } - } - - setTaxInfoObject() { - this.taxInfo.country = this.country; - this.taxInfo.postalCode = this.postalCode; - this.taxInfo.includeTaxId = this.includeTaxId; - this.taxInfo.taxId = this.taxId; - this.taxInfo.line1 = this.line1; - this.taxInfo.line2 = this.line2; - this.taxInfo.city = this.city; - this.taxInfo.state = this.state; - } + this.taxFormGroup.controls.postalCode.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) + .subscribe(() => { + this.taxInformationChanged.emit(); + }); - get showTaxIdCheckbox() { - return ( - (this.organizationId || this.providerId) && - this.country !== "US" && - this.countrySupportsTax(this.taxInfo.country) - ); - } + this.taxFormGroup.controls.taxId.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) + .subscribe(() => { + this.taxInformationChanged.emit(); + }); - get showTaxIdFields() { - return ( - (this.organizationId || this.providerId) && - this.includeTaxId && - this.countrySupportsTax(this.country) - ); + this.loading = false; } - getTaxInfoRequest(): TaxInfoUpdateRequest { - if (this.organizationId || this.providerId) { - const request = new ExpandedTaxInfoUpdateRequest(); - request.country = this.country; - request.postalCode = this.postalCode; - - if (this.includeTaxId) { - request.taxId = this.taxId; - request.line1 = this.line1; - request.line2 = this.line2; - request.city = this.city; - request.state = this.state; - } else { - request.taxId = null; - request.line1 = null; - request.line2 = null; - request.city = null; - request.state = null; - } - return request; - } else { - const request = new TaxInfoUpdateRequest(); - request.postalCode = this.postalCode; - request.country = this.country; - return request; - } + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } submitTaxInfo(): Promise { this.taxFormGroup.updateValueAndValidity(); this.taxFormGroup.markAllAsTouched(); - const request = this.getTaxInfoRequest(); + + const request = new ExpandedTaxInfoUpdateRequest(); + request.country = this.country; + request.postalCode = this.postalCode; + request.taxId = this.taxId; + request.line1 = this.line1; + request.line2 = this.line2; + request.city = this.city; + request.state = this.state; + return this.organizationId ? this.organizationApiService.updateTaxInfo( this.organizationId, @@ -546,97 +195,4 @@ export class TaxInfoComponent implements OnInit { ) : this.apiService.putTaxInfo(request); } - - changeCountry() { - if (!this.countrySupportsTax(this.country)) { - this.includeTaxId = false; - this.taxId = null; - this.line1 = null; - this.line2 = null; - this.city = null; - this.state = null; - this.setTaxInfoObject(); - } - this.onCountryChanged.emit(); - } - - countrySupportsTax(countryCode: string) { - return this.taxSupportedCountryCodes.includes(countryCode); - } - - private taxSupportedCountryCodes: string[] = [ - "CN", - "FR", - "DE", - "CA", - "GB", - "AU", - "IN", - "AD", - "AR", - "AT", - "BE", - "BO", - "BR", - "BG", - "CL", - "CO", - "CR", - "HR", - "CY", - "CZ", - "DK", - "DO", - "EC", - "EG", - "SV", - "EE", - "FI", - "GE", - "GR", - "HK", - "HU", - "IS", - "ID", - "IQ", - "IE", - "IL", - "IT", - "JP", - "KE", - "KR", - "LV", - "LI", - "LT", - "LU", - "MY", - "MT", - "MX", - "NL", - "NZ", - "NO", - "PE", - "PH", - "PL", - "PT", - "RO", - "RU", - "SA", - "RS", - "SG", - "SK", - "SI", - "ZA", - "ES", - "SE", - "CH", - "TW", - "TH", - "TR", - "UA", - "AE", - "UY", - "VE", - "VN", - ]; } diff --git a/apps/web/src/app/key-management/migrate-encryption/migrate-legacy-encryption.component.ts b/apps/web/src/app/key-management/migrate-encryption/migrate-legacy-encryption.component.ts index bb5a1c511c6..2361293fe80 100644 --- a/apps/web/src/app/key-management/migrate-encryption/migrate-legacy-encryption.component.ts +++ b/apps/web/src/app/key-management/migrate-encryption/migrate-legacy-encryption.component.ts @@ -83,7 +83,7 @@ export class MigrateFromLegacyEncryptionComponent { }); if (deleteFolders) { - await this.folderApiService.deleteAll(); + await this.folderApiService.deleteAll(activeUser.id); await this.syncService.fullSync(true, true); await this.submit(); return; diff --git a/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.spec.ts b/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.spec.ts index a07f56db2d7..382ce8e026b 100644 --- a/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.spec.ts +++ b/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.spec.ts @@ -6,7 +6,7 @@ import { BehaviorSubject } from "rxjs"; import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { BitIconButtonComponent } from "@bitwarden/components/src/icon-button/icon-button.component"; +import { IconButtonModule, NavigationModule } from "@bitwarden/components"; import { NavItemComponent } from "@bitwarden/components/src/navigation/nav-item.component"; import { ProductSwitcherItem, ProductSwitcherService } from "../shared/product-switcher.service"; @@ -45,13 +45,8 @@ describe("NavigationProductSwitcherComponent", () => { mockProducts$.next({ bento: [], other: [] }); await TestBed.configureTestingModule({ - imports: [RouterModule], - declarations: [ - NavigationProductSwitcherComponent, - NavItemComponent, - BitIconButtonComponent, - I18nPipe, - ], + imports: [RouterModule, NavigationModule, IconButtonModule], + declarations: [NavigationProductSwitcherComponent, I18nPipe], providers: [ { provide: ProductSwitcherService, useValue: productSwitcherService }, { diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 1903759f959..ad536110b74 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -12,7 +12,7 @@ import { } from "@bitwarden/angular/auth/guards"; import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; import { generatorSwap } from "@bitwarden/angular/tools/generator/generator-swap"; -import { extensionRefreshSwap } from "@bitwarden/angular/utils/extension-refresh-swap"; +import { NewDeviceVerificationNoticeGuard } from "@bitwarden/angular/vault/guards"; import { AnonLayoutWrapperComponent, AnonLayoutWrapperData, @@ -25,7 +25,7 @@ import { RegistrationLinkExpiredComponent, LoginComponent, LoginSecondaryContentComponent, - LockV2Component, + LockComponent, LockIcon, TwoFactorTimeoutIcon, UserLockIcon, @@ -40,6 +40,11 @@ import { LoginDecryptionOptionsComponent, } from "@bitwarden/auth/angular"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { + NewDeviceVerificationNoticePageOneComponent, + NewDeviceVerificationNoticePageTwoComponent, + VaultIcons, +} from "@bitwarden/vault"; import { twofactorRefactorSwap } from "../../../../libs/angular/src/utils/two-factor-component-refactor-route-swap"; import { flagEnabled, Flags } from "../utils/flags"; @@ -50,7 +55,6 @@ import { FamiliesForEnterpriseSetupComponent } from "./admin-console/organizatio import { CreateOrganizationComponent } from "./admin-console/settings/create-organization.component"; import { deepLinkGuard } from "./auth/guards/deep-link.guard"; import { HintComponent } from "./auth/hint.component"; -import { LockComponent } from "./auth/lock.component"; import { LoginDecryptionOptionsComponentV1 } from "./auth/login/login-decryption-options/login-decryption-options-v1.component"; import { LoginComponentV1 } from "./auth/login/login-v1.component"; import { LoginViaAuthRequestComponentV1 } from "./auth/login/login-via-auth-request-v1.component"; @@ -503,44 +507,23 @@ const routes: Routes = [ }, }, }, - ...extensionRefreshSwap( - LockComponent, - LockV2Component, - { - path: "lock", - canActivate: [deepLinkGuard(), lockGuard()], - children: [ - { - path: "", - component: LockComponent, - }, - ], - data: { - pageTitle: { - key: "yourVaultIsLockedV2", - }, - pageIcon: LockIcon, - showReadonlyHostname: true, - } satisfies AnonLayoutWrapperData, - }, - { - path: "lock", - canActivate: [deepLinkGuard(), lockGuard()], - children: [ - { - path: "", - component: LockV2Component, - }, - ], - data: { - pageTitle: { - key: "yourAccountIsLocked", - }, - pageIcon: LockIcon, - showReadonlyHostname: true, - } satisfies AnonLayoutWrapperData, - }, - ), + { + path: "lock", + canActivate: [deepLinkGuard(), lockGuard()], + children: [ + { + path: "", + component: LockComponent, + }, + ], + data: { + pageTitle: { + key: "yourVaultIsLockedV2", + }, + pageIcon: LockIcon, + showReadonlyHostname: true, + } satisfies AnonLayoutWrapperData, + }, { path: "2fa", canActivate: [unauthGuardFn()], @@ -695,10 +678,37 @@ const routes: Routes = [ }, ], }, + { + path: "new-device-notice", + component: AnonLayoutWrapperComponent, + canActivate: [], + children: [ + { + path: "", + component: NewDeviceVerificationNoticePageOneComponent, + data: { + pageIcon: VaultIcons.ExclamationTriangle, + pageTitle: { + key: "importantNotice", + }, + }, + }, + { + path: "setup", + component: NewDeviceVerificationNoticePageTwoComponent, + data: { + pageIcon: VaultIcons.UserLock, + pageTitle: { + key: "setupTwoStepLogin", + }, + }, + }, + ], + }, { path: "", component: UserLayoutComponent, - canActivate: [deepLinkGuard(), authGuard], + canActivate: [deepLinkGuard(), authGuard, NewDeviceVerificationNoticeGuard], children: [ { path: "vault", 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 4c1b51a5482..02dc5ef48bb 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 @@ -419,19 +419,19 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { switch (type) { case CipherType.Login: - this.title = this.i18nService.t(partOne, this.i18nService.t("typeLogin").toLowerCase()); + this.title = this.i18nService.t(partOne, this.i18nService.t("typeLogin")); break; case CipherType.Card: - this.title = this.i18nService.t(partOne, this.i18nService.t("typeCard").toLowerCase()); + this.title = this.i18nService.t(partOne, this.i18nService.t("typeCard")); break; case CipherType.Identity: - this.title = this.i18nService.t(partOne, this.i18nService.t("typeIdentity").toLowerCase()); + this.title = this.i18nService.t(partOne, this.i18nService.t("typeIdentity")); break; case CipherType.SecureNote: - this.title = this.i18nService.t(partOne, this.i18nService.t("note").toLowerCase()); + this.title = this.i18nService.t(partOne, this.i18nService.t("note")); break; case CipherType.SshKey: - this.title = this.i18nService.t(partOne, this.i18nService.t("typeSshKey").toLowerCase()); + this.title = this.i18nService.t(partOne, this.i18nService.t("typeSshKey")); break; } } diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts index d68e3b9d732..b7f99fb7b44 100644 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts +++ b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-move-dialog/bulk-move-dialog.component.ts @@ -3,8 +3,9 @@ import { DialogConfig, DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; import { Component, Inject, OnInit } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; -import { firstValueFrom, Observable } from "rxjs"; +import { firstValueFrom, map, Observable } from "rxjs"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -47,6 +48,8 @@ export class BulkMoveDialogComponent implements OnInit { }); folders$: Observable; + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + constructor( @Inject(DIALOG_DATA) params: BulkMoveDialogParams, private dialogRef: DialogRef, @@ -55,12 +58,14 @@ export class BulkMoveDialogComponent implements OnInit { private i18nService: I18nService, private folderService: FolderService, private formBuilder: FormBuilder, + private accountService: AccountService, ) { this.cipherIds = params.cipherIds ?? []; } async ngOnInit() { - this.folders$ = this.folderService.folderViews$; + const activeUserId = await firstValueFrom(this.activeUserId$); + this.folders$ = this.folderService.folderViews$(activeUserId); this.formGroup.patchValue({ folderId: (await firstValueFrom(this.folders$))[0].id, }); diff --git a/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts b/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts index 4d181a0510d..88af1ef601b 100644 --- a/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts +++ b/apps/web/src/app/vault/individual-vault/folder-add-edit.component.ts @@ -61,7 +61,7 @@ export class FolderAddEditComponent extends BaseFolderAddEditComponent { } try { - await this.folderApiService.delete(this.folder.id); + await this.folderApiService.delete(this.folder.id, await firstValueFrom(this.activeUserId$)); this.toastService.showToast({ variant: "success", title: null, @@ -82,10 +82,10 @@ export class FolderAddEditComponent extends BaseFolderAddEditComponent { } try { - const activeAccountId = (await firstValueFrom(this.accountSerivce.activeAccount$)).id; + const activeAccountId = await firstValueFrom(this.activeUserId$); const userKey = await this.keyService.getUserKeyWithLegacySupport(activeAccountId); const folder = await this.folderService.encrypt(this.folder, userKey); - this.formPromise = this.folderApiService.save(folder); + this.formPromise = this.folderApiService.save(folder, activeAccountId); await this.formPromise; this.platformUtilsService.showToast( "success", diff --git a/apps/web/src/app/vault/individual-vault/password-history.component.html b/apps/web/src/app/vault/individual-vault/password-history.component.html index 7127e7ca649..4eaca8f736e 100644 --- a/apps/web/src/app/vault/individual-vault/password-history.component.html +++ b/apps/web/src/app/vault/individual-vault/password-history.component.html @@ -3,7 +3,7 @@ {{ "passwordHistory" | i18n }} - + -

    +

    {{ "claimedDomainsDesc" | i18n }} - + {{ row.organizationName }} {{ row.userCount }} / {{ row.seats }} - + {{ row.plan }} diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html index 33a20444c2b..74aa468c42e 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.html @@ -29,7 +29,7 @@

    {{ "generalInformation" | i18n }}

    - diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts index 46fd6989681..f773db6c11c 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/setup/setup.component.ts @@ -111,9 +111,7 @@ export class SetupComponent implements OnInit, OnDestroy { try { this.formGroup.markAllAsTouched(); - const formIsValid = this.formGroup.valid && this.manageTaxInformationComponent.touch(); - - if (!formIsValid) { + if (!this.manageTaxInformationComponent.validate() || !this.formGroup.valid) { return; } @@ -131,14 +129,11 @@ export class SetupComponent implements OnInit, OnDestroy { request.taxInfo.country = taxInformation.country; request.taxInfo.postalCode = taxInformation.postalCode; - - if (taxInformation.includeTaxId) { - request.taxInfo.taxId = taxInformation.taxId; - request.taxInfo.line1 = taxInformation.line1; - request.taxInfo.line2 = taxInformation.line2; - request.taxInfo.city = taxInformation.city; - request.taxInfo.state = taxInformation.state; - } + request.taxInfo.taxId = taxInformation.taxId; + request.taxInfo.line1 = taxInformation.line1; + request.taxInfo.line2 = taxInformation.line2; + request.taxInfo.city = taxInformation.city; + request.taxInfo.state = taxInformation.state; const provider = await this.providerApiService.postProviderSetup(this.providerId, request); diff --git a/bitwarden_license/bit-web/src/app/app.module.ts b/bitwarden_license/bit-web/src/app/app.module.ts index 4db1e2f5e20..fd1a3b0b84c 100644 --- a/bitwarden_license/bit-web/src/app/app.module.ts +++ b/bitwarden_license/bit-web/src/app/app.module.ts @@ -20,6 +20,7 @@ import { MaximumVaultTimeoutPolicyComponent } from "./admin-console/policies/max import { AppRoutingModule } from "./app-routing.module"; import { AppComponent } from "./app.component"; import { FreeFamiliesSponsorshipPolicyComponent } from "./billing/policies/free-families-sponsorship.component"; +import { AccessIntelligenceModule } from "./tools/access-intelligence/access-intelligence.module"; /** * This is the AppModule for the commercial version of Bitwarden. @@ -41,6 +42,7 @@ import { FreeFamiliesSponsorshipPolicyComponent } from "./billing/policies/free- AppRoutingModule, OssRoutingModule, OrganizationsModule, // Must be after OssRoutingModule for competing routes to resolve properly + AccessIntelligenceModule, RouterModule, WildcardRoutingModule, // Needs to be last to catch all non-existing routes ], diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.html b/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.html index a08f5710f1e..78f2cb41bef 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.html +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.html @@ -43,6 +43,14 @@

    {{ planCard.name }}

    {{ "organizationName" | i18n }} + + {{ "organizationNameMaxLength" | i18n }} + diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.ts index 18910491a0c..2a27b1b32f3 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/create-client-dialog.component.ts @@ -47,7 +47,7 @@ export class CreateClientDialogComponent implements OnInit { protected discountPercentage: number; protected formGroup = new FormGroup({ clientOwnerEmail: new FormControl("", [Validators.required, Validators.email]), - organizationName: new FormControl("", [Validators.required]), + organizationName: new FormControl("", [Validators.required, Validators.maxLength(50)]), seats: new FormControl(null, [Validators.required, Validators.min(1)]), }); protected loading = true; diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/index.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/index.ts index f8b344372ef..05887fc198e 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/clients/index.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/index.ts @@ -4,3 +4,4 @@ export * from "./manage-client-name-dialog.component"; export * from "./manage-client-subscription-dialog.component"; export * from "./no-clients.component"; export * from "./vnext-manage-clients.component"; +export * from "./replace.pipe"; diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/replace.pipe.ts b/bitwarden_license/bit-web/src/app/billing/providers/clients/replace.pipe.ts new file mode 100644 index 00000000000..4a06e85f533 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/replace.pipe.ts @@ -0,0 +1,11 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +@Pipe({ + name: "replace", + standalone: true, +}) +export class ReplacePipe implements PipeTransform { + transform(value: string, pattern: string, replacement: string): string { + return value.replace(pattern, replacement); + } +} diff --git a/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-manage-clients.component.html b/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-manage-clients.component.html index 99de9352f62..7c560e49579 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-manage-clients.component.html +++ b/bitwarden_license/bit-web/src/app/billing/providers/clients/vnext-manage-clients.component.html @@ -29,7 +29,7 @@ - +
    {{ row.organizationName @@ -45,8 +45,8 @@ {{ row.remainingSeats }} - - {{ row.plan }} + + {{ row.plan | replace: " (Monthly)" : "" }}

    /> - {{ r.name }} + {{ r.applicationName }} - {{ r.atRiskPasswords }} + {{ r.atRiskPasswordCount }} - {{ r.totalPasswords }} + {{ r.passwordCount }} - {{ r.atRiskMembers }} + {{ r.atRiskMemberCount }} - {{ r.totalMembers }} + {{ r.memberCount }} diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/all-applications.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/all-applications.component.ts index 6ee2ecf1690..f4d3656071d 100644 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/all-applications.component.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/all-applications.component.ts @@ -1,20 +1,23 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, DestroyRef, inject, OnInit } from "@angular/core"; +import { Component, DestroyRef, OnDestroy, OnInit, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormControl } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; -import { debounceTime, firstValueFrom, map } from "rxjs"; +import { debounceTime, map, Observable, of, Subscription } from "rxjs"; -import { AuditService } from "@bitwarden/common/abstractions/audit.service"; +import { + RiskInsightsDataService, + RiskInsightsReportService, +} from "@bitwarden/bit-common/tools/reports/risk-insights"; +import { + ApplicationHealthReportDetail, + ApplicationHealthReportSummary, +} from "@bitwarden/bit-common/tools/reports/risk-insights/models/password-health"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { Icons, NoItemsModule, @@ -27,60 +30,76 @@ import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.mod import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; -import { applicationTableMockData } from "./application-table.mock"; +import { ApplicationsLoadingComponent } from "./risk-insights-loading.component"; @Component({ standalone: true, selector: "tools-all-applications", templateUrl: "./all-applications.component.html", - imports: [HeaderModule, CardComponent, SearchModule, PipesModule, NoItemsModule, SharedModule], + imports: [ + ApplicationsLoadingComponent, + HeaderModule, + CardComponent, + SearchModule, + PipesModule, + NoItemsModule, + SharedModule, + ], }) -export class AllApplicationsComponent implements OnInit { - protected dataSource = new TableDataSource(); +export class AllApplicationsComponent implements OnInit, OnDestroy { + protected dataSource = new TableDataSource(); protected selectedIds: Set = new Set(); protected searchControl = new FormControl("", { nonNullable: true }); - private destroyRef = inject(DestroyRef); - protected loading = false; - protected organization: Organization; + protected loading = true; + protected organization = {} as Organization; noItemsIcon = Icons.Security; protected markingAsCritical = false; - isCritialAppsFeatureEnabled = false; + protected applicationSummary = {} as ApplicationHealthReportSummary; + private subscription = new Subscription(); - // MOCK DATA - protected mockData = applicationTableMockData; - protected mockAtRiskMembersCount = 0; - protected mockAtRiskAppsCount = 0; - protected mockTotalMembersCount = 0; - protected mockTotalAppsCount = 0; + destroyRef = inject(DestroyRef); + isLoading$: Observable = of(false); + isCriticalAppsFeatureEnabled = false; async ngOnInit() { - this.activatedRoute.paramMap - .pipe( - takeUntilDestroyed(this.destroyRef), - map(async (params) => { - const organizationId = params.get("organizationId"); - this.organization = await firstValueFrom(this.organizationService.get$(organizationId)); - // TODO: use organizationId to fetch data - }), - ) - .subscribe(); - - this.isCritialAppsFeatureEnabled = await this.configService.getFeatureFlag( + this.isCriticalAppsFeatureEnabled = await this.configService.getFeatureFlag( FeatureFlag.CriticalApps, ); + + const organizationId = this.activatedRoute.snapshot.paramMap.get("organizationId"); + + if (organizationId) { + this.organization = await this.organizationService.get(organizationId); + this.subscription = this.dataService.applications$ + .pipe( + map((applications) => { + if (applications) { + this.dataSource.data = applications; + this.applicationSummary = + this.reportService.generateApplicationsSummary(applications); + } + }), + takeUntilDestroyed(this.destroyRef), + ) + .subscribe(); + this.isLoading$ = this.dataService.isLoading$; + } + } + + ngOnDestroy(): void { + this.subscription?.unsubscribe(); } constructor( protected cipherService: CipherService, - protected passwordStrengthService: PasswordStrengthServiceAbstraction, - protected auditService: AuditService, protected i18nService: I18nService, protected activatedRoute: ActivatedRoute, protected toastService: ToastService, - protected organizationService: OrganizationService, protected configService: ConfigService, + protected dataService: RiskInsightsDataService, + protected organizationService: OrganizationService, + protected reportService: RiskInsightsReportService, ) { - this.dataSource.data = applicationTableMockData; this.searchControl.valueChanges .pipe(debounceTime(200), takeUntilDestroyed()) .subscribe((v) => (this.dataSource.filter = v)); @@ -90,7 +109,7 @@ export class AllApplicationsComponent implements OnInit { // TODO: implement this.toastService.showToast({ variant: "warning", - title: null, + title: "", message: "Not yet implemented", }); }; @@ -103,7 +122,7 @@ export class AllApplicationsComponent implements OnInit { this.selectedIds.clear(); this.toastService.showToast({ variant: "success", - title: null, + title: "", message: this.i18nService.t("appsMarkedAsCritical"), }); resolve(true); @@ -112,8 +131,8 @@ export class AllApplicationsComponent implements OnInit { }); }; - trackByFunction(_: number, item: CipherView) { - return item.id; + trackByFunction(_: number, item: ApplicationHealthReportDetail) { + return item.applicationName; } onCheckboxChange(id: number, event: Event) { diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.html index 5b1fe4610d9..aeaa9f33197 100644 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.html +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.html @@ -34,10 +34,10 @@ - {{ passwordStrengthMap.get(r.id)[0] | i18n }} + {{ r.weakPasswordDetail?.detailValue.label | i18n }} @@ -46,8 +46,8 @@ - - {{ "exposedXTimes" | i18n: exposedPasswordMap.get(r.id) }} + + {{ "exposedXTimes" | i18n: r.exposedPasswordDetail?.exposedXTimes }} diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.spec.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.spec.ts index 98637d0decb..4329cfbde14 100644 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.spec.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.spec.ts @@ -3,17 +3,9 @@ import { ActivatedRoute, convertToParamMap } from "@angular/router"; import { mock } from "jest-mock-extended"; import { of } from "rxjs"; -import { - MemberCipherDetailsApiService, - PasswordHealthService, -} from "@bitwarden/bit-common/tools/reports/risk-insights"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { AuditService } from "@bitwarden/common/abstractions/audit.service"; +import { RiskInsightsReportService } from "@bitwarden/bit-common/tools/reports/risk-insights"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; -import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { TableModule } from "@bitwarden/components"; -import { TableBodyDirective } from "@bitwarden/components/src/table/table.component"; import { LooseComponentsModule } from "@bitwarden/web-vault/app/shared"; import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; @@ -27,21 +19,10 @@ describe("PasswordHealthComponent", () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [PasswordHealthComponent, PipesModule, TableModule, LooseComponentsModule], - declarations: [TableBodyDirective], + declarations: [], providers: [ - { provide: CipherService, useValue: mock() }, + { provide: RiskInsightsReportService, useValue: mock() }, { provide: I18nService, useValue: mock() }, - { provide: AuditService, useValue: mock() }, - { provide: ApiService, useValue: mock() }, - { provide: MemberCipherDetailsApiService, useValue: mock() }, - { - provide: PasswordStrengthServiceAbstraction, - useValue: mock(), - }, - { - provide: PasswordHealthService, - useValue: mock(), - }, { provide: ActivatedRoute, useValue: { diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.ts index 06f7de439cf..62d543a080d 100644 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/password-health.component.ts @@ -4,21 +4,14 @@ import { CommonModule } from "@angular/common"; import { Component, DestroyRef, inject, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute } from "@angular/router"; -import { map } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { - MemberCipherDetailsApiService, - PasswordHealthService, -} from "@bitwarden/bit-common/tools/reports/risk-insights"; -import { AuditService } from "@bitwarden/common/abstractions/audit.service"; +import { RiskInsightsReportService } from "@bitwarden/bit-common/tools/reports/risk-insights"; +import { CipherHealthReportDetail } from "@bitwarden/bit-common/tools/reports/risk-insights/models/password-health"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; -import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; -import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { BadgeModule, - BadgeVariant, ContainerComponent, TableDataSource, TableModule, @@ -41,28 +34,19 @@ import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pip HeaderModule, TableModule, ], - providers: [PasswordHealthService, MemberCipherDetailsApiService], }) export class PasswordHealthComponent implements OnInit { - passwordStrengthMap = new Map(); - passwordUseMap = new Map(); - - exposedPasswordMap = new Map(); - - dataSource = new TableDataSource(); + dataSource = new TableDataSource(); loading = true; private destroyRef = inject(DestroyRef); constructor( - protected cipherService: CipherService, - protected passwordStrengthService: PasswordStrengthServiceAbstraction, - protected auditService: AuditService, + protected riskInsightsReportService: RiskInsightsReportService, protected i18nService: I18nService, protected activatedRoute: ActivatedRoute, - protected memberCipherDetailsApiService: MemberCipherDetailsApiService, ) {} ngOnInit() { @@ -78,20 +62,9 @@ export class PasswordHealthComponent implements OnInit { } async setCiphers(organizationId: string) { - const passwordHealthService = new PasswordHealthService( - this.passwordStrengthService, - this.auditService, - this.cipherService, - this.memberCipherDetailsApiService, - organizationId, + this.dataSource.data = await firstValueFrom( + this.riskInsightsReportService.generateRawDataReport$(organizationId), ); - - await passwordHealthService.generateReport(); - - this.dataSource.data = passwordHealthService.reportCiphers; - this.exposedPasswordMap = passwordHealthService.exposedPasswordMap; - this.passwordStrengthMap = passwordHealthService.passwordStrengthMap; - this.passwordUseMap = passwordHealthService.passwordUseMap; this.loading = false; } } diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights-loading.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights-loading.component.html new file mode 100644 index 00000000000..d6f945bfb92 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights-loading.component.html @@ -0,0 +1,8 @@ +
    + +

    {{ "generatingRiskInsights" | i18n }}

    +
    diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights-loading.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights-loading.component.ts new file mode 100644 index 00000000000..1cafa62c608 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights-loading.component.ts @@ -0,0 +1,14 @@ +import { CommonModule } from "@angular/common"; +import { Component } from "@angular/core"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; + +@Component({ + selector: "tools-risk-insights-loading", + standalone: true, + imports: [CommonModule, JslibModule], + templateUrl: "./risk-insights-loading.component.html", +}) +export class ApplicationsLoadingComponent { + constructor() {} +} diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.html b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.html index 6df47e3c46f..e0618c525a7 100644 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.html +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.html @@ -1,49 +1,58 @@ -
    {{ "accessIntelligence" | i18n }}
    -

    {{ "riskInsights" | i18n }}

    -
    -
    - - {{ - "dataLastUpdated" | i18n: (dataLastUpdated | date: "MMMM d, y 'at' h:mm a") - }} - +
    {{ "accessIntelligence" | i18n }}
    +

    {{ "riskInsights" | i18n }}

    +
    +
    - {{ "refresh" | i18n }} - -
    - - - - - - - - {{ "criticalApplicationsWithCount" | i18n: criticalApps.length }} - - - - - - - - - - - - - - + + {{ + "dataLastUpdated" | i18n: (dataLastUpdated$ | async | date: "MMMM d, y 'at' h:mm a") + }} + + + {{ "refresh" | i18n }} + + + + + +
    + + + + + + + + {{ "criticalApplicationsWithCount" | i18n: criticalAppsCount }} + + + + + + + + + + + + + + diff --git a/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.ts b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.ts index 5ea39bd0513..1a90e18f0df 100644 --- a/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.ts +++ b/bitwarden_license/bit-web/src/app/tools/access-intelligence/risk-insights.component.ts @@ -1,11 +1,13 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { CommonModule } from "@angular/common"; -import { Component, OnInit } from "@angular/core"; +import { Component, DestroyRef, OnInit, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute, Router } from "@angular/router"; +import { Observable, EMPTY } from "rxjs"; +import { map, switchMap } from "rxjs/operators"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { RiskInsightsDataService } from "@bitwarden/bit-common/tools/reports/risk-insights"; +import { ApplicationHealthReportDetail } from "@bitwarden/bit-common/tools/reports/risk-insights/models/password-health"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { AsyncActionsModule, ButtonModule, TabsModule } from "@bitwarden/components"; @@ -43,45 +45,80 @@ export enum RiskInsightsTabType { ], }) export class RiskInsightsComponent implements OnInit { - tabIndex: RiskInsightsTabType; - dataLastUpdated = new Date(); - isCritialAppsFeatureEnabled = false; + tabIndex: RiskInsightsTabType = RiskInsightsTabType.AllApps; - apps: any[] = []; - criticalApps: any[] = []; - notifiedMembers: any[] = []; + dataLastUpdated: Date = new Date(); - async refreshData() { - // TODO: Implement - return new Promise((resolve) => - setTimeout(() => { - this.dataLastUpdated = new Date(); - resolve(true); - }, 1000), - ); - } + isCriticalAppsFeatureEnabled: boolean = false; - onTabChange = async (newIndex: number) => { - await this.router.navigate([], { - relativeTo: this.route, - queryParams: { tabIndex: newIndex }, - queryParamsHandling: "merge", + appsCount: number = 0; + criticalAppsCount: number = 0; + notifiedMembersCount: number = 0; + + private organizationId: string | null = null; + private destroyRef = inject(DestroyRef); + isLoading$: Observable = new Observable(); + isRefreshing$: Observable = new Observable(); + dataLastUpdated$: Observable = new Observable(); + refetching: boolean = false; + + constructor( + private route: ActivatedRoute, + private router: Router, + private configService: ConfigService, + private dataService: RiskInsightsDataService, + ) { + this.route.queryParams.pipe(takeUntilDestroyed()).subscribe(({ tabIndex }) => { + this.tabIndex = !isNaN(Number(tabIndex)) ? Number(tabIndex) : RiskInsightsTabType.AllApps; }); - }; + } async ngOnInit() { - this.isCritialAppsFeatureEnabled = await this.configService.getFeatureFlag( + this.isCriticalAppsFeatureEnabled = await this.configService.getFeatureFlag( FeatureFlag.CriticalApps, ); + + this.route.paramMap + .pipe( + takeUntilDestroyed(this.destroyRef), + map((params) => params.get("organizationId")), + switchMap((orgId: string | null) => { + if (orgId) { + this.organizationId = orgId; + this.dataService.fetchApplicationsReport(orgId); + this.isLoading$ = this.dataService.isLoading$; + this.isRefreshing$ = this.dataService.isRefreshing$; + this.dataLastUpdated$ = this.dataService.dataLastUpdated$; + return this.dataService.applications$; + } else { + return EMPTY; + } + }), + ) + .subscribe({ + next: (applications: ApplicationHealthReportDetail[] | null) => { + if (applications) { + this.appsCount = applications.length; + } + }, + }); } - constructor( - protected route: ActivatedRoute, - private router: Router, - private configService: ConfigService, - ) { - route.queryParams.pipe(takeUntilDestroyed()).subscribe(({ tabIndex }) => { - this.tabIndex = !isNaN(tabIndex) ? tabIndex : RiskInsightsTabType.AllApps; + /** + * Refreshes the data by re-fetching the applications report. + * This will automatically notify child components subscribed to the RiskInsightsDataService observables. + */ + refreshData(): void { + if (this.organizationId) { + this.dataService.refreshApplicationsReport(this.organizationId); + } + } + + async onTabChange(newIndex: number): Promise { + await this.router.navigate([], { + relativeTo: this.route, + queryParams: { tabIndex: newIndex }, + queryParamsHandling: "merge", }); } } diff --git a/libs/angular/src/auth/components/base-login-via-webauthn.component.ts b/libs/angular/src/auth/components/base-login-via-webauthn.component.ts index 82d93ff0b8b..1ad4829767a 100644 --- a/libs/angular/src/auth/components/base-login-via-webauthn.component.ts +++ b/libs/angular/src/auth/components/base-login-via-webauthn.component.ts @@ -2,7 +2,9 @@ // @ts-strict-ignore import { Directive, OnInit } from "@angular/core"; import { Router } from "@angular/router"; +import { firstValueFrom } from "rxjs"; +import { LoginSuccessHandlerService } from "@bitwarden/auth/common"; import { WebAuthnLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login.service.abstraction"; import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; import { WebAuthnLoginCredentialAssertionView } from "@bitwarden/common/auth/models/view/webauthn-login/webauthn-login-credential-assertion.view"; @@ -10,6 +12,7 @@ import { ErrorResponse } from "@bitwarden/common/models/response/error.response" 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 { KeyService } from "@bitwarden/key-management"; export type State = "assert" | "assertFailed"; @@ -26,6 +29,8 @@ export class BaseLoginViaWebAuthnComponent implements OnInit { private logService: LogService, private validationService: ValidationService, private i18nService: I18nService, + private loginSuccessHandlerService: LoginSuccessHandlerService, + private keyService: KeyService, ) {} ngOnInit(): void { @@ -59,11 +64,21 @@ export class BaseLoginViaWebAuthnComponent implements OnInit { this.i18nService.t("twoFactorForPasskeysNotSupportedOnClientUpdateToLogIn"), ); this.currentState = "assertFailed"; - } else if (authResult.forcePasswordReset == ForceSetPasswordReason.AdminForcePasswordReset) { + return; + } + + // Only run loginSuccessHandlerService if webAuthn is used for vault decryption. + const userKey = await firstValueFrom(this.keyService.userKey$(authResult.userId)); + if (userKey) { + await this.loginSuccessHandlerService.run(authResult.userId); + } + + if (authResult.forcePasswordReset == ForceSetPasswordReason.AdminForcePasswordReset) { await this.router.navigate([this.forcePasswordResetRoute]); - } else { - await this.router.navigate([this.successRoute]); + return; } + + await this.router.navigate([this.successRoute]); } catch (error) { if (error instanceof ErrorResponse) { this.validationService.showError(this.i18nService.t("invalidPasskeyPleaseTryAgain")); diff --git a/libs/angular/src/auth/components/lock.component.ts b/libs/angular/src/auth/components/lock.component.ts deleted file mode 100644 index 20ad37fd2b4..00000000000 --- a/libs/angular/src/auth/components/lock.component.ts +++ /dev/null @@ -1,398 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Directive, NgZone, OnDestroy, OnInit } from "@angular/core"; -import { Router } from "@angular/router"; -import { firstValueFrom, Subject } from "rxjs"; -import { concatMap, map, take, takeUntil } from "rxjs/operators"; - -import { PinServiceAbstraction, PinLockType } from "@bitwarden/auth/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service"; -import { VaultTimeoutService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout.service"; -import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; -import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; -import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; -import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction"; -import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction"; -import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; -import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; -import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; -import { - MasterPasswordVerification, - MasterPasswordVerificationResponse, -} from "@bitwarden/common/auth/types/verification"; -import { ClientType } from "@bitwarden/common/enums"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; -import { KeySuffixOptions } from "@bitwarden/common/platform/enums"; -import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; -import { UserId } from "@bitwarden/common/types/guid"; -import { UserKey } from "@bitwarden/common/types/key"; -import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; -import { DialogService, ToastService } from "@bitwarden/components"; -import { - KdfConfigService, - KeyService, - BiometricStateService, - BiometricsService, -} from "@bitwarden/key-management"; - -@Directive() -export class LockComponent implements OnInit, OnDestroy { - masterPassword = ""; - pin = ""; - showPassword = false; - email: string; - pinEnabled = false; - masterPasswordEnabled = false; - webVaultHostname = ""; - formPromise: Promise; - supportsBiometric: boolean; - biometricLock: boolean; - - private activeUserId: UserId; - protected successRoute = "vault"; - protected forcePasswordResetRoute = "update-temp-password"; - protected onSuccessfulSubmit: () => Promise; - - private invalidPinAttempts = 0; - private pinLockType: PinLockType; - - private enforcedMasterPasswordOptions: MasterPasswordPolicyOptions = undefined; - - private destroy$ = new Subject(); - - constructor( - protected masterPasswordService: InternalMasterPasswordServiceAbstraction, - protected router: Router, - protected i18nService: I18nService, - protected platformUtilsService: PlatformUtilsService, - protected messagingService: MessagingService, - protected keyService: KeyService, - protected vaultTimeoutService: VaultTimeoutService, - protected vaultTimeoutSettingsService: VaultTimeoutSettingsService, - protected environmentService: EnvironmentService, - protected stateService: StateService, - protected apiService: ApiService, - protected logService: LogService, - protected ngZone: NgZone, - protected policyApiService: PolicyApiServiceAbstraction, - protected policyService: InternalPolicyService, - protected passwordStrengthService: PasswordStrengthServiceAbstraction, - protected dialogService: DialogService, - protected deviceTrustService: DeviceTrustServiceAbstraction, - protected userVerificationService: UserVerificationService, - protected pinService: PinServiceAbstraction, - protected biometricStateService: BiometricStateService, - protected biometricsService: BiometricsService, - protected accountService: AccountService, - protected authService: AuthService, - protected kdfConfigService: KdfConfigService, - protected syncService: SyncService, - protected toastService: ToastService, - ) {} - - async ngOnInit() { - this.accountService.activeAccount$ - .pipe( - concatMap(async (account) => { - this.activeUserId = account?.id; - await this.load(account?.id); - }), - takeUntil(this.destroy$), - ) - .subscribe(); - } - - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } - - async submit() { - if (this.pinEnabled) { - return await this.handlePinRequiredUnlock(); - } - - await this.handleMasterPasswordRequiredUnlock(); - } - - async logOut() { - const confirmed = await this.dialogService.openSimpleDialog({ - title: { key: "logOut" }, - content: { key: "logOutConfirmation" }, - acceptButtonText: { key: "logOut" }, - type: "warning", - }); - - if (confirmed) { - this.messagingService.send("logout", { userId: this.activeUserId }); - } - } - - async unlockBiometric(): Promise { - if (!this.biometricLock) { - return; - } - - await this.biometricStateService.setUserPromptCancelled(); - const userKey = await this.keyService.getUserKeyFromStorage( - KeySuffixOptions.Biometric, - this.activeUserId, - ); - - if (userKey) { - await this.setUserKeyAndContinue(userKey, this.activeUserId, false); - } - - return !!userKey; - } - - async isBiometricUnlockAvailable(): Promise { - if (!(await this.biometricsService.supportsBiometric())) { - return false; - } - return this.biometricsService.isBiometricUnlockAvailable(); - } - - togglePassword() { - this.showPassword = !this.showPassword; - const input = document.getElementById(this.pinEnabled ? "pin" : "masterPassword"); - if (this.ngZone.isStable) { - input.focus(); - } else { - this.ngZone.onStable.pipe(take(1)).subscribe(() => input.focus()); - } - } - - private async handlePinRequiredUnlock() { - if (this.pin == null || this.pin === "") { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("pinRequired"), - }); - return; - } - - return await this.doUnlockWithPin(); - } - - private async doUnlockWithPin() { - const MAX_INVALID_PIN_ENTRY_ATTEMPTS = 5; - - try { - const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; - const userKey = await this.pinService.decryptUserKeyWithPin(this.pin, userId); - - if (userKey) { - await this.setUserKeyAndContinue(userKey, userId); - return; // successfully unlocked - } - - // Failure state: invalid PIN or failed decryption - this.invalidPinAttempts++; - - // Log user out if they have entered an invalid PIN too many times - if (this.invalidPinAttempts >= MAX_INVALID_PIN_ENTRY_ATTEMPTS) { - this.toastService.showToast({ - variant: "error", - title: null, - message: this.i18nService.t("tooManyInvalidPinEntryAttemptsLoggingOut"), - }); - this.messagingService.send("logout"); - return; - } - - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("invalidPin"), - }); - } catch { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("unexpectedError"), - }); - } - } - - private async handleMasterPasswordRequiredUnlock() { - if (this.masterPassword == null || this.masterPassword === "") { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("masterPasswordRequired"), - }); - return; - } - await this.doUnlockWithMasterPassword(); - } - - private async doUnlockWithMasterPassword() { - const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; - - const verification = { - type: VerificationType.MasterPassword, - secret: this.masterPassword, - } as MasterPasswordVerification; - - let passwordValid = false; - let response: MasterPasswordVerificationResponse; - try { - this.formPromise = this.userVerificationService.verifyUserByMasterPassword( - verification, - userId, - this.email, - ); - response = await this.formPromise; - this.enforcedMasterPasswordOptions = MasterPasswordPolicyOptions.fromResponse( - response.policyOptions, - ); - passwordValid = true; - } catch (e) { - this.logService.error(e); - } finally { - this.formPromise = null; - } - - if (!passwordValid) { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: this.i18nService.t("invalidMasterPassword"), - }); - return; - } - - const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey( - response.masterKey, - userId, - ); - await this.setUserKeyAndContinue(userKey, userId, true); - } - - private async setUserKeyAndContinue( - key: UserKey, - userId: UserId, - evaluatePasswordAfterUnlock = false, - ) { - await this.keyService.setUserKey(key, userId); - - // Now that we have a decrypted user key in memory, we can check if we - // need to establish trust on the current device - const activeAccount = await firstValueFrom(this.accountService.activeAccount$); - await this.deviceTrustService.trustDeviceIfRequired(activeAccount.id); - - await this.doContinue(evaluatePasswordAfterUnlock); - } - - private async doContinue(evaluatePasswordAfterUnlock: boolean) { - await this.biometricStateService.resetUserPromptCancelled(); - this.messagingService.send("unlocked"); - - if (evaluatePasswordAfterUnlock) { - try { - // If we do not have any saved policies, attempt to load them from the service - if (this.enforcedMasterPasswordOptions == undefined) { - this.enforcedMasterPasswordOptions = await firstValueFrom( - this.policyService.masterPasswordPolicyOptions$(), - ); - } - - if (this.requirePasswordChange()) { - const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; - await this.masterPasswordService.setForceSetPasswordReason( - ForceSetPasswordReason.WeakMasterPassword, - userId, - ); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate([this.forcePasswordResetRoute]); - return; - } - } catch (e) { - // Do not prevent unlock if there is an error evaluating policies - this.logService.error(e); - } - } - - // Vault can be de-synced since notifications get ignored while locked. Need to check whether sync is required using the sync service. - const clientType = this.platformUtilsService.getClientType(); - if (clientType === ClientType.Browser || clientType === ClientType.Desktop) { - // Desktop and Browser have better offline support and to facilitate this we don't make the user wait for what - // could be an HTTP Timeout because their server is unreachable. - await Promise.race([ - this.syncService - .fullSync(false) - .catch((err) => this.logService.error("Error during unlock sync", err)), - new Promise((resolve) => - setTimeout(() => { - this.logService.warning("Skipping sync wait, continuing to unlock."); - resolve(); - }, 5_000), - ), - ]); - } else { - await this.syncService.fullSync(false); - } - - if (this.onSuccessfulSubmit != null) { - await this.onSuccessfulSubmit(); - } else if (this.router != null) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate([this.successRoute]); - } - } - - private async load(userId: UserId) { - this.pinLockType = await this.pinService.getPinLockType(userId); - - this.pinEnabled = await this.pinService.isPinDecryptionAvailable(userId); - - this.masterPasswordEnabled = await this.userVerificationService.hasMasterPassword(); - - this.supportsBiometric = await this.biometricsService.supportsBiometric(); - this.biometricLock = - (await this.vaultTimeoutSettingsService.isBiometricLockSet()) && - ((await this.keyService.hasUserKeyStored(KeySuffixOptions.Biometric)) || - !this.platformUtilsService.supportsSecureStorage()); - this.email = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.email)), - ); - - this.webVaultHostname = (await this.environmentService.getEnvironment()).getHostname(); - } - - /** - * Checks if the master password meets the enforced policy requirements - * If not, returns false - */ - private requirePasswordChange(): boolean { - if ( - this.enforcedMasterPasswordOptions == undefined || - !this.enforcedMasterPasswordOptions.enforceOnLogin - ) { - return false; - } - - const passwordStrength = this.passwordStrengthService.getPasswordStrength( - this.masterPassword, - this.email, - )?.score; - - return !this.policyService.evaluateMasterPassword( - passwordStrength, - this.masterPassword, - this.enforcedMasterPasswordOptions, - ); - } -} diff --git a/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.html b/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.html index 0b041bd4c06..3f635656fb7 100644 --- a/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.html +++ b/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.html @@ -1,7 +1,7 @@
    - + {{ "country" | i18n }}
    - + {{ "zipPostalCode" | i18n }}
    -
    - - - {{ "includeVAT" | i18n }} - -
    -
    -
    -
    - - {{ "taxIdNumber" | i18n }} - - -
    -
    -
    -
    - - {{ "address1" | i18n }} - - -
    -
    - - {{ "address2" | i18n }} - - -
    -
    - - {{ "cityTown" | i18n }} - - -
    -
    - - {{ "stateProvince" | i18n }} - - + +
    + + {{ "address1" | i18n }} + + +
    +
    + + {{ "address2" | i18n }} + + +
    +
    + + {{ "cityTown" | i18n }} + + +
    +
    + + {{ "stateProvince" | i18n }} + + +
    +
    + + {{ "taxIdNumber" | i18n }} + + +
    +
    +
    +
    - diff --git a/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.ts b/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.ts index a3564b1ebc9..13a6d2d0cc3 100644 --- a/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.ts +++ b/libs/angular/src/billing/components/manage-tax-information/manage-tax-information.component.ts @@ -3,14 +3,10 @@ import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; import { Subject, takeUntil } from "rxjs"; +import { debounceTime } from "rxjs/operators"; -import { TaxInformation } from "@bitwarden/common/billing/models/domain"; - -type Country = { - name: string; - value: string; - disabled: boolean; -}; +import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; +import { CountryListItem, TaxInformation } from "@bitwarden/common/billing/models/domain"; @Component({ selector: "app-manage-tax-information", @@ -19,12 +15,23 @@ type Country = { export class ManageTaxInformationComponent implements OnInit, OnDestroy { @Input() startWith: TaxInformation; @Input() onSubmit?: (taxInformation: TaxInformation) => Promise; + @Input() showTaxIdField: boolean = true; + + /** + * Emits when the tax information has changed. + */ + @Output() taxInformationChanged = new EventEmitter(); + + /** + * Emits when the tax information has been updated. + */ @Output() taxInformationUpdated = new EventEmitter(); + private taxInformation: TaxInformation; + protected formGroup = this.formBuilder.group({ country: ["", Validators.required], postalCode: ["", Validators.required], - includeTaxId: false, taxId: "", line1: "", line2: "", @@ -32,16 +39,20 @@ export class ManageTaxInformationComponent implements OnInit, OnDestroy { state: "", }); + protected isTaxSupported: boolean; + private destroy$ = new Subject(); - private taxInformation: TaxInformation; + protected readonly countries: CountryListItem[] = this.taxService.getCountries(); - constructor(private formBuilder: FormBuilder) {} + constructor( + private formBuilder: FormBuilder, + private taxService: TaxServiceAbstraction, + ) {} - getTaxInformation = (): TaxInformation & { includeTaxId: boolean } => ({ - ...this.taxInformation, - includeTaxId: this.formGroup.value.includeTaxId, - }); + getTaxInformation(): TaxInformation { + return this.taxInformation; + } submit = async () => { this.formGroup.markAllAsTouched(); @@ -52,23 +63,32 @@ export class ManageTaxInformationComponent implements OnInit, OnDestroy { this.taxInformationUpdated.emit(); }; - touch = (): boolean => { - this.formGroup.markAllAsTouched(); - return this.formGroup.valid; - }; + validate(): boolean { + if (this.formGroup.dirty) { + this.formGroup.markAllAsTouched(); + return this.formGroup.valid; + } else { + return this.formGroup.valid; + } + } async ngOnInit() { if (this.startWith) { - this.formGroup.patchValue({ - ...this.startWith, - includeTaxId: - this.countrySupportsTax(this.startWith.country) && - (!!this.startWith.taxId || - !!this.startWith.line1 || - !!this.startWith.line2 || - !!this.startWith.city || - !!this.startWith.state), - }); + this.formGroup.controls.country.setValue(this.startWith.country); + this.formGroup.controls.postalCode.setValue(this.startWith.postalCode); + + this.isTaxSupported = + this.startWith && this.startWith.country + ? await this.taxService.isCountrySupported(this.startWith.country) + : false; + + if (this.isTaxSupported) { + this.formGroup.controls.taxId.setValue(this.startWith.taxId); + this.formGroup.controls.line1.setValue(this.startWith.line1); + this.formGroup.controls.line2.setValue(this.startWith.line2); + this.formGroup.controls.city.setValue(this.startWith.city); + this.formGroup.controls.state.setValue(this.startWith.state); + } } this.formGroup.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((values) => { @@ -82,354 +102,47 @@ export class ManageTaxInformationComponent implements OnInit, OnDestroy { state: values.state, }; }); - } - ngOnDestroy() { - this.destroy$.next(); - this.destroy$.complete(); - } + this.formGroup.controls.country.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) + .subscribe((country: string) => { + this.taxService + .isCountrySupported(country) + .then((isSupported) => (this.isTaxSupported = isSupported)) + .catch(() => (this.isTaxSupported = false)) + .finally(() => { + if (!this.isTaxSupported) { + this.formGroup.controls.taxId.setValue(null); + this.formGroup.controls.line1.setValue(null); + this.formGroup.controls.line2.setValue(null); + this.formGroup.controls.city.setValue(null); + this.formGroup.controls.state.setValue(null); + } + if (this.taxInformationChanged) { + this.taxInformationChanged.emit(this.taxInformation); + } + }); + }); - protected countrySupportsTax(countryCode: string) { - return this.taxSupportedCountryCodes.includes(countryCode); - } + this.formGroup.controls.postalCode.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) + .subscribe(() => { + if (this.taxInformationChanged) { + this.taxInformationChanged.emit(this.taxInformation); + } + }); - protected get includeTaxIdIsSelected() { - return this.formGroup.value.includeTaxId; + this.formGroup.controls.taxId.valueChanges + .pipe(debounceTime(1000), takeUntil(this.destroy$)) + .subscribe(() => { + if (this.taxInformationChanged) { + this.taxInformationChanged.emit(this.taxInformation); + } + }); } - protected get selectionSupportsAdditionalOptions() { - return ( - this.formGroup.value.country !== "US" && this.countrySupportsTax(this.formGroup.value.country) - ); + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); } - - protected countries: Country[] = [ - { name: "-- Select --", value: "", disabled: false }, - { name: "United States", value: "US", disabled: false }, - { name: "China", value: "CN", disabled: false }, - { name: "France", value: "FR", disabled: false }, - { name: "Germany", value: "DE", disabled: false }, - { name: "Canada", value: "CA", disabled: false }, - { name: "United Kingdom", value: "GB", disabled: false }, - { name: "Australia", value: "AU", disabled: false }, - { name: "India", value: "IN", disabled: false }, - { name: "", value: "-", disabled: true }, - { name: "Afghanistan", value: "AF", disabled: false }, - { name: "Åland Islands", value: "AX", disabled: false }, - { name: "Albania", value: "AL", disabled: false }, - { name: "Algeria", value: "DZ", disabled: false }, - { name: "American Samoa", value: "AS", disabled: false }, - { name: "Andorra", value: "AD", disabled: false }, - { name: "Angola", value: "AO", disabled: false }, - { name: "Anguilla", value: "AI", disabled: false }, - { name: "Antarctica", value: "AQ", disabled: false }, - { name: "Antigua and Barbuda", value: "AG", disabled: false }, - { name: "Argentina", value: "AR", disabled: false }, - { name: "Armenia", value: "AM", disabled: false }, - { name: "Aruba", value: "AW", disabled: false }, - { name: "Austria", value: "AT", disabled: false }, - { name: "Azerbaijan", value: "AZ", disabled: false }, - { name: "Bahamas", value: "BS", disabled: false }, - { name: "Bahrain", value: "BH", disabled: false }, - { name: "Bangladesh", value: "BD", disabled: false }, - { name: "Barbados", value: "BB", disabled: false }, - { name: "Belarus", value: "BY", disabled: false }, - { name: "Belgium", value: "BE", disabled: false }, - { name: "Belize", value: "BZ", disabled: false }, - { name: "Benin", value: "BJ", disabled: false }, - { name: "Bermuda", value: "BM", disabled: false }, - { name: "Bhutan", value: "BT", disabled: false }, - { name: "Bolivia, Plurinational State of", value: "BO", disabled: false }, - { name: "Bonaire, Sint Eustatius and Saba", value: "BQ", disabled: false }, - { name: "Bosnia and Herzegovina", value: "BA", disabled: false }, - { name: "Botswana", value: "BW", disabled: false }, - { name: "Bouvet Island", value: "BV", disabled: false }, - { name: "Brazil", value: "BR", disabled: false }, - { name: "British Indian Ocean Territory", value: "IO", disabled: false }, - { name: "Brunei Darussalam", value: "BN", disabled: false }, - { name: "Bulgaria", value: "BG", disabled: false }, - { name: "Burkina Faso", value: "BF", disabled: false }, - { name: "Burundi", value: "BI", disabled: false }, - { name: "Cambodia", value: "KH", disabled: false }, - { name: "Cameroon", value: "CM", disabled: false }, - { name: "Cape Verde", value: "CV", disabled: false }, - { name: "Cayman Islands", value: "KY", disabled: false }, - { name: "Central African Republic", value: "CF", disabled: false }, - { name: "Chad", value: "TD", disabled: false }, - { name: "Chile", value: "CL", disabled: false }, - { name: "Christmas Island", value: "CX", disabled: false }, - { name: "Cocos (Keeling) Islands", value: "CC", disabled: false }, - { name: "Colombia", value: "CO", disabled: false }, - { name: "Comoros", value: "KM", disabled: false }, - { name: "Congo", value: "CG", disabled: false }, - { name: "Congo, the Democratic Republic of the", value: "CD", disabled: false }, - { name: "Cook Islands", value: "CK", disabled: false }, - { name: "Costa Rica", value: "CR", disabled: false }, - { name: "Côte d'Ivoire", value: "CI", disabled: false }, - { name: "Croatia", value: "HR", disabled: false }, - { name: "Cuba", value: "CU", disabled: false }, - { name: "Curaçao", value: "CW", disabled: false }, - { name: "Cyprus", value: "CY", disabled: false }, - { name: "Czech Republic", value: "CZ", disabled: false }, - { name: "Denmark", value: "DK", disabled: false }, - { name: "Djibouti", value: "DJ", disabled: false }, - { name: "Dominica", value: "DM", disabled: false }, - { name: "Dominican Republic", value: "DO", disabled: false }, - { name: "Ecuador", value: "EC", disabled: false }, - { name: "Egypt", value: "EG", disabled: false }, - { name: "El Salvador", value: "SV", disabled: false }, - { name: "Equatorial Guinea", value: "GQ", disabled: false }, - { name: "Eritrea", value: "ER", disabled: false }, - { name: "Estonia", value: "EE", disabled: false }, - { name: "Ethiopia", value: "ET", disabled: false }, - { name: "Falkland Islands (Malvinas)", value: "FK", disabled: false }, - { name: "Faroe Islands", value: "FO", disabled: false }, - { name: "Fiji", value: "FJ", disabled: false }, - { name: "Finland", value: "FI", disabled: false }, - { name: "French Guiana", value: "GF", disabled: false }, - { name: "French Polynesia", value: "PF", disabled: false }, - { name: "French Southern Territories", value: "TF", disabled: false }, - { name: "Gabon", value: "GA", disabled: false }, - { name: "Gambia", value: "GM", disabled: false }, - { name: "Georgia", value: "GE", disabled: false }, - { name: "Ghana", value: "GH", disabled: false }, - { name: "Gibraltar", value: "GI", disabled: false }, - { name: "Greece", value: "GR", disabled: false }, - { name: "Greenland", value: "GL", disabled: false }, - { name: "Grenada", value: "GD", disabled: false }, - { name: "Guadeloupe", value: "GP", disabled: false }, - { name: "Guam", value: "GU", disabled: false }, - { name: "Guatemala", value: "GT", disabled: false }, - { name: "Guernsey", value: "GG", disabled: false }, - { name: "Guinea", value: "GN", disabled: false }, - { name: "Guinea-Bissau", value: "GW", disabled: false }, - { name: "Guyana", value: "GY", disabled: false }, - { name: "Haiti", value: "HT", disabled: false }, - { name: "Heard Island and McDonald Islands", value: "HM", disabled: false }, - { name: "Holy See (Vatican City State)", value: "VA", disabled: false }, - { name: "Honduras", value: "HN", disabled: false }, - { name: "Hong Kong", value: "HK", disabled: false }, - { name: "Hungary", value: "HU", disabled: false }, - { name: "Iceland", value: "IS", disabled: false }, - { name: "Indonesia", value: "ID", disabled: false }, - { name: "Iran, Islamic Republic of", value: "IR", disabled: false }, - { name: "Iraq", value: "IQ", disabled: false }, - { name: "Ireland", value: "IE", disabled: false }, - { name: "Isle of Man", value: "IM", disabled: false }, - { name: "Israel", value: "IL", disabled: false }, - { name: "Italy", value: "IT", disabled: false }, - { name: "Jamaica", value: "JM", disabled: false }, - { name: "Japan", value: "JP", disabled: false }, - { name: "Jersey", value: "JE", disabled: false }, - { name: "Jordan", value: "JO", disabled: false }, - { name: "Kazakhstan", value: "KZ", disabled: false }, - { name: "Kenya", value: "KE", disabled: false }, - { name: "Kiribati", value: "KI", disabled: false }, - { name: "Korea, Democratic People's Republic of", value: "KP", disabled: false }, - { name: "Korea, Republic of", value: "KR", disabled: false }, - { name: "Kuwait", value: "KW", disabled: false }, - { name: "Kyrgyzstan", value: "KG", disabled: false }, - { name: "Lao People's Democratic Republic", value: "LA", disabled: false }, - { name: "Latvia", value: "LV", disabled: false }, - { name: "Lebanon", value: "LB", disabled: false }, - { name: "Lesotho", value: "LS", disabled: false }, - { name: "Liberia", value: "LR", disabled: false }, - { name: "Libya", value: "LY", disabled: false }, - { name: "Liechtenstein", value: "LI", disabled: false }, - { name: "Lithuania", value: "LT", disabled: false }, - { name: "Luxembourg", value: "LU", disabled: false }, - { name: "Macao", value: "MO", disabled: false }, - { name: "Macedonia, the former Yugoslav Republic of", value: "MK", disabled: false }, - { name: "Madagascar", value: "MG", disabled: false }, - { name: "Malawi", value: "MW", disabled: false }, - { name: "Malaysia", value: "MY", disabled: false }, - { name: "Maldives", value: "MV", disabled: false }, - { name: "Mali", value: "ML", disabled: false }, - { name: "Malta", value: "MT", disabled: false }, - { name: "Marshall Islands", value: "MH", disabled: false }, - { name: "Martinique", value: "MQ", disabled: false }, - { name: "Mauritania", value: "MR", disabled: false }, - { name: "Mauritius", value: "MU", disabled: false }, - { name: "Mayotte", value: "YT", disabled: false }, - { name: "Mexico", value: "MX", disabled: false }, - { name: "Micronesia, Federated States of", value: "FM", disabled: false }, - { name: "Moldova, Republic of", value: "MD", disabled: false }, - { name: "Monaco", value: "MC", disabled: false }, - { name: "Mongolia", value: "MN", disabled: false }, - { name: "Montenegro", value: "ME", disabled: false }, - { name: "Montserrat", value: "MS", disabled: false }, - { name: "Morocco", value: "MA", disabled: false }, - { name: "Mozambique", value: "MZ", disabled: false }, - { name: "Myanmar", value: "MM", disabled: false }, - { name: "Namibia", value: "NA", disabled: false }, - { name: "Nauru", value: "NR", disabled: false }, - { name: "Nepal", value: "NP", disabled: false }, - { name: "Netherlands", value: "NL", disabled: false }, - { name: "New Caledonia", value: "NC", disabled: false }, - { name: "New Zealand", value: "NZ", disabled: false }, - { name: "Nicaragua", value: "NI", disabled: false }, - { name: "Niger", value: "NE", disabled: false }, - { name: "Nigeria", value: "NG", disabled: false }, - { name: "Niue", value: "NU", disabled: false }, - { name: "Norfolk Island", value: "NF", disabled: false }, - { name: "Northern Mariana Islands", value: "MP", disabled: false }, - { name: "Norway", value: "NO", disabled: false }, - { name: "Oman", value: "OM", disabled: false }, - { name: "Pakistan", value: "PK", disabled: false }, - { name: "Palau", value: "PW", disabled: false }, - { name: "Palestinian Territory, Occupied", value: "PS", disabled: false }, - { name: "Panama", value: "PA", disabled: false }, - { name: "Papua New Guinea", value: "PG", disabled: false }, - { name: "Paraguay", value: "PY", disabled: false }, - { name: "Peru", value: "PE", disabled: false }, - { name: "Philippines", value: "PH", disabled: false }, - { name: "Pitcairn", value: "PN", disabled: false }, - { name: "Poland", value: "PL", disabled: false }, - { name: "Portugal", value: "PT", disabled: false }, - { name: "Puerto Rico", value: "PR", disabled: false }, - { name: "Qatar", value: "QA", disabled: false }, - { name: "Réunion", value: "RE", disabled: false }, - { name: "Romania", value: "RO", disabled: false }, - { name: "Russian Federation", value: "RU", disabled: false }, - { name: "Rwanda", value: "RW", disabled: false }, - { name: "Saint Barthélemy", value: "BL", disabled: false }, - { name: "Saint Helena, Ascension and Tristan da Cunha", value: "SH", disabled: false }, - { name: "Saint Kitts and Nevis", value: "KN", disabled: false }, - { name: "Saint Lucia", value: "LC", disabled: false }, - { name: "Saint Martin (French part)", value: "MF", disabled: false }, - { name: "Saint Pierre and Miquelon", value: "PM", disabled: false }, - { name: "Saint Vincent and the Grenadines", value: "VC", disabled: false }, - { name: "Samoa", value: "WS", disabled: false }, - { name: "San Marino", value: "SM", disabled: false }, - { name: "Sao Tome and Principe", value: "ST", disabled: false }, - { name: "Saudi Arabia", value: "SA", disabled: false }, - { name: "Senegal", value: "SN", disabled: false }, - { name: "Serbia", value: "RS", disabled: false }, - { name: "Seychelles", value: "SC", disabled: false }, - { name: "Sierra Leone", value: "SL", disabled: false }, - { name: "Singapore", value: "SG", disabled: false }, - { name: "Sint Maarten (Dutch part)", value: "SX", disabled: false }, - { name: "Slovakia", value: "SK", disabled: false }, - { name: "Slovenia", value: "SI", disabled: false }, - { name: "Solomon Islands", value: "SB", disabled: false }, - { name: "Somalia", value: "SO", disabled: false }, - { name: "South Africa", value: "ZA", disabled: false }, - { name: "South Georgia and the South Sandwich Islands", value: "GS", disabled: false }, - { name: "South Sudan", value: "SS", disabled: false }, - { name: "Spain", value: "ES", disabled: false }, - { name: "Sri Lanka", value: "LK", disabled: false }, - { name: "Sudan", value: "SD", disabled: false }, - { name: "Suriname", value: "SR", disabled: false }, - { name: "Svalbard and Jan Mayen", value: "SJ", disabled: false }, - { name: "Swaziland", value: "SZ", disabled: false }, - { name: "Sweden", value: "SE", disabled: false }, - { name: "Switzerland", value: "CH", disabled: false }, - { name: "Syrian Arab Republic", value: "SY", disabled: false }, - { name: "Taiwan", value: "TW", disabled: false }, - { name: "Tajikistan", value: "TJ", disabled: false }, - { name: "Tanzania, United Republic of", value: "TZ", disabled: false }, - { name: "Thailand", value: "TH", disabled: false }, - { name: "Timor-Leste", value: "TL", disabled: false }, - { name: "Togo", value: "TG", disabled: false }, - { name: "Tokelau", value: "TK", disabled: false }, - { name: "Tonga", value: "TO", disabled: false }, - { name: "Trinidad and Tobago", value: "TT", disabled: false }, - { name: "Tunisia", value: "TN", disabled: false }, - { name: "Turkey", value: "TR", disabled: false }, - { name: "Turkmenistan", value: "TM", disabled: false }, - { name: "Turks and Caicos Islands", value: "TC", disabled: false }, - { name: "Tuvalu", value: "TV", disabled: false }, - { name: "Uganda", value: "UG", disabled: false }, - { name: "Ukraine", value: "UA", disabled: false }, - { name: "United Arab Emirates", value: "AE", disabled: false }, - { name: "United States Minor Outlying Islands", value: "UM", disabled: false }, - { name: "Uruguay", value: "UY", disabled: false }, - { name: "Uzbekistan", value: "UZ", disabled: false }, - { name: "Vanuatu", value: "VU", disabled: false }, - { name: "Venezuela, Bolivarian Republic of", value: "VE", disabled: false }, - { name: "Viet Nam", value: "VN", disabled: false }, - { name: "Virgin Islands, British", value: "VG", disabled: false }, - { name: "Virgin Islands, U.S.", value: "VI", disabled: false }, - { name: "Wallis and Futuna", value: "WF", disabled: false }, - { name: "Western Sahara", value: "EH", disabled: false }, - { name: "Yemen", value: "YE", disabled: false }, - { name: "Zambia", value: "ZM", disabled: false }, - { name: "Zimbabwe", value: "ZW", disabled: false }, - ]; - - private taxSupportedCountryCodes: string[] = [ - "CN", - "FR", - "DE", - "CA", - "GB", - "AU", - "IN", - "AD", - "AR", - "AT", - "BE", - "BO", - "BR", - "BG", - "CL", - "CO", - "CR", - "HR", - "CY", - "CZ", - "DK", - "DO", - "EC", - "EG", - "SV", - "EE", - "FI", - "GE", - "GR", - "HK", - "HU", - "IS", - "ID", - "IQ", - "IE", - "IL", - "IT", - "JP", - "KE", - "KR", - "LV", - "LI", - "LT", - "LU", - "MY", - "MT", - "MX", - "NL", - "NZ", - "NO", - "PE", - "PH", - "PL", - "PT", - "RO", - "RU", - "SA", - "RS", - "SG", - "SK", - "SI", - "ZA", - "ES", - "SE", - "CH", - "TW", - "TH", - "TR", - "UA", - "AE", - "UY", - "VE", - "VN", - ]; } diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 92042a4162f..149d553696e 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -37,6 +37,8 @@ import { RegisterRouteService, AuthRequestApiService, DefaultAuthRequestApiService, + DefaultLoginSuccessHandlerService, + LoginSuccessHandlerService, } from "@bitwarden/auth/common"; import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service"; import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service"; @@ -136,11 +138,13 @@ import { import { AccountBillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/account/account-billing-api.service.abstraction"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { OrganizationBillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/organizations/organization-billing-api.service.abstraction"; +import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; import { AccountBillingApiService } from "@bitwarden/common/billing/services/account/account-billing-api.service"; import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service"; import { BillingApiService } from "@bitwarden/common/billing/services/billing-api.service"; import { OrganizationBillingApiService } from "@bitwarden/common/billing/services/organization/organization-billing-api.service"; import { OrganizationBillingService } from "@bitwarden/common/billing/services/organization-billing.service"; +import { TaxService } from "@bitwarden/common/billing/services/tax.service"; import { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/platform/abstractions/app-id.service"; import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; import { BulkEncryptService } from "@bitwarden/common/platform/abstractions/bulk-encrypt.service"; @@ -281,6 +285,10 @@ import { DefaultBiometricStateService, KdfConfigService, DefaultKdfConfigService, + UserAsymmetricKeysRegenerationService, + DefaultUserAsymmetricKeysRegenerationService, + UserAsymmetricKeysRegenerationApiService, + DefaultUserAsymmetricKeysRegenerationApiService, } from "@bitwarden/key-management"; import { PasswordRepromptService } from "@bitwarden/vault"; import { @@ -292,6 +300,7 @@ import { IndividualVaultExportServiceAbstraction, } from "@bitwarden/vault-export-core"; +import { NewDeviceVerificationNoticeService } from "../../../vault/src/services/new-device-verification-notice.service"; import { FormValidationErrorsService as FormValidationErrorsServiceAbstraction } from "../platform/abstractions/form-validation-errors.service"; import { ViewCacheService } from "../platform/abstractions/view-cache.service"; import { FormValidationErrorsService } from "../platform/services/form-validation-errors.service"; @@ -1264,6 +1273,11 @@ const safeProviders: SafeProvider[] = [ useClass: BillingApiService, deps: [ApiServiceAbstraction, LogService, ToastService], }), + safeProvider({ + provide: TaxServiceAbstraction, + useClass: TaxService, + deps: [ApiServiceAbstraction], + }), safeProvider({ provide: BillingAccountProfileStateService, useClass: DefaultBillingAccountProfileStateService, @@ -1377,7 +1391,6 @@ const safeProviders: SafeProvider[] = [ AccountServiceAbstraction, KdfConfigService, KeyServiceAbstraction, - ApiServiceAbstraction, ], }), safeProvider({ @@ -1395,6 +1408,30 @@ const safeProviders: SafeProvider[] = [ useClass: DefaultLoginDecryptionOptionsService, deps: [MessagingServiceAbstraction], }), + safeProvider(NewDeviceVerificationNoticeService), + safeProvider({ + provide: UserAsymmetricKeysRegenerationApiService, + useClass: DefaultUserAsymmetricKeysRegenerationApiService, + deps: [ApiServiceAbstraction], + }), + safeProvider({ + provide: UserAsymmetricKeysRegenerationService, + useClass: DefaultUserAsymmetricKeysRegenerationService, + deps: [ + KeyServiceAbstraction, + CipherServiceAbstraction, + UserAsymmetricKeysRegenerationApiService, + LogService, + SdkService, + ApiServiceAbstraction, + ConfigService, + ], + }), + safeProvider({ + provide: LoginSuccessHandlerService, + useClass: DefaultLoginSuccessHandlerService, + deps: [SyncService, UserAsymmetricKeysRegenerationService], + }), ]; @NgModule({ diff --git a/libs/angular/src/vault/components/add-edit.component.ts b/libs/angular/src/vault/components/add-edit.component.ts index 3a9d0289971..4bd3e9710fb 100644 --- a/libs/angular/src/vault/components/add-edit.component.ts +++ b/libs/angular/src/vault/components/add-edit.component.ts @@ -102,6 +102,8 @@ export class AddEditComponent implements OnInit, OnDestroy { private personalOwnershipPolicyAppliesToActiveUser: boolean; private previousCipherId: string; + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + get fido2CredentialCreationDateValue(): string { const dateCreated = this.i18nService.t("dateCreated"); const creationDate = this.datePipe.transform( @@ -259,12 +261,10 @@ export class AddEditComponent implements OnInit, OnDestroy { const loadedAddEditCipherInfo = await this.loadAddEditCipherInfo(); + const activeUserId = await firstValueFrom(this.activeUserId$); if (this.cipher == null) { if (this.editMode) { const cipher = await this.loadCipher(); - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); this.cipher = await cipher.decrypt( await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId), ); @@ -323,7 +323,7 @@ export class AddEditComponent implements OnInit, OnDestroy { this.cipher.login.fido2Credentials = null; } - this.folders$ = this.folderService.folderViews$; + this.folders$ = this.folderService.folderViews$(activeUserId); if (this.editMode && this.previousCipherId !== this.cipherId) { void this.eventCollectionService.collectMany(EventType.Cipher_ClientViewed, [this.cipher]); diff --git a/libs/angular/src/vault/components/folder-add-edit.component.ts b/libs/angular/src/vault/components/folder-add-edit.component.ts index 6d9ae53cc66..205733ba48d 100644 --- a/libs/angular/src/vault/components/folder-add-edit.component.ts +++ b/libs/angular/src/vault/components/folder-add-edit.component.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core"; import { Validators, FormBuilder } from "@angular/forms"; -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, map } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -27,6 +27,8 @@ export class FolderAddEditComponent implements OnInit { deletePromise: Promise; protected componentName = ""; + protected activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + formGroup = this.formBuilder.group({ name: ["", [Validators.required]], }); @@ -59,10 +61,10 @@ export class FolderAddEditComponent implements OnInit { } try { - const activeAccountId = await firstValueFrom(this.accountService.activeAccount$); - const userKey = await this.keyService.getUserKeyWithLegacySupport(activeAccountId.id); + const activeUserId = await firstValueFrom(this.activeUserId$); + const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId); const folder = await this.folderService.encrypt(this.folder, userKey); - this.formPromise = this.folderApiService.save(folder); + this.formPromise = this.folderApiService.save(folder, activeUserId); await this.formPromise; this.platformUtilsService.showToast( "success", @@ -90,7 +92,8 @@ export class FolderAddEditComponent implements OnInit { } try { - this.deletePromise = this.folderApiService.delete(this.folder.id); + const activeUserId = await firstValueFrom(this.activeUserId$); + this.deletePromise = this.folderApiService.delete(this.folder.id, activeUserId); await this.deletePromise; this.platformUtilsService.showToast("success", null, this.i18nService.t("deletedFolder")); this.onDeletedFolder.emit(this.folder); @@ -107,8 +110,10 @@ export class FolderAddEditComponent implements OnInit { if (this.editMode) { this.editMode = true; this.title = this.i18nService.t("editFolder"); - const folder = await this.folderService.get(this.folderId); - this.folder = await folder.decrypt(); + const activeUserId = await firstValueFrom(this.activeUserId$); + this.folder = await firstValueFrom( + this.folderService.getDecrypted$(this.folderId, activeUserId), + ); } else { this.title = this.i18nService.t("addFolder"); } diff --git a/libs/angular/src/vault/components/view.component.ts b/libs/angular/src/vault/components/view.component.ts index a6b9a571730..6bea4cd6150 100644 --- a/libs/angular/src/vault/components/view.component.ts +++ b/libs/angular/src/vault/components/view.component.ts @@ -79,6 +79,8 @@ export class ViewComponent implements OnDestroy, OnInit { private previousCipherId: string; private passwordReprompted = false; + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + get fido2CredentialCreationDateValue(): string { const dateCreated = this.i18nService.t("dateCreated"); const creationDate = this.datePipe.transform( @@ -141,9 +143,7 @@ export class ViewComponent implements OnDestroy, OnInit { this.cleanUp(); const cipher = await this.cipherService.get(this.cipherId); - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); + const activeUserId = await firstValueFrom(this.activeUserId$); this.cipher = await cipher.decrypt( await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId), ); @@ -158,7 +158,7 @@ export class ViewComponent implements OnDestroy, OnInit { if (this.cipher.folderId) { this.folder = await ( - await firstValueFrom(this.folderService.folderViews$) + await firstValueFrom(this.folderService.folderViews$(activeUserId)) ).find((f) => f.id == this.cipher.folderId); } diff --git a/libs/angular/src/vault/guards/index.ts b/libs/angular/src/vault/guards/index.ts new file mode 100644 index 00000000000..001a4832372 --- /dev/null +++ b/libs/angular/src/vault/guards/index.ts @@ -0,0 +1 @@ +export * from "./new-device-verification-notice.guard"; diff --git a/libs/angular/src/vault/guards/new-device-verification-notice.guard.spec.ts b/libs/angular/src/vault/guards/new-device-verification-notice.guard.spec.ts new file mode 100644 index 00000000000..e278113a653 --- /dev/null +++ b/libs/angular/src/vault/guards/new-device-verification-notice.guard.spec.ts @@ -0,0 +1,225 @@ +import { TestBed } from "@angular/core/testing"; +import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from "@angular/router"; +import { BehaviorSubject } from "rxjs"; + +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; + +import { NewDeviceVerificationNoticeService } from "../../../../vault/src/services/new-device-verification-notice.service"; +import { VaultProfileService } from "../services/vault-profile.service"; + +import { NewDeviceVerificationNoticeGuard } from "./new-device-verification-notice.guard"; + +describe("NewDeviceVerificationNoticeGuard", () => { + const _state = Object.freeze({}) as RouterStateSnapshot; + const emptyRoute = Object.freeze({ queryParams: {} }) as ActivatedRouteSnapshot; + const eightDaysAgo = new Date(); + eightDaysAgo.setDate(eightDaysAgo.getDate() - 8); + + const account = { + id: "account-id", + } as unknown as Account; + + const activeAccount$ = new BehaviorSubject(account); + + const createUrlTree = jest.fn(); + const getFeatureFlag = jest.fn().mockImplementation((key) => { + if (key === FeatureFlag.NewDeviceVerificationTemporaryDismiss) { + return Promise.resolve(true); + } + + return Promise.resolve(false); + }); + const isSelfHost = jest.fn().mockResolvedValue(false); + const getProfileTwoFactorEnabled = jest.fn().mockResolvedValue(false); + const policyAppliesToActiveUser$ = jest.fn().mockReturnValue(new BehaviorSubject(false)); + const noticeState$ = jest.fn().mockReturnValue(new BehaviorSubject(null)); + const getProfileCreationDate = jest.fn().mockResolvedValue(eightDaysAgo); + + beforeEach(() => { + getFeatureFlag.mockClear(); + isSelfHost.mockClear(); + getProfileCreationDate.mockClear(); + getProfileTwoFactorEnabled.mockClear(); + policyAppliesToActiveUser$.mockClear(); + createUrlTree.mockClear(); + + TestBed.configureTestingModule({ + providers: [ + { provide: Router, useValue: { createUrlTree } }, + { provide: ConfigService, useValue: { getFeatureFlag } }, + { provide: NewDeviceVerificationNoticeService, useValue: { noticeState$ } }, + { provide: AccountService, useValue: { activeAccount$ } }, + { provide: PlatformUtilsService, useValue: { isSelfHost } }, + { provide: PolicyService, useValue: { policyAppliesToActiveUser$ } }, + { + provide: VaultProfileService, + useValue: { getProfileCreationDate, getProfileTwoFactorEnabled }, + }, + ], + }); + }); + + function newDeviceGuard(route?: ActivatedRouteSnapshot) { + // Run the guard within injection context so `inject` works as you'd expect + // Pass state object to make TypeScript happy + return TestBed.runInInjectionContext(async () => + NewDeviceVerificationNoticeGuard(route ?? emptyRoute, _state), + ); + } + + describe("fromNewDeviceVerification", () => { + const route = { + queryParams: { fromNewDeviceVerification: "true" }, + } as unknown as ActivatedRouteSnapshot; + + it("returns `true` when `fromNewDeviceVerification` is present", async () => { + expect(await newDeviceGuard(route)).toBe(true); + }); + + it("does not execute other logic", async () => { + // `fromNewDeviceVerification` param should exit early, + // not foolproof but a quick way to test that other logic isn't executed + await newDeviceGuard(route); + + expect(getFeatureFlag).not.toHaveBeenCalled(); + expect(isSelfHost).not.toHaveBeenCalled(); + expect(getProfileTwoFactorEnabled).not.toHaveBeenCalled(); + expect(getProfileCreationDate).not.toHaveBeenCalled(); + expect(policyAppliesToActiveUser$).not.toHaveBeenCalled(); + }); + }); + + describe("missing current account", () => { + afterAll(() => { + // reset `activeAccount$` observable + activeAccount$.next(account); + }); + + it("redirects to login when account is missing", async () => { + activeAccount$.next(null); + + await newDeviceGuard(); + + expect(createUrlTree).toHaveBeenCalledWith(["/login"]); + }); + }); + + it("returns `true` when 2FA is enabled", async () => { + getProfileTwoFactorEnabled.mockResolvedValueOnce(true); + + expect(await newDeviceGuard()).toBe(true); + }); + + it("returns `true` when the user is self hosted", async () => { + isSelfHost.mockReturnValueOnce(true); + + expect(await newDeviceGuard()).toBe(true); + }); + + it("returns `true` SSO is required", async () => { + policyAppliesToActiveUser$.mockReturnValueOnce(new BehaviorSubject(true)); + + expect(await newDeviceGuard()).toBe(true); + expect(policyAppliesToActiveUser$).toHaveBeenCalledWith(PolicyType.RequireSso); + }); + + it("returns `true` when the profile was created less than a week ago", async () => { + const sixDaysAgo = new Date(); + sixDaysAgo.setDate(sixDaysAgo.getDate() - 6); + + getProfileCreationDate.mockResolvedValueOnce(sixDaysAgo); + + expect(await newDeviceGuard()).toBe(true); + }); + + describe("temp flag", () => { + beforeEach(() => { + getFeatureFlag.mockImplementation((key) => { + if (key === FeatureFlag.NewDeviceVerificationTemporaryDismiss) { + return Promise.resolve(true); + } + + return Promise.resolve(false); + }); + }); + + afterAll(() => { + getFeatureFlag.mockReturnValue(false); + }); + + it("redirects to notice when the user has not dismissed it", async () => { + noticeState$.mockReturnValueOnce(new BehaviorSubject(null)); + + await newDeviceGuard(); + + expect(createUrlTree).toHaveBeenCalledWith(["/new-device-notice"]); + expect(noticeState$).toHaveBeenCalledWith(account.id); + }); + + it("redirects to notice when the user dismissed it more than 7 days ago", async () => { + const eighteenDaysAgo = new Date(); + eighteenDaysAgo.setDate(eighteenDaysAgo.getDate() - 18); + + noticeState$.mockReturnValueOnce( + new BehaviorSubject({ last_dismissal: eighteenDaysAgo.toISOString() }), + ); + + await newDeviceGuard(); + + expect(createUrlTree).toHaveBeenCalledWith(["/new-device-notice"]); + }); + + it("returns true when the user dismissed less than 7 days ago", async () => { + const fourDaysAgo = new Date(); + fourDaysAgo.setDate(fourDaysAgo.getDate() - 4); + + noticeState$.mockReturnValueOnce( + new BehaviorSubject({ last_dismissal: fourDaysAgo.toISOString() }), + ); + + expect(await newDeviceGuard()).toBe(true); + }); + }); + + describe("permanent flag", () => { + beforeEach(() => { + getFeatureFlag.mockImplementation((key) => { + if (key === FeatureFlag.NewDeviceVerificationPermanentDismiss) { + return Promise.resolve(true); + } + + return Promise.resolve(false); + }); + }); + + afterAll(() => { + getFeatureFlag.mockReturnValue(false); + }); + + it("redirects when the user has not dismissed", async () => { + noticeState$.mockReturnValueOnce(new BehaviorSubject(null)); + + await newDeviceGuard(); + + expect(createUrlTree).toHaveBeenCalledWith(["/new-device-notice"]); + + noticeState$.mockReturnValueOnce(new BehaviorSubject({ permanent_dismissal: null })); + + await newDeviceGuard(); + + expect(createUrlTree).toHaveBeenCalledTimes(2); + expect(createUrlTree).toHaveBeenCalledWith(["/new-device-notice"]); + }); + + it("returns `true` when the user has dismissed", async () => { + noticeState$.mockReturnValueOnce(new BehaviorSubject({ permanent_dismissal: true })); + + expect(await newDeviceGuard()).toBe(true); + }); + }); +}); diff --git a/libs/angular/src/vault/guards/new-device-verification-notice.guard.ts b/libs/angular/src/vault/guards/new-device-verification-notice.guard.ts new file mode 100644 index 00000000000..20550e0e8cf --- /dev/null +++ b/libs/angular/src/vault/guards/new-device-verification-notice.guard.ts @@ -0,0 +1,121 @@ +import { inject } from "@angular/core"; +import { ActivatedRouteSnapshot, CanActivateFn, Router } from "@angular/router"; +import { Observable, firstValueFrom } from "rxjs"; + +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; + +import { NewDeviceVerificationNoticeService } from "../../../../vault/src/services/new-device-verification-notice.service"; +import { VaultProfileService } from "../services/vault-profile.service"; + +export const NewDeviceVerificationNoticeGuard: CanActivateFn = async ( + route: ActivatedRouteSnapshot, +) => { + const router = inject(Router); + const configService = inject(ConfigService); + const newDeviceVerificationNoticeService = inject(NewDeviceVerificationNoticeService); + const accountService = inject(AccountService); + const platformUtilsService = inject(PlatformUtilsService); + const policyService = inject(PolicyService); + const vaultProfileService = inject(VaultProfileService); + + if (route.queryParams["fromNewDeviceVerification"]) { + return true; + } + + const tempNoticeFlag = await configService.getFeatureFlag( + FeatureFlag.NewDeviceVerificationTemporaryDismiss, + ); + const permNoticeFlag = await configService.getFeatureFlag( + FeatureFlag.NewDeviceVerificationPermanentDismiss, + ); + + if (!tempNoticeFlag && !permNoticeFlag) { + return true; + } + + const currentAcct$: Observable = accountService.activeAccount$; + const currentAcct = await firstValueFrom(currentAcct$); + + if (!currentAcct) { + return router.createUrlTree(["/login"]); + } + + const has2FAEnabled = await hasATwoFactorProviderEnabled(vaultProfileService, currentAcct.id); + const isSelfHosted = await platformUtilsService.isSelfHost(); + const requiresSSO = await isSSORequired(policyService); + const isProfileLessThanWeekOld = await profileIsLessThanWeekOld( + vaultProfileService, + currentAcct.id, + ); + + // When any of the following are true, the device verification notice is + // not applicable for the user. + if (has2FAEnabled || isSelfHosted || requiresSSO || isProfileLessThanWeekOld) { + return true; + } + + const userItems$ = newDeviceVerificationNoticeService.noticeState$(currentAcct.id); + const userItems = await firstValueFrom(userItems$); + + // Show the notice when: + // - The temp notice flag is enabled + // - The user hasn't dismissed the notice or the user dismissed it more than 7 days ago + if ( + tempNoticeFlag && + (!userItems?.last_dismissal || isMoreThan7DaysAgo(userItems?.last_dismissal)) + ) { + return router.createUrlTree(["/new-device-notice"]); + } + + // Show the notice when: + // - The permanent notice flag is enabled + // - The user hasn't dismissed the notice + if (permNoticeFlag && !userItems?.permanent_dismissal) { + return router.createUrlTree(["/new-device-notice"]); + } + + return true; +}; + +/** Returns true has one 2FA provider enabled */ +async function hasATwoFactorProviderEnabled( + vaultProfileService: VaultProfileService, + userId: string, +): Promise { + return vaultProfileService.getProfileTwoFactorEnabled(userId); +} + +/** Returns true when the user's profile is less than a week old */ +async function profileIsLessThanWeekOld( + vaultProfileService: VaultProfileService, + userId: string, +): Promise { + const creationDate = await vaultProfileService.getProfileCreationDate(userId); + return !isMoreThan7DaysAgo(creationDate); +} + +/** Returns true when the user is required to login via SSO */ +async function isSSORequired(policyService: PolicyService) { + return firstValueFrom(policyService.policyAppliesToActiveUser$(PolicyType.RequireSso)); +} + +/** Returns the true when the date given is older than 7 days */ +function isMoreThan7DaysAgo(date?: string | Date): boolean { + if (!date) { + return false; + } + + const inputDate = new Date(date).getTime(); + const today = new Date().getTime(); + + const differenceInMS = today - inputDate; + const msInADay = 1000 * 60 * 60 * 24; + const differenceInDays = Math.round(differenceInMS / msInADay); + + return differenceInDays > 7; +} diff --git a/libs/angular/src/vault/services/vault-profile.service.spec.ts b/libs/angular/src/vault/services/vault-profile.service.spec.ts new file mode 100644 index 00000000000..7761503253a --- /dev/null +++ b/libs/angular/src/vault/services/vault-profile.service.spec.ts @@ -0,0 +1,94 @@ +import { TestBed } from "@angular/core/testing"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; + +import { VaultProfileService } from "./vault-profile.service"; + +describe("VaultProfileService", () => { + let service: VaultProfileService; + const userId = "profile-id"; + const hardcodedDateString = "2024-02-24T12:00:00Z"; + + const getProfile = jest.fn().mockResolvedValue({ + creationDate: hardcodedDateString, + twoFactorEnabled: true, + id: "new-user-id", + }); + + beforeEach(() => { + getProfile.mockClear(); + + TestBed.configureTestingModule({ + providers: [{ provide: ApiService, useValue: { getProfile } }], + }); + + jest.useFakeTimers(); + jest.setSystemTime(new Date("2024-02-22T00:00:00Z")); + service = TestBed.runInInjectionContext(() => new VaultProfileService()); + service["userId"] = userId; + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + describe("getProfileCreationDate", () => { + it("calls `getProfile` when stored profile date is not set", async () => { + expect(service["profileCreatedDate"]).toBeNull(); + + const date = await service.getProfileCreationDate(userId); + + expect(date.toISOString()).toBe("2024-02-24T12:00:00.000Z"); + expect(getProfile).toHaveBeenCalled(); + }); + + it("calls `getProfile` when stored profile id does not match", async () => { + service["profileCreatedDate"] = hardcodedDateString; + service["userId"] = "old-user-id"; + + const date = await service.getProfileCreationDate(userId); + + expect(date.toISOString()).toBe("2024-02-24T12:00:00.000Z"); + expect(getProfile).toHaveBeenCalled(); + }); + + it("does not call `getProfile` when the date is already stored", async () => { + service["profileCreatedDate"] = hardcodedDateString; + + const date = await service.getProfileCreationDate(userId); + + expect(date.toISOString()).toBe("2024-02-24T12:00:00.000Z"); + expect(getProfile).not.toHaveBeenCalled(); + }); + }); + + describe("getProfileTwoFactorEnabled", () => { + it("calls `getProfile` when stored 2FA property is not stored", async () => { + expect(service["profile2FAEnabled"]).toBeNull(); + + const twoFactorEnabled = await service.getProfileTwoFactorEnabled(userId); + + expect(twoFactorEnabled).toBe(true); + expect(getProfile).toHaveBeenCalled(); + }); + + it("calls `getProfile` when stored profile id does not match", async () => { + service["profile2FAEnabled"] = false; + service["userId"] = "old-user-id"; + + const twoFactorEnabled = await service.getProfileTwoFactorEnabled(userId); + + expect(twoFactorEnabled).toBe(true); + expect(getProfile).toHaveBeenCalled(); + }); + + it("does not call `getProfile` when 2FA property is already stored", async () => { + service["profile2FAEnabled"] = false; + + const twoFactorEnabled = await service.getProfileTwoFactorEnabled(userId); + + expect(twoFactorEnabled).toBe(false); + expect(getProfile).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/libs/angular/src/vault/services/vault-profile.service.ts b/libs/angular/src/vault/services/vault-profile.service.ts new file mode 100644 index 00000000000..b368a973781 --- /dev/null +++ b/libs/angular/src/vault/services/vault-profile.service.ts @@ -0,0 +1,64 @@ +import { Injectable, inject } from "@angular/core"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { ProfileResponse } from "@bitwarden/common/models/response/profile.response"; + +@Injectable({ + providedIn: "root", +}) +/** + * Class to provide profile level details without having to call the API each time. + * NOTE: This is a temporary service and can be replaced once the `UnauthenticatedExtensionUIRefresh` flag goes live. + * The `UnauthenticatedExtensionUIRefresh` introduces a sync that takes place upon logging in. These details can then + * be added to account object and retrieved from there. + * TODO: PM-16202 + */ +export class VaultProfileService { + private apiService = inject(ApiService); + + private userId: string | null = null; + + /** Profile creation stored as a string. */ + private profileCreatedDate: string | null = null; + + /** True when 2FA is enabled on the profile. */ + private profile2FAEnabled: boolean | null = null; + + /** + * Returns the creation date of the profile. + * Note: `Date`s are mutable in JS, creating a new + * instance is important to avoid unwanted changes. + */ + async getProfileCreationDate(userId: string): Promise { + if (this.profileCreatedDate && userId === this.userId) { + return Promise.resolve(new Date(this.profileCreatedDate)); + } + + const profile = await this.fetchAndCacheProfile(); + + return new Date(profile.creationDate); + } + + /** + * Returns whether there is a 2FA provider on the profile. + */ + async getProfileTwoFactorEnabled(userId: string): Promise { + if (this.profile2FAEnabled !== null && userId === this.userId) { + return Promise.resolve(this.profile2FAEnabled); + } + + const profile = await this.fetchAndCacheProfile(); + + return profile.twoFactorEnabled; + } + + private async fetchAndCacheProfile(): Promise { + const profile = await this.apiService.getProfile(); + + this.userId = profile.id; + this.profileCreatedDate = profile.creationDate; + this.profile2FAEnabled = profile.twoFactorEnabled; + + return profile; + } +} diff --git a/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts b/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts index a2b624876be..dd0b49f356a 100644 --- a/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts +++ b/libs/angular/src/vault/vault-filter/services/vault-filter.service.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Injectable } from "@angular/core"; -import { firstValueFrom, from, map, mergeMap, Observable } from "rxjs"; +import { firstValueFrom, from, map, mergeMap, Observable, switchMap } from "rxjs"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; import { @@ -11,6 +11,7 @@ import { import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { ActiveUserState, StateProvider } from "@bitwarden/common/platform/state"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; @@ -32,6 +33,8 @@ export class VaultFilterService implements DeprecatedVaultFilterServiceAbstracti private readonly collapsedGroupings$: Observable> = this.collapsedGroupingsState.state$.pipe(map((c) => new Set(c))); + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + constructor( protected organizationService: OrganizationService, protected folderService: FolderService, @@ -39,6 +42,7 @@ export class VaultFilterService implements DeprecatedVaultFilterServiceAbstracti protected collectionService: CollectionService, protected policyService: PolicyService, protected stateProvider: StateProvider, + protected accountService: AccountService, ) {} async storeCollapsedFilterNodes(collapsedFilterNodes: Set): Promise { @@ -81,7 +85,8 @@ export class VaultFilterService implements DeprecatedVaultFilterServiceAbstracti }); }; - return this.folderService.folderViews$.pipe( + return this.activeUserId$.pipe( + switchMap((userId) => this.folderService.folderViews$(userId)), mergeMap((folders) => from(transformation(folders))), ); } @@ -126,8 +131,9 @@ export class VaultFilterService implements DeprecatedVaultFilterServiceAbstracti } async getFolderNested(id: string): Promise> { + const activeUserId = await firstValueFrom(this.activeUserId$); const folders = await this.getAllFoldersNested( - await firstValueFrom(this.folderService.folderViews$), + await firstValueFrom(this.folderService.folderViews$(activeUserId)), ); return ServiceUtils.getTreeNodeObjectFromList(folders, id) as TreeNode; } diff --git a/libs/auth/src/angular/lock/lock.component.ts b/libs/auth/src/angular/lock/lock.component.ts index 14a5553577f..aa7b43c2e53 100644 --- a/libs/auth/src/angular/lock/lock.component.ts +++ b/libs/auth/src/angular/lock/lock.component.ts @@ -37,7 +37,11 @@ import { IconButtonModule, ToastService, } from "@bitwarden/components"; -import { KeyService, BiometricStateService } from "@bitwarden/key-management"; +import { + KeyService, + BiometricStateService, + UserAsymmetricKeysRegenerationService, +} from "@bitwarden/key-management"; import { PinServiceAbstraction } from "../../common/abstractions"; import { AnonLayoutWrapperDataService } from "../anon-layout/anon-layout-wrapper-data.service"; @@ -71,7 +75,7 @@ const clientTypeToSuccessRouteRecord: Partial> = { IconButtonModule, ], }) -export class LockV2Component implements OnInit, OnDestroy { +export class LockComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); activeAccount: Account | null; @@ -139,6 +143,7 @@ export class LockV2Component implements OnInit, OnDestroy { private passwordStrengthService: PasswordStrengthServiceAbstraction, private formBuilder: FormBuilder, private toastService: ToastService, + private userAsymmetricKeysRegenerationService: UserAsymmetricKeysRegenerationService, private lockComponentService: LockComponentService, private anonLayoutWrapperDataService: AnonLayoutWrapperDataService, @@ -532,12 +537,14 @@ export class LockV2Component implements OnInit, OnDestroy { // Vault can be de-synced since notifications get ignored while locked. Need to check whether sync is required using the sync service. await this.syncService.fullSync(false); + await this.userAsymmetricKeysRegenerationService.regenerateIfNeeded(this.activeAccount.id); + if (this.clientType === "browser") { const previousUrl = this.lockComponentService.getPreviousUrl(); /** * In a passkey flow, the `previousUrl` will still be `/fido2?` at this point - * because the `/lockV2` route doesn't save the URL in the `BrowserRouterService`. This is - * handled by the `doNotSaveUrl` property on the `lockV2` route in `app-routing.module.ts`. + * because the `/lock` route doesn't save the URL in the `BrowserRouterService`. This is + * handled by the `doNotSaveUrl` property on the `/lock` route in `app-routing.module.ts`. */ if (previousUrl) { await this.router.navigateByUrl(previousUrl); diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html index cb340f646f1..b3d218389bf 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.html @@ -56,5 +56,9 @@ > {{ "requestAdminApproval" | i18n }} + +
    diff --git a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts index 070debf2205..5600077c363 100644 --- a/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts +++ b/libs/auth/src/angular/login-decryption-options/login-decryption-options.component.ts @@ -30,6 +30,7 @@ import { AsyncActionsModule, ButtonModule, CheckboxModule, + DialogService, FormFieldModule, ToastService, TypographyModule, @@ -90,6 +91,7 @@ export class LoginDecryptionOptionsComponent implements OnInit { private apiService: ApiService, private destroyRef: DestroyRef, private deviceTrustService: DeviceTrustServiceAbstraction, + private dialogService: DialogService, private formBuilder: FormBuilder, private i18nService: I18nService, private keyService: KeyService, @@ -298,4 +300,18 @@ export class LoginDecryptionOptionsComponent implements OnInit { this.loginEmailService.setLoginEmail(this.email); await this.router.navigate(["/admin-approval-requested"]); } + + async logOut() { + const confirmed = await this.dialogService.openSimpleDialog({ + title: { key: "logOut" }, + content: { key: "logOutConfirmation" }, + acceptButtonText: { key: "logOut" }, + type: "warning", + }); + + const userId = (await firstValueFrom(this.accountService.activeAccount$))?.id; + if (confirmed) { + this.messagingService.send("logout", { userId: userId }); + } + } } diff --git a/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts b/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts index 99e52d30914..b9a5ee4fe73 100644 --- a/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts +++ b/libs/auth/src/angular/login-via-auth-request/login-via-auth-request.component.ts @@ -12,6 +12,7 @@ import { AuthRequestServiceAbstraction, LoginEmailServiceAbstraction, LoginStrategyServiceAbstraction, + LoginSuccessHandlerService, } from "@bitwarden/auth/common"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AnonymousHubService } from "@bitwarden/common/auth/abstractions/anonymous-hub.service"; @@ -34,7 +35,6 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { UserId } from "@bitwarden/common/types/guid"; -import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { ButtonModule, LinkModule, ToastService } from "@bitwarden/components"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; @@ -88,9 +88,9 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy { private passwordGenerationService: PasswordGenerationServiceAbstraction, private platformUtilsService: PlatformUtilsService, private router: Router, - private syncService: SyncService, private toastService: ToastService, private validationService: ValidationService, + private loginSuccessHandlerService: LoginSuccessHandlerService, ) { this.clientType = this.platformUtilsService.getClientType(); @@ -485,7 +485,7 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy { const activeAccount = await firstValueFrom(this.accountService.activeAccount$); await this.deviceTrustService.trustDeviceIfRequired(activeAccount.id); - await this.handleSuccessfulLoginNavigation(); + await this.handleSuccessfulLoginNavigation(userId); } /** @@ -555,17 +555,17 @@ export class LoginViaAuthRequestComponent implements OnInit, OnDestroy { } else if (loginResponse.forcePasswordReset != ForceSetPasswordReason.None) { await this.router.navigate(["update-temp-password"]); } else { - await this.handleSuccessfulLoginNavigation(); + await this.handleSuccessfulLoginNavigation(loginResponse.userId); } } - private async handleSuccessfulLoginNavigation() { + private async handleSuccessfulLoginNavigation(userId: UserId) { if (this.flow === Flow.StandardAuthRequest) { // Only need to set remembered email on standard login with auth req flow await this.loginEmailService.saveEmailSettings(); } - await this.syncService.fullSync(true); + await this.loginSuccessHandlerService.run(userId); await this.router.navigate(["vault"]); } } diff --git a/libs/auth/src/angular/login/login.component.html b/libs/auth/src/angular/login/login.component.html index efea2917527..54a04d3de6c 100644 --- a/libs/auth/src/angular/login/login.component.html +++ b/libs/auth/src/angular/login/login.component.html @@ -121,13 +121,7 @@ - diff --git a/libs/auth/src/angular/login/login.component.ts b/libs/auth/src/angular/login/login.component.ts index 4eb92085747..33c167dcaed 100644 --- a/libs/auth/src/angular/login/login.component.ts +++ b/libs/auth/src/angular/login/login.component.ts @@ -10,6 +10,7 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { LoginEmailServiceAbstraction, LoginStrategyServiceAbstraction, + LoginSuccessHandlerService, PasswordLoginCredentials, RegisterRouteService, } from "@bitwarden/auth/common"; @@ -31,7 +32,6 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { SyncService } from "@bitwarden/common/platform/sync"; import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength"; import { AsyncActionsModule, @@ -127,16 +127,19 @@ export class LoginComponent implements OnInit, OnDestroy { private policyService: InternalPolicyService, private registerRouteService: RegisterRouteService, private router: Router, - private syncService: SyncService, private toastService: ToastService, private logService: LogService, private validationService: ValidationService, private configService: ConfigService, + private loginSuccessHandlerService: LoginSuccessHandlerService, ) { this.clientType = this.platformUtilsService.getClientType(); } async ngOnInit(): Promise { + // Add popstate listener to listen for browser back button clicks + window.addEventListener("popstate", this.handlePopState); + // TODO: remove this when the UnauthenticatedExtensionUIRefresh feature flag is removed. this.listenForUnauthUiRefreshFlagChanges(); @@ -148,6 +151,9 @@ export class LoginComponent implements OnInit, OnDestroy { } ngOnDestroy(): void { + // Remove popstate listener + window.removeEventListener("popstate", this.handlePopState); + if (this.clientType === ClientType.Desktop) { // TODO: refactor to not use deprecated broadcaster service. this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); @@ -228,11 +234,14 @@ export class LoginComponent implements OnInit, OnDestroy { message: this.i18nService.t("invalidMasterPassword"), }, }); + } else { + // Allow other 400 responses to be handled by toast + this.validationService.showError(error); } break; } default: { - // Allow all other errors to be handled by toast + // Allow all other error codes to be handled by toast this.validationService.showError(error); } } @@ -271,7 +280,7 @@ export class LoginComponent implements OnInit, OnDestroy { return; } - await this.syncService.fullSync(true); + await this.loginSuccessHandlerService.run(authResult.userId); if (authResult.forcePasswordReset != ForceSetPasswordReason.None) { this.loginEmailService.clearValues(); @@ -559,4 +568,28 @@ export class LoginComponent implements OnInit, OnDestroy { this.clientType !== ClientType.Browser ); } + + /** + * Handle the back button click to transition back to the email entry state. + */ + protected async backButtonClicked() { + // Replace the history so the "forward" button doesn't show (which wouldn't do anything) + history.pushState(null, "", window.location.pathname); + await this.toggleLoginUiState(LoginUiState.EMAIL_ENTRY); + } + + /** + * Handle the popstate event to transition back to the email entry state when the back button is clicked. + * @param event - The popstate event. + */ + private handlePopState = (event: PopStateEvent) => { + if (this.loginUiState === LoginUiState.MASTER_PASSWORD_ENTRY) { + // Prevent default navigation + event.preventDefault(); + // Replace the history so the "forward" button doesn't show (which wouldn't do anything) + history.pushState(null, "", window.location.pathname); + // Transition back to email entry state + void this.toggleLoginUiState(LoginUiState.EMAIL_ENTRY); + } + }; } diff --git a/libs/auth/src/common/abstractions/index.ts b/libs/auth/src/common/abstractions/index.ts index 88a13b490d6..c0dc500ddb9 100644 --- a/libs/auth/src/common/abstractions/index.ts +++ b/libs/auth/src/common/abstractions/index.ts @@ -5,3 +5,4 @@ export * from "./login-strategy.service"; export * from "./user-decryption-options.service.abstraction"; export * from "./auth-request.service.abstraction"; export * from "./login-approval-component.service.abstraction"; +export * from "./login-success-handler.service"; diff --git a/libs/auth/src/common/abstractions/login-success-handler.service.ts b/libs/auth/src/common/abstractions/login-success-handler.service.ts new file mode 100644 index 00000000000..8dee1dd32b9 --- /dev/null +++ b/libs/auth/src/common/abstractions/login-success-handler.service.ts @@ -0,0 +1,10 @@ +import { UserId } from "@bitwarden/common/types/guid"; + +export abstract class LoginSuccessHandlerService { + /** + * Runs any service calls required after a successful login. + * Service calls that should be included in this method are only those required to be awaited after successful login. + * @param userId The user id. + */ + abstract run(userId: UserId): Promise; +} diff --git a/libs/auth/src/common/services/index.ts b/libs/auth/src/common/services/index.ts index 41e0ba087ae..d1cedebcf36 100644 --- a/libs/auth/src/common/services/index.ts +++ b/libs/auth/src/common/services/index.ts @@ -6,3 +6,4 @@ export * from "./auth-request/auth-request.service"; export * from "./auth-request/auth-request-api.service"; export * from "./register-route.service"; export * from "./accounts/lock.service"; +export * from "./login-success-handler/default-login-success-handler.service"; diff --git a/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.ts b/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.ts new file mode 100644 index 00000000000..215329051df --- /dev/null +++ b/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.ts @@ -0,0 +1,16 @@ +import { SyncService } from "@bitwarden/common/platform/sync"; +import { UserId } from "@bitwarden/common/types/guid"; +import { UserAsymmetricKeysRegenerationService } from "@bitwarden/key-management"; + +import { LoginSuccessHandlerService } from "../../abstractions/login-success-handler.service"; + +export class DefaultLoginSuccessHandlerService implements LoginSuccessHandlerService { + constructor( + private syncService: SyncService, + private userAsymmetricKeysRegenerationService: UserAsymmetricKeysRegenerationService, + ) {} + async run(userId: UserId): Promise { + await this.syncService.fullSync(true); + await this.userAsymmetricKeysRegenerationService.regenerateIfNeeded(userId); + } +} diff --git a/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts b/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts index 9fd3b358568..0c45c919e95 100644 --- a/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts +++ b/libs/common/src/admin-console/abstractions/organization/organization-api.service.abstraction.ts @@ -53,11 +53,11 @@ export class OrganizationApiServiceAbstraction { updatePasswordManagerSeats: ( id: string, request: OrganizationSubscriptionUpdateRequest, - ) => Promise; + ) => Promise; updateSecretsManagerSubscription: ( id: string, request: OrganizationSmSubscriptionUpdateRequest, - ) => Promise; + ) => Promise; updateSeats: (id: string, request: SeatRequest) => Promise; updateStorage: (id: string, request: StorageRequest) => Promise; verifyBank: (id: string, request: VerifyBankRequest) => Promise; diff --git a/libs/common/src/admin-console/models/domain/organization.ts b/libs/common/src/admin-console/models/domain/organization.ts index eeadcb4421d..8441298bbff 100644 --- a/libs/common/src/admin-console/models/domain/organization.ts +++ b/libs/common/src/admin-console/models/domain/organization.ts @@ -360,4 +360,15 @@ export class Organization { familySponsorshipValidUntil: new Date(json.familySponsorshipValidUntil), }); } + + get canAccessIntegrations() { + return ( + (this.productTierType === ProductTierType.Teams || + this.productTierType === ProductTierType.Enterprise) && + (this.isAdmin || + this.permissions.manageUsers || + this.permissions.manageGroups || + this.permissions.accessEventLogs) + ); + } } diff --git a/libs/common/src/admin-console/services/organization/organization-api.service.ts b/libs/common/src/admin-console/services/organization/organization-api.service.ts index d62fd49a6a4..b3fd11982b8 100644 --- a/libs/common/src/admin-console/services/organization/organization-api.service.ts +++ b/libs/common/src/admin-console/services/organization/organization-api.service.ts @@ -161,27 +161,29 @@ export class OrganizationApiService implements OrganizationApiServiceAbstraction async updatePasswordManagerSeats( id: string, request: OrganizationSubscriptionUpdateRequest, - ): Promise { - return this.apiService.send( + ): Promise { + const r = await this.apiService.send( "POST", "/organizations/" + id + "/subscription", request, true, - false, + true, ); + return new ProfileOrganizationResponse(r); } async updateSecretsManagerSubscription( id: string, request: OrganizationSmSubscriptionUpdateRequest, - ): Promise { - return this.apiService.send( + ): Promise { + const r = await this.apiService.send( "POST", "/organizations/" + id + "/sm-subscription", request, true, - false, + true, ); + return new ProfileOrganizationResponse(r); } async updateSeats(id: string, request: SeatRequest): Promise { diff --git a/libs/common/src/autofill/utils.spec.ts b/libs/common/src/autofill/utils.spec.ts index b09dc723b8e..516a09e03d1 100644 --- a/libs/common/src/autofill/utils.spec.ts +++ b/libs/common/src/autofill/utils.spec.ts @@ -86,6 +86,8 @@ function getCardExpiryDateValues() { // `Date` months are zero-indexed, our expiry date month inputs are one-indexed const currentMonth = currentDate.getMonth() + 1; + const currentDateLastMonth = new Date(currentDate.setMonth(-1)); + return [ [null, null, false], // no month, no year [undefined, undefined, false], // no month, no year, invalid values @@ -103,7 +105,7 @@ function getCardExpiryDateValues() { [`${currentMonth + 36}`, `${currentYear - 1}`, true], // even though the month value would put the date 3 years into the future when calculated with `Date`, an explicit year in the past indicates the card is expired [`${currentMonth}`, `${currentYear}`, false], // this year, this month (not expired until the month is over) [`${currentMonth}`, `${currentYear}`.slice(-2), false], // This month, this year (not expired until the month is over) - [`${currentMonth - 1}`, `${currentYear}`, true], // last month + [`${currentDateLastMonth.getMonth() + 1}`, `${currentDateLastMonth.getFullYear()}`, true], // last month [`${currentMonth - 1}`, `${currentYear + 1}`, false], // 11 months from now ]; } diff --git a/libs/common/src/autofill/utils.ts b/libs/common/src/autofill/utils.ts index 271952e27d1..d9276cdbc8b 100644 --- a/libs/common/src/autofill/utils.ts +++ b/libs/common/src/autofill/utils.ts @@ -64,25 +64,32 @@ export function isCardExpired(cipherCard: CardView): boolean { const now = new Date(); const normalizedYear = normalizeExpiryYearFormat(expYear); + const parsedYear = parseInt(normalizedYear, 10); - // If the card year is before the current year, don't bother checking the month - if (normalizedYear && parseInt(normalizedYear, 10) < now.getFullYear()) { + const expiryYearIsBeforeThisYear = parsedYear < now.getFullYear(); + const expiryYearIsAfterThisYear = parsedYear > now.getFullYear(); + + // If the expiry year is before the current year, skip checking the month, since it must be expired + if (normalizedYear && expiryYearIsBeforeThisYear) { return true; } + // If the expiry year is after the current year, skip checking the month, since it cannot be expired + if (normalizedYear && expiryYearIsAfterThisYear) { + return false; + } + if (normalizedYear && expMonth) { const parsedMonthInteger = parseInt(expMonth, 10); + const parsedMonthIsInvalid = !parsedMonthInteger || isNaN(parsedMonthInteger); - const parsedMonth = isNaN(parsedMonthInteger) - ? 0 - : // Add a month floor of 0 to protect against an invalid low month value of "0" or negative integers - Math.max( - // `Date` months are zero-indexed - parsedMonthInteger - 1, - 0, - ); + // If the parsed month value is 0, we don't know when the expiry passes this year, so treat it as expired + if (parsedMonthIsInvalid) { + return true; + } - const parsedYear = parseInt(normalizedYear, 10); + // `Date` months are zero-indexed + const parsedMonth = parsedMonthInteger - 1; // First day of the next month const cardExpiry = new Date(parsedYear, parsedMonth + 1, 1); diff --git a/libs/common/src/billing/abstractions/tax.service.abstraction.ts b/libs/common/src/billing/abstractions/tax.service.abstraction.ts new file mode 100644 index 00000000000..438d3f394e0 --- /dev/null +++ b/libs/common/src/billing/abstractions/tax.service.abstraction.ts @@ -0,0 +1,18 @@ +import { CountryListItem } from "@bitwarden/common/billing/models/domain"; +import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request"; +import { PreviewOrganizationInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-organization-invoice.request"; +import { PreviewInvoiceResponse } from "@bitwarden/common/billing/models/response/preview-invoice.response"; + +export abstract class TaxServiceAbstraction { + abstract getCountries(): CountryListItem[]; + + abstract isCountrySupported(country: string): Promise; + + abstract previewIndividualInvoice( + request: PreviewIndividualInvoiceRequest, + ): Promise; + + abstract previewOrganizationInvoice( + request: PreviewOrganizationInvoiceRequest, + ): Promise; +} diff --git a/libs/common/src/billing/models/domain/country-list-item.ts b/libs/common/src/billing/models/domain/country-list-item.ts new file mode 100644 index 00000000000..79abb96871c --- /dev/null +++ b/libs/common/src/billing/models/domain/country-list-item.ts @@ -0,0 +1,5 @@ +export type CountryListItem = { + name: string; + value: string; + disabled: boolean; +}; diff --git a/libs/common/src/billing/models/domain/index.ts b/libs/common/src/billing/models/domain/index.ts index 0f53c3e116c..057f6dc4e84 100644 --- a/libs/common/src/billing/models/domain/index.ts +++ b/libs/common/src/billing/models/domain/index.ts @@ -1,2 +1,3 @@ export * from "./bank-account"; +export * from "./country-list-item"; export * from "./tax-information"; diff --git a/libs/common/src/billing/models/request/expanded-tax-info-update.request.ts b/libs/common/src/billing/models/request/expanded-tax-info-update.request.ts index 838249e2d8c..784d2691629 100644 --- a/libs/common/src/billing/models/request/expanded-tax-info-update.request.ts +++ b/libs/common/src/billing/models/request/expanded-tax-info-update.request.ts @@ -12,6 +12,10 @@ export class ExpandedTaxInfoUpdateRequest extends TaxInfoUpdateRequest { state: string; static From(taxInformation: TaxInformation): ExpandedTaxInfoUpdateRequest { + if (!taxInformation) { + return null; + } + const request = new ExpandedTaxInfoUpdateRequest(); request.country = taxInformation.country; request.postalCode = taxInformation.postalCode; diff --git a/libs/common/src/billing/models/request/preview-individual-invoice.request.ts b/libs/common/src/billing/models/request/preview-individual-invoice.request.ts new file mode 100644 index 00000000000..f817398c629 --- /dev/null +++ b/libs/common/src/billing/models/request/preview-individual-invoice.request.ts @@ -0,0 +1,28 @@ +// @ts-strict-ignore +export class PreviewIndividualInvoiceRequest { + passwordManager: PasswordManager; + taxInformation: TaxInformation; + + constructor(passwordManager: PasswordManager, taxInformation: TaxInformation) { + this.passwordManager = passwordManager; + this.taxInformation = taxInformation; + } +} + +class PasswordManager { + additionalStorage: number; + + constructor(additionalStorage: number) { + this.additionalStorage = additionalStorage; + } +} + +class TaxInformation { + postalCode: string; + country: string; + + constructor(postalCode: string, country: string) { + this.postalCode = postalCode; + this.country = country; + } +} diff --git a/libs/common/src/billing/models/request/preview-organization-invoice.request.ts b/libs/common/src/billing/models/request/preview-organization-invoice.request.ts new file mode 100644 index 00000000000..365dff5c110 --- /dev/null +++ b/libs/common/src/billing/models/request/preview-organization-invoice.request.ts @@ -0,0 +1,54 @@ +import { PlanType } from "@bitwarden/common/billing/enums"; + +export class PreviewOrganizationInvoiceRequest { + organizationId?: string; + passwordManager: PasswordManager; + secretsManager?: SecretsManager; + taxInformation: TaxInformation; + + constructor( + passwordManager: PasswordManager, + taxInformation: TaxInformation, + organizationId?: string, + secretsManager?: SecretsManager, + ) { + this.organizationId = organizationId; + this.passwordManager = passwordManager; + this.secretsManager = secretsManager; + this.taxInformation = taxInformation; + } +} + +class PasswordManager { + plan: PlanType; + seats: number; + additionalStorage: number; + + constructor(plan: PlanType, seats: number, additionalStorage: number) { + this.plan = plan; + this.seats = seats; + this.additionalStorage = additionalStorage; + } +} + +class SecretsManager { + seats: number; + additionalMachineAccounts: number; + + constructor(seats: number, additionalMachineAccounts: number) { + this.seats = seats; + this.additionalMachineAccounts = additionalMachineAccounts; + } +} + +class TaxInformation { + postalCode: string; + country: string; + taxId: string; + + constructor(postalCode: string, country: string, taxId: string) { + this.postalCode = postalCode; + this.country = country; + this.taxId = taxId; + } +} diff --git a/libs/common/src/billing/models/response/organization-billing-metadata.response.ts b/libs/common/src/billing/models/response/organization-billing-metadata.response.ts index d9733aa80f2..c5023cb64c1 100644 --- a/libs/common/src/billing/models/response/organization-billing-metadata.response.ts +++ b/libs/common/src/billing/models/response/organization-billing-metadata.response.ts @@ -6,6 +6,10 @@ export class OrganizationBillingMetadataResponse extends BaseResponse { isOnSecretsManagerStandalone: boolean; isSubscriptionUnpaid: boolean; hasSubscription: boolean; + hasOpenInvoice: boolean; + invoiceDueDate: Date | null; + invoiceCreatedDate: Date | null; + subPeriodEndDate: Date | null; constructor(response: any) { super(response); @@ -14,5 +18,14 @@ export class OrganizationBillingMetadataResponse extends BaseResponse { this.isOnSecretsManagerStandalone = this.getResponseProperty("IsOnSecretsManagerStandalone"); this.isSubscriptionUnpaid = this.getResponseProperty("IsSubscriptionUnpaid"); this.hasSubscription = this.getResponseProperty("HasSubscription"); + this.hasOpenInvoice = this.getResponseProperty("HasOpenInvoice"); + + this.invoiceDueDate = this.parseDate(this.getResponseProperty("InvoiceDueDate")); + this.invoiceCreatedDate = this.parseDate(this.getResponseProperty("InvoiceCreatedDate")); + this.subPeriodEndDate = this.parseDate(this.getResponseProperty("SubPeriodEndDate")); + } + + private parseDate(dateString: any): Date | null { + return dateString ? new Date(dateString) : null; } } diff --git a/libs/common/src/billing/models/response/preview-invoice.response.ts b/libs/common/src/billing/models/response/preview-invoice.response.ts new file mode 100644 index 00000000000..c822a569bb3 --- /dev/null +++ b/libs/common/src/billing/models/response/preview-invoice.response.ts @@ -0,0 +1,16 @@ +import { BaseResponse } from "@bitwarden/common/models/response/base.response"; + +export class PreviewInvoiceResponse extends BaseResponse { + effectiveTaxRate: number; + taxableBaseAmount: number; + taxAmount: number; + totalAmount: number; + + constructor(response: any) { + super(response); + this.effectiveTaxRate = this.getResponseProperty("EffectiveTaxRate"); + this.taxableBaseAmount = this.getResponseProperty("TaxableBaseAmount"); + this.taxAmount = this.getResponseProperty("TaxAmount"); + this.totalAmount = this.getResponseProperty("TotalAmount"); + } +} diff --git a/libs/common/src/billing/models/response/tax-id-types.response.ts b/libs/common/src/billing/models/response/tax-id-types.response.ts new file mode 100644 index 00000000000..0d5cce46c8c --- /dev/null +++ b/libs/common/src/billing/models/response/tax-id-types.response.ts @@ -0,0 +1,28 @@ +import { BaseResponse } from "@bitwarden/common/models/response/base.response"; + +export class TaxIdTypesResponse extends BaseResponse { + taxIdTypes: TaxIdTypeResponse[] = []; + + constructor(response: any) { + super(response); + const taxIdTypes = this.getResponseProperty("TaxIdTypes"); + if (taxIdTypes && taxIdTypes.length) { + this.taxIdTypes = taxIdTypes.map((t: any) => new TaxIdTypeResponse(t)); + } + } +} + +export class TaxIdTypeResponse extends BaseResponse { + code: string; + country: string; + description: string; + example: string; + + constructor(response: any) { + super(response); + this.code = this.getResponseProperty("Code"); + this.country = this.getResponseProperty("Country"); + this.description = this.getResponseProperty("Description"); + this.example = this.getResponseProperty("Example"); + } +} diff --git a/libs/common/src/billing/services/tax.service.ts b/libs/common/src/billing/services/tax.service.ts new file mode 100644 index 00000000000..45e57267ec0 --- /dev/null +++ b/libs/common/src/billing/services/tax.service.ts @@ -0,0 +1,303 @@ +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { TaxServiceAbstraction } from "@bitwarden/common/billing/abstractions/tax.service.abstraction"; +import { CountryListItem } from "@bitwarden/common/billing/models/domain"; +import { PreviewIndividualInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-individual-invoice.request"; +import { PreviewOrganizationInvoiceRequest } from "@bitwarden/common/billing/models/request/preview-organization-invoice.request"; +import { PreviewInvoiceResponse } from "@bitwarden/common/billing/models/response/preview-invoice.response"; + +export class TaxService implements TaxServiceAbstraction { + constructor(private apiService: ApiService) {} + + getCountries(): CountryListItem[] { + return [ + { name: "-- Select --", value: "", disabled: false }, + { name: "United States", value: "US", disabled: false }, + { name: "China", value: "CN", disabled: false }, + { name: "France", value: "FR", disabled: false }, + { name: "Germany", value: "DE", disabled: false }, + { name: "Canada", value: "CA", disabled: false }, + { name: "United Kingdom", value: "GB", disabled: false }, + { name: "Australia", value: "AU", disabled: false }, + { name: "India", value: "IN", disabled: false }, + { name: "", value: "-", disabled: true }, + { name: "Afghanistan", value: "AF", disabled: false }, + { name: "Åland Islands", value: "AX", disabled: false }, + { name: "Albania", value: "AL", disabled: false }, + { name: "Algeria", value: "DZ", disabled: false }, + { name: "American Samoa", value: "AS", disabled: false }, + { name: "Andorra", value: "AD", disabled: false }, + { name: "Angola", value: "AO", disabled: false }, + { name: "Anguilla", value: "AI", disabled: false }, + { name: "Antarctica", value: "AQ", disabled: false }, + { name: "Antigua and Barbuda", value: "AG", disabled: false }, + { name: "Argentina", value: "AR", disabled: false }, + { name: "Armenia", value: "AM", disabled: false }, + { name: "Aruba", value: "AW", disabled: false }, + { name: "Austria", value: "AT", disabled: false }, + { name: "Azerbaijan", value: "AZ", disabled: false }, + { name: "Bahamas", value: "BS", disabled: false }, + { name: "Bahrain", value: "BH", disabled: false }, + { name: "Bangladesh", value: "BD", disabled: false }, + { name: "Barbados", value: "BB", disabled: false }, + { name: "Belarus", value: "BY", disabled: false }, + { name: "Belgium", value: "BE", disabled: false }, + { name: "Belize", value: "BZ", disabled: false }, + { name: "Benin", value: "BJ", disabled: false }, + { name: "Bermuda", value: "BM", disabled: false }, + { name: "Bhutan", value: "BT", disabled: false }, + { name: "Bolivia, Plurinational State of", value: "BO", disabled: false }, + { name: "Bonaire, Sint Eustatius and Saba", value: "BQ", disabled: false }, + { name: "Bosnia and Herzegovina", value: "BA", disabled: false }, + { name: "Botswana", value: "BW", disabled: false }, + { name: "Bouvet Island", value: "BV", disabled: false }, + { name: "Brazil", value: "BR", disabled: false }, + { name: "British Indian Ocean Territory", value: "IO", disabled: false }, + { name: "Brunei Darussalam", value: "BN", disabled: false }, + { name: "Bulgaria", value: "BG", disabled: false }, + { name: "Burkina Faso", value: "BF", disabled: false }, + { name: "Burundi", value: "BI", disabled: false }, + { name: "Cambodia", value: "KH", disabled: false }, + { name: "Cameroon", value: "CM", disabled: false }, + { name: "Cape Verde", value: "CV", disabled: false }, + { name: "Cayman Islands", value: "KY", disabled: false }, + { name: "Central African Republic", value: "CF", disabled: false }, + { name: "Chad", value: "TD", disabled: false }, + { name: "Chile", value: "CL", disabled: false }, + { name: "Christmas Island", value: "CX", disabled: false }, + { name: "Cocos (Keeling) Islands", value: "CC", disabled: false }, + { name: "Colombia", value: "CO", disabled: false }, + { name: "Comoros", value: "KM", disabled: false }, + { name: "Congo", value: "CG", disabled: false }, + { name: "Congo, the Democratic Republic of the", value: "CD", disabled: false }, + { name: "Cook Islands", value: "CK", disabled: false }, + { name: "Costa Rica", value: "CR", disabled: false }, + { name: "Côte d'Ivoire", value: "CI", disabled: false }, + { name: "Croatia", value: "HR", disabled: false }, + { name: "Cuba", value: "CU", disabled: false }, + { name: "Curaçao", value: "CW", disabled: false }, + { name: "Cyprus", value: "CY", disabled: false }, + { name: "Czech Republic", value: "CZ", disabled: false }, + { name: "Denmark", value: "DK", disabled: false }, + { name: "Djibouti", value: "DJ", disabled: false }, + { name: "Dominica", value: "DM", disabled: false }, + { name: "Dominican Republic", value: "DO", disabled: false }, + { name: "Ecuador", value: "EC", disabled: false }, + { name: "Egypt", value: "EG", disabled: false }, + { name: "El Salvador", value: "SV", disabled: false }, + { name: "Equatorial Guinea", value: "GQ", disabled: false }, + { name: "Eritrea", value: "ER", disabled: false }, + { name: "Estonia", value: "EE", disabled: false }, + { name: "Ethiopia", value: "ET", disabled: false }, + { name: "Falkland Islands (Malvinas)", value: "FK", disabled: false }, + { name: "Faroe Islands", value: "FO", disabled: false }, + { name: "Fiji", value: "FJ", disabled: false }, + { name: "Finland", value: "FI", disabled: false }, + { name: "French Guiana", value: "GF", disabled: false }, + { name: "French Polynesia", value: "PF", disabled: false }, + { name: "French Southern Territories", value: "TF", disabled: false }, + { name: "Gabon", value: "GA", disabled: false }, + { name: "Gambia", value: "GM", disabled: false }, + { name: "Georgia", value: "GE", disabled: false }, + { name: "Ghana", value: "GH", disabled: false }, + { name: "Gibraltar", value: "GI", disabled: false }, + { name: "Greece", value: "GR", disabled: false }, + { name: "Greenland", value: "GL", disabled: false }, + { name: "Grenada", value: "GD", disabled: false }, + { name: "Guadeloupe", value: "GP", disabled: false }, + { name: "Guam", value: "GU", disabled: false }, + { name: "Guatemala", value: "GT", disabled: false }, + { name: "Guernsey", value: "GG", disabled: false }, + { name: "Guinea", value: "GN", disabled: false }, + { name: "Guinea-Bissau", value: "GW", disabled: false }, + { name: "Guyana", value: "GY", disabled: false }, + { name: "Haiti", value: "HT", disabled: false }, + { name: "Heard Island and McDonald Islands", value: "HM", disabled: false }, + { name: "Holy See (Vatican City State)", value: "VA", disabled: false }, + { name: "Honduras", value: "HN", disabled: false }, + { name: "Hong Kong", value: "HK", disabled: false }, + { name: "Hungary", value: "HU", disabled: false }, + { name: "Iceland", value: "IS", disabled: false }, + { name: "Indonesia", value: "ID", disabled: false }, + { name: "Iran, Islamic Republic of", value: "IR", disabled: false }, + { name: "Iraq", value: "IQ", disabled: false }, + { name: "Ireland", value: "IE", disabled: false }, + { name: "Isle of Man", value: "IM", disabled: false }, + { name: "Israel", value: "IL", disabled: false }, + { name: "Italy", value: "IT", disabled: false }, + { name: "Jamaica", value: "JM", disabled: false }, + { name: "Japan", value: "JP", disabled: false }, + { name: "Jersey", value: "JE", disabled: false }, + { name: "Jordan", value: "JO", disabled: false }, + { name: "Kazakhstan", value: "KZ", disabled: false }, + { name: "Kenya", value: "KE", disabled: false }, + { name: "Kiribati", value: "KI", disabled: false }, + { name: "Korea, Democratic People's Republic of", value: "KP", disabled: false }, + { name: "Korea, Republic of", value: "KR", disabled: false }, + { name: "Kuwait", value: "KW", disabled: false }, + { name: "Kyrgyzstan", value: "KG", disabled: false }, + { name: "Lao People's Democratic Republic", value: "LA", disabled: false }, + { name: "Latvia", value: "LV", disabled: false }, + { name: "Lebanon", value: "LB", disabled: false }, + { name: "Lesotho", value: "LS", disabled: false }, + { name: "Liberia", value: "LR", disabled: false }, + { name: "Libya", value: "LY", disabled: false }, + { name: "Liechtenstein", value: "LI", disabled: false }, + { name: "Lithuania", value: "LT", disabled: false }, + { name: "Luxembourg", value: "LU", disabled: false }, + { name: "Macao", value: "MO", disabled: false }, + { name: "Macedonia, the former Yugoslav Republic of", value: "MK", disabled: false }, + { name: "Madagascar", value: "MG", disabled: false }, + { name: "Malawi", value: "MW", disabled: false }, + { name: "Malaysia", value: "MY", disabled: false }, + { name: "Maldives", value: "MV", disabled: false }, + { name: "Mali", value: "ML", disabled: false }, + { name: "Malta", value: "MT", disabled: false }, + { name: "Marshall Islands", value: "MH", disabled: false }, + { name: "Martinique", value: "MQ", disabled: false }, + { name: "Mauritania", value: "MR", disabled: false }, + { name: "Mauritius", value: "MU", disabled: false }, + { name: "Mayotte", value: "YT", disabled: false }, + { name: "Mexico", value: "MX", disabled: false }, + { name: "Micronesia, Federated States of", value: "FM", disabled: false }, + { name: "Moldova, Republic of", value: "MD", disabled: false }, + { name: "Monaco", value: "MC", disabled: false }, + { name: "Mongolia", value: "MN", disabled: false }, + { name: "Montenegro", value: "ME", disabled: false }, + { name: "Montserrat", value: "MS", disabled: false }, + { name: "Morocco", value: "MA", disabled: false }, + { name: "Mozambique", value: "MZ", disabled: false }, + { name: "Myanmar", value: "MM", disabled: false }, + { name: "Namibia", value: "NA", disabled: false }, + { name: "Nauru", value: "NR", disabled: false }, + { name: "Nepal", value: "NP", disabled: false }, + { name: "Netherlands", value: "NL", disabled: false }, + { name: "New Caledonia", value: "NC", disabled: false }, + { name: "New Zealand", value: "NZ", disabled: false }, + { name: "Nicaragua", value: "NI", disabled: false }, + { name: "Niger", value: "NE", disabled: false }, + { name: "Nigeria", value: "NG", disabled: false }, + { name: "Niue", value: "NU", disabled: false }, + { name: "Norfolk Island", value: "NF", disabled: false }, + { name: "Northern Mariana Islands", value: "MP", disabled: false }, + { name: "Norway", value: "NO", disabled: false }, + { name: "Oman", value: "OM", disabled: false }, + { name: "Pakistan", value: "PK", disabled: false }, + { name: "Palau", value: "PW", disabled: false }, + { name: "Palestinian Territory, Occupied", value: "PS", disabled: false }, + { name: "Panama", value: "PA", disabled: false }, + { name: "Papua New Guinea", value: "PG", disabled: false }, + { name: "Paraguay", value: "PY", disabled: false }, + { name: "Peru", value: "PE", disabled: false }, + { name: "Philippines", value: "PH", disabled: false }, + { name: "Pitcairn", value: "PN", disabled: false }, + { name: "Poland", value: "PL", disabled: false }, + { name: "Portugal", value: "PT", disabled: false }, + { name: "Puerto Rico", value: "PR", disabled: false }, + { name: "Qatar", value: "QA", disabled: false }, + { name: "Réunion", value: "RE", disabled: false }, + { name: "Romania", value: "RO", disabled: false }, + { name: "Russian Federation", value: "RU", disabled: false }, + { name: "Rwanda", value: "RW", disabled: false }, + { name: "Saint Barthélemy", value: "BL", disabled: false }, + { name: "Saint Helena, Ascension and Tristan da Cunha", value: "SH", disabled: false }, + { name: "Saint Kitts and Nevis", value: "KN", disabled: false }, + { name: "Saint Lucia", value: "LC", disabled: false }, + { name: "Saint Martin (French part)", value: "MF", disabled: false }, + { name: "Saint Pierre and Miquelon", value: "PM", disabled: false }, + { name: "Saint Vincent and the Grenadines", value: "VC", disabled: false }, + { name: "Samoa", value: "WS", disabled: false }, + { name: "San Marino", value: "SM", disabled: false }, + { name: "Sao Tome and Principe", value: "ST", disabled: false }, + { name: "Saudi Arabia", value: "SA", disabled: false }, + { name: "Senegal", value: "SN", disabled: false }, + { name: "Serbia", value: "RS", disabled: false }, + { name: "Seychelles", value: "SC", disabled: false }, + { name: "Sierra Leone", value: "SL", disabled: false }, + { name: "Singapore", value: "SG", disabled: false }, + { name: "Sint Maarten (Dutch part)", value: "SX", disabled: false }, + { name: "Slovakia", value: "SK", disabled: false }, + { name: "Slovenia", value: "SI", disabled: false }, + { name: "Solomon Islands", value: "SB", disabled: false }, + { name: "Somalia", value: "SO", disabled: false }, + { name: "South Africa", value: "ZA", disabled: false }, + { name: "South Georgia and the South Sandwich Islands", value: "GS", disabled: false }, + { name: "South Sudan", value: "SS", disabled: false }, + { name: "Spain", value: "ES", disabled: false }, + { name: "Sri Lanka", value: "LK", disabled: false }, + { name: "Sudan", value: "SD", disabled: false }, + { name: "Suriname", value: "SR", disabled: false }, + { name: "Svalbard and Jan Mayen", value: "SJ", disabled: false }, + { name: "Swaziland", value: "SZ", disabled: false }, + { name: "Sweden", value: "SE", disabled: false }, + { name: "Switzerland", value: "CH", disabled: false }, + { name: "Syrian Arab Republic", value: "SY", disabled: false }, + { name: "Taiwan", value: "TW", disabled: false }, + { name: "Tajikistan", value: "TJ", disabled: false }, + { name: "Tanzania, United Republic of", value: "TZ", disabled: false }, + { name: "Thailand", value: "TH", disabled: false }, + { name: "Timor-Leste", value: "TL", disabled: false }, + { name: "Togo", value: "TG", disabled: false }, + { name: "Tokelau", value: "TK", disabled: false }, + { name: "Tonga", value: "TO", disabled: false }, + { name: "Trinidad and Tobago", value: "TT", disabled: false }, + { name: "Tunisia", value: "TN", disabled: false }, + { name: "Turkey", value: "TR", disabled: false }, + { name: "Turkmenistan", value: "TM", disabled: false }, + { name: "Turks and Caicos Islands", value: "TC", disabled: false }, + { name: "Tuvalu", value: "TV", disabled: false }, + { name: "Uganda", value: "UG", disabled: false }, + { name: "Ukraine", value: "UA", disabled: false }, + { name: "United Arab Emirates", value: "AE", disabled: false }, + { name: "United States Minor Outlying Islands", value: "UM", disabled: false }, + { name: "Uruguay", value: "UY", disabled: false }, + { name: "Uzbekistan", value: "UZ", disabled: false }, + { name: "Vanuatu", value: "VU", disabled: false }, + { name: "Venezuela, Bolivarian Republic of", value: "VE", disabled: false }, + { name: "Viet Nam", value: "VN", disabled: false }, + { name: "Virgin Islands, British", value: "VG", disabled: false }, + { name: "Virgin Islands, U.S.", value: "VI", disabled: false }, + { name: "Wallis and Futuna", value: "WF", disabled: false }, + { name: "Western Sahara", value: "EH", disabled: false }, + { name: "Yemen", value: "YE", disabled: false }, + { name: "Zambia", value: "ZM", disabled: false }, + { name: "Zimbabwe", value: "ZW", disabled: false }, + ]; + } + + async isCountrySupported(country: string): Promise { + const response = await this.apiService.send( + "GET", + "/tax/is-country-supported?country=" + country, + null, + true, + true, + ); + return response; + } + + async previewIndividualInvoice( + request: PreviewIndividualInvoiceRequest, + ): Promise { + const response = await this.apiService.send( + "POST", + "/accounts/billing/preview-invoice", + request, + true, + true, + ); + return new PreviewInvoiceResponse(response); + } + + async previewOrganizationInvoice( + request: PreviewOrganizationInvoiceRequest, + ): Promise { + const response = await this.apiService.send( + "POST", + `/invoices/preview-organization`, + request, + true, + true, + ); + return new PreviewInvoiceResponse(response); + } +} diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 2c3f81c9c75..135119bf133 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -42,6 +42,8 @@ export enum FeatureFlag { MacOsNativeCredentialSync = "macos-native-credential-sync", PM11360RemoveProviderExportPermission = "pm-11360-remove-provider-export-permission", PM12443RemovePagingLogic = "pm-12443-remove-paging-logic", + PrivateKeyRegeneration = "pm-12241-private-key-regeneration", + ResellerManagedOrgAlert = "PM-15814-alert-owners-of-reseller-managed-orgs", } export type AllowedFeatureFlagTypes = boolean | number | string; @@ -94,6 +96,8 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.MacOsNativeCredentialSync]: FALSE, [FeatureFlag.PM11360RemoveProviderExportPermission]: FALSE, [FeatureFlag.PM12443RemovePagingLogic]: FALSE, + [FeatureFlag.PrivateKeyRegeneration]: FALSE, + [FeatureFlag.ResellerManagedOrgAlert]: FALSE, } satisfies Record; export type DefaultFeatureFlagValueType = typeof DefaultFeatureFlagValue; diff --git a/libs/common/src/enums/notification-type.enum.ts b/libs/common/src/enums/notification-type.enum.ts index c5853cbe2c0..69cbdff9dd2 100644 --- a/libs/common/src/enums/notification-type.enum.ts +++ b/libs/common/src/enums/notification-type.enum.ts @@ -22,4 +22,5 @@ export enum NotificationType { AuthRequestResponse = 16, SyncOrganizations = 17, + SyncOrganizationStatusChanged = 18, } diff --git a/libs/common/src/models/response/notification.response.ts b/libs/common/src/models/response/notification.response.ts index af79b883f08..473e6fc1d10 100644 --- a/libs/common/src/models/response/notification.response.ts +++ b/libs/common/src/models/response/notification.response.ts @@ -42,6 +42,9 @@ export class NotificationResponse extends BaseResponse { case NotificationType.AuthRequestResponse: this.payload = new AuthRequestPushNotification(payload); break; + case NotificationType.SyncOrganizationStatusChanged: + this.payload = new OrganizationStatusPushNotification(payload); + break; default: break; } @@ -112,3 +115,14 @@ export class AuthRequestPushNotification extends BaseResponse { this.userId = this.getResponseProperty("UserId"); } } + +export class OrganizationStatusPushNotification extends BaseResponse { + organizationId: string; + enabled: boolean; + + constructor(response: any) { + super(response); + this.organizationId = this.getResponseProperty("OrganizationId"); + this.enabled = this.getResponseProperty("Enabled"); + } +} diff --git a/libs/common/src/platform/abstractions/environment.service.ts b/libs/common/src/platform/abstractions/environment.service.ts index 0293d68903c..8d32fc4231d 100644 --- a/libs/common/src/platform/abstractions/environment.service.ts +++ b/libs/common/src/platform/abstractions/environment.service.ts @@ -128,5 +128,10 @@ export abstract class EnvironmentService { /** * Get the environment from state. Useful if you need to get the environment for another user. */ + abstract getEnvironment$(userId?: string): Observable; + + /** + * @deprecated Use {@link getEnvironment$} instead. + */ abstract getEnvironment(userId?: string): Promise; } diff --git a/libs/common/src/platform/abstractions/fido2/fido2-authenticator.service.abstraction.ts b/libs/common/src/platform/abstractions/fido2/fido2-authenticator.service.abstraction.ts index bacbafcb323..e9e68ca92c3 100644 --- a/libs/common/src/platform/abstractions/fido2/fido2-authenticator.service.abstraction.ts +++ b/libs/common/src/platform/abstractions/fido2/fido2-authenticator.service.abstraction.ts @@ -8,7 +8,7 @@ import { Fido2CredentialView } from "../../../vault/models/view/fido2-credential * * The authenticator provides key management and cryptographic signatures. */ -export abstract class Fido2AuthenticatorService { +export abstract class Fido2AuthenticatorService { /** * Create and save a new credential as described in: * https://www.w3.org/TR/webauthn-3/#sctn-op-make-cred @@ -19,7 +19,7 @@ export abstract class Fido2AuthenticatorService { **/ makeCredential: ( params: Fido2AuthenticatorMakeCredentialsParams, - tab: chrome.tabs.Tab, + window: ParentWindowReference, abortController?: AbortController, ) => Promise; @@ -33,7 +33,7 @@ export abstract class Fido2AuthenticatorService { */ getAssertion: ( params: Fido2AuthenticatorGetAssertionParams, - tab: chrome.tabs.Tab, + window: ParentWindowReference, abortController?: AbortController, ) => Promise; diff --git a/libs/common/src/platform/abstractions/fido2/fido2-client.service.abstraction.ts b/libs/common/src/platform/abstractions/fido2/fido2-client.service.abstraction.ts index d9cb20995ad..55d9cce8049 100644 --- a/libs/common/src/platform/abstractions/fido2/fido2-client.service.abstraction.ts +++ b/libs/common/src/platform/abstractions/fido2/fido2-client.service.abstraction.ts @@ -15,7 +15,7 @@ export type UserVerification = "discouraged" | "preferred" | "required"; * It is responsible for both marshalling the inputs for the underlying authenticator operations, * and for returning the results of the latter operations to the Web Authentication API's callers. */ -export abstract class Fido2ClientService { +export abstract class Fido2ClientService { isFido2FeatureEnabled: (hostname: string, origin: string) => Promise; /** @@ -28,7 +28,7 @@ export abstract class Fido2ClientService { */ createCredential: ( params: CreateCredentialParams, - tab: chrome.tabs.Tab, + window: ParentWindowReference, abortController?: AbortController, ) => Promise; @@ -43,7 +43,7 @@ export abstract class Fido2ClientService { */ assertCredential: ( params: AssertCredentialParams, - tab: chrome.tabs.Tab, + window: ParentWindowReference, abortController?: AbortController, ) => Promise; } diff --git a/libs/common/src/platform/abstractions/fido2/fido2-user-interface.service.abstraction.ts b/libs/common/src/platform/abstractions/fido2/fido2-user-interface.service.abstraction.ts index 49752138527..7beefc3b4cc 100644 --- a/libs/common/src/platform/abstractions/fido2/fido2-user-interface.service.abstraction.ts +++ b/libs/common/src/platform/abstractions/fido2/fido2-user-interface.service.abstraction.ts @@ -61,7 +61,7 @@ export interface PickCredentialParams { * The service is session based and is intended to be used by the FIDO2 authenticator to open a window, * and then use this window to ask the user for input and/or display messages to the user. */ -export abstract class Fido2UserInterfaceService { +export abstract class Fido2UserInterfaceService { /** * Creates a new session. * Note: This will not necessarily open a window until it is needed to request something from the user. @@ -71,7 +71,7 @@ export abstract class Fido2UserInterfaceService { */ newSession: ( fallbackSupported: boolean, - tab: chrome.tabs.Tab, + window: ParentWindowReference, abortController?: AbortController, ) => Promise; } diff --git a/libs/common/src/platform/abstractions/sdk/sdk.service.ts b/libs/common/src/platform/abstractions/sdk/sdk.service.ts index ec606896408..d44d38c36ab 100644 --- a/libs/common/src/platform/abstractions/sdk/sdk.service.ts +++ b/libs/common/src/platform/abstractions/sdk/sdk.service.ts @@ -7,11 +7,6 @@ import { BitwardenClient } from "@bitwarden/sdk-internal"; import { UserId } from "../../../types/guid"; export abstract class SdkService { - /** - * Check if the SDK is supported in the current environment. - */ - supported$: Observable; - /** * Retrieve the version of the SDK. */ @@ -35,6 +30,4 @@ export abstract class SdkService { * @param userId */ abstract userClient$(userId: UserId): Observable; - - abstract failedToInitialize(category: string, error?: Error): Promise; } diff --git a/libs/common/src/platform/services/default-environment.service.spec.ts b/libs/common/src/platform/services/default-environment.service.spec.ts index 7d266e93fc3..870f887c160 100644 --- a/libs/common/src/platform/services/default-environment.service.spec.ts +++ b/libs/common/src/platform/services/default-environment.service.spec.ts @@ -314,7 +314,7 @@ describe("EnvironmentService", () => { await switchUser(testUser); - const env = await sut.getEnvironment(); + const env = await firstValueFrom(sut.getEnvironment$()); expect(env.getHostname()).toBe(expectedHost); }); @@ -325,7 +325,7 @@ describe("EnvironmentService", () => { setGlobalData(region, new EnvironmentUrls()); setUserData(Region.US, new EnvironmentUrls()); - const env = await sut.getEnvironment(); + const env = await firstValueFrom(sut.getEnvironment$()); expect(env.getHostname()).toBe(expectedHost); }); @@ -338,7 +338,7 @@ describe("EnvironmentService", () => { setGlobalData(region, new EnvironmentUrls()); setUserData(Region.US, new EnvironmentUrls()); - const env = await sut.getEnvironment(testUser); + const env = await firstValueFrom(sut.getEnvironment$(testUser)); expect(env.getHostname()).toBe(expectedHost); }, ); @@ -355,7 +355,7 @@ describe("EnvironmentService", () => { await switchUser(testUser); - const env = await sut.getEnvironment(alternateTestUser); + const env = await firstValueFrom(sut.getEnvironment$(alternateTestUser)); expect(env.getHostname()).toBe(expectedHost); }, ); @@ -366,7 +366,7 @@ describe("EnvironmentService", () => { setGlobalData(Region.SelfHosted, globalSelfHostUrls); setUserData(Region.EU, new EnvironmentUrls()); - const env = await sut.getEnvironment(); + const env = await firstValueFrom(sut.getEnvironment$()); expect(env.getHostname()).toBe("base.example.com"); }); @@ -377,7 +377,7 @@ describe("EnvironmentService", () => { setGlobalData(Region.SelfHosted, globalSelfHostUrls); setUserData(Region.EU, new EnvironmentUrls()); - const env = await sut.getEnvironment(); + const env = await firstValueFrom(sut.getEnvironment$()); expect(env.getHostname()).toBe("vault.example.com"); }); @@ -391,7 +391,7 @@ describe("EnvironmentService", () => { await switchUser(testUser); - const env = await sut.getEnvironment(alternateTestUser); + const env = await firstValueFrom(sut.getEnvironment$(alternateTestUser)); expect(env.getHostname()).toBe("base.example.com"); }); }); diff --git a/libs/common/src/platform/services/default-environment.service.ts b/libs/common/src/platform/services/default-environment.service.ts index 69d990ce691..ac3e39b2bb3 100644 --- a/libs/common/src/platform/services/default-environment.service.ts +++ b/libs/common/src/platform/services/default-environment.service.ts @@ -271,23 +271,30 @@ export class DefaultEnvironmentService implements EnvironmentService { } } - async getEnvironment(userId?: UserId): Promise { + getEnvironment$(userId?: UserId): Observable { if (userId == null) { - return await firstValueFrom(this.environment$); + return this.environment$; } - const state = await this.getEnvironmentState(userId); - return this.buildEnvironment(state.region, state.urls); + return this.activeAccountId$.pipe( + switchMap((activeUserId) => { + // Previous rules dictated that we only get from user scoped state if there is an active user. + if (activeUserId == null) { + return this.globalState.state$; + } + return this.stateProvider.getUser(userId ?? activeUserId, USER_ENVIRONMENT_KEY).state$; + }), + map((state) => { + return this.buildEnvironment(state?.region, state?.urls); + }), + ); } - private async getEnvironmentState(userId: UserId | null) { - // Previous rules dictated that we only get from user scoped state if there is an active user. - const activeUserId = await firstValueFrom(this.activeAccountId$); - return activeUserId == null - ? await firstValueFrom(this.globalState.state$) - : await firstValueFrom( - this.stateProvider.getUser(userId ?? activeUserId, USER_ENVIRONMENT_KEY).state$, - ); + /** + * @deprecated Use getEnvironment$ instead. + */ + async getEnvironment(userId?: UserId): Promise { + return firstValueFrom(this.getEnvironment$(userId)); } async seedUserEnvironment(userId: UserId) { diff --git a/libs/common/src/platform/services/fido2/fido2-authenticator.service.spec.ts b/libs/common/src/platform/services/fido2/fido2-authenticator.service.spec.ts index e3f79ff9d58..226f4c2cfe9 100644 --- a/libs/common/src/platform/services/fido2/fido2-authenticator.service.spec.ts +++ b/libs/common/src/platform/services/fido2/fido2-authenticator.service.spec.ts @@ -30,6 +30,8 @@ import { parseCredentialId } from "./credential-id-utils"; import { AAGUID, Fido2AuthenticatorService } from "./fido2-authenticator.service"; import { Fido2Utils } from "./fido2-utils"; +type ParentWindowReference = string; + const RpId = "bitwarden.com"; describe("FidoAuthenticatorService", () => { @@ -41,16 +43,16 @@ describe("FidoAuthenticatorService", () => { }); let cipherService!: MockProxy; - let userInterface!: MockProxy; + let userInterface!: MockProxy>; let userInterfaceSession!: MockProxy; let syncService!: MockProxy; let accountService!: MockProxy; - let authenticator!: Fido2AuthenticatorService; - let tab!: chrome.tabs.Tab; + let authenticator!: Fido2AuthenticatorService; + let windowReference!: ParentWindowReference; beforeEach(async () => { cipherService = mock(); - userInterface = mock(); + userInterface = mock>(); userInterfaceSession = mock(); userInterface.newSession.mockResolvedValue(userInterfaceSession); syncService = mock({ @@ -63,7 +65,7 @@ describe("FidoAuthenticatorService", () => { syncService, accountService, ); - tab = { id: 123, windowId: 456 } as chrome.tabs.Tab; + windowReference = Utils.newGuid(); accountService.activeAccount$ = activeAccountSubject; }); @@ -78,19 +80,21 @@ describe("FidoAuthenticatorService", () => { // Spec: Check if at least one of the specified combinations of PublicKeyCredentialType and cryptographic parameters in credTypesAndPubKeyAlgs is supported. If not, return an error code equivalent to "NotSupportedError" and terminate the operation. it("should throw error when input does not contain any supported algorithms", async () => { const result = async () => - await authenticator.makeCredential(invalidParams.unsupportedAlgorithm, tab); + await authenticator.makeCredential(invalidParams.unsupportedAlgorithm, windowReference); await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.NotSupported); }); it("should throw error when requireResidentKey has invalid value", async () => { - const result = async () => await authenticator.makeCredential(invalidParams.invalidRk, tab); + const result = async () => + await authenticator.makeCredential(invalidParams.invalidRk, windowReference); await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Unknown); }); it("should throw error when requireUserVerification has invalid value", async () => { - const result = async () => await authenticator.makeCredential(invalidParams.invalidUv, tab); + const result = async () => + await authenticator.makeCredential(invalidParams.invalidUv, windowReference); await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Unknown); }); @@ -103,7 +107,7 @@ describe("FidoAuthenticatorService", () => { it.skip("should throw error if requireUserVerification is set to true", async () => { const params = await createParams({ requireUserVerification: true }); - const result = async () => await authenticator.makeCredential(params, tab); + const result = async () => await authenticator.makeCredential(params, windowReference); await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Constraint); }); @@ -117,7 +121,7 @@ describe("FidoAuthenticatorService", () => { for (const p of Object.values(invalidParams)) { try { - await authenticator.makeCredential(p, tab); + await authenticator.makeCredential(p, windowReference); // eslint-disable-next-line no-empty } catch {} } @@ -158,7 +162,7 @@ describe("FidoAuthenticatorService", () => { userInterfaceSession.informExcludedCredential.mockResolvedValue(); try { - await authenticator.makeCredential(params, tab); + await authenticator.makeCredential(params, windowReference); // eslint-disable-next-line no-empty } catch {} @@ -169,7 +173,7 @@ describe("FidoAuthenticatorService", () => { it("should throw error", async () => { userInterfaceSession.informExcludedCredential.mockResolvedValue(); - const result = async () => await authenticator.makeCredential(params, tab); + const result = async () => await authenticator.makeCredential(params, windowReference); await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.NotAllowed); }); @@ -180,7 +184,7 @@ describe("FidoAuthenticatorService", () => { excludedCipher.organizationId = "someOrganizationId"; try { - await authenticator.makeCredential(params, tab); + await authenticator.makeCredential(params, windowReference); // eslint-disable-next-line no-empty } catch {} @@ -193,7 +197,7 @@ describe("FidoAuthenticatorService", () => { for (const p of Object.values(invalidParams)) { try { - await authenticator.makeCredential(p, tab); + await authenticator.makeCredential(p, windowReference); // eslint-disable-next-line no-empty } catch {} } @@ -230,7 +234,7 @@ describe("FidoAuthenticatorService", () => { userVerified: userVerification, }); - await authenticator.makeCredential(params, tab); + await authenticator.makeCredential(params, windowReference); expect(userInterfaceSession.confirmNewCredential).toHaveBeenCalledWith({ credentialName: params.rpEntity.name, @@ -250,7 +254,7 @@ describe("FidoAuthenticatorService", () => { }); cipherService.encrypt.mockResolvedValue(encryptedCipher as unknown as Cipher); - await authenticator.makeCredential(params, tab); + await authenticator.makeCredential(params, windowReference); const saved = cipherService.encrypt.mock.lastCall?.[0]; expect(saved).toEqual( @@ -288,7 +292,7 @@ describe("FidoAuthenticatorService", () => { }); const params = await createParams(); - const result = async () => await authenticator.makeCredential(params, tab); + const result = async () => await authenticator.makeCredential(params, windowReference); await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.NotAllowed); }); @@ -302,7 +306,7 @@ describe("FidoAuthenticatorService", () => { const encryptedCipher = { ...existingCipher, reprompt: CipherRepromptType.Password }; cipherService.get.mockResolvedValue(encryptedCipher as unknown as Cipher); - const result = async () => await authenticator.makeCredential(params, tab); + const result = async () => await authenticator.makeCredential(params, windowReference); await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Unknown); }); @@ -317,7 +321,7 @@ describe("FidoAuthenticatorService", () => { cipherService.encrypt.mockResolvedValue(encryptedCipher as unknown as Cipher); cipherService.updateWithServer.mockRejectedValue(new Error("Internal error")); - const result = async () => await authenticator.makeCredential(params, tab); + const result = async () => await authenticator.makeCredential(params, windowReference); await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Unknown); }); @@ -358,7 +362,7 @@ describe("FidoAuthenticatorService", () => { }); it("should return attestation object", async () => { - const result = await authenticator.makeCredential(params, tab); + const result = await authenticator.makeCredential(params, windowReference); const attestationObject = CBOR.decode( Fido2Utils.bufferSourceToUint8Array(result.attestationObject).buffer, @@ -455,7 +459,8 @@ describe("FidoAuthenticatorService", () => { describe("invalid input parameters", () => { it("should throw error when requireUserVerification has invalid value", async () => { - const result = async () => await authenticator.getAssertion(invalidParams.invalidUv, tab); + const result = async () => + await authenticator.getAssertion(invalidParams.invalidUv, windowReference); await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Unknown); }); @@ -468,7 +473,7 @@ describe("FidoAuthenticatorService", () => { it.skip("should throw error if requireUserVerification is set to true", async () => { const params = await createParams({ requireUserVerification: true }); - const result = async () => await authenticator.getAssertion(params, tab); + const result = async () => await authenticator.getAssertion(params, windowReference); await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Constraint); }); @@ -498,7 +503,7 @@ describe("FidoAuthenticatorService", () => { userInterfaceSession.informCredentialNotFound.mockResolvedValue(); try { - await authenticator.getAssertion(params, tab); + await authenticator.getAssertion(params, windowReference); // eslint-disable-next-line no-empty } catch {} @@ -513,7 +518,7 @@ describe("FidoAuthenticatorService", () => { userInterfaceSession.informCredentialNotFound.mockResolvedValue(); try { - await authenticator.getAssertion(params, tab); + await authenticator.getAssertion(params, windowReference); // eslint-disable-next-line no-empty } catch {} @@ -534,7 +539,7 @@ describe("FidoAuthenticatorService", () => { /** Spec: If credentialOptions is now empty, return an error code equivalent to "NotAllowedError" and terminate the operation. */ it("should throw error", async () => { - const result = async () => await authenticator.getAssertion(params, tab); + const result = async () => await authenticator.getAssertion(params, windowReference); await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.NotAllowed); }); @@ -573,7 +578,7 @@ describe("FidoAuthenticatorService", () => { userVerified: false, }); - await authenticator.getAssertion(params, tab); + await authenticator.getAssertion(params, windowReference); expect(userInterfaceSession.pickCredential).toHaveBeenCalledWith({ cipherIds: ciphers.map((c) => c.id), @@ -590,7 +595,7 @@ describe("FidoAuthenticatorService", () => { userVerified: false, }); - await authenticator.getAssertion(params, tab); + await authenticator.getAssertion(params, windowReference); expect(userInterfaceSession.pickCredential).toHaveBeenCalledWith({ cipherIds: [discoverableCiphers[0].id], @@ -608,7 +613,7 @@ describe("FidoAuthenticatorService", () => { userVerified: userVerification, }); - await authenticator.getAssertion(params, tab); + await authenticator.getAssertion(params, windowReference); expect(userInterfaceSession.pickCredential).toHaveBeenCalledWith({ cipherIds: ciphers.map((c) => c.id), @@ -625,7 +630,7 @@ describe("FidoAuthenticatorService", () => { userVerified: false, }); - const result = async () => await authenticator.getAssertion(params, tab); + const result = async () => await authenticator.getAssertion(params, windowReference); await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.NotAllowed); }); @@ -637,7 +642,7 @@ describe("FidoAuthenticatorService", () => { userVerified: false, }); - const result = async () => await authenticator.getAssertion(params, tab); + const result = async () => await authenticator.getAssertion(params, windowReference); await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.NotAllowed); }); @@ -686,7 +691,7 @@ describe("FidoAuthenticatorService", () => { cipherService.encrypt.mockResolvedValue(encrypted as any); ciphers[0].login.fido2Credentials[0].counter = 9000; - await authenticator.getAssertion(params, tab); + await authenticator.getAssertion(params, windowReference); expect(cipherService.updateWithServer).toHaveBeenCalledWith(encrypted); expect(cipherService.encrypt).toHaveBeenCalledWith( @@ -710,13 +715,13 @@ describe("FidoAuthenticatorService", () => { cipherService.encrypt.mockResolvedValue(encrypted as any); ciphers[0].login.fido2Credentials[0].counter = 0; - await authenticator.getAssertion(params, tab); + await authenticator.getAssertion(params, windowReference); expect(cipherService.updateWithServer).not.toHaveBeenCalled(); }); it("should return an assertion result", async () => { - const result = await authenticator.getAssertion(params, tab); + const result = await authenticator.getAssertion(params, windowReference); const encAuthData = result.authenticatorData; const rpIdHash = encAuthData.slice(0, 32); @@ -757,7 +762,7 @@ describe("FidoAuthenticatorService", () => { for (let i = 0; i < 10; ++i) { await init(); // Reset inputs - const result = await authenticator.getAssertion(params, tab); + const result = await authenticator.getAssertion(params, windowReference); const counter = result.authenticatorData.slice(33, 37); expect(counter).toEqual(new Uint8Array([0, 0, 0x23, 0x29])); // double check that the counter doesn't change @@ -774,7 +779,7 @@ describe("FidoAuthenticatorService", () => { it("should throw unkown error if creation fails", async () => { cipherService.updateWithServer.mockRejectedValue(new Error("Internal error")); - const result = async () => await authenticator.getAssertion(params, tab); + const result = async () => await authenticator.getAssertion(params, windowReference); await expect(result).rejects.toThrowError(Fido2AuthenticatorErrorCode.Unknown); }); diff --git a/libs/common/src/platform/services/fido2/fido2-authenticator.service.ts b/libs/common/src/platform/services/fido2/fido2-authenticator.service.ts index 34117e852ea..376f4dcdced 100644 --- a/libs/common/src/platform/services/fido2/fido2-authenticator.service.ts +++ b/libs/common/src/platform/services/fido2/fido2-authenticator.service.ts @@ -43,10 +43,12 @@ const KeyUsages: KeyUsage[] = ["sign"]; * * It is highly recommended that the W3C specification is used a reference when reading this code. */ -export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstraction { +export class Fido2AuthenticatorService + implements Fido2AuthenticatorServiceAbstraction +{ constructor( private cipherService: CipherService, - private userInterface: Fido2UserInterfaceService, + private userInterface: Fido2UserInterfaceService, private syncService: SyncService, private accountService: AccountService, private logService?: LogService, @@ -54,12 +56,12 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr async makeCredential( params: Fido2AuthenticatorMakeCredentialsParams, - tab: chrome.tabs.Tab, + window: ParentWindowReference, abortController?: AbortController, ): Promise { const userInterfaceSession = await this.userInterface.newSession( params.fallbackSupported, - tab, + window, abortController, ); @@ -209,12 +211,12 @@ export class Fido2AuthenticatorService implements Fido2AuthenticatorServiceAbstr async getAssertion( params: Fido2AuthenticatorGetAssertionParams, - tab: chrome.tabs.Tab, + window: ParentWindowReference, abortController?: AbortController, ): Promise { const userInterfaceSession = await this.userInterface.newSession( params.fallbackSupported, - tab, + window, abortController, ); try { diff --git a/libs/common/src/platform/services/fido2/fido2-autofill-utils.ts b/libs/common/src/platform/services/fido2/fido2-autofill-utils.ts index 7ef705b95f9..31f6ce10e01 100644 --- a/libs/common/src/platform/services/fido2/fido2-autofill-utils.ts +++ b/libs/common/src/platform/services/fido2/fido2-autofill-utils.ts @@ -3,6 +3,9 @@ import { CipherType } from "../../../vault/enums"; import { CipherView } from "../../../vault/models/view/cipher.view"; import { Fido2CredentialAutofillView } from "../../../vault/models/view/fido2-credential-autofill.view"; +import { Utils } from "../../misc/utils"; + +import { parseCredentialId } from "./credential-id-utils"; // TODO: Move into Fido2AuthenticatorService export async function getCredentialsForAutofill( @@ -15,9 +18,14 @@ export async function getCredentialsForAutofill( ) .map((cipher) => { const credential = cipher.login.fido2Credentials[0]; + + // Credentials are stored as a GUID or b64 string with `b64.` prepended, + // but we need to return them as a URL-safe base64 string + const credId = Utils.fromBufferToUrlB64(parseCredentialId(credential.credentialId)); + return { cipherId: cipher.id, - credentialId: credential.credentialId, + credentialId: credId, rpId: credential.rpId, userHandle: credential.userHandle, userName: credential.userName, diff --git a/libs/common/src/platform/services/fido2/fido2-client.service.spec.ts b/libs/common/src/platform/services/fido2/fido2-client.service.spec.ts index 582849ebc12..51c3d8617ab 100644 --- a/libs/common/src/platform/services/fido2/fido2-client.service.spec.ts +++ b/libs/common/src/platform/services/fido2/fido2-client.service.spec.ts @@ -32,12 +32,14 @@ import { Fido2ClientService } from "./fido2-client.service"; import { Fido2Utils } from "./fido2-utils"; import { guidToRawFormat } from "./guid-utils"; +type ParentWindowReference = string; + const RpId = "bitwarden.com"; const Origin = "https://bitwarden.com"; const VaultUrl = "https://vault.bitwarden.com"; describe("FidoAuthenticatorService", () => { - let authenticator!: MockProxy; + let authenticator!: MockProxy>; let configService!: MockProxy; let authService!: MockProxy; let vaultSettingsService: MockProxy; @@ -45,12 +47,12 @@ describe("FidoAuthenticatorService", () => { let taskSchedulerService: MockProxy; let activeRequest!: MockProxy; let requestManager!: MockProxy; - let client!: Fido2ClientService; - let tab!: chrome.tabs.Tab; + let client!: Fido2ClientService; + let windowReference!: ParentWindowReference; let isValidRpId!: jest.SpyInstance; beforeEach(async () => { - authenticator = mock(); + authenticator = mock>(); configService = mock(); authService = mock(); vaultSettingsService = mock(); @@ -82,7 +84,7 @@ describe("FidoAuthenticatorService", () => { vaultSettingsService.enablePasskeys$ = of(true); domainSettingsService.neverDomains$ = of({}); authService.activeAccountStatus$ = of(AuthenticationStatus.Unlocked); - tab = { id: 123, windowId: 456 } as chrome.tabs.Tab; + windowReference = Utils.newGuid(); }); afterEach(() => { @@ -95,7 +97,7 @@ describe("FidoAuthenticatorService", () => { it("should throw error if sameOriginWithAncestors is false", async () => { const params = createParams({ sameOriginWithAncestors: false }); - const result = async () => await client.createCredential(params, tab); + const result = async () => await client.createCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "NotAllowedError" }); @@ -106,7 +108,7 @@ describe("FidoAuthenticatorService", () => { it("should throw error if user.id is too small", async () => { const params = createParams({ user: { id: "", displayName: "displayName", name: "name" } }); - const result = async () => await client.createCredential(params, tab); + const result = async () => await client.createCredential(params, windowReference); await expect(result).rejects.toBeInstanceOf(TypeError); }); @@ -121,7 +123,7 @@ describe("FidoAuthenticatorService", () => { }, }); - const result = async () => await client.createCredential(params, tab); + const result = async () => await client.createCredential(params, windowReference); await expect(result).rejects.toBeInstanceOf(TypeError); }); @@ -136,7 +138,7 @@ describe("FidoAuthenticatorService", () => { origin: "invalid-domain-name", }); - const result = async () => await client.createCredential(params, tab); + const result = async () => await client.createCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "SecurityError" }); @@ -151,7 +153,7 @@ describe("FidoAuthenticatorService", () => { rp: { id: "bitwarden.com", name: "Bitwarden" }, }); - const result = async () => await client.createCredential(params, tab); + const result = async () => await client.createCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "SecurityError" }); @@ -165,7 +167,7 @@ describe("FidoAuthenticatorService", () => { // `params` actually has a valid rp.id, but we're mocking the function to return false isValidRpId.mockReturnValue(false); - const result = async () => await client.createCredential(params, tab); + const result = async () => await client.createCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "SecurityError" }); @@ -179,7 +181,7 @@ describe("FidoAuthenticatorService", () => { }); domainSettingsService.neverDomains$ = of({ "bitwarden.com": null }); - const result = async () => await client.createCredential(params, tab); + const result = async () => await client.createCredential(params, windowReference); await expect(result).rejects.toThrow(FallbackRequestedError); }); @@ -190,7 +192,7 @@ describe("FidoAuthenticatorService", () => { rp: { id: "bitwarden.com", name: "Bitwarden" }, }); - const result = async () => await client.createCredential(params, tab); + const result = async () => await client.createCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "SecurityError" }); @@ -204,7 +206,7 @@ describe("FidoAuthenticatorService", () => { }); authenticator.makeCredential.mockResolvedValue(createAuthenticatorMakeResult()); - await client.createCredential(params, tab); + await client.createCredential(params, windowReference); }); // Spec: If credTypesAndPubKeyAlgs is empty, return a DOMException whose name is "NotSupportedError", and terminate this algorithm. @@ -216,7 +218,7 @@ describe("FidoAuthenticatorService", () => { ], }); - const result = async () => await client.createCredential(params, tab); + const result = async () => await client.createCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "NotSupportedError" }); @@ -231,7 +233,8 @@ describe("FidoAuthenticatorService", () => { const abortController = new AbortController(); abortController.abort(); - const result = async () => await client.createCredential(params, tab, abortController); + const result = async () => + await client.createCredential(params, windowReference, abortController); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "AbortError" }); @@ -246,7 +249,7 @@ describe("FidoAuthenticatorService", () => { }); authenticator.makeCredential.mockResolvedValue(createAuthenticatorMakeResult()); - await client.createCredential(params, tab); + await client.createCredential(params, windowReference); expect(authenticator.makeCredential).toHaveBeenCalledWith( expect.objectContaining({ @@ -259,7 +262,7 @@ describe("FidoAuthenticatorService", () => { displayName: params.user.displayName, }), }), - tab, + windowReference, expect.anything(), ); }); @@ -271,7 +274,7 @@ describe("FidoAuthenticatorService", () => { }); authenticator.makeCredential.mockResolvedValue(createAuthenticatorMakeResult()); - const result = await client.createCredential(params, tab); + const result = await client.createCredential(params, windowReference); expect(result.extensions.credProps?.rk).toBe(true); }); @@ -283,7 +286,7 @@ describe("FidoAuthenticatorService", () => { }); authenticator.makeCredential.mockResolvedValue(createAuthenticatorMakeResult()); - const result = await client.createCredential(params, tab); + const result = await client.createCredential(params, windowReference); expect(result.extensions.credProps?.rk).toBe(false); }); @@ -295,7 +298,7 @@ describe("FidoAuthenticatorService", () => { }); authenticator.makeCredential.mockResolvedValue(createAuthenticatorMakeResult()); - const result = await client.createCredential(params, tab); + const result = await client.createCredential(params, windowReference); expect(result.extensions.credProps).toBeUndefined(); }); @@ -307,7 +310,7 @@ describe("FidoAuthenticatorService", () => { new Fido2AuthenticatorError(Fido2AuthenticatorErrorCode.InvalidState), ); - const result = async () => await client.createCredential(params, tab); + const result = async () => await client.createCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "InvalidStateError" }); @@ -319,7 +322,7 @@ describe("FidoAuthenticatorService", () => { const params = createParams(); authenticator.makeCredential.mockRejectedValue(new Error("unknown error")); - const result = async () => await client.createCredential(params, tab); + const result = async () => await client.createCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "NotAllowedError" }); @@ -330,7 +333,7 @@ describe("FidoAuthenticatorService", () => { const params = createParams(); vaultSettingsService.enablePasskeys$ = of(false); - const result = async () => await client.createCredential(params, tab); + const result = async () => await client.createCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toThrow(FallbackRequestedError); @@ -340,7 +343,7 @@ describe("FidoAuthenticatorService", () => { const params = createParams(); authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.LoggedOut); - const result = async () => await client.createCredential(params, tab); + const result = async () => await client.createCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toThrow(FallbackRequestedError); @@ -349,7 +352,7 @@ describe("FidoAuthenticatorService", () => { it("should throw FallbackRequestedError if origin equals the bitwarden vault", async () => { const params = createParams({ origin: VaultUrl }); - const result = async () => await client.createCredential(params, tab); + const result = async () => await client.createCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toThrow(FallbackRequestedError); @@ -408,7 +411,7 @@ describe("FidoAuthenticatorService", () => { origin: "invalid-domain-name", }); - const result = async () => await client.assertCredential(params, tab); + const result = async () => await client.assertCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "SecurityError" }); @@ -423,7 +426,7 @@ describe("FidoAuthenticatorService", () => { rpId: "bitwarden.com", }); - const result = async () => await client.assertCredential(params, tab); + const result = async () => await client.assertCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "SecurityError" }); @@ -437,7 +440,7 @@ describe("FidoAuthenticatorService", () => { // `params` actually has a valid rp.id, but we're mocking the function to return false isValidRpId.mockReturnValue(false); - const result = async () => await client.assertCredential(params, tab); + const result = async () => await client.assertCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "SecurityError" }); @@ -451,7 +454,7 @@ describe("FidoAuthenticatorService", () => { domainSettingsService.neverDomains$ = of({ "bitwarden.com": null }); - const result = async () => await client.assertCredential(params, tab); + const result = async () => await client.assertCredential(params, windowReference); await expect(result).rejects.toThrow(FallbackRequestedError); }); @@ -462,7 +465,7 @@ describe("FidoAuthenticatorService", () => { rpId: "bitwarden.com", }); - const result = async () => await client.assertCredential(params, tab); + const result = async () => await client.assertCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "SecurityError" }); @@ -477,7 +480,8 @@ describe("FidoAuthenticatorService", () => { const abortController = new AbortController(); abortController.abort(); - const result = async () => await client.assertCredential(params, tab, abortController); + const result = async () => + await client.assertCredential(params, windowReference, abortController); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "AbortError" }); @@ -493,7 +497,7 @@ describe("FidoAuthenticatorService", () => { new Fido2AuthenticatorError(Fido2AuthenticatorErrorCode.InvalidState), ); - const result = async () => await client.assertCredential(params, tab); + const result = async () => await client.assertCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "InvalidStateError" }); @@ -505,7 +509,7 @@ describe("FidoAuthenticatorService", () => { const params = createParams(); authenticator.getAssertion.mockRejectedValue(new Error("unknown error")); - const result = async () => await client.assertCredential(params, tab); + const result = async () => await client.assertCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toMatchObject({ name: "NotAllowedError" }); @@ -516,7 +520,7 @@ describe("FidoAuthenticatorService", () => { const params = createParams(); vaultSettingsService.enablePasskeys$ = of(false); - const result = async () => await client.assertCredential(params, tab); + const result = async () => await client.assertCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toThrow(FallbackRequestedError); @@ -526,7 +530,7 @@ describe("FidoAuthenticatorService", () => { const params = createParams(); authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.LoggedOut); - const result = async () => await client.assertCredential(params, tab); + const result = async () => await client.assertCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toThrow(FallbackRequestedError); @@ -535,7 +539,7 @@ describe("FidoAuthenticatorService", () => { it("should throw FallbackRequestedError if origin equals the bitwarden vault", async () => { const params = createParams({ origin: VaultUrl }); - const result = async () => await client.assertCredential(params, tab); + const result = async () => await client.assertCredential(params, windowReference); const rejects = expect(result).rejects; await rejects.toThrow(FallbackRequestedError); @@ -555,7 +559,7 @@ describe("FidoAuthenticatorService", () => { }); authenticator.getAssertion.mockResolvedValue(createAuthenticatorAssertResult()); - await client.assertCredential(params, tab); + await client.assertCredential(params, windowReference); expect(authenticator.getAssertion).toHaveBeenCalledWith( expect.objectContaining({ @@ -573,7 +577,7 @@ describe("FidoAuthenticatorService", () => { }), ], }), - tab, + windowReference, expect.anything(), ); }); @@ -585,7 +589,7 @@ describe("FidoAuthenticatorService", () => { params.rpId = undefined; authenticator.getAssertion.mockResolvedValue(createAuthenticatorAssertResult()); - await client.assertCredential(params, tab); + await client.assertCredential(params, windowReference); }); }); @@ -597,7 +601,7 @@ describe("FidoAuthenticatorService", () => { }); authenticator.getAssertion.mockResolvedValue(createAuthenticatorAssertResult()); - await client.assertCredential(params, tab); + await client.assertCredential(params, windowReference); expect(authenticator.getAssertion).toHaveBeenCalledWith( expect.objectContaining({ @@ -605,7 +609,7 @@ describe("FidoAuthenticatorService", () => { rpId: RpId, allowCredentialDescriptorList: [], }), - tab, + windowReference, expect.anything(), ); }); @@ -627,7 +631,7 @@ describe("FidoAuthenticatorService", () => { }); it("creates an active mediated conditional request", async () => { - await client.assertCredential(params, tab); + await client.assertCredential(params, windowReference); expect(requestManager.newActiveRequest).toHaveBeenCalled(); expect(authenticator.getAssertion).toHaveBeenCalledWith( @@ -635,14 +639,14 @@ describe("FidoAuthenticatorService", () => { assumeUserPresence: true, rpId: RpId, }), - tab, + windowReference, ); }); it("restarts the mediated conditional request if a user aborts the request", async () => { authenticator.getAssertion.mockRejectedValueOnce(new Error()); - await client.assertCredential(params, tab); + await client.assertCredential(params, windowReference); expect(authenticator.getAssertion).toHaveBeenCalledTimes(2); }); @@ -652,7 +656,7 @@ describe("FidoAuthenticatorService", () => { abortController.abort(); authenticator.getAssertion.mockRejectedValueOnce(new DOMException("AbortError")); - await client.assertCredential(params, tab); + await client.assertCredential(params, windowReference); expect(authenticator.getAssertion).toHaveBeenCalledTimes(2); }); diff --git a/libs/common/src/platform/services/fido2/fido2-client.service.ts b/libs/common/src/platform/services/fido2/fido2-client.service.ts index d08d1e2a42d..4bf30ef6537 100644 --- a/libs/common/src/platform/services/fido2/fido2-client.service.ts +++ b/libs/common/src/platform/services/fido2/fido2-client.service.ts @@ -47,7 +47,9 @@ import { guidToRawFormat } from "./guid-utils"; * * It is highly recommended that the W3C specification is used a reference when reading this code. */ -export class Fido2ClientService implements Fido2ClientServiceAbstraction { +export class Fido2ClientService + implements Fido2ClientServiceAbstraction +{ private timeoutAbortController: AbortController; private readonly TIMEOUTS = { NO_VERIFICATION: { @@ -63,7 +65,7 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction { }; constructor( - private authenticator: Fido2AuthenticatorService, + private authenticator: Fido2AuthenticatorService, private configService: ConfigService, private authService: AuthService, private vaultSettingsService: VaultSettingsService, @@ -102,7 +104,7 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction { async createCredential( params: CreateCredentialParams, - tab: chrome.tabs.Tab, + window: ParentWindowReference, abortController = new AbortController(), ): Promise { const parsedOrigin = parse(params.origin, { allowPrivateDomains: true }); @@ -201,7 +203,7 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction { try { makeCredentialResult = await this.authenticator.makeCredential( makeCredentialParams, - tab, + window, abortController, ); } catch (error) { @@ -256,7 +258,7 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction { async assertCredential( params: AssertCredentialParams, - tab: chrome.tabs.Tab, + window: ParentWindowReference, abortController = new AbortController(), ): Promise { const parsedOrigin = parse(params.origin, { allowPrivateDomains: true }); @@ -300,7 +302,7 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction { if (params.mediation === "conditional") { return this.handleMediatedConditionalRequest( params, - tab, + window, abortController, clientDataJSONBytes, ); @@ -324,7 +326,7 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction { try { getAssertionResult = await this.authenticator.getAssertion( getAssertionParams, - tab, + window, abortController, ); } catch (error) { @@ -363,7 +365,7 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction { private async handleMediatedConditionalRequest( params: AssertCredentialParams, - tab: chrome.tabs.Tab, + tab: ParentWindowReference, abortController: AbortController, clientDataJSONBytes: Uint8Array, ): Promise { @@ -379,7 +381,10 @@ export class Fido2ClientService implements Fido2ClientServiceAbstraction { `[Fido2Client] started mediated request, available credentials: ${availableCredentials.length}`, ); const requestResult = await this.requestManager.newActiveRequest( - tab.id, + // TODO: This isn't correct, but this.requestManager.newActiveRequest expects a number, + // while this class is currently generic over ParentWindowReference. + // Consider moving requestManager into browser and adding support for ParentWindowReference => tab.id + (tab as any).id, availableCredentials, abortController, ); diff --git a/libs/common/src/platform/services/fido2/noop-fido2-user-interface.service.ts b/libs/common/src/platform/services/fido2/noop-fido2-user-interface.service.ts index 440bd519002..14b4da0ef1b 100644 --- a/libs/common/src/platform/services/fido2/noop-fido2-user-interface.service.ts +++ b/libs/common/src/platform/services/fido2/noop-fido2-user-interface.service.ts @@ -7,7 +7,7 @@ import { * Noop implementation of the {@link Fido2UserInterfaceService}. * This implementation does not provide any user interface. */ -export class Fido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction { +export class Fido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction { newSession(): Promise { throw new Error("Not implemented exception"); } diff --git a/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts b/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts index de8b079621a..c5917e0230f 100644 --- a/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts +++ b/libs/common/src/platform/services/sdk/default-sdk.service.spec.ts @@ -4,7 +4,6 @@ import { BehaviorSubject, firstValueFrom, of } from "rxjs"; import { KdfConfigService, KeyService, PBKDF2KdfConfig } from "@bitwarden/key-management"; import { BitwardenClient } from "@bitwarden/sdk-internal"; -import { ApiService } from "../../../abstractions/api.service"; import { AccountInfo, AccountService } from "../../../auth/abstractions/account.service"; import { UserId } from "../../../types/guid"; import { UserKey } from "../../../types/key"; @@ -24,7 +23,6 @@ describe("DefaultSdkService", () => { let accountService!: MockProxy; let kdfConfigService!: MockProxy; let keyService!: MockProxy; - let apiService!: MockProxy; let service!: DefaultSdkService; let mockClient!: MockProxy; @@ -36,7 +34,6 @@ describe("DefaultSdkService", () => { accountService = mock(); kdfConfigService = mock(); keyService = mock(); - apiService = mock(); // Can't use `of(mock())` for some reason environmentService.environment$ = new BehaviorSubject(mock()); @@ -48,7 +45,6 @@ describe("DefaultSdkService", () => { accountService, kdfConfigService, keyService, - apiService, ); mockClient = mock(); @@ -60,6 +56,9 @@ describe("DefaultSdkService", () => { const userId = "user-id" as UserId; beforeEach(() => { + environmentService.getEnvironment$ + .calledWith(userId) + .mockReturnValue(new BehaviorSubject(mock())); accountService.accounts$ = of({ [userId]: { email: "email", emailVerified: true, name: "name" } as AccountInfo, }); diff --git a/libs/common/src/platform/services/sdk/default-sdk.service.ts b/libs/common/src/platform/services/sdk/default-sdk.service.ts index be6f99ab040..e9cecbb15dc 100644 --- a/libs/common/src/platform/services/sdk/default-sdk.service.ts +++ b/libs/common/src/platform/services/sdk/default-sdk.service.ts @@ -3,7 +3,6 @@ import { combineLatest, concatMap, - firstValueFrom, Observable, shareReplay, map, @@ -21,7 +20,6 @@ import { DeviceType as SdkDeviceType, } from "@bitwarden/sdk-internal"; -import { ApiService } from "../../../abstractions/api.service"; import { EncryptedOrganizationKeyData } from "../../../admin-console/models/data/encrypted-organization-key.data"; import { AccountInfo, AccountService } from "../../../auth/abstractions/account.service"; import { DeviceType } from "../../../enums/device-type.enum"; @@ -34,43 +32,17 @@ import { SdkService } from "../../abstractions/sdk/sdk.service"; import { compareValues } from "../../misc/compare-values"; import { EncryptedString } from "../../models/domain/enc-string"; -export class RecoverableSDKError extends Error { - sdk: BitwardenClient; - timeout: number; - - constructor(sdk: BitwardenClient, timeout: number) { - super(`SDK took ${timeout}s to initialize`); - - this.sdk = sdk; - this.timeout = timeout; - } -} - export class DefaultSdkService implements SdkService { private sdkClientCache = new Map>(); client$ = this.environmentService.environment$.pipe( concatMap(async (env) => { const settings = this.toSettings(env); - try { - return await this.sdkClientFactory.createSdkClient(settings, LogLevel.Info); - } catch (e) { - if (e instanceof RecoverableSDKError) { - await this.failedToInitialize("sdk", e); - return e.sdk; - } - throw e; - } + return await this.sdkClientFactory.createSdkClient(settings, LogLevel.Info); }), shareReplay({ refCount: true, bufferSize: 1 }), ); - supported$ = this.client$.pipe( - concatMap(async (client) => { - return client.echo("bitwarden wasm!") === "bitwarden wasm!"; - }), - ); - version$ = this.client$.pipe( map((client) => client.version()), catchError(() => "Unsupported"), @@ -83,7 +55,6 @@ export class DefaultSdkService implements SdkService { private accountService: AccountService, private kdfConfigService: KdfConfigService, private keyService: KeyService, - private apiService: ApiService, // Yes we shouldn't import ApiService, but it's temporary private userAgent: string = null, ) {} @@ -107,7 +78,7 @@ export class DefaultSdkService implements SdkService { ); const client$ = combineLatest([ - this.environmentService.environment$, + this.environmentService.getEnvironment$(userId), account$, kdfParams$, privateKey$, @@ -155,31 +126,6 @@ export class DefaultSdkService implements SdkService { return client$; } - async failedToInitialize(category: string, error?: Error): Promise { - // Only log on cloud instances - if ( - this.platformUtilsService.isDev() || - !(await firstValueFrom(this.environmentService.environment$)).isCloud - ) { - return; - } - - return this.apiService.send( - "POST", - "/wasm-debug", - { - category: category, - error: error?.message, - }, - false, - false, - null, - (headers) => { - headers.append("SDK-Version", "1.0.0"); - }, - ); - } - private async initializeClient( client: BitwardenClient, account: AccountInfo, diff --git a/libs/common/src/platform/state/state-definitions.ts b/libs/common/src/platform/state/state-definitions.ts index a600901df4f..1ed5227cb13 100644 --- a/libs/common/src/platform/state/state-definitions.ts +++ b/libs/common/src/platform/state/state-definitions.ts @@ -150,6 +150,9 @@ export const COLLECTION_DATA = new StateDefinition("collection", "disk", { web: "memory", }); export const FOLDER_DISK = new StateDefinition("folder", "disk", { web: "memory" }); +export const FOLDER_MEMORY = new StateDefinition("decryptedFolders", "memory", { + browser: "memory-large-object", +}); export const VAULT_FILTER_DISK = new StateDefinition("vaultFilter", "disk", { web: "disk-local", }); @@ -180,4 +183,8 @@ export const VAULT_BROWSER_UI_ONBOARDING = new StateDefinition("vaultBrowserUiOn export const NEW_DEVICE_VERIFICATION_NOTICE = new StateDefinition( "newDeviceVerificationNotice", "disk", + { + web: "disk-local", + }, ); +export const VAULT_APPEARANCE = new StateDefinition("vaultAppearance", "disk"); diff --git a/libs/common/src/platform/sync/core-sync.service.ts b/libs/common/src/platform/sync/core-sync.service.ts index 180767ccd16..cfa9030c9de 100644 --- a/libs/common/src/platform/sync/core-sync.service.ts +++ b/libs/common/src/platform/sync/core-sync.service.ts @@ -85,18 +85,25 @@ export abstract class CoreSyncService implements SyncService { await this.stateProvider.getUser(userId, LAST_SYNC_DATE).update(() => date); } - async syncUpsertFolder(notification: SyncFolderNotification, isEdit: boolean): Promise { + async syncUpsertFolder( + notification: SyncFolderNotification, + isEdit: boolean, + userId: UserId, + ): Promise { this.syncStarted(); - if (await this.stateService.getIsAuthenticated()) { + + const authStatus = await firstValueFrom(this.authService.authStatusFor$(userId)); + + if (authStatus >= AuthenticationStatus.Locked) { try { - const localFolder = await this.folderService.get(notification.id); + const localFolder = await this.folderService.get(notification.id, userId); if ( (!isEdit && localFolder == null) || (isEdit && localFolder != null && localFolder.revisionDate < notification.revisionDate) ) { const remoteFolder = await this.folderApiService.get(notification.id); if (remoteFolder != null) { - await this.folderService.upsert(new FolderData(remoteFolder)); + await this.folderService.upsert(new FolderData(remoteFolder), userId); this.messageSender.send("syncedUpsertedFolder", { folderId: notification.id }); return this.syncCompleted(true); } @@ -108,10 +115,13 @@ export abstract class CoreSyncService implements SyncService { return this.syncCompleted(false); } - async syncDeleteFolder(notification: SyncFolderNotification): Promise { + async syncDeleteFolder(notification: SyncFolderNotification, userId: UserId): Promise { this.syncStarted(); - if (await this.stateService.getIsAuthenticated()) { - await this.folderService.delete(notification.id); + + const authStatus = await firstValueFrom(this.authService.authStatusFor$(userId)); + + if (authStatus >= AuthenticationStatus.Locked) { + await this.folderService.delete(notification.id, userId); this.messageSender.send("syncedDeletedFolder", { folderId: notification.id }); this.syncCompleted(true); return true; diff --git a/libs/common/src/platform/sync/sync.service.ts b/libs/common/src/platform/sync/sync.service.ts index 733b7beaff5..6763e01cab7 100644 --- a/libs/common/src/platform/sync/sync.service.ts +++ b/libs/common/src/platform/sync/sync.service.ts @@ -56,8 +56,9 @@ export abstract class SyncService { abstract syncUpsertFolder( notification: SyncFolderNotification, isEdit: boolean, + userId: UserId, ): Promise; - abstract syncDeleteFolder(notification: SyncFolderNotification): Promise; + abstract syncDeleteFolder(notification: SyncFolderNotification, userId: UserId): Promise; abstract syncUpsertCipher( notification: SyncCipherNotification, isEdit: boolean, diff --git a/libs/common/src/services/notifications.service.ts b/libs/common/src/services/notifications.service.ts index e240886cf29..4a14332af8a 100644 --- a/libs/common/src/services/notifications.service.ts +++ b/libs/common/src/services/notifications.service.ts @@ -168,10 +168,14 @@ export class NotificationsService implements NotificationsServiceAbstraction { await this.syncService.syncUpsertFolder( notification.payload as SyncFolderNotification, notification.type === NotificationType.SyncFolderUpdate, + payloadUserId, ); break; case NotificationType.SyncFolderDelete: - await this.syncService.syncDeleteFolder(notification.payload as SyncFolderNotification); + await this.syncService.syncDeleteFolder( + notification.payload as SyncFolderNotification, + payloadUserId, + ); break; case NotificationType.SyncVault: case NotificationType.SyncCiphers: @@ -218,6 +222,11 @@ export class NotificationsService implements NotificationsServiceAbstraction { }); } break; + case NotificationType.SyncOrganizationStatusChanged: + if (isAuthenticated) { + await this.syncService.fullSync(true); + } + break; default: break; } diff --git a/libs/common/src/services/vault-timeout/vault-timeout.service.spec.ts b/libs/common/src/services/vault-timeout/vault-timeout.service.spec.ts index 71341a98a62..1350010f849 100644 --- a/libs/common/src/services/vault-timeout/vault-timeout.service.spec.ts +++ b/libs/common/src/services/vault-timeout/vault-timeout.service.spec.ts @@ -334,7 +334,7 @@ describe("VaultTimeoutService", () => { // Active users should have additional steps ran expect(searchService.clearIndex).toHaveBeenCalled(); - expect(folderService.clearCache).toHaveBeenCalled(); + expect(folderService.clearDecryptedFolderState).toHaveBeenCalled(); expectUserToHaveLoggedOut("3"); // They have chosen logout as their action and it's available, log them out expectUserToHaveLoggedOut("4"); // They may have had lock as their chosen action but it's not available to them so logout diff --git a/libs/common/src/services/vault-timeout/vault-timeout.service.ts b/libs/common/src/services/vault-timeout/vault-timeout.service.ts index f6ad0f17e9a..f465174bf40 100644 --- a/libs/common/src/services/vault-timeout/vault-timeout.service.ts +++ b/libs/common/src/services/vault-timeout/vault-timeout.service.ts @@ -135,10 +135,10 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction { if (userId == null || userId === currentUserId) { await this.searchService.clearIndex(); - await this.folderService.clearCache(); await this.collectionService.clearActiveUserCache(); } + await this.folderService.clearDecryptedFolderState(userId); await this.masterPasswordService.clearMasterKey(lockingUserId); await this.stateService.setUserKeyAutoUnlock(null, { userId: lockingUserId }); diff --git a/libs/common/src/vault/abstractions/folder/folder-api.service.abstraction.ts b/libs/common/src/vault/abstractions/folder/folder-api.service.abstraction.ts index a732acfa991..859f2183edb 100644 --- a/libs/common/src/vault/abstractions/folder/folder-api.service.abstraction.ts +++ b/libs/common/src/vault/abstractions/folder/folder-api.service.abstraction.ts @@ -1,11 +1,13 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { UserId } from "@bitwarden/common/types/guid"; + import { Folder } from "../../models/domain/folder"; import { FolderResponse } from "../../models/response/folder.response"; export class FolderApiServiceAbstraction { - save: (folder: Folder) => Promise; - delete: (id: string) => Promise; + save: (folder: Folder, userId: UserId) => Promise; + delete: (id: string, userId: UserId) => Promise; get: (id: string) => Promise; - deleteAll: () => Promise; + deleteAll: (userId: UserId) => Promise; } diff --git a/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts b/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts index c5722e0f57b..b7241e3ae37 100644 --- a/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts +++ b/libs/common/src/vault/abstractions/folder/folder.service.abstraction.ts @@ -13,23 +13,27 @@ import { FolderWithIdRequest } from "../../models/request/folder-with-id.request import { FolderView } from "../../models/view/folder.view"; export abstract class FolderService implements UserKeyRotationDataProvider { - folders$: Observable; - folderViews$: Observable; + folders$: (userId: UserId) => Observable; + folderViews$: (userId: UserId) => Observable; - clearCache: () => Promise; + clearDecryptedFolderState: (userId: UserId) => Promise; encrypt: (model: FolderView, key: SymmetricCryptoKey) => Promise; - get: (id: string) => Promise; - getDecrypted$: (id: string) => Observable; - getAllFromState: () => Promise; + get: (id: string, userId: UserId) => Promise; + getDecrypted$: (id: string, userId: UserId) => Observable; + /** + * @deprecated Use firstValueFrom(folders$) directly instead + * @param userId The user id + * @returns Promise of folders array + */ + getAllFromState: (userId: UserId) => Promise; /** * @deprecated Only use in CLI! */ - getFromState: (id: string) => Promise; + getFromState: (id: string, userId: UserId) => Promise; /** * @deprecated Only use in CLI! */ - getAllDecryptedFromState: () => Promise; - decryptFolders: (folders: Folder[]) => Promise; + getAllDecryptedFromState: (userId: UserId) => Promise; /** * Returns user folders re-encrypted with the new user key. * @param originalUserKey the original user key @@ -46,8 +50,8 @@ export abstract class FolderService implements UserKeyRotationDataProvider Promise; + upsert: (folder: FolderData | FolderData[], userId: UserId) => Promise; replace: (folders: { [id: string]: FolderData }, userId: UserId) => Promise; - clear: (userId?: string) => Promise; - delete: (id: string | string[]) => Promise; + clear: (userId: UserId) => Promise; + delete: (id: string | string[], userId: UserId) => Promise; } diff --git a/libs/common/src/vault/abstractions/vault-settings/vault-settings.service.ts b/libs/common/src/vault/abstractions/vault-settings/vault-settings.service.ts index 9b3fde47a15..ea1e73c2685 100644 --- a/libs/common/src/vault/abstractions/vault-settings/vault-settings.service.ts +++ b/libs/common/src/vault/abstractions/vault-settings/vault-settings.service.ts @@ -19,6 +19,12 @@ export abstract class VaultSettingsService { */ showIdentitiesCurrentTab$: Observable; /** + /** + * An observable monitoring the state of the click items on the Vault view + * for Autofill suggestions. + */ + clickItemsToAutofillVaultView$: Observable; + /** /** * Saves the enable passkeys setting to disk. @@ -35,4 +41,10 @@ export abstract class VaultSettingsService { * @param value The new value for the show identities on tab page setting. */ setShowIdentitiesCurrentTab: (value: boolean) => Promise; + /** + * Saves the click items on vault View for Autofill suggestions to disk. + * @param value The new value for the click items on vault View for + * Autofill suggestions setting. + */ + setClickItemsToAutofillVaultView: (value: boolean) => Promise; } diff --git a/libs/common/src/vault/abstractions/view-password-history.service.ts b/libs/common/src/vault/abstractions/view-password-history.service.ts index d9b1306eacb..2e47a456ff2 100644 --- a/libs/common/src/vault/abstractions/view-password-history.service.ts +++ b/libs/common/src/vault/abstractions/view-password-history.service.ts @@ -1,8 +1,8 @@ -import { CipherId } from "../../types/guid"; +import { CipherView } from "../models/view/cipher.view"; /** * The ViewPasswordHistoryService is responsible for displaying the password history for a cipher. */ export abstract class ViewPasswordHistoryService { - abstract viewPasswordHistory(cipherId?: CipherId): Promise; + abstract viewPasswordHistory(cipher: CipherView): Promise; } diff --git a/libs/common/src/vault/services/folder/folder-api.service.ts b/libs/common/src/vault/services/folder/folder-api.service.ts index e46df37c176..24831393668 100644 --- a/libs/common/src/vault/services/folder/folder-api.service.ts +++ b/libs/common/src/vault/services/folder/folder-api.service.ts @@ -1,3 +1,5 @@ +import { UserId } from "@bitwarden/common/types/guid"; + import { ApiService } from "../../../abstractions/api.service"; import { FolderApiServiceAbstraction } from "../../../vault/abstractions/folder/folder-api.service.abstraction"; import { InternalFolderService } from "../../../vault/abstractions/folder/folder.service.abstraction"; @@ -12,7 +14,7 @@ export class FolderApiService implements FolderApiServiceAbstraction { private apiService: ApiService, ) {} - async save(folder: Folder): Promise { + async save(folder: Folder, userId: UserId): Promise { const request = new FolderRequest(folder); let response: FolderResponse; @@ -24,17 +26,17 @@ export class FolderApiService implements FolderApiServiceAbstraction { } const data = new FolderData(response); - await this.folderService.upsert(data); + await this.folderService.upsert(data, userId); } - async delete(id: string): Promise { + async delete(id: string, userId: UserId): Promise { await this.deleteFolder(id); - await this.folderService.delete(id); + await this.folderService.delete(id, userId); } - async deleteAll(): Promise { + async deleteAll(userId: UserId): Promise { await this.apiService.send("DELETE", "/folders/all", null, true, false); - await this.folderService.clear(); + await this.folderService.clear(userId); } async get(id: string): Promise { diff --git a/libs/common/src/vault/services/folder/folder.service.spec.ts b/libs/common/src/vault/services/folder/folder.service.spec.ts index 193d0e85e61..612cd83d99b 100644 --- a/libs/common/src/vault/services/folder/folder.service.spec.ts +++ b/libs/common/src/vault/services/folder/folder.service.spec.ts @@ -1,10 +1,10 @@ import { mock, MockProxy } from "jest-mock-extended"; -import { firstValueFrom } from "rxjs"; +import { BehaviorSubject, firstValueFrom } from "rxjs"; import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; -import { makeStaticByteArray } from "../../../../spec"; +import { makeEncString } from "../../../../spec"; import { FakeAccountService, mockAccountServiceWith } from "../../../../spec/fake-account-service"; -import { FakeActiveUserState } from "../../../../spec/fake-state"; +import { FakeSingleUserState } from "../../../../spec/fake-state"; import { FakeStateProvider } from "../../../../spec/fake-state-provider"; import { EncryptService } from "../../../platform/abstractions/encrypt.service"; import { I18nService } from "../../../platform/abstractions/i18n.service"; @@ -17,7 +17,7 @@ import { CipherService } from "../../abstractions/cipher.service"; import { FolderData } from "../../models/data/folder.data"; import { FolderView } from "../../models/view/folder.view"; import { FolderService } from "../../services/folder/folder.service"; -import { FOLDER_ENCRYPTED_FOLDERS } from "../key-state/folder.state"; +import { FOLDER_DECRYPTED_FOLDERS, FOLDER_ENCRYPTED_FOLDERS } from "../key-state/folder.state"; describe("Folder Service", () => { let folderService: FolderService; @@ -30,7 +30,7 @@ describe("Folder Service", () => { const mockUserId = Utils.newGuid() as UserId; let accountService: FakeAccountService; - let folderState: FakeActiveUserState>; + let folderState: FakeSingleUserState>; beforeEach(() => { keyService = mock(); @@ -42,11 +42,9 @@ describe("Folder Service", () => { stateProvider = new FakeStateProvider(accountService); i18nService.collator = new Intl.Collator("en"); + i18nService.t.mockReturnValue("No Folder"); - keyService.hasUserKey.mockResolvedValue(true); - keyService.getUserKeyWithLegacySupport.mockResolvedValue( - new SymmetricCryptoKey(makeStaticByteArray(32)) as UserKey, - ); + keyService.userKey$.mockReturnValue(new BehaviorSubject("mockOriginalUserKey" as any)); encryptService.decryptToUtf8.mockResolvedValue("DEC"); folderService = new FolderService( @@ -57,10 +55,53 @@ describe("Folder Service", () => { stateProvider, ); - folderState = stateProvider.activeUser.getFake(FOLDER_ENCRYPTED_FOLDERS); + folderState = stateProvider.singleUser.getFake(mockUserId, FOLDER_ENCRYPTED_FOLDERS); // Initial state - folderState.nextState({ "1": folderData("1", "test") }); + folderState.nextState({ "1": folderData("1") }); + }); + + describe("folders$", () => { + it("emits encrypted folders from state", async () => { + const folder1 = folderData("1"); + const folder2 = folderData("2"); + + await stateProvider.setUserState( + FOLDER_ENCRYPTED_FOLDERS, + Object.fromEntries([folder1, folder2].map((f) => [f.id, f])), + mockUserId, + ); + + const result = await firstValueFrom(folderService.folders$(mockUserId)); + + expect(result.length).toBe(2); + expect(result).toIncludeAllPartialMembers([ + { id: "1", name: makeEncString("ENC_STRING_1") }, + { id: "2", name: makeEncString("ENC_STRING_2") }, + ]); + }); + }); + + describe("folderView$", () => { + it("emits decrypted folders from state", async () => { + const folder1 = folderData("1"); + const folder2 = folderData("2"); + + await stateProvider.setUserState( + FOLDER_ENCRYPTED_FOLDERS, + Object.fromEntries([folder1, folder2].map((f) => [f.id, f])), + mockUserId, + ); + + const result = await firstValueFrom(folderService.folderViews$(mockUserId)); + + expect(result.length).toBe(3); + expect(result).toIncludeAllPartialMembers([ + { id: "1", name: "DEC" }, + { id: "2", name: "DEC" }, + { name: "No Folder" }, + ]); + }); }); it("encrypt", async () => { @@ -83,105 +124,83 @@ describe("Folder Service", () => { describe("get", () => { it("exists", async () => { - const result = await folderService.get("1"); + const result = await folderService.get("1", mockUserId); expect(result).toEqual({ id: "1", - name: { - encryptedString: "test", - encryptionType: 0, - }, + name: makeEncString("ENC_STRING_" + 1), revisionDate: null, }); }); it("not exists", async () => { - const result = await folderService.get("2"); + const result = await folderService.get("2", mockUserId); expect(result).toBe(undefined); }); }); it("upsert", async () => { - await folderService.upsert(folderData("2", "test 2")); + await folderService.upsert(folderData("2"), mockUserId); - expect(await firstValueFrom(folderService.folders$)).toEqual([ + expect(await firstValueFrom(folderService.folders$(mockUserId))).toEqual([ { id: "1", - name: { - encryptedString: "test", - encryptionType: 0, - }, + name: makeEncString("ENC_STRING_" + 1), revisionDate: null, }, { id: "2", - name: { - encryptedString: "test 2", - encryptionType: 0, - }, + name: makeEncString("ENC_STRING_" + 2), revisionDate: null, }, ]); }); it("replace", async () => { - await folderService.replace({ "2": folderData("2", "test 2") }, mockUserId); + await folderService.replace({ "4": folderData("4") }, mockUserId); - expect(await firstValueFrom(folderService.folders$)).toEqual([ + expect(await firstValueFrom(folderService.folders$(mockUserId))).toEqual([ { - id: "2", - name: { - encryptedString: "test 2", - encryptionType: 0, - }, + id: "4", + name: makeEncString("ENC_STRING_" + 4), revisionDate: null, }, ]); }); it("delete", async () => { - await folderService.delete("1"); + await folderService.delete("1", mockUserId); - expect((await firstValueFrom(folderService.folders$)).length).toBe(0); + expect((await firstValueFrom(folderService.folders$(mockUserId))).length).toBe(0); }); - it("clearCache", async () => { - await folderService.clearCache(); + describe("clearDecryptedFolderState", () => { + it("null userId", async () => { + await expect(folderService.clearDecryptedFolderState(null)).rejects.toThrow( + "User ID is required.", + ); + }); + + it("userId provided", async () => { + await folderService.clearDecryptedFolderState(mockUserId); - expect((await firstValueFrom(folderService.folders$)).length).toBe(1); - expect((await firstValueFrom(folderService.folderViews$)).length).toBe(0); + expect((await firstValueFrom(folderService.folders$(mockUserId))).length).toBe(1); + expect( + (await firstValueFrom(stateProvider.getUserState$(FOLDER_DECRYPTED_FOLDERS, mockUserId))) + .length, + ).toBe(0); + }); }); - describe("clear", () => { - it("null userId", async () => { - await folderService.clear(); + it("clear", async () => { + await folderService.clear(mockUserId); - expect((await firstValueFrom(folderService.folders$)).length).toBe(0); - expect((await firstValueFrom(folderService.folderViews$)).length).toBe(0); - }); + expect((await firstValueFrom(folderService.folders$(mockUserId))).length).toBe(0); - /** - * TODO: Fix this test to address the problem where the fakes for the active user state is not - * updated as expected - */ - // it("matching userId", async () => { - // stateService.getUserId.mockResolvedValue("1"); - // await folderService.clear("1" as UserId); - - // expect((await firstValueFrom(folderService.folders$)).length).toBe(0); - // }); - - /** - * TODO: Fix this test to address the problem where the fakes for the active user state is not - * updated as expected - */ - // it("mismatching userId", async () => { - // await folderService.clear("12" as UserId); - - // expect((await firstValueFrom(folderService.folders$)).length).toBe(1); - // expect((await firstValueFrom(folderService.folderViews$)).length).toBe(2); - // }); + const folderViews = await firstValueFrom(folderService.folderViews$(mockUserId)); + expect(folderViews.length).toBe(1); + expect(folderViews[0].id).toBeNull(); // Should be the "No Folder" folder }); describe("getRotatedData", () => { @@ -207,10 +226,10 @@ describe("Folder Service", () => { }); }); - function folderData(id: string, name: string) { + function folderData(id: string) { const data = new FolderData({} as any); data.id = id; - data.name = name; + data.name = makeEncString("ENC_STRING_" + data.id).encryptedString; return data; } diff --git a/libs/common/src/vault/services/folder/folder.service.ts b/libs/common/src/vault/services/folder/folder.service.ts index 07b162b4f14..3aac5374fcb 100644 --- a/libs/common/src/vault/services/folder/folder.service.ts +++ b/libs/common/src/vault/services/folder/folder.service.ts @@ -1,14 +1,14 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Observable, firstValueFrom, map, shareReplay } from "rxjs"; +import { Observable, Subject, firstValueFrom, map, shareReplay, switchMap, merge } from "rxjs"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; import { I18nService } from "../../../platform/abstractions/i18n.service"; -import { Utils } from "../../../platform/misc/utils"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; -import { ActiveUserState, DerivedState, StateProvider } from "../../../platform/state"; +import { StateProvider } from "../../../platform/state"; import { UserId } from "../../../types/guid"; import { UserKey } from "../../../types/key"; import { CipherService } from "../../../vault/abstractions/cipher.service"; @@ -21,11 +21,18 @@ import { FolderWithIdRequest } from "../../models/request/folder-with-id.request import { FOLDER_DECRYPTED_FOLDERS, FOLDER_ENCRYPTED_FOLDERS } from "../key-state/folder.state"; export class FolderService implements InternalFolderServiceAbstraction { - folders$: Observable; - folderViews$: Observable; + /** + * Ensures we reuse the same observable stream for each userId rather than + * creating a new one on each folderViews$ call. + */ + private folderViewCache = new Map>(); - private encryptedFoldersState: ActiveUserState>; - private decryptedFoldersState: DerivedState; + /** + * Used to force the folderviews$ Observable to re-emit with a provided value. + * Required because shareReplay with refCount: false maintains last emission. + * Used during cleanup to force emit empty arrays, ensuring stale data isn't retained. + */ + private forceFolderViews: Record> = {}; constructor( private keyService: KeyService, @@ -33,23 +40,44 @@ export class FolderService implements InternalFolderServiceAbstraction { private i18nService: I18nService, private cipherService: CipherService, private stateProvider: StateProvider, - ) { - this.encryptedFoldersState = this.stateProvider.getActive(FOLDER_ENCRYPTED_FOLDERS); - this.decryptedFoldersState = this.stateProvider.getDerived( - this.encryptedFoldersState.state$, - FOLDER_DECRYPTED_FOLDERS, - { folderService: this, keyService: this.keyService }, - ); + ) {} - this.folders$ = this.encryptedFoldersState.state$.pipe( - map((folderData) => Object.values(folderData).map((f) => new Folder(f))), - ); + folders$(userId: UserId): Observable { + return this.encryptedFoldersState(userId).state$.pipe( + map((folders) => { + if (folders == null) { + return []; + } - this.folderViews$ = this.decryptedFoldersState.state$; + return Object.values(folders).map((f) => new Folder(f)); + }), + ); } - async clearCache(): Promise { - await this.decryptedFoldersState.forceValue([]); + /** + * Returns an Observable of decrypted folder views for the given userId. + * Uses folderViewCache to maintain a single Observable instance per user, + * combining normal folder state updates with forced updates. + */ + folderViews$(userId: UserId): Observable { + if (!this.folderViewCache.has(userId)) { + if (!this.forceFolderViews[userId]) { + this.forceFolderViews[userId] = new Subject(); + } + + const observable = merge( + this.forceFolderViews[userId], + this.encryptedFoldersState(userId).state$.pipe( + switchMap((folderData) => { + return this.decryptFolders(userId, folderData); + }), + ), + ).pipe(shareReplay({ refCount: false, bufferSize: 1 })); + + this.folderViewCache.set(userId, observable); + } + + return this.folderViewCache.get(userId); } // TODO: This should be moved to EncryptService or something @@ -60,29 +88,29 @@ export class FolderService implements InternalFolderServiceAbstraction { return folder; } - async get(id: string): Promise { - const folders = await firstValueFrom(this.folders$); + async get(id: string, userId: UserId): Promise { + const folders = await firstValueFrom(this.folders$(userId)); return folders.find((folder) => folder.id === id); } - getDecrypted$(id: string): Observable { - return this.folderViews$.pipe( + getDecrypted$(id: string, userId: UserId): Observable { + return this.folderViews$(userId).pipe( map((folders) => folders.find((folder) => folder.id === id)), shareReplay({ refCount: true, bufferSize: 1 }), ); } - async getAllFromState(): Promise { - return await firstValueFrom(this.folders$); + async getAllFromState(userId: UserId): Promise { + return await firstValueFrom(this.folders$(userId)); } /** * @deprecated For the CLI only * @param id id of the folder */ - async getFromState(id: string): Promise { - const folder = await this.get(id); + async getFromState(id: string, userId: UserId): Promise { + const folder = await this.get(id, userId); if (!folder) { return null; } @@ -93,12 +121,13 @@ export class FolderService implements InternalFolderServiceAbstraction { /** * @deprecated Only use in CLI! */ - async getAllDecryptedFromState(): Promise { - return await firstValueFrom(this.folderViews$); + async getAllDecryptedFromState(userId: UserId): Promise { + return await firstValueFrom(this.folderViews$(userId)); } - async upsert(folderData: FolderData | FolderData[]): Promise { - await this.encryptedFoldersState.update((folders) => { + async upsert(folderData: FolderData | FolderData[], userId: UserId): Promise { + await this.clearDecryptedFolderState(userId); + await this.encryptedFoldersState(userId).update((folders) => { if (folders == null) { folders = {}; } @@ -120,24 +149,31 @@ export class FolderService implements InternalFolderServiceAbstraction { if (!folders) { return; } - + await this.clearDecryptedFolderState(userId); await this.stateProvider.getUser(userId, FOLDER_ENCRYPTED_FOLDERS).update(() => { const newFolders: Record = { ...folders }; return newFolders; }); } - async clear(userId?: UserId): Promise { + async clearDecryptedFolderState(userId: UserId): Promise { if (userId == null) { - await this.encryptedFoldersState.update(() => ({})); - await this.decryptedFoldersState.forceValue([]); - } else { - await this.stateProvider.getUser(userId, FOLDER_ENCRYPTED_FOLDERS).update(() => ({})); + throw new Error("User ID is required."); } + + await this.setDecryptedFolders([], userId); + } + + async clear(userId: UserId): Promise { + this.forceFolderViews[userId]?.next([]); + + await this.encryptedFoldersState(userId).update(() => ({})); + await this.clearDecryptedFolderState(userId); } - async delete(id: string | string[]): Promise { - await this.encryptedFoldersState.update((folders) => { + async delete(id: string | string[], userId: UserId): Promise { + await this.clearDecryptedFolderState(userId); + await this.encryptedFoldersState(userId).update((folders) => { if (folders == null) { return; } @@ -164,25 +200,11 @@ export class FolderService implements InternalFolderServiceAbstraction { } } if (updates.length > 0) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.cipherService.upsert(updates.map((c) => c.toCipherData())); + await this.cipherService.upsert(updates.map((c) => c.toCipherData())); } } } - async decryptFolders(folders: Folder[]) { - const decryptFolderPromises = folders.map((f) => f.decrypt()); - const decryptedFolders = await Promise.all(decryptFolderPromises); - - decryptedFolders.sort(Utils.getSortFunction(this.i18nService, "name")); - - const noneFolder = new FolderView(); - noneFolder.name = this.i18nService.t("noneFolder"); - decryptedFolders.push(noneFolder); - return decryptedFolders; - } - async getRotatedData( originalUserKey: UserKey, newUserKey: UserKey, @@ -193,7 +215,7 @@ export class FolderService implements InternalFolderServiceAbstraction { } let encryptedFolders: FolderWithIdRequest[] = []; - const folders = await firstValueFrom(this.folderViews$); + const folders = await firstValueFrom(this.folderViews$(userId)); if (!folders) { return encryptedFolders; } @@ -205,4 +227,63 @@ export class FolderService implements InternalFolderServiceAbstraction { ); return encryptedFolders; } + + /** + * Decrypts the folders for a user. + * @param userId the user id + * @param folderData encrypted folders + * @returns a list of decrypted folders + */ + private async decryptFolders( + userId: UserId, + folderData: Record, + ): Promise { + // Check if the decrypted folders are already cached + const decrypted = await firstValueFrom( + this.stateProvider.getUser(userId, FOLDER_DECRYPTED_FOLDERS).state$, + ); + if (decrypted?.length) { + return decrypted; + } + + if (folderData == null) { + return []; + } + + const folders = Object.values(folderData).map((f) => new Folder(f)); + const userKey = await firstValueFrom(this.keyService.userKey$(userId)); + if (!userKey) { + return []; + } + + const decryptFolderPromises = folders.map((f) => + f.decryptWithKey(userKey, this.encryptService), + ); + const decryptedFolders = await Promise.all(decryptFolderPromises); + decryptedFolders.sort(Utils.getSortFunction(this.i18nService, "name")); + + const noneFolder = new FolderView(); + noneFolder.name = this.i18nService.t("noneFolder"); + decryptedFolders.push(noneFolder); + + // Cache the decrypted folders + await this.setDecryptedFolders(decryptedFolders, userId); + return decryptedFolders; + } + + /** + * @returns a SingleUserState for the encrypted folders. + */ + private encryptedFoldersState(userId: UserId) { + return this.stateProvider.getUser(userId, FOLDER_ENCRYPTED_FOLDERS); + } + + /** + * Sets the decrypted folders state for a user. + * @param folders the decrypted folders + * @param userId the user id + */ + private async setDecryptedFolders(folders: FolderView[], userId: UserId): Promise { + await this.stateProvider.setUserState(FOLDER_DECRYPTED_FOLDERS, folders, userId); + } } diff --git a/libs/common/src/vault/services/key-state/folder.state.spec.ts b/libs/common/src/vault/services/key-state/folder.state.spec.ts index ece66b5d451..217a200ea88 100644 --- a/libs/common/src/vault/services/key-state/folder.state.spec.ts +++ b/libs/common/src/vault/services/key-state/folder.state.spec.ts @@ -1,11 +1,3 @@ -import { mock } from "jest-mock-extended"; - -import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; -import { FolderService } from "../../abstractions/folder/folder.service.abstraction"; -import { FolderData } from "../../models/data/folder.data"; -import { Folder } from "../../models/domain/folder"; -import { FolderView } from "../../models/view/folder.view"; - import { FOLDER_DECRYPTED_FOLDERS, FOLDER_ENCRYPTED_FOLDERS } from "./folder.state"; describe("encrypted folders", () => { @@ -31,48 +23,32 @@ describe("encrypted folders", () => { }); describe("derived decrypted folders", () => { - const keyService = mock(); - const folderService = mock(); const sut = FOLDER_DECRYPTED_FOLDERS; - let data: FolderData; - - beforeEach(() => { - data = { - id: "id", - name: "encName", - revisionDate: "2024-01-31T12:00:00.000Z", - }; - }); - afterEach(() => { - jest.resetAllMocks(); - }); - - it("should deserialize encrypted folders", async () => { - const inputObj = [data]; + it("should deserialize decrypted folders", async () => { + const inputObj = [ + { + id: "id", + name: "encName", + revisionDate: "2024-01-31T12:00:00.000Z", + }, + ]; - const expectedFolderView = { - id: "id", - name: "encName", - revisionDate: new Date("2024-01-31T12:00:00.000Z"), - }; + const expectedFolderView = [ + { + id: "id", + name: "encName", + revisionDate: new Date("2024-01-31T12:00:00.000Z"), + }, + ]; - const result = sut.deserialize(JSON.parse(JSON.stringify(inputObj))); + const result = sut.deserializer(JSON.parse(JSON.stringify(inputObj))); - expect(result).toEqual([expectedFolderView]); + expect(result).toEqual(expectedFolderView); }); - it("should derive encrypted folders", async () => { - const folderViewMock = new FolderView(new Folder(data)); - keyService.hasUserKey.mockResolvedValue(true); - folderService.decryptFolders.mockResolvedValue([folderViewMock]); - - const encryptedFoldersState = { id: data }; - const derivedStateResult = await sut.derive(encryptedFoldersState, { - folderService, - keyService, - }); - - expect(derivedStateResult).toEqual([folderViewMock]); + it("should handle null input", async () => { + const result = sut.deserializer(null); + expect(result).toEqual([]); }); }); diff --git a/libs/common/src/vault/services/key-state/folder.state.ts b/libs/common/src/vault/services/key-state/folder.state.ts index 7262d72d58e..99ad8e5ae35 100644 --- a/libs/common/src/vault/services/key-state/folder.state.ts +++ b/libs/common/src/vault/services/key-state/folder.state.ts @@ -1,34 +1,23 @@ import { Jsonify } from "type-fest"; -import { KeyService } from "../../../../../key-management/src/abstractions/key.service"; -import { DeriveDefinition, FOLDER_DISK, UserKeyDefinition } from "../../../platform/state"; -import { FolderService } from "../../abstractions/folder/folder.service.abstraction"; +import { FOLDER_DISK, FOLDER_MEMORY, UserKeyDefinition } from "../../../platform/state"; import { FolderData } from "../../models/data/folder.data"; -import { Folder } from "../../models/domain/folder"; import { FolderView } from "../../models/view/folder.view"; export const FOLDER_ENCRYPTED_FOLDERS = UserKeyDefinition.record( FOLDER_DISK, - "folders", + "folder", { deserializer: (obj: Jsonify) => FolderData.fromJSON(obj), clearOn: ["logout"], }, ); -export const FOLDER_DECRYPTED_FOLDERS = DeriveDefinition.from< - Record, - FolderView[], - { folderService: FolderService; keyService: KeyService } ->(FOLDER_ENCRYPTED_FOLDERS, { - deserializer: (obj) => obj.map((f) => FolderView.fromJSON(f)), - derive: async (from, { folderService, keyService }) => { - const folders = Object.values(from || {}).map((f) => new Folder(f)); - - if (await keyService.hasUserKey()) { - return await folderService.decryptFolders(folders); - } else { - return []; - } +export const FOLDER_DECRYPTED_FOLDERS = new UserKeyDefinition( + FOLDER_MEMORY, + "decryptedFolders", + { + deserializer: (obj: Jsonify) => obj?.map((f) => FolderView.fromJSON(f)) ?? [], + clearOn: ["logout", "lock"], }, -}); +); diff --git a/libs/common/src/vault/services/key-state/vault-settings.state.ts b/libs/common/src/vault/services/key-state/vault-settings.state.ts index 21364bbbf8e..35bb776cc96 100644 --- a/libs/common/src/vault/services/key-state/vault-settings.state.ts +++ b/libs/common/src/vault/services/key-state/vault-settings.state.ts @@ -25,3 +25,12 @@ export const SHOW_IDENTITIES_CURRENT_TAB = new UserKeyDefinition( clearOn: [], // do not clear user settings }, ); + +export const CLICK_ITEMS_AUTOFILL_VAULT_VIEW = new UserKeyDefinition( + VAULT_SETTINGS_DISK, + "clickItemsToAutofillOnVaultView", + { + deserializer: (obj) => obj, + clearOn: [], // do not clear user settings + }, +); diff --git a/libs/common/src/vault/services/vault-settings/vault-settings.service.ts b/libs/common/src/vault/services/vault-settings/vault-settings.service.ts index 39b96318217..85ab3914158 100644 --- a/libs/common/src/vault/services/vault-settings/vault-settings.service.ts +++ b/libs/common/src/vault/services/vault-settings/vault-settings.service.ts @@ -6,6 +6,7 @@ import { SHOW_CARDS_CURRENT_TAB, SHOW_IDENTITIES_CURRENT_TAB, USER_ENABLE_PASSKEYS, + CLICK_ITEMS_AUTOFILL_VAULT_VIEW, } from "../key-state/vault-settings.state"; /** @@ -39,6 +40,14 @@ export class VaultSettingsService implements VaultSettingsServiceAbstraction { readonly showIdentitiesCurrentTab$: Observable = this.showIdentitiesCurrentTabState.state$.pipe(map((x) => x ?? true)); + private clickItemsToAutofillVaultViewState: ActiveUserState = + this.stateProvider.getActive(CLICK_ITEMS_AUTOFILL_VAULT_VIEW); + /** + * {@link VaultSettingsServiceAbstraction.clickItemsToAutofillVaultView$$} + */ + readonly clickItemsToAutofillVaultView$: Observable = + this.clickItemsToAutofillVaultViewState.state$.pipe(map((x) => x ?? false)); + constructor(private stateProvider: StateProvider) {} /** @@ -55,6 +64,13 @@ export class VaultSettingsService implements VaultSettingsServiceAbstraction { await this.showIdentitiesCurrentTabState.update(() => value); } + /** + * {@link VaultSettingsServiceAbstraction.setClickItemsToAutofillVaultView} + */ + async setClickItemsToAutofillVaultView(value: boolean): Promise { + await this.clickItemsToAutofillVaultViewState.update(() => value); + } + /** * {@link VaultSettingsServiceAbstraction.setEnablePasskeys} */ diff --git a/libs/components/src/async-actions/async-actions.module.ts b/libs/components/src/async-actions/async-actions.module.ts index 8ff1deb2784..bff4286f890 100644 --- a/libs/components/src/async-actions/async-actions.module.ts +++ b/libs/components/src/async-actions/async-actions.module.ts @@ -1,14 +1,11 @@ import { NgModule } from "@angular/core"; -import { SharedModule } from "../shared"; - import { BitActionDirective } from "./bit-action.directive"; import { BitSubmitDirective } from "./bit-submit.directive"; import { BitFormButtonDirective } from "./form-button.directive"; @NgModule({ - imports: [SharedModule], - declarations: [BitActionDirective, BitFormButtonDirective, BitSubmitDirective], + imports: [BitActionDirective, BitFormButtonDirective, BitSubmitDirective], exports: [BitActionDirective, BitFormButtonDirective, BitSubmitDirective], }) export class AsyncActionsModule {} diff --git a/libs/components/src/async-actions/bit-action.directive.ts b/libs/components/src/async-actions/bit-action.directive.ts index 32ac73f418d..3e793ae2ecd 100644 --- a/libs/components/src/async-actions/bit-action.directive.ts +++ b/libs/components/src/async-actions/bit-action.directive.ts @@ -15,6 +15,7 @@ import { FunctionReturningAwaitable, functionToObservable } from "../utils/funct */ @Directive({ selector: "[bitAction]", + standalone: true, }) export class BitActionDirective implements OnDestroy { private destroy$ = new Subject(); diff --git a/libs/components/src/async-actions/bit-submit.directive.ts b/libs/components/src/async-actions/bit-submit.directive.ts index 838d78af8b2..a38e76aaca6 100644 --- a/libs/components/src/async-actions/bit-submit.directive.ts +++ b/libs/components/src/async-actions/bit-submit.directive.ts @@ -14,6 +14,7 @@ import { FunctionReturningAwaitable, functionToObservable } from "../utils/funct */ @Directive({ selector: "[formGroup][bitSubmit]", + standalone: true, }) export class BitSubmitDirective implements OnInit, OnDestroy { private destroy$ = new Subject(); diff --git a/libs/components/src/async-actions/form-button.directive.ts b/libs/components/src/async-actions/form-button.directive.ts index e4685188693..7c92865b984 100644 --- a/libs/components/src/async-actions/form-button.directive.ts +++ b/libs/components/src/async-actions/form-button.directive.ts @@ -25,6 +25,7 @@ import { BitSubmitDirective } from "./bit-submit.directive"; */ @Directive({ selector: "button[bitFormButton]", + standalone: true, }) export class BitFormButtonDirective implements OnDestroy { private destroy$ = new Subject(); diff --git a/libs/components/src/async-actions/in-forms.stories.ts b/libs/components/src/async-actions/in-forms.stories.ts index ec6005dd607..fb94e43b196 100644 --- a/libs/components/src/async-actions/in-forms.stories.ts +++ b/libs/components/src/async-actions/in-forms.stories.ts @@ -109,20 +109,17 @@ export default { title: "Component Library/Async Actions/In Forms", decorators: [ moduleMetadata({ - declarations: [ + declarations: [PromiseExampleComponent, ObservableExampleComponent], + imports: [ BitSubmitDirective, BitFormButtonDirective, - PromiseExampleComponent, - ObservableExampleComponent, - BitActionDirective, - ], - imports: [ FormsModule, ReactiveFormsModule, FormFieldModule, InputModule, ButtonModule, IconButtonModule, + BitActionDirective, ], providers: [ { diff --git a/libs/components/src/async-actions/standalone.stories.ts b/libs/components/src/async-actions/standalone.stories.ts index 5e15135dc5d..f658dfb0f01 100644 --- a/libs/components/src/async-actions/standalone.stories.ts +++ b/libs/components/src/async-actions/standalone.stories.ts @@ -56,12 +56,11 @@ export default { decorators: [ moduleMetadata({ declarations: [ - BitActionDirective, PromiseExampleComponent, ObservableExampleComponent, RejectedPromiseExampleComponent, ], - imports: [ButtonModule, IconButtonModule], + imports: [ButtonModule, IconButtonModule, BitActionDirective], providers: [ { provide: ValidationService, diff --git a/libs/components/src/avatar/avatar.component.ts b/libs/components/src/avatar/avatar.component.ts index e1758d795d6..76ff702e88b 100644 --- a/libs/components/src/avatar/avatar.component.ts +++ b/libs/components/src/avatar/avatar.component.ts @@ -1,5 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { NgIf, NgClass } from "@angular/common"; import { Component, Input, OnChanges } from "@angular/core"; import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser"; @@ -18,6 +19,8 @@ const SizeClasses: Record = { @Component({ selector: "bit-avatar", template: ``, + standalone: true, + imports: [NgIf, NgClass], }) export class AvatarComponent implements OnChanges { @Input() border = false; diff --git a/libs/components/src/avatar/avatar.module.ts b/libs/components/src/avatar/avatar.module.ts index ea78ff3a1d2..4ef0978cbec 100644 --- a/libs/components/src/avatar/avatar.module.ts +++ b/libs/components/src/avatar/avatar.module.ts @@ -1,11 +1,9 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { AvatarComponent } from "./avatar.component"; @NgModule({ - imports: [CommonModule], + imports: [AvatarComponent], exports: [AvatarComponent], - declarations: [AvatarComponent], }) export class AvatarModule {} diff --git a/libs/components/src/badge-list/badge-list.component.ts b/libs/components/src/badge-list/badge-list.component.ts index 9270e5e1238..ac8cb3281ab 100644 --- a/libs/components/src/badge-list/badge-list.component.ts +++ b/libs/components/src/badge-list/badge-list.component.ts @@ -1,12 +1,16 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { CommonModule } from "@angular/common"; import { Component, Input, OnChanges } from "@angular/core"; -import { BadgeVariant } from "../badge"; +import { BadgeModule, BadgeVariant } from "../badge"; +import { I18nPipe } from "../shared/i18n.pipe"; @Component({ selector: "bit-badge-list", templateUrl: "badge-list.component.html", + standalone: true, + imports: [CommonModule, BadgeModule, I18nPipe], }) export class BadgeListComponent implements OnChanges { private _maxItems: number; diff --git a/libs/components/src/badge-list/badge-list.module.ts b/libs/components/src/badge-list/badge-list.module.ts index d2a4ce211b1..9359fe2c5c5 100644 --- a/libs/components/src/badge-list/badge-list.module.ts +++ b/libs/components/src/badge-list/badge-list.module.ts @@ -1,13 +1,9 @@ import { NgModule } from "@angular/core"; -import { BadgeModule } from "../badge"; -import { SharedModule } from "../shared"; - import { BadgeListComponent } from "./badge-list.component"; @NgModule({ - imports: [SharedModule, BadgeModule], + imports: [BadgeListComponent], exports: [BadgeListComponent], - declarations: [BadgeListComponent], }) export class BadgeListModule {} diff --git a/libs/components/src/badge-list/badge-list.stories.ts b/libs/components/src/badge-list/badge-list.stories.ts index 24024cb6bbd..ede005f6fd6 100644 --- a/libs/components/src/badge-list/badge-list.stories.ts +++ b/libs/components/src/badge-list/badge-list.stories.ts @@ -13,8 +13,7 @@ export default { component: BadgeListComponent, decorators: [ moduleMetadata({ - imports: [SharedModule, BadgeModule], - declarations: [BadgeListComponent], + imports: [SharedModule, BadgeModule, BadgeListComponent], providers: [ { provide: I18nService, diff --git a/libs/components/src/badge/badge.component.html b/libs/components/src/badge/badge.component.html new file mode 100644 index 00000000000..6f586ec3523 --- /dev/null +++ b/libs/components/src/badge/badge.component.html @@ -0,0 +1,3 @@ + + + diff --git a/libs/components/src/badge/badge.directive.ts b/libs/components/src/badge/badge.component.ts similarity index 86% rename from libs/components/src/badge/badge.directive.ts rename to libs/components/src/badge/badge.component.ts index f39f8f87639..0f8a64ee998 100644 --- a/libs/components/src/badge/badge.directive.ts +++ b/libs/components/src/badge/badge.component.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Directive, ElementRef, HostBinding, Input } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { Component, ElementRef, HostBinding, Input } from "@angular/core"; import { FocusableElement } from "../shared/focusable-element"; @@ -28,11 +29,14 @@ const hoverStyles: Record = { info: ["hover:tw-bg-info-600", "hover:tw-border-info-600", "hover:!tw-text-black"], }; -@Directive({ +@Component({ selector: "span[bitBadge], a[bitBadge], button[bitBadge]", - providers: [{ provide: FocusableElement, useExisting: BadgeDirective }], + providers: [{ provide: FocusableElement, useExisting: BadgeComponent }], + imports: [CommonModule], + templateUrl: "badge.component.html", + standalone: true, }) -export class BadgeDirective implements FocusableElement { +export class BadgeComponent implements FocusableElement { @HostBinding("class") get classList() { return [ "tw-inline-block", @@ -61,8 +65,8 @@ export class BadgeDirective implements FocusableElement { "disabled:tw-cursor-not-allowed", ] .concat(styles[this.variant]) - .concat(this.hasHoverEffects ? hoverStyles[this.variant] : []) - .concat(this.truncate ? ["tw-truncate", this.maxWidthClass] : []); + .concat(this.hasHoverEffects ? [...hoverStyles[this.variant], "tw-min-w-10"] : []) + .concat(this.truncate ? this.maxWidthClass : []); } @HostBinding("attr.title") get titleAttr() { if (this.title !== undefined) { diff --git a/libs/components/src/badge/badge.module.ts b/libs/components/src/badge/badge.module.ts index e1b8292363f..d9a6e712820 100644 --- a/libs/components/src/badge/badge.module.ts +++ b/libs/components/src/badge/badge.module.ts @@ -1,11 +1,9 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { BadgeDirective } from "./badge.directive"; +import { BadgeComponent } from "./badge.component"; @NgModule({ - imports: [CommonModule], - exports: [BadgeDirective], - declarations: [BadgeDirective], + imports: [BadgeComponent], + exports: [BadgeComponent], }) export class BadgeModule {} diff --git a/libs/components/src/badge/badge.stories.ts b/libs/components/src/badge/badge.stories.ts index 6c57bc0cbfb..5d697f8ad8f 100644 --- a/libs/components/src/badge/badge.stories.ts +++ b/libs/components/src/badge/badge.stories.ts @@ -1,15 +1,14 @@ import { CommonModule } from "@angular/common"; import { Meta, moduleMetadata, StoryObj } from "@storybook/angular"; -import { BadgeDirective } from "./badge.directive"; +import { BadgeComponent } from "./badge.component"; export default { title: "Component Library/Badge", - component: BadgeDirective, + component: BadgeComponent, decorators: [ moduleMetadata({ - imports: [CommonModule], - declarations: [BadgeDirective], + imports: [CommonModule, BadgeComponent], }), ], args: { @@ -22,9 +21,9 @@ export default { url: "https://www.figma.com/file/Zt3YSeb6E6lebAffrNLa0h/Tailwind-Component-Library?node-id=1881%3A16956", }, }, -} as Meta; +} as Meta; -type Story = StoryObj; +type Story = StoryObj; export const Variants: Story = { render: (args) => ({ diff --git a/libs/components/src/badge/index.ts b/libs/components/src/badge/index.ts index a8f5babda91..ae19f4df288 100644 --- a/libs/components/src/badge/index.ts +++ b/libs/components/src/badge/index.ts @@ -1,2 +1,2 @@ -export { BadgeDirective, BadgeVariant } from "./badge.directive"; +export { BadgeComponent, BadgeVariant } from "./badge.component"; export * from "./badge.module"; diff --git a/libs/components/src/banner/banner.component.spec.ts b/libs/components/src/banner/banner.component.spec.ts index 29f10016a15..2bbc7965642 100644 --- a/libs/components/src/banner/banner.component.spec.ts +++ b/libs/components/src/banner/banner.component.spec.ts @@ -2,7 +2,6 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { SharedModule } from "../shared/shared.module"; import { I18nMockService } from "../utils/i18n-mock.service"; import { BannerComponent } from "./banner.component"; @@ -13,8 +12,7 @@ describe("BannerComponent", () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [SharedModule], - declarations: [BannerComponent], + imports: [BannerComponent], providers: [ { provide: I18nService, diff --git a/libs/components/src/banner/banner.component.ts b/libs/components/src/banner/banner.component.ts index 7d59ceb0ee9..d3f64329978 100644 --- a/libs/components/src/banner/banner.component.ts +++ b/libs/components/src/banner/banner.component.ts @@ -1,7 +1,11 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { CommonModule } from "@angular/common"; import { Component, Input, OnInit, Output, EventEmitter } from "@angular/core"; +import { IconButtonModule } from "../icon-button"; +import { I18nPipe } from "../shared/i18n.pipe"; + type BannerTypes = "premium" | "info" | "warning" | "danger"; const defaultIcon: Record = { @@ -14,6 +18,8 @@ const defaultIcon: Record = { @Component({ selector: "bit-banner", templateUrl: "./banner.component.html", + standalone: true, + imports: [CommonModule, IconButtonModule, I18nPipe], }) export class BannerComponent implements OnInit { @Input("bannerType") bannerType: BannerTypes = "info"; diff --git a/libs/components/src/banner/banner.module.ts b/libs/components/src/banner/banner.module.ts index 2c819fbc5b4..3301218ed1a 100644 --- a/libs/components/src/banner/banner.module.ts +++ b/libs/components/src/banner/banner.module.ts @@ -1,14 +1,9 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { IconButtonModule } from "../icon-button"; -import { SharedModule } from "../shared/shared.module"; - import { BannerComponent } from "./banner.component"; @NgModule({ - imports: [CommonModule, SharedModule, IconButtonModule], + imports: [BannerComponent], exports: [BannerComponent], - declarations: [BannerComponent], }) export class BannerModule {} diff --git a/libs/components/src/breadcrumbs/breadcrumb.component.ts b/libs/components/src/breadcrumbs/breadcrumb.component.ts index d6128540442..ce18bde171f 100644 --- a/libs/components/src/breadcrumbs/breadcrumb.component.ts +++ b/libs/components/src/breadcrumbs/breadcrumb.component.ts @@ -1,11 +1,14 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { NgIf } from "@angular/common"; import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from "@angular/core"; import { QueryParamsHandling } from "@angular/router"; @Component({ selector: "bit-breadcrumb", templateUrl: "./breadcrumb.component.html", + standalone: true, + imports: [NgIf], }) export class BreadcrumbComponent { @Input() diff --git a/libs/components/src/breadcrumbs/breadcrumbs.component.ts b/libs/components/src/breadcrumbs/breadcrumbs.component.ts index 64ca8146c80..6e8fbf5c25a 100644 --- a/libs/components/src/breadcrumbs/breadcrumbs.component.ts +++ b/libs/components/src/breadcrumbs/breadcrumbs.component.ts @@ -1,10 +1,18 @@ +import { CommonModule } from "@angular/common"; import { Component, ContentChildren, Input, QueryList } from "@angular/core"; +import { RouterModule } from "@angular/router"; + +import { IconButtonModule } from "../icon-button"; +import { LinkModule } from "../link"; +import { MenuModule } from "../menu"; import { BreadcrumbComponent } from "./breadcrumb.component"; @Component({ selector: "bit-breadcrumbs", templateUrl: "./breadcrumbs.component.html", + standalone: true, + imports: [CommonModule, LinkModule, RouterModule, IconButtonModule, MenuModule], }) export class BreadcrumbsComponent { @Input() diff --git a/libs/components/src/breadcrumbs/breadcrumbs.module.ts b/libs/components/src/breadcrumbs/breadcrumbs.module.ts index 0812b552f9a..89b57fd19b5 100644 --- a/libs/components/src/breadcrumbs/breadcrumbs.module.ts +++ b/libs/components/src/breadcrumbs/breadcrumbs.module.ts @@ -1,17 +1,10 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { RouterModule } from "@angular/router"; - -import { IconButtonModule } from "../icon-button"; -import { LinkModule } from "../link"; -import { MenuModule } from "../menu"; import { BreadcrumbComponent } from "./breadcrumb.component"; import { BreadcrumbsComponent } from "./breadcrumbs.component"; @NgModule({ - imports: [CommonModule, LinkModule, IconButtonModule, MenuModule, RouterModule], - declarations: [BreadcrumbsComponent, BreadcrumbComponent], + imports: [BreadcrumbsComponent, BreadcrumbComponent], exports: [BreadcrumbsComponent, BreadcrumbComponent], }) export class BreadcrumbsModule {} diff --git a/libs/components/src/breadcrumbs/breadcrumbs.stories.ts b/libs/components/src/breadcrumbs/breadcrumbs.stories.ts index 300369f2454..9c8ccbccd3f 100644 --- a/libs/components/src/breadcrumbs/breadcrumbs.stories.ts +++ b/libs/components/src/breadcrumbs/breadcrumbs.stories.ts @@ -25,8 +25,7 @@ export default { component: BreadcrumbsComponent, decorators: [ moduleMetadata({ - declarations: [BreadcrumbComponent], - imports: [LinkModule, MenuModule, IconButtonModule, RouterModule], + imports: [LinkModule, MenuModule, IconButtonModule, RouterModule, BreadcrumbComponent], }), applicationConfig({ providers: [ diff --git a/libs/components/src/button/button.component.spec.ts b/libs/components/src/button/button.component.spec.ts index f3c3aa3175c..d63f611a5f8 100644 --- a/libs/components/src/button/button.component.spec.ts +++ b/libs/components/src/button/button.component.spec.ts @@ -1,3 +1,5 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore import { Component, DebugElement } from "@angular/core"; import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; diff --git a/libs/components/src/button/button.component.ts b/libs/components/src/button/button.component.ts index 67b57d576ab..96311f91529 100644 --- a/libs/components/src/button/button.component.ts +++ b/libs/components/src/button/button.component.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { coerceBooleanProperty } from "@angular/cdk/coercion"; +import { NgClass } from "@angular/common"; import { Input, HostBinding, Component } from "@angular/core"; import { ButtonLikeAbstraction, ButtonType } from "../shared/button-like.abstraction"; @@ -46,6 +47,8 @@ const buttonStyles: Record = { selector: "button[bitButton], a[bitButton]", templateUrl: "button.component.html", providers: [{ provide: ButtonLikeAbstraction, useExisting: ButtonComponent }], + standalone: true, + imports: [NgClass], }) export class ButtonComponent implements ButtonLikeAbstraction { @HostBinding("class") get classList() { diff --git a/libs/components/src/button/button.module.ts b/libs/components/src/button/button.module.ts index 448e7c9dcf6..f1a86eff3ab 100644 --- a/libs/components/src/button/button.module.ts +++ b/libs/components/src/button/button.module.ts @@ -1,11 +1,9 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { ButtonComponent } from "./button.component"; @NgModule({ - imports: [CommonModule], + imports: [ButtonComponent], exports: [ButtonComponent], - declarations: [ButtonComponent], }) export class ButtonModule {} diff --git a/libs/components/src/checkbox/checkbox.component.ts b/libs/components/src/checkbox/checkbox.component.ts index 1ca27e84b82..0ce6f1889b5 100644 --- a/libs/components/src/checkbox/checkbox.component.ts +++ b/libs/components/src/checkbox/checkbox.component.ts @@ -9,6 +9,7 @@ import { BitFormControlAbstraction } from "../form-control"; selector: "input[type=checkbox][bitCheckbox]", template: "", providers: [{ provide: BitFormControlAbstraction, useExisting: CheckboxComponent }], + standalone: true, }) export class CheckboxComponent implements BitFormControlAbstraction { @HostBinding("class") diff --git a/libs/components/src/checkbox/checkbox.module.ts b/libs/components/src/checkbox/checkbox.module.ts index d03b9cf5050..3abfb4b1bfd 100644 --- a/libs/components/src/checkbox/checkbox.module.ts +++ b/libs/components/src/checkbox/checkbox.module.ts @@ -1,14 +1,9 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { FormControlModule } from "../form-control"; -import { SharedModule } from "../shared"; - import { CheckboxComponent } from "./checkbox.component"; @NgModule({ - imports: [SharedModule, CommonModule, FormControlModule], - declarations: [CheckboxComponent], + imports: [CheckboxComponent], exports: [CheckboxComponent], }) export class CheckboxModule {} diff --git a/libs/components/src/color-password/color-password.component.ts b/libs/components/src/color-password/color-password.component.ts index 35732760ac7..cbf746e9d73 100644 --- a/libs/components/src/color-password/color-password.component.ts +++ b/libs/components/src/color-password/color-password.component.ts @@ -1,5 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { NgFor, NgIf } from "@angular/common"; import { Component, HostBinding, Input } from "@angular/core"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -23,6 +24,8 @@ enum CharacterType { }} `, preserveWhitespaces: false, + standalone: true, + imports: [NgFor, NgIf], }) export class ColorPasswordComponent { @Input() password: string = null; diff --git a/libs/components/src/color-password/color-password.module.ts b/libs/components/src/color-password/color-password.module.ts index 692c206bb4c..3ebc1c80e12 100644 --- a/libs/components/src/color-password/color-password.module.ts +++ b/libs/components/src/color-password/color-password.module.ts @@ -1,11 +1,9 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { ColorPasswordComponent } from "./color-password.component"; @NgModule({ - imports: [CommonModule], + imports: [ColorPasswordComponent], exports: [ColorPasswordComponent], - declarations: [ColorPasswordComponent], }) export class ColorPasswordModule {} diff --git a/libs/components/src/dialog/dialog.module.ts b/libs/components/src/dialog/dialog.module.ts index bc37f749c05..f31fdd52060 100644 --- a/libs/components/src/dialog/dialog.module.ts +++ b/libs/components/src/dialog/dialog.module.ts @@ -1,44 +1,25 @@ import { DialogModule as CdkDialogModule } from "@angular/cdk/dialog"; import { NgModule } from "@angular/core"; -import { ReactiveFormsModule } from "@angular/forms"; - -import { AsyncActionsModule } from "../async-actions"; -import { ButtonModule } from "../button"; -import { IconButtonModule } from "../icon-button"; -import { SharedModule } from "../shared"; -import { TypographyModule } from "../typography"; import { DialogComponent } from "./dialog/dialog.component"; import { DialogService } from "./dialog.service"; import { DialogCloseDirective } from "./directives/dialog-close.directive"; -import { DialogTitleContainerDirective } from "./directives/dialog-title-container.directive"; -import { SimpleConfigurableDialogComponent } from "./simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component"; import { IconDirective, SimpleDialogComponent } from "./simple-dialog/simple-dialog.component"; @NgModule({ imports: [ - SharedModule, - AsyncActionsModule, - ButtonModule, CdkDialogModule, - IconButtonModule, - ReactiveFormsModule, - TypographyModule, - ], - declarations: [ DialogCloseDirective, - DialogTitleContainerDirective, DialogComponent, SimpleDialogComponent, - SimpleConfigurableDialogComponent, IconDirective, ], exports: [ CdkDialogModule, - DialogComponent, - SimpleDialogComponent, DialogCloseDirective, + DialogComponent, IconDirective, + SimpleDialogComponent, ], providers: [DialogService], }) diff --git a/libs/components/src/dialog/dialog.service.stories.ts b/libs/components/src/dialog/dialog.service.stories.ts index e28f0ac4b19..5e938412804 100644 --- a/libs/components/src/dialog/dialog.service.stories.ts +++ b/libs/components/src/dialog/dialog.service.stories.ts @@ -64,13 +64,16 @@ export default { component: StoryDialogComponent, decorators: [ moduleMetadata({ - declarations: [ + declarations: [StoryDialogContentComponent], + imports: [ + SharedModule, + ButtonModule, + DialogModule, + IconButtonModule, DialogCloseDirective, DialogComponent, DialogTitleContainerDirective, - StoryDialogContentComponent, ], - imports: [SharedModule, ButtonModule, DialogModule, IconButtonModule], providers: [ DialogService, { diff --git a/libs/components/src/dialog/dialog/dialog.component.ts b/libs/components/src/dialog/dialog/dialog.component.ts index 2f901d10d2d..ed47201805a 100644 --- a/libs/components/src/dialog/dialog/dialog.component.ts +++ b/libs/components/src/dialog/dialog/dialog.component.ts @@ -1,14 +1,29 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { coerceBooleanProperty } from "@angular/cdk/coercion"; +import { CommonModule } from "@angular/common"; import { Component, HostBinding, Input } from "@angular/core"; +import { BitIconButtonComponent } from "../../icon-button/icon-button.component"; +import { I18nPipe } from "../../shared/i18n.pipe"; +import { TypographyDirective } from "../../typography/typography.directive"; import { fadeIn } from "../animations"; +import { DialogCloseDirective } from "../directives/dialog-close.directive"; +import { DialogTitleContainerDirective } from "../directives/dialog-title-container.directive"; @Component({ selector: "bit-dialog", templateUrl: "./dialog.component.html", animations: [fadeIn], + standalone: true, + imports: [ + CommonModule, + DialogTitleContainerDirective, + TypographyDirective, + BitIconButtonComponent, + DialogCloseDirective, + I18nPipe, + ], }) export class DialogComponent { /** Background color */ diff --git a/libs/components/src/dialog/directives/dialog-close.directive.ts b/libs/components/src/dialog/directives/dialog-close.directive.ts index 5e44ced7c21..5e5fda3e014 100644 --- a/libs/components/src/dialog/directives/dialog-close.directive.ts +++ b/libs/components/src/dialog/directives/dialog-close.directive.ts @@ -3,6 +3,7 @@ import { Directive, HostBinding, HostListener, Input, Optional } from "@angular/ @Directive({ selector: "[bitDialogClose]", + standalone: true, }) export class DialogCloseDirective { @Input("bitDialogClose") dialogResult: any; diff --git a/libs/components/src/dialog/directives/dialog-title-container.directive.ts b/libs/components/src/dialog/directives/dialog-title-container.directive.ts index e17487f2780..cf46396967b 100644 --- a/libs/components/src/dialog/directives/dialog-title-container.directive.ts +++ b/libs/components/src/dialog/directives/dialog-title-container.directive.ts @@ -6,6 +6,7 @@ let nextId = 0; @Directive({ selector: "[bitDialogTitleContainer]", + standalone: true, }) export class DialogTitleContainerDirective implements OnInit { @HostBinding("id") id = `bit-dialog-title-${nextId++}`; diff --git a/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts b/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts index 29d52e9cf07..60b2e1c3a3f 100644 --- a/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts +++ b/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts @@ -1,12 +1,17 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; +import { NgIf } from "@angular/common"; import { Component, Inject } from "@angular/core"; -import { FormGroup } from "@angular/forms"; +import { FormGroup, ReactiveFormsModule } from "@angular/forms"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SimpleDialogOptions, SimpleDialogType, Translation } from "../.."; +import { BitSubmitDirective } from "../../../async-actions/bit-submit.directive"; +import { BitFormButtonDirective } from "../../../async-actions/form-button.directive"; +import { ButtonComponent } from "../../../button/button.component"; +import { SimpleDialogComponent, IconDirective } from "../simple-dialog.component"; const DEFAULT_ICON: Record = { primary: "bwi-business", @@ -26,6 +31,16 @@ const DEFAULT_COLOR: Record = { @Component({ templateUrl: "./simple-configurable-dialog.component.html", + standalone: true, + imports: [ + ReactiveFormsModule, + BitSubmitDirective, + SimpleDialogComponent, + IconDirective, + ButtonComponent, + BitFormButtonDirective, + NgIf, + ], }) export class SimpleConfigurableDialogComponent { get iconClasses() { diff --git a/libs/components/src/dialog/simple-dialog/simple-dialog.component.ts b/libs/components/src/dialog/simple-dialog/simple-dialog.component.ts index 912b0299f66..c02a13bd150 100644 --- a/libs/components/src/dialog/simple-dialog/simple-dialog.component.ts +++ b/libs/components/src/dialog/simple-dialog/simple-dialog.component.ts @@ -1,14 +1,22 @@ +import { NgIf } from "@angular/common"; import { Component, ContentChild, Directive } from "@angular/core"; +import { TypographyDirective } from "../../typography/typography.directive"; import { fadeIn } from "../animations"; +import { DialogTitleContainerDirective } from "../directives/dialog-title-container.directive"; -@Directive({ selector: "[bitDialogIcon]" }) +@Directive({ + selector: "[bitDialogIcon]", + standalone: true, +}) export class IconDirective {} @Component({ selector: "bit-simple-dialog", templateUrl: "./simple-dialog.component.html", animations: [fadeIn], + standalone: true, + imports: [NgIf, DialogTitleContainerDirective, TypographyDirective], }) export class SimpleDialogComponent { @ContentChild(IconDirective) icon!: IconDirective; diff --git a/libs/components/src/form-control/form-control.component.ts b/libs/components/src/form-control/form-control.component.ts index 6c24e7a53e6..9b87c44157a 100644 --- a/libs/components/src/form-control/form-control.component.ts +++ b/libs/components/src/form-control/form-control.component.ts @@ -1,15 +1,21 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { coerceBooleanProperty } from "@angular/cdk/coercion"; +import { NgClass, NgIf } from "@angular/common"; import { Component, ContentChild, HostBinding, Input } from "@angular/core"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { I18nPipe } from "../shared/i18n.pipe"; +import { TypographyDirective } from "../typography/typography.directive"; + import { BitFormControlAbstraction } from "./form-control.abstraction"; @Component({ selector: "bit-form-control", templateUrl: "form-control.component.html", + standalone: true, + imports: [NgClass, TypographyDirective, NgIf, I18nPipe], }) export class FormControlComponent { @Input() label: string; diff --git a/libs/components/src/form-control/form-control.module.ts b/libs/components/src/form-control/form-control.module.ts index f6969a97e9c..df168d8e98f 100644 --- a/libs/components/src/form-control/form-control.module.ts +++ b/libs/components/src/form-control/form-control.module.ts @@ -1,15 +1,11 @@ import { NgModule } from "@angular/core"; -import { SharedModule } from "../shared"; -import { TypographyModule } from "../typography"; - import { FormControlComponent } from "./form-control.component"; import { BitHintComponent } from "./hint.component"; import { BitLabel } from "./label.component"; @NgModule({ - imports: [SharedModule, BitLabel, TypographyModule], - declarations: [FormControlComponent, BitHintComponent], + imports: [BitLabel, FormControlComponent, BitHintComponent], exports: [FormControlComponent, BitLabel, BitHintComponent], }) export class FormControlModule {} diff --git a/libs/components/src/form-control/hint.component.ts b/libs/components/src/form-control/hint.component.ts index c1f21bf2545..4fee0d4560f 100644 --- a/libs/components/src/form-control/hint.component.ts +++ b/libs/components/src/form-control/hint.component.ts @@ -8,6 +8,7 @@ let nextId = 0; host: { class: "tw-text-muted tw-font-normal tw-inline-block tw-mt-1 tw-text-xs", }, + standalone: true, }) export class BitHintComponent { @HostBinding() id = `bit-hint-${nextId++}`; diff --git a/libs/components/src/form-field/bit-validators/forbidden-characters.validator.spec.ts b/libs/components/src/form-field/bit-validators/forbidden-characters.validator.spec.ts index 332294b26ec..ecd9aa550a0 100644 --- a/libs/components/src/form-field/bit-validators/forbidden-characters.validator.spec.ts +++ b/libs/components/src/form-field/bit-validators/forbidden-characters.validator.spec.ts @@ -1,3 +1,5 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore import { FormControl } from "@angular/forms"; import { forbiddenCharacters } from "./forbidden-characters.validator"; diff --git a/libs/components/src/form-field/bit-validators/trim.validator.spec.ts b/libs/components/src/form-field/bit-validators/trim.validator.spec.ts index 471f5396786..38dd36a7706 100644 --- a/libs/components/src/form-field/bit-validators/trim.validator.spec.ts +++ b/libs/components/src/form-field/bit-validators/trim.validator.spec.ts @@ -1,3 +1,5 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore import { FormControl } from "@angular/forms"; import { trimValidator as validate } from "./trim.validator"; diff --git a/libs/components/src/form-field/error-summary.component.ts b/libs/components/src/form-field/error-summary.component.ts index f374740b20e..beed32a88ac 100644 --- a/libs/components/src/form-field/error-summary.component.ts +++ b/libs/components/src/form-field/error-summary.component.ts @@ -1,8 +1,11 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { NgIf } from "@angular/common"; import { Component, Input } from "@angular/core"; import { AbstractControl, UntypedFormGroup } from "@angular/forms"; +import { I18nPipe } from "../shared/i18n.pipe"; + @Component({ selector: "bit-error-summary", template: ` @@ -12,6 +15,8 @@ import { AbstractControl, UntypedFormGroup } from "@angular/forms"; class: "tw-block tw-text-danger tw-mt-2", "aria-live": "assertive", }, + standalone: true, + imports: [NgIf, I18nPipe], }) export class BitErrorSummary { @Input() diff --git a/libs/components/src/form-field/error.component.ts b/libs/components/src/form-field/error.component.ts index a0f7906b366..27adbf7d313 100644 --- a/libs/components/src/form-field/error.component.ts +++ b/libs/components/src/form-field/error.component.ts @@ -14,6 +14,7 @@ let nextId = 0; class: "tw-block tw-mt-1 tw-text-danger tw-text-xs", "aria-live": "assertive", }, + standalone: true, }) export class BitErrorComponent { @HostBinding() id = `bit-error-${nextId++}`; diff --git a/libs/components/src/form-field/form-field.component.ts b/libs/components/src/form-field/form-field.component.ts index 6f425e41496..9f41c6cf6ac 100644 --- a/libs/components/src/form-field/form-field.component.ts +++ b/libs/components/src/form-field/form-field.component.ts @@ -1,5 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { CommonModule } from "@angular/common"; import { AfterContentChecked, booleanAttribute, @@ -16,6 +17,7 @@ import { import { BitHintComponent } from "../form-control/hint.component"; import { BitLabel } from "../form-control/label.component"; import { inputBorderClasses } from "../input/input.directive"; +import { I18nPipe } from "../shared/i18n.pipe"; import { BitErrorComponent } from "./error.component"; import { BitFormFieldControl } from "./form-field-control"; @@ -23,6 +25,8 @@ import { BitFormFieldControl } from "./form-field-control"; @Component({ selector: "bit-form-field", templateUrl: "./form-field.component.html", + standalone: true, + imports: [CommonModule, BitErrorComponent, I18nPipe], }) export class BitFormFieldComponent implements AfterContentChecked { @ContentChild(BitFormFieldControl) input: BitFormFieldControl; diff --git a/libs/components/src/form-field/form-field.module.ts b/libs/components/src/form-field/form-field.module.ts index 989375167d4..88d7ffcc78b 100644 --- a/libs/components/src/form-field/form-field.module.ts +++ b/libs/components/src/form-field/form-field.module.ts @@ -1,11 +1,8 @@ import { NgModule } from "@angular/core"; import { FormControlModule } from "../form-control"; -import { BitInputDirective } from "../input/input.directive"; import { InputModule } from "../input/input.module"; -import { MultiSelectComponent } from "../multi-select/multi-select.component"; import { MultiSelectModule } from "../multi-select/multi-select.module"; -import { SharedModule } from "../shared"; import { BitErrorSummary } from "./error-summary.component"; import { BitErrorComponent } from "./error.component"; @@ -15,8 +12,11 @@ import { BitPrefixDirective } from "./prefix.directive"; import { BitSuffixDirective } from "./suffix.directive"; @NgModule({ - imports: [SharedModule, FormControlModule, InputModule, MultiSelectModule], - declarations: [ + imports: [ + FormControlModule, + InputModule, + MultiSelectModule, + BitErrorComponent, BitErrorSummary, BitFormFieldComponent, @@ -25,15 +25,16 @@ import { BitSuffixDirective } from "./suffix.directive"; BitSuffixDirective, ], exports: [ + FormControlModule, + InputModule, + MultiSelectModule, + BitErrorComponent, BitErrorSummary, BitFormFieldComponent, - BitInputDirective, BitPasswordInputToggleDirective, BitPrefixDirective, BitSuffixDirective, - MultiSelectComponent, - FormControlModule, ], }) export class FormFieldModule {} diff --git a/libs/components/src/form-field/password-input-toggle.directive.ts b/libs/components/src/form-field/password-input-toggle.directive.ts index a696a88c468..933722db5b4 100644 --- a/libs/components/src/form-field/password-input-toggle.directive.ts +++ b/libs/components/src/form-field/password-input-toggle.directive.ts @@ -18,6 +18,7 @@ import { BitFormFieldComponent } from "./form-field.component"; @Directive({ selector: "[bitPasswordInputToggle]", + standalone: true, }) export class BitPasswordInputToggleDirective implements AfterContentInit, OnChanges { /** diff --git a/libs/components/src/form-field/prefix.directive.ts b/libs/components/src/form-field/prefix.directive.ts index 34fcbf85233..b44e90cbaad 100644 --- a/libs/components/src/form-field/prefix.directive.ts +++ b/libs/components/src/form-field/prefix.directive.ts @@ -4,6 +4,7 @@ import { BitIconButtonComponent } from "../icon-button/icon-button.component"; @Directive({ selector: "[bitPrefix]", + standalone: true, }) export class BitPrefixDirective implements OnInit { @HostBinding("class") @Input() get classList() { diff --git a/libs/components/src/form-field/suffix.directive.ts b/libs/components/src/form-field/suffix.directive.ts index 28736ce78a9..baf1afce763 100644 --- a/libs/components/src/form-field/suffix.directive.ts +++ b/libs/components/src/form-field/suffix.directive.ts @@ -4,6 +4,7 @@ import { BitIconButtonComponent } from "../icon-button/icon-button.component"; @Directive({ selector: "[bitSuffix]", + standalone: true, }) export class BitSuffixDirective implements OnInit { @HostBinding("class") @Input() get classList() { diff --git a/libs/components/src/icon-button/icon-button.component.ts b/libs/components/src/icon-button/icon-button.component.ts index 97016f9fd0c..ac7dff0408b 100644 --- a/libs/components/src/icon-button/icon-button.component.ts +++ b/libs/components/src/icon-button/icon-button.component.ts @@ -1,5 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { NgClass } from "@angular/common"; import { Component, ElementRef, HostBinding, Input } from "@angular/core"; import { ButtonLikeAbstraction, ButtonType } from "../shared/button-like.abstraction"; @@ -134,6 +135,8 @@ const sizes: Record = { { provide: ButtonLikeAbstraction, useExisting: BitIconButtonComponent }, { provide: FocusableElement, useExisting: BitIconButtonComponent }, ], + standalone: true, + imports: [NgClass], }) export class BitIconButtonComponent implements ButtonLikeAbstraction, FocusableElement { @Input("bitIconButton") icon: string; diff --git a/libs/components/src/icon-button/icon-button.module.ts b/libs/components/src/icon-button/icon-button.module.ts index fb4e8589717..26f48cdb177 100644 --- a/libs/components/src/icon-button/icon-button.module.ts +++ b/libs/components/src/icon-button/icon-button.module.ts @@ -1,11 +1,9 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { BitIconButtonComponent } from "./icon-button.component"; @NgModule({ - imports: [CommonModule], - declarations: [BitIconButtonComponent], + imports: [BitIconButtonComponent], exports: [BitIconButtonComponent], }) export class IconButtonModule {} diff --git a/libs/components/src/icon/icon.component.ts b/libs/components/src/icon/icon.component.ts index 55615d4dae3..2382d197bec 100644 --- a/libs/components/src/icon/icon.component.ts +++ b/libs/components/src/icon/icon.component.ts @@ -8,6 +8,7 @@ import { Icon, isIcon } from "./icon"; @Component({ selector: "bit-icon", template: ``, + standalone: true, }) export class BitIconComponent { @Input() set icon(icon: Icon) { diff --git a/libs/components/src/icon/icon.components.spec.ts b/libs/components/src/icon/icon.components.spec.ts index 351ed5f0218..7d499cdd419 100644 --- a/libs/components/src/icon/icon.components.spec.ts +++ b/libs/components/src/icon/icon.components.spec.ts @@ -9,7 +9,7 @@ describe("IconComponent", () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [BitIconComponent], + imports: [BitIconComponent], }).compileComponents(); fixture = TestBed.createComponent(BitIconComponent); diff --git a/libs/components/src/icon/icon.module.ts b/libs/components/src/icon/icon.module.ts index 32e95fd0468..3d15b5bb3c3 100644 --- a/libs/components/src/icon/icon.module.ts +++ b/libs/components/src/icon/icon.module.ts @@ -1,11 +1,9 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { BitIconComponent } from "./icon.component"; @NgModule({ - imports: [CommonModule], - declarations: [BitIconComponent], + imports: [BitIconComponent], exports: [BitIconComponent], }) export class IconModule {} diff --git a/libs/components/src/input/input.directive.ts b/libs/components/src/input/input.directive.ts index 4a6a03295d4..f6c6c3d542e 100644 --- a/libs/components/src/input/input.directive.ts +++ b/libs/components/src/input/input.directive.ts @@ -30,6 +30,7 @@ export function inputBorderClasses(error: boolean) { @Directive({ selector: "input[bitInput], select[bitInput], textarea[bitInput]", providers: [{ provide: BitFormFieldControl, useExisting: BitInputDirective }], + standalone: true, }) export class BitInputDirective implements BitFormFieldControl { @HostBinding("class") @Input() get classList() { diff --git a/libs/components/src/input/input.module.ts b/libs/components/src/input/input.module.ts index cfc49cefb7d..9399cb06517 100644 --- a/libs/components/src/input/input.module.ts +++ b/libs/components/src/input/input.module.ts @@ -1,11 +1,9 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { BitInputDirective } from "./input.directive"; @NgModule({ - imports: [CommonModule], - declarations: [BitInputDirective], + imports: [BitInputDirective], exports: [BitInputDirective], }) export class InputModule {} diff --git a/libs/components/src/item/item-action.component.ts b/libs/components/src/item/item-action.component.ts index 8cabf5c5c23..a6ee3a34e6d 100644 --- a/libs/components/src/item/item-action.component.ts +++ b/libs/components/src/item/item-action.component.ts @@ -8,5 +8,13 @@ import { A11yCellDirective } from "../a11y/a11y-cell.directive"; imports: [], template: ``, providers: [{ provide: A11yCellDirective, useExisting: ItemActionComponent }], + host: { + class: + /** + * `top` and `bottom` units should be kept in sync with `item-content.component.ts`'s y-axis padding. + * we want this `:after` element to be the same height as the `item-content` + */ + "[&>button]:tw-relative [&>button:not([bit-item-content])]:after:tw-content-[''] [&>button]:after:tw-absolute [&>button]:after:tw-block bit-compact:[&>button]:after:tw-top-[-0.7rem] bit-compact:[&>button]:after:tw-bottom-[-0.7rem] [&>button]:after:tw-top-[-0.8rem] [&>button]:after:tw-bottom-[-0.80rem] [&>button]:after:tw-right-[-0.25rem] [&>button]:after:tw-left-[-0.25rem]", + }, }) export class ItemActionComponent extends A11yCellDirective {} diff --git a/libs/components/src/item/item-content.component.ts b/libs/components/src/item/item-content.component.ts index 824b6a596a0..f6cc3f133ad 100644 --- a/libs/components/src/item/item-content.component.ts +++ b/libs/components/src/item/item-content.component.ts @@ -19,6 +19,10 @@ import { TypographyModule } from "../typography"; templateUrl: `item-content.component.html`, host: { class: + /** + * y-axis padding should be kept in sync with `item-action.component.ts`'s `top` and `bottom` units. + * we want this to be the same height as the `item-action`'s `:after` element + */ "fvw-target tw-outline-none tw-text-main hover:tw-text-main tw-no-underline hover:tw-no-underline tw-text-base tw-py-2 tw-px-4 bit-compact:tw-py-1.5 bit-compact:tw-px-2 tw-bg-transparent tw-w-full tw-border-none tw-flex tw-gap-4 tw-items-center tw-justify-between", }, changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/libs/components/src/item/item.stories.ts b/libs/components/src/item/item.stories.ts index 675172565f1..5adf9d3c49d 100644 --- a/libs/components/src/item/item.stories.ts +++ b/libs/components/src/item/item.stories.ts @@ -70,7 +70,7 @@ export const Default: Story = { - + @@ -163,7 +163,7 @@ const multipleActionListTemplate = /*html*/ ` - + @@ -182,7 +182,7 @@ const multipleActionListTemplate = /*html*/ ` - + @@ -201,7 +201,7 @@ const multipleActionListTemplate = /*html*/ ` - + @@ -220,7 +220,7 @@ const multipleActionListTemplate = /*html*/ ` - + @@ -239,7 +239,7 @@ const multipleActionListTemplate = /*html*/ ` - + @@ -258,7 +258,7 @@ const multipleActionListTemplate = /*html*/ ` - + @@ -332,14 +332,14 @@ export const SingleActionWithBadge: Story = { Foobar - Auto-fill + Fill Helloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo! - Auto-fill + Fill @@ -375,7 +375,7 @@ export const VirtualScrolling: Story = { - + @@ -405,7 +405,7 @@ export const WithoutBorderRadius: Story = { - + diff --git a/libs/components/src/link/link.directive.ts b/libs/components/src/link/link.directive.ts index b127d80fedf..52aba557661 100644 --- a/libs/components/src/link/link.directive.ts +++ b/libs/components/src/link/link.directive.ts @@ -68,6 +68,7 @@ abstract class LinkDirective { @Directive({ selector: "a[bitLink]", + standalone: true, }) export class AnchorLinkDirective extends LinkDirective { @HostBinding("class") get classList() { @@ -79,6 +80,7 @@ export class AnchorLinkDirective extends LinkDirective { @Directive({ selector: "button[bitLink]", + standalone: true, }) export class ButtonLinkDirective extends LinkDirective { @HostBinding("class") get classList() { diff --git a/libs/components/src/link/link.module.ts b/libs/components/src/link/link.module.ts index b8b54d57c00..52d2f29e53c 100644 --- a/libs/components/src/link/link.module.ts +++ b/libs/components/src/link/link.module.ts @@ -1,11 +1,9 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { AnchorLinkDirective, ButtonLinkDirective } from "./link.directive"; @NgModule({ - imports: [CommonModule], + imports: [AnchorLinkDirective, ButtonLinkDirective], exports: [AnchorLinkDirective, ButtonLinkDirective], - declarations: [AnchorLinkDirective, ButtonLinkDirective], }) export class LinkModule {} diff --git a/libs/components/src/menu/menu-divider.component.ts b/libs/components/src/menu/menu-divider.component.ts index 194506ee50f..55b5c013c93 100644 --- a/libs/components/src/menu/menu-divider.component.ts +++ b/libs/components/src/menu/menu-divider.component.ts @@ -3,5 +3,6 @@ import { Component } from "@angular/core"; @Component({ selector: "bit-menu-divider", templateUrl: "./menu-divider.component.html", + standalone: true, }) export class MenuDividerComponent {} diff --git a/libs/components/src/menu/menu-item.directive.ts b/libs/components/src/menu/menu-item.directive.ts index 5fdc8fabfce..d0975e8e391 100644 --- a/libs/components/src/menu/menu-item.directive.ts +++ b/libs/components/src/menu/menu-item.directive.ts @@ -1,10 +1,13 @@ import { FocusableOption } from "@angular/cdk/a11y"; import { coerceBooleanProperty } from "@angular/cdk/coercion"; +import { NgClass } from "@angular/common"; import { Component, ElementRef, HostBinding, Input } from "@angular/core"; @Component({ selector: "[bitMenuItem]", templateUrl: "menu-item.component.html", + standalone: true, + imports: [NgClass], }) export class MenuItemDirective implements FocusableOption { @HostBinding("class") classList = [ diff --git a/libs/components/src/menu/menu-trigger-for.directive.ts b/libs/components/src/menu/menu-trigger-for.directive.ts index d318a77ef00..96d430c5e6a 100644 --- a/libs/components/src/menu/menu-trigger-for.directive.ts +++ b/libs/components/src/menu/menu-trigger-for.directive.ts @@ -19,6 +19,7 @@ import { MenuComponent } from "./menu.component"; @Directive({ selector: "[bitMenuTriggerFor]", exportAs: "menuTrigger", + standalone: true, }) export class MenuTriggerForDirective implements OnDestroy { @HostBinding("attr.aria-expanded") isOpen = false; @@ -35,7 +36,7 @@ export class MenuTriggerForDirective implements OnDestroy { private defaultMenuConfig: OverlayConfig = { panelClass: "bit-menu-panel", hasBackdrop: true, - backdropClass: "cdk-overlay-transparent-backdrop", + backdropClass: ["cdk-overlay-transparent-backdrop", "bit-menu-panel-backdrop"], scrollStrategy: this.overlay.scrollStrategies.reposition(), positionStrategy: this.overlay .position() diff --git a/libs/components/src/menu/menu.component.ts b/libs/components/src/menu/menu.component.ts index f0bf4f81df9..a39dceb4454 100644 --- a/libs/components/src/menu/menu.component.ts +++ b/libs/components/src/menu/menu.component.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { FocusKeyManager } from "@angular/cdk/a11y"; +import { FocusKeyManager, CdkTrapFocus } from "@angular/cdk/a11y"; import { Component, Output, @@ -19,6 +19,8 @@ import { MenuItemDirective } from "./menu-item.directive"; selector: "bit-menu", templateUrl: "./menu.component.html", exportAs: "menuComponent", + standalone: true, + imports: [CdkTrapFocus], }) export class MenuComponent implements AfterContentInit { @ViewChild(TemplateRef) templateRef: TemplateRef; diff --git a/libs/components/src/menu/menu.module.ts b/libs/components/src/menu/menu.module.ts index b165629e6c5..117460df559 100644 --- a/libs/components/src/menu/menu.module.ts +++ b/libs/components/src/menu/menu.module.ts @@ -1,6 +1,3 @@ -import { A11yModule } from "@angular/cdk/a11y"; -import { OverlayModule } from "@angular/cdk/overlay"; -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { MenuDividerComponent } from "./menu-divider.component"; @@ -9,8 +6,7 @@ import { MenuTriggerForDirective } from "./menu-trigger-for.directive"; import { MenuComponent } from "./menu.component"; @NgModule({ - imports: [A11yModule, CommonModule, OverlayModule], - declarations: [MenuComponent, MenuTriggerForDirective, MenuItemDirective, MenuDividerComponent], + imports: [MenuComponent, MenuTriggerForDirective, MenuItemDirective, MenuDividerComponent], exports: [MenuComponent, MenuTriggerForDirective, MenuItemDirective, MenuDividerComponent], }) export class MenuModule {} diff --git a/libs/components/src/menu/menu.stories.ts b/libs/components/src/menu/menu.stories.ts index c5d232b2057..65fafd2d04d 100644 --- a/libs/components/src/menu/menu.stories.ts +++ b/libs/components/src/menu/menu.stories.ts @@ -3,23 +3,15 @@ import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; import { ButtonModule } from "../button/button.module"; -import { MenuDividerComponent } from "./menu-divider.component"; -import { MenuItemDirective } from "./menu-item.directive"; import { MenuTriggerForDirective } from "./menu-trigger-for.directive"; -import { MenuComponent } from "./menu.component"; +import { MenuModule } from "./menu.module"; export default { title: "Component Library/Menu", component: MenuTriggerForDirective, decorators: [ moduleMetadata({ - declarations: [ - MenuTriggerForDirective, - MenuComponent, - MenuItemDirective, - MenuDividerComponent, - ], - imports: [OverlayModule, ButtonModule], + imports: [MenuModule, OverlayModule, ButtonModule], }), ], parameters: { @@ -51,7 +43,7 @@ export const OpenMenu: Story = { Disabled button - +
    @@ -67,7 +59,7 @@ export const ClosedMenu: Story = {
    - + Anchor link Another link diff --git a/libs/components/src/multi-select/multi-select.component.ts b/libs/components/src/multi-select/multi-select.component.ts index a18d5aa0b60..53e51bfe2f9 100644 --- a/libs/components/src/multi-select/multi-select.component.ts +++ b/libs/components/src/multi-select/multi-select.component.ts @@ -2,6 +2,7 @@ // @ts-strict-ignore import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { hasModifierKey } from "@angular/cdk/keycodes"; +import { NgIf } from "@angular/common"; import { Component, Input, @@ -13,12 +14,20 @@ import { Optional, Self, } from "@angular/core"; -import { ControlValueAccessor, NgControl, Validators } from "@angular/forms"; -import { NgSelectComponent } from "@ng-select/ng-select"; +import { + ControlValueAccessor, + NgControl, + Validators, + ReactiveFormsModule, + FormsModule, +} from "@angular/forms"; +import { NgSelectComponent, NgSelectModule } from "@ng-select/ng-select"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { BadgeModule } from "../badge"; import { BitFormFieldControl } from "../form-field/form-field-control"; +import { I18nPipe } from "../shared/i18n.pipe"; import { SelectItemView } from "./models/select-item-view"; @@ -29,6 +38,8 @@ let nextId = 0; selector: "bit-multi-select", templateUrl: "./multi-select.component.html", providers: [{ provide: BitFormFieldControl, useExisting: MultiSelectComponent }], + standalone: true, + imports: [NgSelectModule, ReactiveFormsModule, FormsModule, BadgeModule, NgIf, I18nPipe], }) /** * This component has been implemented to only support Multi-select list events diff --git a/libs/components/src/multi-select/multi-select.module.ts b/libs/components/src/multi-select/multi-select.module.ts index 88de53b5481..c8cc899db00 100644 --- a/libs/components/src/multi-select/multi-select.module.ts +++ b/libs/components/src/multi-select/multi-select.module.ts @@ -1,16 +1,9 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { FormsModule } from "@angular/forms"; -import { NgSelectModule } from "@ng-select/ng-select"; - -import { BadgeModule } from "../badge"; -import { SharedModule } from "../shared"; import { MultiSelectComponent } from "./multi-select.component"; @NgModule({ - imports: [CommonModule, FormsModule, NgSelectModule, BadgeModule, SharedModule], + imports: [MultiSelectComponent], exports: [MultiSelectComponent], - declarations: [MultiSelectComponent], }) export class MultiSelectModule {} diff --git a/libs/components/src/navigation/nav-divider.component.ts b/libs/components/src/navigation/nav-divider.component.ts index 008d3f46c35..eff381e1c94 100644 --- a/libs/components/src/navigation/nav-divider.component.ts +++ b/libs/components/src/navigation/nav-divider.component.ts @@ -1,3 +1,4 @@ +import { CommonModule } from "@angular/common"; import { Component } from "@angular/core"; import { SideNavService } from "./side-nav.service"; @@ -5,6 +6,8 @@ import { SideNavService } from "./side-nav.service"; @Component({ selector: "bit-nav-divider", templateUrl: "./nav-divider.component.html", + standalone: true, + imports: [CommonModule], }) export class NavDividerComponent { constructor(protected sideNavService: SideNavService) {} diff --git a/libs/components/src/navigation/nav-group.component.ts b/libs/components/src/navigation/nav-group.component.ts index 07494c0b7da..58d93ddd3a4 100644 --- a/libs/components/src/navigation/nav-group.component.ts +++ b/libs/components/src/navigation/nav-group.component.ts @@ -1,3 +1,4 @@ +import { CommonModule } from "@angular/common"; import { AfterContentInit, booleanAttribute, @@ -11,13 +12,22 @@ import { SkipSelf, } from "@angular/core"; +import { IconButtonModule } from "../icon-button"; +import { I18nPipe } from "../shared/i18n.pipe"; + import { NavBaseComponent } from "./nav-base.component"; +import { NavGroupAbstraction, NavItemComponent } from "./nav-item.component"; import { SideNavService } from "./side-nav.service"; @Component({ selector: "bit-nav-group", templateUrl: "./nav-group.component.html", - providers: [{ provide: NavBaseComponent, useExisting: NavGroupComponent }], + providers: [ + { provide: NavBaseComponent, useExisting: NavGroupComponent }, + { provide: NavGroupAbstraction, useExisting: NavGroupComponent }, + ], + standalone: true, + imports: [CommonModule, NavItemComponent, IconButtonModule, I18nPipe], }) export class NavGroupComponent extends NavBaseComponent implements AfterContentInit { @ContentChildren(NavBaseComponent, { diff --git a/libs/components/src/navigation/nav-item.component.ts b/libs/components/src/navigation/nav-item.component.ts index 8348638568b..c8d464119ce 100644 --- a/libs/components/src/navigation/nav-item.component.ts +++ b/libs/components/src/navigation/nav-item.component.ts @@ -1,14 +1,24 @@ +import { CommonModule } from "@angular/common"; import { Component, HostListener, Input, Optional } from "@angular/core"; +import { RouterModule } from "@angular/router"; import { BehaviorSubject, map } from "rxjs"; +import { IconButtonModule } from "../icon-button"; + import { NavBaseComponent } from "./nav-base.component"; -import { NavGroupComponent } from "./nav-group.component"; import { SideNavService } from "./side-nav.service"; +// Resolves a circular dependency between `NavItemComponent` and `NavItemGroup` when using standalone components. +export abstract class NavGroupAbstraction { + abstract setOpen(open: boolean): void; +} + @Component({ selector: "bit-nav-item", templateUrl: "./nav-item.component.html", providers: [{ provide: NavBaseComponent, useExisting: NavItemComponent }], + standalone: true, + imports: [CommonModule, IconButtonModule, RouterModule], }) export class NavItemComponent extends NavBaseComponent { /** Forces active styles to be shown, regardless of the `routerLinkActiveOptions` */ @@ -52,7 +62,7 @@ export class NavItemComponent extends NavBaseComponent { constructor( protected sideNavService: SideNavService, - @Optional() private parentNavGroup: NavGroupComponent, + @Optional() private parentNavGroup: NavGroupAbstraction, ) { super(); } diff --git a/libs/components/src/navigation/nav-logo.component.ts b/libs/components/src/navigation/nav-logo.component.ts index cbad5b869e7..8a84970500c 100644 --- a/libs/components/src/navigation/nav-logo.component.ts +++ b/libs/components/src/navigation/nav-logo.component.ts @@ -1,14 +1,20 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { NgIf } from "@angular/common"; import { Component, Input } from "@angular/core"; +import { RouterLinkActive, RouterLink } from "@angular/router"; import { Icon } from "../icon"; +import { BitIconComponent } from "../icon/icon.component"; +import { NavItemComponent } from "./nav-item.component"; import { SideNavService } from "./side-nav.service"; @Component({ selector: "bit-nav-logo", templateUrl: "./nav-logo.component.html", + standalone: true, + imports: [NgIf, RouterLinkActive, RouterLink, BitIconComponent, NavItemComponent], }) export class NavLogoComponent { /** Icon that is displayed when the side nav is closed */ diff --git a/libs/components/src/navigation/navigation.module.ts b/libs/components/src/navigation/navigation.module.ts index 852bd1c0a25..a08fbaddb98 100644 --- a/libs/components/src/navigation/navigation.module.ts +++ b/libs/components/src/navigation/navigation.module.ts @@ -1,13 +1,4 @@ -import { A11yModule } from "@angular/cdk/a11y"; -import { OverlayModule } from "@angular/cdk/overlay"; -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { RouterModule } from "@angular/router"; - -import { IconModule } from "../icon"; -import { IconButtonModule } from "../icon-button/icon-button.module"; -import { LinkModule } from "../link"; -import { SharedModule } from "../shared/shared.module"; import { NavDividerComponent } from "./nav-divider.component"; import { NavGroupComponent } from "./nav-group.component"; @@ -17,16 +8,6 @@ import { SideNavComponent } from "./side-nav.component"; @NgModule({ imports: [ - CommonModule, - SharedModule, - IconButtonModule, - OverlayModule, - RouterModule, - IconModule, - A11yModule, - LinkModule, - ], - declarations: [ NavDividerComponent, NavGroupComponent, NavItemComponent, diff --git a/libs/components/src/navigation/side-nav.component.ts b/libs/components/src/navigation/side-nav.component.ts index a4af51772b3..c86a517100f 100644 --- a/libs/components/src/navigation/side-nav.component.ts +++ b/libs/components/src/navigation/side-nav.component.ts @@ -1,7 +1,13 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { CdkTrapFocus } from "@angular/cdk/a11y"; +import { CommonModule } from "@angular/common"; import { Component, ElementRef, Input, ViewChild } from "@angular/core"; +import { BitIconButtonComponent } from "../icon-button/icon-button.component"; +import { I18nPipe } from "../shared/i18n.pipe"; + +import { NavDividerComponent } from "./nav-divider.component"; import { SideNavService } from "./side-nav.service"; export type SideNavVariant = "primary" | "secondary"; @@ -9,6 +15,8 @@ export type SideNavVariant = "primary" | "secondary"; @Component({ selector: "bit-side-nav", templateUrl: "side-nav.component.html", + standalone: true, + imports: [CommonModule, CdkTrapFocus, NavDividerComponent, BitIconButtonComponent, I18nPipe], }) export class SideNavComponent { @Input() variant: SideNavVariant = "primary"; diff --git a/libs/components/src/no-items/no-items.component.ts b/libs/components/src/no-items/no-items.component.ts index d85c6a34571..ee9e0ee0581 100644 --- a/libs/components/src/no-items/no-items.component.ts +++ b/libs/components/src/no-items/no-items.component.ts @@ -1,6 +1,7 @@ import { Component, Input } from "@angular/core"; import { Icons } from ".."; +import { BitIconComponent } from "../icon/icon.component"; /** * Component for displaying a message when there are no items to display. Expects title, description and button slots. @@ -8,6 +9,8 @@ import { Icons } from ".."; @Component({ selector: "bit-no-items", templateUrl: "./no-items.component.html", + standalone: true, + imports: [BitIconComponent], }) export class NoItemsComponent { @Input() icon = Icons.Search; diff --git a/libs/components/src/no-items/no-items.module.ts b/libs/components/src/no-items/no-items.module.ts index 9fe6eb37aa9..49c3c73f133 100644 --- a/libs/components/src/no-items/no-items.module.ts +++ b/libs/components/src/no-items/no-items.module.ts @@ -1,13 +1,9 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { IconModule } from "../icon"; - import { NoItemsComponent } from "./no-items.component"; @NgModule({ - imports: [CommonModule, IconModule], + imports: [NoItemsComponent], exports: [NoItemsComponent], - declarations: [NoItemsComponent], }) export class NoItemsModule {} diff --git a/libs/components/src/progress/progress.component.ts b/libs/components/src/progress/progress.component.ts index 37206dc6ae4..04e535158b1 100644 --- a/libs/components/src/progress/progress.component.ts +++ b/libs/components/src/progress/progress.component.ts @@ -1,3 +1,4 @@ +import { CommonModule } from "@angular/common"; import { Component, Input } from "@angular/core"; type SizeTypes = "small" | "default" | "large"; @@ -19,6 +20,8 @@ const BackgroundClasses: Record = { @Component({ selector: "bit-progress", templateUrl: "./progress.component.html", + standalone: true, + imports: [CommonModule], }) export class ProgressComponent { @Input() barWidth = 0; diff --git a/libs/components/src/progress/progress.module.ts b/libs/components/src/progress/progress.module.ts index 8ab09189d19..cc93c4c3bd0 100644 --- a/libs/components/src/progress/progress.module.ts +++ b/libs/components/src/progress/progress.module.ts @@ -1,11 +1,9 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { ProgressComponent } from "./progress.component"; @NgModule({ - imports: [CommonModule], + imports: [ProgressComponent], exports: [ProgressComponent], - declarations: [ProgressComponent], }) export class ProgressModule {} diff --git a/libs/components/src/radio-button/radio-button.component.spec.ts b/libs/components/src/radio-button/radio-button.component.spec.ts index c7344f1bd38..f8cdae00664 100644 --- a/libs/components/src/radio-button/radio-button.component.spec.ts +++ b/libs/components/src/radio-button/radio-button.component.spec.ts @@ -1,3 +1,5 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore import { Component } from "@angular/core"; import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; diff --git a/libs/components/src/radio-button/radio-button.component.ts b/libs/components/src/radio-button/radio-button.component.ts index dc294103d42..042a54edf47 100644 --- a/libs/components/src/radio-button/radio-button.component.ts +++ b/libs/components/src/radio-button/radio-button.component.ts @@ -1,12 +1,17 @@ import { Component, HostBinding, Input } from "@angular/core"; +import { FormControlModule } from "../form-control/form-control.module"; + import { RadioGroupComponent } from "./radio-group.component"; +import { RadioInputComponent } from "./radio-input.component"; let nextId = 0; @Component({ selector: "bit-radio-button", templateUrl: "radio-button.component.html", + standalone: true, + imports: [FormControlModule, RadioInputComponent], }) export class RadioButtonComponent { @HostBinding("attr.id") @Input() id = `bit-radio-button-${nextId++}`; diff --git a/libs/components/src/radio-button/radio-button.module.ts b/libs/components/src/radio-button/radio-button.module.ts index 21fd9427046..7b05c27b4ff 100644 --- a/libs/components/src/radio-button/radio-button.module.ts +++ b/libs/components/src/radio-button/radio-button.module.ts @@ -1,16 +1,13 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { FormControlModule } from "../form-control"; -import { SharedModule } from "../shared"; import { RadioButtonComponent } from "./radio-button.component"; import { RadioGroupComponent } from "./radio-group.component"; import { RadioInputComponent } from "./radio-input.component"; @NgModule({ - imports: [CommonModule, SharedModule, FormControlModule], - declarations: [RadioInputComponent, RadioButtonComponent, RadioGroupComponent], + imports: [FormControlModule, RadioInputComponent, RadioButtonComponent, RadioGroupComponent], exports: [FormControlModule, RadioInputComponent, RadioButtonComponent, RadioGroupComponent], }) export class RadioButtonModule {} diff --git a/libs/components/src/radio-button/radio-group.component.ts b/libs/components/src/radio-button/radio-group.component.ts index 2cddb4fb7bc..b9e48f46445 100644 --- a/libs/components/src/radio-button/radio-group.component.ts +++ b/libs/components/src/radio-button/radio-group.component.ts @@ -1,15 +1,19 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { NgIf, NgTemplateOutlet } from "@angular/common"; import { Component, ContentChild, HostBinding, Input, Optional, Self } from "@angular/core"; import { ControlValueAccessor, NgControl, Validators } from "@angular/forms"; import { BitLabel } from "../form-control/label.component"; +import { I18nPipe } from "../shared/i18n.pipe"; let nextId = 0; @Component({ selector: "bit-radio-group", templateUrl: "radio-group.component.html", + standalone: true, + imports: [NgIf, NgTemplateOutlet, I18nPipe], }) export class RadioGroupComponent implements ControlValueAccessor { selected: unknown; diff --git a/libs/components/src/radio-button/radio-input.component.ts b/libs/components/src/radio-button/radio-input.component.ts index 580e5bca25e..4a9f5dede60 100644 --- a/libs/components/src/radio-button/radio-input.component.ts +++ b/libs/components/src/radio-button/radio-input.component.ts @@ -11,6 +11,7 @@ let nextId = 0; selector: "input[type=radio][bitRadio]", template: "", providers: [{ provide: BitFormControlAbstraction, useExisting: RadioInputComponent }], + standalone: true, }) export class RadioInputComponent implements BitFormControlAbstraction { @HostBinding("attr.id") @Input() id = `bit-radio-input-${nextId++}`; diff --git a/libs/components/src/search/search.component.ts b/libs/components/src/search/search.component.ts index bc98e5a293b..6ec79eaa84e 100644 --- a/libs/components/src/search/search.component.ts +++ b/libs/components/src/search/search.component.ts @@ -1,11 +1,18 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Component, ElementRef, Input, ViewChild } from "@angular/core"; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + ReactiveFormsModule, + FormsModule, +} from "@angular/forms"; import { isBrowserSafariApi } from "@bitwarden/platform"; +import { InputModule } from "../input/input.module"; import { FocusableElement } from "../shared/focusable-element"; +import { I18nPipe } from "../shared/i18n.pipe"; let nextId = 0; @@ -23,6 +30,8 @@ let nextId = 0; useExisting: SearchComponent, }, ], + standalone: true, + imports: [InputModule, ReactiveFormsModule, FormsModule, I18nPipe], }) export class SearchComponent implements ControlValueAccessor, FocusableElement { private notifyOnChange: (v: string) => void; diff --git a/libs/components/src/search/search.module.ts b/libs/components/src/search/search.module.ts index 62072774900..cb9761eae6b 100644 --- a/libs/components/src/search/search.module.ts +++ b/libs/components/src/search/search.module.ts @@ -1,14 +1,9 @@ import { NgModule } from "@angular/core"; -import { FormsModule } from "@angular/forms"; - -import { InputModule } from "../input/input.module"; -import { SharedModule } from "../shared"; import { SearchComponent } from "./search.component"; @NgModule({ - imports: [SharedModule, InputModule, FormsModule], - declarations: [SearchComponent], + imports: [SearchComponent], exports: [SearchComponent], }) export class SearchModule {} diff --git a/libs/components/src/select/option.component.ts b/libs/components/src/select/option.component.ts index b32b124be25..841ceda3648 100644 --- a/libs/components/src/select/option.component.ts +++ b/libs/components/src/select/option.component.ts @@ -7,6 +7,7 @@ import { Option } from "./option"; @Component({ selector: "bit-option", template: ``, + standalone: true, }) export class OptionComponent implements Option { @Input() diff --git a/libs/components/src/select/select.component.ts b/libs/components/src/select/select.component.ts index cdcf794e489..8f75c5be42b 100644 --- a/libs/components/src/select/select.component.ts +++ b/libs/components/src/select/select.component.ts @@ -1,5 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { NgIf } from "@angular/common"; import { Component, ContentChildren, @@ -12,8 +13,14 @@ import { Output, EventEmitter, } from "@angular/core"; -import { ControlValueAccessor, NgControl, Validators } from "@angular/forms"; -import { NgSelectComponent } from "@ng-select/ng-select"; +import { + ControlValueAccessor, + NgControl, + Validators, + ReactiveFormsModule, + FormsModule, +} from "@angular/forms"; +import { NgSelectComponent, NgSelectModule } from "@ng-select/ng-select"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -28,6 +35,8 @@ let nextId = 0; selector: "bit-select", templateUrl: "select.component.html", providers: [{ provide: BitFormFieldControl, useExisting: SelectComponent }], + standalone: true, + imports: [NgSelectModule, ReactiveFormsModule, FormsModule, NgIf], }) export class SelectComponent implements BitFormFieldControl, ControlValueAccessor { @ViewChild(NgSelectComponent) select: NgSelectComponent; diff --git a/libs/components/src/select/select.module.ts b/libs/components/src/select/select.module.ts index 4391a518174..8807ed63a48 100644 --- a/libs/components/src/select/select.module.ts +++ b/libs/components/src/select/select.module.ts @@ -1,14 +1,10 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { FormsModule } from "@angular/forms"; -import { NgSelectModule } from "@ng-select/ng-select"; import { OptionComponent } from "./option.component"; import { SelectComponent } from "./select.component"; @NgModule({ - imports: [CommonModule, NgSelectModule, FormsModule], - declarations: [SelectComponent, OptionComponent], + imports: [SelectComponent, OptionComponent], exports: [SelectComponent, OptionComponent], }) export class SelectModule {} diff --git a/libs/components/src/shared/i18n.pipe.ts b/libs/components/src/shared/i18n.pipe.ts index f428d9297c0..91bf0b3198d 100644 --- a/libs/components/src/shared/i18n.pipe.ts +++ b/libs/components/src/shared/i18n.pipe.ts @@ -7,6 +7,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic */ @Pipe({ name: "i18n", + standalone: true, }) export class I18nPipe implements PipeTransform { constructor(private i18nService: I18nService) {} diff --git a/libs/components/src/shared/shared.module.ts b/libs/components/src/shared/shared.module.ts index dcf2e2bc05f..253b049f8fe 100644 --- a/libs/components/src/shared/shared.module.ts +++ b/libs/components/src/shared/shared.module.ts @@ -4,8 +4,7 @@ import { NgModule } from "@angular/core"; import { I18nPipe } from "./i18n.pipe"; @NgModule({ - imports: [CommonModule], - declarations: [I18nPipe], + imports: [CommonModule, I18nPipe], exports: [CommonModule, I18nPipe], }) export class SharedModule {} diff --git a/libs/components/src/stories/kitchen-sink/components/dialog-virtual-scroll-block.component.ts b/libs/components/src/stories/kitchen-sink/components/dialog-virtual-scroll-block.component.ts index a867d9cdf53..02b49a3e915 100644 --- a/libs/components/src/stories/kitchen-sink/components/dialog-virtual-scroll-block.component.ts +++ b/libs/components/src/stories/kitchen-sink/components/dialog-virtual-scroll-block.component.ts @@ -10,9 +10,9 @@ import { TableDataSource, TableModule } from "../../../table"; selector: "dialog-virtual-scroll-block", standalone: true, imports: [DialogModule, IconButtonModule, SectionComponent, TableModule, ScrollingModule], - template: ` + template: /*html*/ ` - + Id diff --git a/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts b/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts index 203c510f814..44080e29049 100644 --- a/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts +++ b/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts @@ -130,6 +130,9 @@ export const MenuOpen: Story = { const menuButton = getAllByRole(table, "button")[0]; await userEvent.click(menuButton); }, + parameters: { + chromatic: { ignoreSelectors: [".bit-menu-panel-backdrop"] }, + }, }; export const DefaultDialogOpen: Story = { diff --git a/libs/components/src/table/cell.directive.ts b/libs/components/src/table/cell.directive.ts index 61c75571063..8928fe7c095 100644 --- a/libs/components/src/table/cell.directive.ts +++ b/libs/components/src/table/cell.directive.ts @@ -2,6 +2,7 @@ import { Directive, HostBinding } from "@angular/core"; @Directive({ selector: "th[bitCell], td[bitCell]", + standalone: true, }) export class CellDirective { @HostBinding("class") get classList() { diff --git a/libs/components/src/table/row.directive.ts b/libs/components/src/table/row.directive.ts index 19f3d3f775b..23347224af9 100644 --- a/libs/components/src/table/row.directive.ts +++ b/libs/components/src/table/row.directive.ts @@ -2,6 +2,7 @@ import { Directive, HostBinding, Input } from "@angular/core"; @Directive({ selector: "tr[bitRow]", + standalone: true, }) export class RowDirective { @Input() alignContent: "top" | "middle" | "bottom" | "baseline" = "middle"; diff --git a/libs/components/src/table/sortable.component.ts b/libs/components/src/table/sortable.component.ts index dc3d8dc14f0..d3309c03aa9 100644 --- a/libs/components/src/table/sortable.component.ts +++ b/libs/components/src/table/sortable.component.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { coerceBooleanProperty } from "@angular/cdk/coercion"; +import { NgClass } from "@angular/common"; import { Component, HostBinding, Input, OnInit } from "@angular/core"; import type { SortDirection, SortFn } from "./table-data-source"; @@ -14,6 +15,8 @@ import { TableComponent } from "./table.component"; `, + standalone: true, + imports: [NgClass], }) export class SortableComponent implements OnInit { /** diff --git a/libs/components/src/table/table-scroll.component.ts b/libs/components/src/table/table-scroll.component.ts index 9e308b7da59..34cd8c5d9ca 100644 --- a/libs/components/src/table/table-scroll.component.ts +++ b/libs/components/src/table/table-scroll.component.ts @@ -1,5 +1,12 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { + CdkVirtualScrollViewport, + CdkVirtualScrollableWindow, + CdkFixedSizeVirtualScroll, + CdkVirtualForOf, +} from "@angular/cdk/scrolling"; +import { CommonModule } from "@angular/common"; import { AfterContentChecked, Component, @@ -14,6 +21,7 @@ import { TrackByFunction, } from "@angular/core"; +import { RowDirective } from "./row.directive"; import { TableComponent } from "./table.component"; /** @@ -42,6 +50,15 @@ export class BitRowDef { selector: "bit-table-scroll", templateUrl: "./table-scroll.component.html", providers: [{ provide: TableComponent, useExisting: TableScrollComponent }], + standalone: true, + imports: [ + CommonModule, + CdkVirtualScrollViewport, + CdkVirtualScrollableWindow, + CdkFixedSizeVirtualScroll, + CdkVirtualForOf, + RowDirective, + ], }) export class TableScrollComponent extends TableComponent diff --git a/libs/components/src/table/table.component.ts b/libs/components/src/table/table.component.ts index 8bc7754b16b..cd0a2a6c65e 100644 --- a/libs/components/src/table/table.component.ts +++ b/libs/components/src/table/table.component.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { isDataSource } from "@angular/cdk/collections"; +import { CommonModule } from "@angular/common"; import { AfterContentChecked, Component, @@ -16,6 +17,7 @@ import { TableDataSource } from "./table-data-source"; @Directive({ selector: "ng-template[body]", + standalone: true, }) export class TableBodyDirective { // eslint-disable-next-line @typescript-eslint/explicit-member-accessibility @@ -25,6 +27,8 @@ export class TableBodyDirective { @Component({ selector: "bit-table", templateUrl: "./table.component.html", + standalone: true, + imports: [CommonModule], }) export class TableComponent implements OnDestroy, AfterContentChecked { @Input() dataSource: TableDataSource; diff --git a/libs/components/src/table/table.module.ts b/libs/components/src/table/table.module.ts index 1f1b705c69e..68993612772 100644 --- a/libs/components/src/table/table.module.ts +++ b/libs/components/src/table/table.module.ts @@ -9,8 +9,10 @@ import { BitRowDef, TableScrollComponent } from "./table-scroll.component"; import { TableBodyDirective, TableComponent } from "./table.component"; @NgModule({ - imports: [CommonModule, ScrollingModule, BitRowDef], - declarations: [ + imports: [ + CommonModule, + ScrollingModule, + BitRowDef, CellDirective, RowDirective, SortableComponent, diff --git a/libs/components/src/tabs/shared/tab-header.component.ts b/libs/components/src/tabs/shared/tab-header.component.ts index 4712df0549a..c45bafb3d52 100644 --- a/libs/components/src/tabs/shared/tab-header.component.ts +++ b/libs/components/src/tabs/shared/tab-header.component.ts @@ -10,5 +10,6 @@ import { Component } from "@angular/core"; "tw-h-16 tw-pl-4 tw-bg-background-alt tw-flex tw-items-end tw-border-0 tw-border-b tw-border-solid tw-border-secondary-300", }, template: ``, + standalone: true, }) export class TabHeaderComponent {} diff --git a/libs/components/src/tabs/shared/tab-list-container.directive.ts b/libs/components/src/tabs/shared/tab-list-container.directive.ts index 1cf8a762d58..cedae44e582 100644 --- a/libs/components/src/tabs/shared/tab-list-container.directive.ts +++ b/libs/components/src/tabs/shared/tab-list-container.directive.ts @@ -8,5 +8,6 @@ import { Directive } from "@angular/core"; host: { class: "tw-inline-flex tw-flex-wrap tw-leading-5", }, + standalone: true, }) export class TabListContainerDirective {} diff --git a/libs/components/src/tabs/shared/tab-list-item.directive.ts b/libs/components/src/tabs/shared/tab-list-item.directive.ts index 7514f5417e6..87435133a23 100644 --- a/libs/components/src/tabs/shared/tab-list-item.directive.ts +++ b/libs/components/src/tabs/shared/tab-list-item.directive.ts @@ -7,7 +7,10 @@ import { Directive, ElementRef, HostBinding, Input } from "@angular/core"; * Directive used for styling tab header items for both nav links (anchor tags) * and content tabs (button tags) */ -@Directive({ selector: "[bitTabListItem]" }) +@Directive({ + selector: "[bitTabListItem]", + standalone: true, +}) export class TabListItemDirective implements FocusableOption { @Input() active: boolean; @Input() disabled: boolean; diff --git a/libs/components/src/tabs/tab-group/tab-body.component.ts b/libs/components/src/tabs/tab-group/tab-body.component.ts index 7cb6664b7c5..45a6a05e7c2 100644 --- a/libs/components/src/tabs/tab-group/tab-body.component.ts +++ b/libs/components/src/tabs/tab-group/tab-body.component.ts @@ -1,11 +1,13 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { TemplatePortal } from "@angular/cdk/portal"; +import { TemplatePortal, CdkPortalOutlet } from "@angular/cdk/portal"; import { Component, HostBinding, Input } from "@angular/core"; @Component({ selector: "bit-tab-body", templateUrl: "tab-body.component.html", + standalone: true, + imports: [CdkPortalOutlet], }) export class TabBodyComponent { private _firstRender: boolean; diff --git a/libs/components/src/tabs/tab-group/tab-group.component.ts b/libs/components/src/tabs/tab-group/tab-group.component.ts index 7b0cb60bb12..54d00343b38 100644 --- a/libs/components/src/tabs/tab-group/tab-group.component.ts +++ b/libs/components/src/tabs/tab-group/tab-group.component.ts @@ -2,6 +2,7 @@ // @ts-strict-ignore import { FocusKeyManager } from "@angular/cdk/a11y"; import { coerceNumberProperty } from "@angular/cdk/coercion"; +import { CommonModule } from "@angular/common"; import { AfterContentChecked, AfterContentInit, @@ -17,8 +18,11 @@ import { } from "@angular/core"; import { Subject, takeUntil } from "rxjs"; +import { TabHeaderComponent } from "../shared/tab-header.component"; +import { TabListContainerDirective } from "../shared/tab-list-container.directive"; import { TabListItemDirective } from "../shared/tab-list-item.directive"; +import { TabBodyComponent } from "./tab-body.component"; import { TabComponent } from "./tab.component"; /** Used to generate unique ID's for each tab component */ @@ -27,6 +31,14 @@ let nextId = 0; @Component({ selector: "bit-tab-group", templateUrl: "./tab-group.component.html", + standalone: true, + imports: [ + CommonModule, + TabHeaderComponent, + TabListContainerDirective, + TabListItemDirective, + TabBodyComponent, + ], }) export class TabGroupComponent implements AfterContentChecked, AfterContentInit, AfterViewInit, OnDestroy diff --git a/libs/components/src/tabs/tab-group/tab-label.directive.ts b/libs/components/src/tabs/tab-group/tab-label.directive.ts index 45da163631b..9a0e59845a1 100644 --- a/libs/components/src/tabs/tab-group/tab-label.directive.ts +++ b/libs/components/src/tabs/tab-group/tab-label.directive.ts @@ -16,6 +16,7 @@ import { Directive, TemplateRef } from "@angular/core"; */ @Directive({ selector: "[bitTabLabel]", + standalone: true, }) export class TabLabelDirective { constructor(public templateRef: TemplateRef) {} diff --git a/libs/components/src/tabs/tab-group/tab.component.ts b/libs/components/src/tabs/tab-group/tab.component.ts index 260cb0c8193..b2c9b1999bc 100644 --- a/libs/components/src/tabs/tab-group/tab.component.ts +++ b/libs/components/src/tabs/tab-group/tab.component.ts @@ -19,6 +19,7 @@ import { TabLabelDirective } from "./tab-label.directive"; host: { role: "tabpanel", }, + standalone: true, }) export class TabComponent implements OnInit { @Input() disabled = false; diff --git a/libs/components/src/tabs/tab-nav-bar/tab-link.component.ts b/libs/components/src/tabs/tab-nav-bar/tab-link.component.ts index 483aa9600b3..0dac6681475 100644 --- a/libs/components/src/tabs/tab-nav-bar/tab-link.component.ts +++ b/libs/components/src/tabs/tab-nav-bar/tab-link.component.ts @@ -2,7 +2,7 @@ // @ts-strict-ignore import { FocusableOption } from "@angular/cdk/a11y"; import { AfterViewInit, Component, HostListener, Input, OnDestroy, ViewChild } from "@angular/core"; -import { IsActiveMatchOptions, RouterLinkActive } from "@angular/router"; +import { IsActiveMatchOptions, RouterLinkActive, RouterModule } from "@angular/router"; import { Subject, takeUntil } from "rxjs"; import { TabListItemDirective } from "../shared/tab-list-item.directive"; @@ -12,6 +12,8 @@ import { TabNavBarComponent } from "./tab-nav-bar.component"; @Component({ selector: "bit-tab-link", templateUrl: "tab-link.component.html", + standalone: true, + imports: [TabListItemDirective, RouterModule], }) export class TabLinkComponent implements FocusableOption, AfterViewInit, OnDestroy { private destroy$ = new Subject(); diff --git a/libs/components/src/tabs/tab-nav-bar/tab-nav-bar.component.ts b/libs/components/src/tabs/tab-nav-bar/tab-nav-bar.component.ts index 81f7f1d4947..305196a0c69 100644 --- a/libs/components/src/tabs/tab-nav-bar/tab-nav-bar.component.ts +++ b/libs/components/src/tabs/tab-nav-bar/tab-nav-bar.component.ts @@ -10,6 +10,9 @@ import { QueryList, } from "@angular/core"; +import { TabHeaderComponent } from "../shared/tab-header.component"; +import { TabListContainerDirective } from "../shared/tab-list-container.directive"; + import { TabLinkComponent } from "./tab-link.component"; @Component({ @@ -18,6 +21,8 @@ import { TabLinkComponent } from "./tab-link.component"; host: { class: "tw-block", }, + standalone: true, + imports: [TabHeaderComponent, TabListContainerDirective], }) export class TabNavBarComponent implements AfterContentInit { @ContentChildren(forwardRef(() => TabLinkComponent)) tabLabels: QueryList; diff --git a/libs/components/src/tabs/tabs.module.ts b/libs/components/src/tabs/tabs.module.ts index fee1a8a7d08..ef1537db67e 100644 --- a/libs/components/src/tabs/tabs.module.ts +++ b/libs/components/src/tabs/tabs.module.ts @@ -1,11 +1,6 @@ -import { PortalModule } from "@angular/cdk/portal"; import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { RouterModule } from "@angular/router"; -import { TabHeaderComponent } from "./shared/tab-header.component"; -import { TabListContainerDirective } from "./shared/tab-list-container.directive"; -import { TabListItemDirective } from "./shared/tab-list-item.directive"; import { TabBodyComponent } from "./tab-group/tab-body.component"; import { TabGroupComponent } from "./tab-group/tab-group.component"; import { TabLabelDirective } from "./tab-group/tab-label.directive"; @@ -14,24 +9,21 @@ import { TabLinkComponent } from "./tab-nav-bar/tab-link.component"; import { TabNavBarComponent } from "./tab-nav-bar/tab-nav-bar.component"; @NgModule({ - imports: [CommonModule, RouterModule, PortalModule], - exports: [ + imports: [ + CommonModule, TabGroupComponent, TabComponent, TabLabelDirective, TabNavBarComponent, TabLinkComponent, + TabBodyComponent, ], - declarations: [ + exports: [ TabGroupComponent, TabComponent, TabLabelDirective, - TabListContainerDirective, - TabListItemDirective, - TabHeaderComponent, TabNavBarComponent, TabLinkComponent, - TabBodyComponent, ], }) export class TabsModule {} diff --git a/libs/components/src/toast/toast.module.ts b/libs/components/src/toast/toast.module.ts index bf39a0be9ad..bf17fde223f 100644 --- a/libs/components/src/toast/toast.module.ts +++ b/libs/components/src/toast/toast.module.ts @@ -1,13 +1,10 @@ -import { CommonModule } from "@angular/common"; import { ModuleWithProviders, NgModule } from "@angular/core"; import { DefaultNoComponentGlobalConfig, GlobalConfig, TOAST_CONFIG } from "ngx-toastr"; -import { ToastComponent } from "./toast.component"; import { BitwardenToastrComponent } from "./toastr.component"; @NgModule({ - imports: [CommonModule, ToastComponent], - declarations: [BitwardenToastrComponent], + imports: [BitwardenToastrComponent], exports: [BitwardenToastrComponent], }) export class ToastModule { diff --git a/libs/components/src/toast/toastr.component.ts b/libs/components/src/toast/toastr.component.ts index 70085dfc474..24209054948 100644 --- a/libs/components/src/toast/toastr.component.ts +++ b/libs/components/src/toast/toastr.component.ts @@ -2,13 +2,15 @@ import { animate, state, style, transition, trigger } from "@angular/animations" import { Component } from "@angular/core"; import { Toast as BaseToastrComponent } from "ngx-toastr"; +import { ToastComponent } from "./toast.component"; + @Component({ template: ` `, @@ -22,5 +24,7 @@ import { Toast as BaseToastrComponent } from "ngx-toastr"; ]), ], preserveWhitespaces: false, + standalone: true, + imports: [ToastComponent], }) export class BitwardenToastrComponent extends BaseToastrComponent {} diff --git a/libs/components/src/toggle-group/toggle-group.component.spec.ts b/libs/components/src/toggle-group/toggle-group.component.spec.ts index 0fe863fcb9f..e418a7b410c 100644 --- a/libs/components/src/toggle-group/toggle-group.component.spec.ts +++ b/libs/components/src/toggle-group/toggle-group.component.spec.ts @@ -1,3 +1,5 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore import { Component } from "@angular/core"; import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; diff --git a/libs/components/src/toggle-group/toggle-group.component.ts b/libs/components/src/toggle-group/toggle-group.component.ts index 7afe39e4cf6..5033a27ed6d 100644 --- a/libs/components/src/toggle-group/toggle-group.component.ts +++ b/libs/components/src/toggle-group/toggle-group.component.ts @@ -13,6 +13,7 @@ let nextId = 0; selector: "bit-toggle-group", templateUrl: "./toggle-group.component.html", preserveWhitespaces: false, + standalone: true, }) export class ToggleGroupComponent { private id = nextId++; diff --git a/libs/components/src/toggle-group/toggle-group.module.ts b/libs/components/src/toggle-group/toggle-group.module.ts index fe1ce0ec52f..654149611f0 100644 --- a/libs/components/src/toggle-group/toggle-group.module.ts +++ b/libs/components/src/toggle-group/toggle-group.module.ts @@ -1,14 +1,10 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { BadgeModule } from "../badge"; - import { ToggleGroupComponent } from "./toggle-group.component"; import { ToggleComponent } from "./toggle.component"; @NgModule({ - imports: [CommonModule, BadgeModule], + imports: [ToggleGroupComponent, ToggleComponent], exports: [ToggleGroupComponent, ToggleComponent], - declarations: [ToggleGroupComponent, ToggleComponent], }) export class ToggleGroupModule {} diff --git a/libs/components/src/toggle-group/toggle-group.stories.ts b/libs/components/src/toggle-group/toggle-group.stories.ts index edfa832d6ce..fc8ea0ea929 100644 --- a/libs/components/src/toggle-group/toggle-group.stories.ts +++ b/libs/components/src/toggle-group/toggle-group.stories.ts @@ -13,8 +13,7 @@ export default { }, decorators: [ moduleMetadata({ - declarations: [ToggleGroupComponent, ToggleComponent], - imports: [BadgeModule], + imports: [BadgeModule, ToggleGroupComponent, ToggleComponent], }), ], parameters: { diff --git a/libs/components/src/toggle-group/toggle.component.spec.ts b/libs/components/src/toggle-group/toggle.component.spec.ts index 73809a97f76..fe91f94071d 100644 --- a/libs/components/src/toggle-group/toggle.component.spec.ts +++ b/libs/components/src/toggle-group/toggle.component.spec.ts @@ -1,3 +1,5 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore import { Component } from "@angular/core"; import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; diff --git a/libs/components/src/toggle-group/toggle.component.ts b/libs/components/src/toggle-group/toggle.component.ts index c7d9dc5bf38..7bd62056763 100644 --- a/libs/components/src/toggle-group/toggle.component.ts +++ b/libs/components/src/toggle-group/toggle.component.ts @@ -1,5 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { NgClass } from "@angular/common"; import { AfterContentChecked, AfterViewInit, @@ -19,6 +20,8 @@ let nextId = 0; selector: "bit-toggle", templateUrl: "./toggle.component.html", preserveWhitespaces: false, + standalone: true, + imports: [NgClass], }) export class ToggleComponent implements AfterContentChecked, AfterViewInit { id = nextId++; diff --git a/libs/components/src/typography/typography.directive.ts b/libs/components/src/typography/typography.directive.ts index e48ef67001f..36d6b996dbe 100644 --- a/libs/components/src/typography/typography.directive.ts +++ b/libs/components/src/typography/typography.directive.ts @@ -31,6 +31,7 @@ const margins: Record = { @Directive({ selector: "[bitTypography]", + standalone: true, }) export class TypographyDirective { @Input("bitTypography") bitTypography: TypographyType; diff --git a/libs/components/src/typography/typography.module.ts b/libs/components/src/typography/typography.module.ts index 7ee66906360..74d1d4d6e6a 100644 --- a/libs/components/src/typography/typography.module.ts +++ b/libs/components/src/typography/typography.module.ts @@ -1,11 +1,9 @@ -import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; import { TypographyDirective } from "./typography.directive"; @NgModule({ - imports: [CommonModule], + imports: [TypographyDirective], exports: [TypographyDirective], - declarations: [TypographyDirective], }) export class TypographyModule {} diff --git a/libs/components/tsconfig.json b/libs/components/tsconfig.json index 3c947bf582d..dabcecf78e9 100644 --- a/libs/components/tsconfig.json +++ b/libs/components/tsconfig.json @@ -22,7 +22,12 @@ "@bitwarden/common/*": ["../common/src/*"], "@bitwarden/angular/*": ["../angular/src/*"], "@bitwarden/platform": ["../platform/src"] - } + }, + "plugins": [ + { + "name": "typescript-strict-plugin" + } + ] }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/importer/spec/passwordxp-csv-importer.spec.ts b/libs/importer/spec/passwordxp-csv-importer.spec.ts index f707b1138c5..fda323450c6 100644 --- a/libs/importer/spec/passwordxp-csv-importer.spec.ts +++ b/libs/importer/spec/passwordxp-csv-importer.spec.ts @@ -3,10 +3,46 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { PasswordXPCsvImporter } from "../src/importers"; import { ImportResult } from "../src/models/import-result"; +import { dutchHeaders } from "./test-data/passwordxp-csv/dutch-headers"; +import { germanHeaders } from "./test-data/passwordxp-csv/german-headers"; import { noFolder } from "./test-data/passwordxp-csv/no-folder.csv"; import { withFolders } from "./test-data/passwordxp-csv/passwordxp-with-folders.csv"; import { withoutFolders } from "./test-data/passwordxp-csv/passwordxp-without-folders.csv"; +async function importLoginWithCustomFields(importer: PasswordXPCsvImporter, csvData: string) { + const result: ImportResult = await importer.parse(csvData); + expect(result.success).toBe(true); + + const cipher = result.ciphers.shift(); + expect(cipher.type).toBe(CipherType.Login); + expect(cipher.name).toBe("Title2"); + expect(cipher.notes).toBe("Test Notes"); + expect(cipher.login.username).toBe("Username2"); + expect(cipher.login.password).toBe("12345678"); + expect(cipher.login.uris[0].uri).toBe("http://URL2.com"); + + expect(cipher.fields.length).toBe(5); + let field = cipher.fields.shift(); + expect(field.name).toBe("Account"); + expect(field.value).toBe("Account2"); + + field = cipher.fields.shift(); + expect(field.name).toBe("Modified"); + expect(field.value).toBe("27-3-2024 08:11:21"); + + field = cipher.fields.shift(); + expect(field.name).toBe("Created"); + expect(field.value).toBe("27-3-2024 08:11:21"); + + field = cipher.fields.shift(); + expect(field.name).toBe("Expire on"); + expect(field.value).toBe("27-5-2024 08:11:21"); + + field = cipher.fields.shift(); + expect(field.name).toBe("Modified by"); + expect(field.value).toBe("someone"); +} + describe("PasswordXPCsvImporter", () => { let importer: PasswordXPCsvImporter; @@ -20,6 +56,12 @@ describe("PasswordXPCsvImporter", () => { expect(result.success).toBe(false); }); + it("should return success false if CSV headers did not get translated", async () => { + const data = germanHeaders.replace("Titel;", "UnknownTitle;"); + const result: ImportResult = await importer.parse(data); + expect(result.success).toBe(false); + }); + it("should skip rows starting with >>>", async () => { const result: ImportResult = await importer.parse(noFolder); expect(result.success).toBe(true); @@ -61,38 +103,16 @@ describe("PasswordXPCsvImporter", () => { expect(cipher.login.uris[0].uri).toBe("http://test"); }); - it("should parse CSV data and import unmapped columns as custom fields", async () => { - const result: ImportResult = await importer.parse(withoutFolders); - expect(result.success).toBe(true); - - const cipher = result.ciphers.shift(); - expect(cipher.type).toBe(CipherType.Login); - expect(cipher.name).toBe("Title2"); - expect(cipher.notes).toBe("Test Notes"); - expect(cipher.login.username).toBe("Username2"); - expect(cipher.login.password).toBe("12345678"); - expect(cipher.login.uris[0].uri).toBe("http://URL2.com"); - - expect(cipher.fields.length).toBe(5); - let field = cipher.fields.shift(); - expect(field.name).toBe("Account"); - expect(field.value).toBe("Account2"); - - field = cipher.fields.shift(); - expect(field.name).toBe("Modified"); - expect(field.value).toBe("27-3-2024 08:11:21"); - - field = cipher.fields.shift(); - expect(field.name).toBe("Created"); - expect(field.value).toBe("27-3-2024 08:11:21"); + it("should parse CSV data with English headers and import unmapped columns as custom fields", async () => { + await importLoginWithCustomFields(importer, withoutFolders); + }); - field = cipher.fields.shift(); - expect(field.name).toBe("Expire on"); - expect(field.value).toBe("27-5-2024 08:11:21"); + it("should parse CSV data with German headers and import unmapped columns as custom fields", async () => { + await importLoginWithCustomFields(importer, germanHeaders); + }); - field = cipher.fields.shift(); - expect(field.name).toBe("Modified by"); - expect(field.value).toBe("someone"); + it("should parse CSV data with Dutch headers and import unmapped columns as custom fields", async () => { + await importLoginWithCustomFields(importer, dutchHeaders); }); it("should parse CSV data with folders and assign items to them", async () => { diff --git a/libs/importer/spec/test-data/passwordxp-csv/dutch-headers.ts b/libs/importer/spec/test-data/passwordxp-csv/dutch-headers.ts new file mode 100644 index 00000000000..9cab04f1e6d --- /dev/null +++ b/libs/importer/spec/test-data/passwordxp-csv/dutch-headers.ts @@ -0,0 +1,7 @@ +export const dutchHeaders = `Titel;Gebruikersnaam;Account;URL;Wachtwoord;Gewijzigd;Gemaakt;Verloopt op;Beschrijving;Gewijzigd door +>>> +Title2;Username2;Account2;http://URL2.com;12345678;27-3-2024 08:11:21;27-3-2024 08:11:21;27-5-2024 08:11:21;Test Notes;someone +Title Test 1;Username1;Account1;http://URL1.com;Password1;27-3-2024 08:10:52;27-3-2024 08:10:52;;Test Notes 2; +Certificate 1;;;;;27-3-2024 10:22:39;27-3-2024 10:22:39;;Test Notes Certicate 1; +test;testtest;;http://test;test;27-3-2024 12:36:59;27-3-2024 12:36:59;;Test Notes 3; +`; diff --git a/libs/importer/spec/test-data/passwordxp-csv/german-headers.ts b/libs/importer/spec/test-data/passwordxp-csv/german-headers.ts new file mode 100644 index 00000000000..a6ac21c76d6 --- /dev/null +++ b/libs/importer/spec/test-data/passwordxp-csv/german-headers.ts @@ -0,0 +1,7 @@ +export const germanHeaders = `Titel;Benutzername;Konto;URL;Passwort;Geändert am;Erstellt am;Läuft ab am;Beschreibung;Geändert von +>>> +Title2;Username2;Account2;http://URL2.com;12345678;27-3-2024 08:11:21;27-3-2024 08:11:21;27-5-2024 08:11:21;Test Notes;someone +Title Test 1;Username1;Account1;http://URL1.com;Password1;27-3-2024 08:10:52;27-3-2024 08:10:52;;Test Notes 2; +Certificate 1;;;;;27-3-2024 10:22:39;27-3-2024 10:22:39;;Test Notes Certicate 1; +test;testtest;;http://test;test;27-3-2024 12:36:59;27-3-2024 12:36:59;;Test Notes 3; +`; diff --git a/libs/importer/src/components/import.component.html b/libs/importer/src/components/import.component.html index 33056265de4..0da8127369e 100644 --- a/libs/importer/src/components/import.component.html +++ b/libs/importer/src/components/import.component.html @@ -80,7 +80,13 @@

    {{ "data" | i18n }}

    See detailed instructions on our help site at - + https://bitwarden.com/help/export-your-data/ diff --git a/libs/importer/src/components/import.component.ts b/libs/importer/src/components/import.component.ts index 247b33aa6a9..f2bf7471d44 100644 --- a/libs/importer/src/components/import.component.ts +++ b/libs/importer/src/components/import.component.ts @@ -16,7 +16,7 @@ import { import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; import * as JSZip from "jszip"; import { concat, Observable, Subject, lastValueFrom, combineLatest, firstValueFrom } from "rxjs"; -import { filter, map, takeUntil } from "rxjs/operators"; +import { filter, map, switchMap, takeUntil } from "rxjs/operators"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; @@ -53,6 +53,7 @@ import { SectionHeaderComponent, SelectModule, ToastService, + LinkModule, } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; @@ -115,6 +116,7 @@ const safeProviders: SafeProvider[] = [ ContainerComponent, SectionHeaderComponent, SectionComponent, + LinkModule, ], providers: safeProviders, }) @@ -151,6 +153,8 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit { private _importBlockedByPolicy = false; protected isFromAC = false; + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + formGroup = this.formBuilder.group({ vaultSelector: [ "myVault", @@ -204,6 +208,7 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit { @Optional() protected importCollectionService: ImportCollectionServiceAbstraction, protected toastService: ToastService, + protected accountService: AccountService, ) {} protected get importBlockedByPolicy(): boolean { @@ -255,7 +260,10 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit { private handleImportInit() { // Filter out the no folder-item from folderViews$ - this.folders$ = this.folderService.folderViews$.pipe( + this.folders$ = this.activeUserId$.pipe( + switchMap((userId) => { + return this.folderService.folderViews$(userId); + }), map((folders) => folders.filter((f) => f.id != null)), ); @@ -288,7 +296,9 @@ export class ImportComponent implements OnInit, OnDestroy, AfterViewInit { private async initializeOrganizations() { this.organizations$ = concat( this.organizationService.memberOrganizations$.pipe( - map((orgs) => orgs.filter((org) => org.canAccessImport)), + // Import is an alternative way to create collections during onboarding, so import from Password Manager + // is available to any user who can create collections in the organization. + map((orgs) => orgs.filter((org) => org.canAccessImport || org.canCreateNewCollections)), map((orgs) => orgs.sort(Utils.getSortFunction(this.i18nService, "name"))), ), ); diff --git a/libs/importer/src/importers/index.ts b/libs/importer/src/importers/index.ts index 19b22cfa80d..1ba3a0d9eb8 100644 --- a/libs/importer/src/importers/index.ts +++ b/libs/importer/src/importers/index.ts @@ -45,7 +45,7 @@ export { PasswordBossJsonImporter } from "./passwordboss-json-importer"; export { PasswordDragonXmlImporter } from "./passworddragon-xml-importer"; export { PasswordSafeXmlImporter } from "./passwordsafe-xml-importer"; export { PasswordWalletTxtImporter } from "./passwordwallet-txt-importer"; -export { PasswordXPCsvImporter } from "./passwordxp-csv-importer"; +export { PasswordXPCsvImporter } from "./passsordxp/passwordxp-csv-importer"; export { ProtonPassJsonImporter } from "./protonpass/protonpass-json-importer"; export { PsonoJsonImporter } from "./psono/psono-json-importer"; export { RememBearCsvImporter } from "./remembear-csv-importer"; diff --git a/libs/importer/src/importers/passsordxp/dutch-csv-headers.ts b/libs/importer/src/importers/passsordxp/dutch-csv-headers.ts new file mode 100644 index 00000000000..7f9c219de56 --- /dev/null +++ b/libs/importer/src/importers/passsordxp/dutch-csv-headers.ts @@ -0,0 +1,10 @@ +export const dutchHeaderTranslations: { [key: string]: string } = { + Titel: "Title", + Gebruikersnaam: "Username", + Wachtwoord: "Password", + Gewijzigd: "Modified", + Gemaakt: "Created", + "Verloopt op": "Expire on", + Beschrijving: "Description", + "Gewijzigd door": "Modified by", +}; diff --git a/libs/importer/src/importers/passsordxp/german-csv-headers.ts b/libs/importer/src/importers/passsordxp/german-csv-headers.ts new file mode 100644 index 00000000000..584ad0badca --- /dev/null +++ b/libs/importer/src/importers/passsordxp/german-csv-headers.ts @@ -0,0 +1,11 @@ +export const germanHeaderTranslations: { [key: string]: string } = { + Titel: "Title", + Benutzername: "Username", + Konto: "Account", + Passwort: "Password", + "Geändert am": "Modified", + "Erstellt am": "Created", + "Läuft ab am": "Expire on", + Beschreibung: "Description", + "Geändert von": "Modified by", +}; diff --git a/libs/importer/src/importers/passwordxp-csv-importer.ts b/libs/importer/src/importers/passsordxp/passwordxp-csv-importer.ts similarity index 68% rename from libs/importer/src/importers/passwordxp-csv-importer.ts rename to libs/importer/src/importers/passsordxp/passwordxp-csv-importer.ts index 461432e98d4..226a284ec91 100644 --- a/libs/importer/src/importers/passwordxp-csv-importer.ts +++ b/libs/importer/src/importers/passsordxp/passwordxp-csv-importer.ts @@ -1,12 +1,28 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { ImportResult } from "../models/import-result"; - -import { BaseImporter } from "./base-importer"; -import { Importer } from "./importer"; +import { ImportResult } from "../../models/import-result"; +import { BaseImporter } from "../base-importer"; +import { Importer } from "../importer"; const _mappedColumns = new Set(["Title", "Username", "URL", "Password", "Description"]); +import { dutchHeaderTranslations } from "./dutch-csv-headers"; +import { germanHeaderTranslations } from "./german-csv-headers"; + +/* Translates the headers from non-English to English + * This is necessary because the parser only maps English headers to ciphers + * Currently only supports German and Dutch translations + */ +function translateIntoEnglishHeaders(header: string): string { + const translations: { [key: string]: string } = { + // The header column 'User name' is parsed by the parser, but cannot be used as a variable. This converts it to a valid variable name, prior to parsing. + "User name": "Username", + ...germanHeaderTranslations, + ...dutchHeaderTranslations, + }; + + return translations[header] || header; +} /** * PasswordXP CSV importer @@ -17,15 +33,22 @@ export class PasswordXPCsvImporter extends BaseImporter implements Importer { * @param data */ parse(data: string): Promise { - // The header column 'User name' is parsed by the parser, but cannot be used as a variable. This converts it to a valid variable name, prior to parsing. - data = data.replace(";User name;", ";Username;"); - const result = new ImportResult(); - const results = this.parseCsv(data, true, { skipEmptyLines: true }); + const results = this.parseCsv(data, true, { + skipEmptyLines: true, + transformHeader: translateIntoEnglishHeaders, + }); if (results == null) { result.success = false; return Promise.resolve(result); } + + // If the first row (header check) does not contain the column "Title", then the data is invalid (no translation found) + if (!results[0].Title) { + result.success = false; + return Promise.resolve(result); + } + let currentFolderName = ""; results.forEach((row) => { // Skip rows starting with '>>>' as they indicate items following have no folder assigned to them diff --git a/libs/key-management/src/index.ts b/libs/key-management/src/index.ts index a779d3a9caf..1734d857a0c 100644 --- a/libs/key-management/src/index.ts +++ b/libs/key-management/src/index.ts @@ -17,3 +17,5 @@ export { export { KdfConfigService } from "./abstractions/kdf-config.service"; export { DefaultKdfConfigService } from "./kdf-config.service"; export { KdfType } from "./enums/kdf-type.enum"; + +export * from "./user-asymmetric-key-regeneration"; diff --git a/libs/key-management/src/user-asymmetric-key-regeneration/abstractions/user-asymmetric-key-regeneration-api.service.ts b/libs/key-management/src/user-asymmetric-key-regeneration/abstractions/user-asymmetric-key-regeneration-api.service.ts new file mode 100644 index 00000000000..2b6e093d796 --- /dev/null +++ b/libs/key-management/src/user-asymmetric-key-regeneration/abstractions/user-asymmetric-key-regeneration-api.service.ts @@ -0,0 +1,8 @@ +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; + +export abstract class UserAsymmetricKeysRegenerationApiService { + abstract regenerateUserAsymmetricKeys( + userPublicKey: string, + userKeyEncryptedUserPrivateKey: EncString, + ): Promise; +} diff --git a/libs/key-management/src/user-asymmetric-key-regeneration/abstractions/user-asymmetric-key-regeneration.service.ts b/libs/key-management/src/user-asymmetric-key-regeneration/abstractions/user-asymmetric-key-regeneration.service.ts new file mode 100644 index 00000000000..4703d836db7 --- /dev/null +++ b/libs/key-management/src/user-asymmetric-key-regeneration/abstractions/user-asymmetric-key-regeneration.service.ts @@ -0,0 +1,10 @@ +import { UserId } from "@bitwarden/common/types/guid"; + +export abstract class UserAsymmetricKeysRegenerationService { + /** + * Attempts to regenerate the user's asymmetric keys if they are invalid. + * Requires the PrivateKeyRegeneration feature flag to be enabled if not the method will do nothing. + * @param userId The user id. + */ + abstract regenerateIfNeeded(userId: UserId): Promise; +} diff --git a/libs/key-management/src/user-asymmetric-key-regeneration/index.ts b/libs/key-management/src/user-asymmetric-key-regeneration/index.ts new file mode 100644 index 00000000000..8147d76b492 --- /dev/null +++ b/libs/key-management/src/user-asymmetric-key-regeneration/index.ts @@ -0,0 +1,5 @@ +export { UserAsymmetricKeysRegenerationService } from "./abstractions/user-asymmetric-key-regeneration.service"; +export { DefaultUserAsymmetricKeysRegenerationService } from "./services/default-user-asymmetric-key-regeneration.service"; + +export { UserAsymmetricKeysRegenerationApiService } from "./abstractions/user-asymmetric-key-regeneration-api.service"; +export { DefaultUserAsymmetricKeysRegenerationApiService } from "./services/default-user-asymmetric-key-regeneration-api.service"; diff --git a/libs/key-management/src/user-asymmetric-key-regeneration/models/requests/key-regeneration.request.ts b/libs/key-management/src/user-asymmetric-key-regeneration/models/requests/key-regeneration.request.ts new file mode 100644 index 00000000000..2d3b62aedad --- /dev/null +++ b/libs/key-management/src/user-asymmetric-key-regeneration/models/requests/key-regeneration.request.ts @@ -0,0 +1,11 @@ +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; + +export class KeyRegenerationRequest { + userPublicKey: string; + userKeyEncryptedUserPrivateKey: EncString; + + constructor(userPublicKey: string, userKeyEncryptedUserPrivateKey: EncString) { + this.userPublicKey = userPublicKey; + this.userKeyEncryptedUserPrivateKey = userKeyEncryptedUserPrivateKey; + } +} diff --git a/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration-api.service.ts b/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration-api.service.ts new file mode 100644 index 00000000000..d1fe89a74eb --- /dev/null +++ b/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration-api.service.ts @@ -0,0 +1,29 @@ +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; + +import { UserAsymmetricKeysRegenerationApiService } from "../abstractions/user-asymmetric-key-regeneration-api.service"; +import { KeyRegenerationRequest } from "../models/requests/key-regeneration.request"; + +export class DefaultUserAsymmetricKeysRegenerationApiService + implements UserAsymmetricKeysRegenerationApiService +{ + constructor(private apiService: ApiService) {} + + async regenerateUserAsymmetricKeys( + userPublicKey: string, + userKeyEncryptedUserPrivateKey: EncString, + ): Promise { + const request: KeyRegenerationRequest = { + userPublicKey, + userKeyEncryptedUserPrivateKey, + }; + + await this.apiService.send( + "POST", + "/accounts/key-management/regenerate-keys", + request, + true, + true, + ); + } +} diff --git a/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration.service.spec.ts b/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration.service.spec.ts new file mode 100644 index 00000000000..77d7ebbb814 --- /dev/null +++ b/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration.service.spec.ts @@ -0,0 +1,306 @@ +import { MockProxy, mock } from "jest-mock-extended"; +import { of, throwError } from "rxjs"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; +import { EncryptedString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { ContainerService } from "@bitwarden/common/platform/services/container.service"; +import { makeStaticByteArray, mockEnc } from "@bitwarden/common/spec"; +import { CsprngArray } from "@bitwarden/common/types/csprng"; +import { UserId } from "@bitwarden/common/types/guid"; +import { UserKey } from "@bitwarden/common/types/key"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; +import { BitwardenClient, VerifyAsymmetricKeysResponse } from "@bitwarden/sdk-internal"; + +import { KeyService } from "../../abstractions/key.service"; +import { UserAsymmetricKeysRegenerationApiService } from "../abstractions/user-asymmetric-key-regeneration-api.service"; + +import { DefaultUserAsymmetricKeysRegenerationService } from "./default-user-asymmetric-key-regeneration.service"; + +function setupVerificationResponse( + mockVerificationResponse: VerifyAsymmetricKeysResponse, + sdkService: MockProxy, +) { + const mockKeyPairResponse = { + userPublicKey: "userPublicKey", + userKeyEncryptedPrivateKey: "userKeyEncryptedPrivateKey", + }; + + sdkService.client$ = of({ + crypto: () => ({ + verify_asymmetric_keys: jest.fn().mockReturnValue(mockVerificationResponse), + make_key_pair: jest.fn().mockReturnValue(mockKeyPairResponse), + }), + free: jest.fn(), + echo: jest.fn(), + version: jest.fn(), + throw: jest.fn(), + catch: jest.fn(), + } as unknown as BitwardenClient); +} + +function setupUserKeyValidation( + cipherService: MockProxy, + keyService: MockProxy, + encryptService: MockProxy, +) { + const cipher = new Cipher(); + cipher.id = "id"; + cipher.edit = true; + cipher.viewPassword = true; + cipher.favorite = false; + cipher.name = mockEnc("EncryptedString"); + cipher.notes = mockEnc("EncryptedString"); + cipher.key = mockEnc("EncKey"); + cipherService.getAll.mockResolvedValue([cipher]); + encryptService.decryptToBytes.mockResolvedValue(makeStaticByteArray(64)); + (window as any).bitwardenContainerService = new ContainerService(keyService, encryptService); +} + +describe("regenerateIfNeeded", () => { + let sut: DefaultUserAsymmetricKeysRegenerationService; + const userId = "userId" as UserId; + + let keyService: MockProxy; + let cipherService: MockProxy; + let userAsymmetricKeysRegenerationApiService: MockProxy; + let logService: MockProxy; + let sdkService: MockProxy; + let apiService: MockProxy; + let configService: MockProxy; + let encryptService: MockProxy; + + beforeEach(() => { + keyService = mock(); + cipherService = mock(); + userAsymmetricKeysRegenerationApiService = mock(); + logService = mock(); + sdkService = mock(); + apiService = mock(); + configService = mock(); + encryptService = mock(); + + sut = new DefaultUserAsymmetricKeysRegenerationService( + keyService, + cipherService, + userAsymmetricKeysRegenerationApiService, + logService, + sdkService, + apiService, + configService, + ); + + configService.getFeatureFlag.mockResolvedValue(true); + + const mockRandomBytes = new Uint8Array(64) as CsprngArray; + const mockEncryptedString = new SymmetricCryptoKey( + mockRandomBytes, + ).toString() as EncryptedString; + const mockUserKey = new SymmetricCryptoKey(mockRandomBytes) as UserKey; + keyService.userKey$.mockReturnValue(of(mockUserKey)); + keyService.userEncryptedPrivateKey$.mockReturnValue(of(mockEncryptedString)); + apiService.getUserPublicKey.mockResolvedValue({ + userId: "userId", + publicKey: "publicKey", + } as any); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it("should not call regeneration code when feature flag is off", async () => { + configService.getFeatureFlag.mockResolvedValue(false); + + await sut.regenerateIfNeeded(userId); + + expect(keyService.userKey$).not.toHaveBeenCalled(); + }); + + it("should not regenerate when top level error is thrown", async () => { + const mockVerificationResponse: VerifyAsymmetricKeysResponse = { + privateKeyDecryptable: true, + validPrivateKey: false, + }; + setupVerificationResponse(mockVerificationResponse, sdkService); + keyService.userKey$.mockReturnValue(throwError(() => new Error("error"))); + + await sut.regenerateIfNeeded(userId); + + expect( + userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys, + ).not.toHaveBeenCalled(); + expect(keyService.setPrivateKey).not.toHaveBeenCalled(); + }); + + it("should not regenerate when private key is decryptable and valid", async () => { + const mockVerificationResponse: VerifyAsymmetricKeysResponse = { + privateKeyDecryptable: true, + validPrivateKey: true, + }; + setupVerificationResponse(mockVerificationResponse, sdkService); + + await sut.regenerateIfNeeded(userId); + + expect( + userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys, + ).not.toHaveBeenCalled(); + expect(keyService.setPrivateKey).not.toHaveBeenCalled(); + }); + + it("should regenerate when private key is decryptable and invalid", async () => { + const mockVerificationResponse: VerifyAsymmetricKeysResponse = { + privateKeyDecryptable: true, + validPrivateKey: false, + }; + setupVerificationResponse(mockVerificationResponse, sdkService); + + await sut.regenerateIfNeeded(userId); + + expect( + userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys, + ).toHaveBeenCalled(); + expect(keyService.setPrivateKey).toHaveBeenCalled(); + }); + + it("should not set private key on known API error", async () => { + const mockVerificationResponse: VerifyAsymmetricKeysResponse = { + privateKeyDecryptable: true, + validPrivateKey: false, + }; + setupVerificationResponse(mockVerificationResponse, sdkService); + + userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys.mockRejectedValue( + new Error("Key regeneration not supported for this user."), + ); + + await sut.regenerateIfNeeded(userId); + + expect( + userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys, + ).toHaveBeenCalled(); + expect(keyService.setPrivateKey).not.toHaveBeenCalled(); + }); + + it("should not set private key on unknown API error", async () => { + const mockVerificationResponse: VerifyAsymmetricKeysResponse = { + privateKeyDecryptable: true, + validPrivateKey: false, + }; + setupVerificationResponse(mockVerificationResponse, sdkService); + + userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys.mockRejectedValue( + new Error("error"), + ); + + await sut.regenerateIfNeeded(userId); + + expect( + userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys, + ).toHaveBeenCalled(); + expect(keyService.setPrivateKey).not.toHaveBeenCalled(); + }); + + it("should regenerate when private key is not decryptable and user key is valid", async () => { + const mockVerificationResponse: VerifyAsymmetricKeysResponse = { + privateKeyDecryptable: false, + validPrivateKey: true, + }; + setupVerificationResponse(mockVerificationResponse, sdkService); + setupUserKeyValidation(cipherService, keyService, encryptService); + + await sut.regenerateIfNeeded(userId); + + expect( + userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys, + ).toHaveBeenCalled(); + expect(keyService.setPrivateKey).toHaveBeenCalled(); + }); + + it("should not regenerate when private key is not decryptable and user key is invalid", async () => { + const mockVerificationResponse: VerifyAsymmetricKeysResponse = { + privateKeyDecryptable: false, + validPrivateKey: true, + }; + setupVerificationResponse(mockVerificationResponse, sdkService); + setupUserKeyValidation(cipherService, keyService, encryptService); + encryptService.decryptToBytes.mockRejectedValue(new Error("error")); + + await sut.regenerateIfNeeded(userId); + + expect( + userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys, + ).not.toHaveBeenCalled(); + expect(keyService.setPrivateKey).not.toHaveBeenCalled(); + }); + + it("should not regenerate when private key is not decryptable and no ciphers to check", async () => { + const mockVerificationResponse: VerifyAsymmetricKeysResponse = { + privateKeyDecryptable: false, + validPrivateKey: true, + }; + setupVerificationResponse(mockVerificationResponse, sdkService); + cipherService.getAll.mockResolvedValue([]); + + await sut.regenerateIfNeeded(userId); + + expect( + userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys, + ).not.toHaveBeenCalled(); + expect(keyService.setPrivateKey).not.toHaveBeenCalled(); + }); + + it("should regenerate when private key is not decryptable and invalid and user key is valid", async () => { + const mockVerificationResponse: VerifyAsymmetricKeysResponse = { + privateKeyDecryptable: false, + validPrivateKey: false, + }; + setupVerificationResponse(mockVerificationResponse, sdkService); + setupUserKeyValidation(cipherService, keyService, encryptService); + + await sut.regenerateIfNeeded(userId); + + expect( + userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys, + ).toHaveBeenCalled(); + expect(keyService.setPrivateKey).toHaveBeenCalled(); + }); + + it("should not regenerate when private key is not decryptable and invalid and user key is invalid", async () => { + const mockVerificationResponse: VerifyAsymmetricKeysResponse = { + privateKeyDecryptable: false, + validPrivateKey: false, + }; + setupVerificationResponse(mockVerificationResponse, sdkService); + setupUserKeyValidation(cipherService, keyService, encryptService); + encryptService.decryptToBytes.mockRejectedValue(new Error("error")); + + await sut.regenerateIfNeeded(userId); + + expect( + userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys, + ).not.toHaveBeenCalled(); + expect(keyService.setPrivateKey).not.toHaveBeenCalled(); + }); + + it("should not regenerate when private key is not decryptable and invalid and no ciphers to check", async () => { + const mockVerificationResponse: VerifyAsymmetricKeysResponse = { + privateKeyDecryptable: false, + validPrivateKey: false, + }; + setupVerificationResponse(mockVerificationResponse, sdkService); + cipherService.getAll.mockResolvedValue([]); + + await sut.regenerateIfNeeded(userId); + + expect( + userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys, + ).not.toHaveBeenCalled(); + expect(keyService.setPrivateKey).not.toHaveBeenCalled(); + }); +}); diff --git a/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration.service.ts b/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration.service.ts new file mode 100644 index 00000000000..ffaa3a82608 --- /dev/null +++ b/libs/key-management/src/user-asymmetric-key-regeneration/services/default-user-asymmetric-key-regeneration.service.ts @@ -0,0 +1,158 @@ +import { combineLatest, firstValueFrom, map } from "rxjs"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; +import { EncString } from "@bitwarden/common/platform/models/domain/enc-string"; +import { UserId } from "@bitwarden/common/types/guid"; +import { UserKey } from "@bitwarden/common/types/key"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; + +import { KeyService } from "../../abstractions/key.service"; +import { UserAsymmetricKeysRegenerationApiService } from "../abstractions/user-asymmetric-key-regeneration-api.service"; +import { UserAsymmetricKeysRegenerationService } from "../abstractions/user-asymmetric-key-regeneration.service"; + +export class DefaultUserAsymmetricKeysRegenerationService + implements UserAsymmetricKeysRegenerationService +{ + constructor( + private keyService: KeyService, + private cipherService: CipherService, + private userAsymmetricKeysRegenerationApiService: UserAsymmetricKeysRegenerationApiService, + private logService: LogService, + private sdkService: SdkService, + private apiService: ApiService, + private configService: ConfigService, + ) {} + + async regenerateIfNeeded(userId: UserId): Promise { + try { + const privateKeyRegenerationFlag = await this.configService.getFeatureFlag( + FeatureFlag.PrivateKeyRegeneration, + ); + + if (privateKeyRegenerationFlag) { + const shouldRegenerate = await this.shouldRegenerate(userId); + if (shouldRegenerate) { + await this.regenerateUserAsymmetricKeys(userId); + } + } + } catch (error) { + this.logService.error( + "[UserAsymmetricKeyRegeneration] An error occurred: " + + error + + " Skipping regeneration for the user.", + ); + } + } + + private async shouldRegenerate(userId: UserId): Promise { + const [userKey, userKeyEncryptedPrivateKey, publicKeyResponse] = await firstValueFrom( + combineLatest([ + this.keyService.userKey$(userId), + this.keyService.userEncryptedPrivateKey$(userId), + this.apiService.getUserPublicKey(userId), + ]), + ); + + const verificationResponse = await firstValueFrom( + this.sdkService.client$.pipe( + map((sdk) => { + if (sdk === undefined) { + throw new Error("SDK is undefined"); + } + return sdk.crypto().verify_asymmetric_keys({ + userKey: userKey.keyB64, + userPublicKey: publicKeyResponse.publicKey, + userKeyEncryptedPrivateKey: userKeyEncryptedPrivateKey, + }); + }), + ), + ); + + if (verificationResponse.privateKeyDecryptable) { + if (verificationResponse.validPrivateKey) { + // The private key is decryptable and valid. Should not regenerate. + return false; + } else { + // The private key is decryptable but not valid so we should regenerate it. + this.logService.info( + "[UserAsymmetricKeyRegeneration] User's private key is decryptable but not a valid key, attempting regeneration.", + ); + return true; + } + } + + // The private isn't decryptable, check to see if we can decrypt something with the userKey. + const userKeyCanDecrypt = await this.userKeyCanDecrypt(userKey); + if (userKeyCanDecrypt) { + this.logService.info( + "[UserAsymmetricKeyRegeneration] User Asymmetric Key decryption failure detected, attempting regeneration.", + ); + return true; + } + + this.logService.warning( + "[UserAsymmetricKeyRegeneration] User Asymmetric Key decryption failure detected, but unable to determine User Symmetric Key validity, skipping regeneration.", + ); + return false; + } + + private async regenerateUserAsymmetricKeys(userId: UserId): Promise { + const userKey = await firstValueFrom(this.keyService.userKey$(userId)); + const makeKeyPairResponse = await firstValueFrom( + this.sdkService.client$.pipe( + map((sdk) => { + if (sdk === undefined) { + throw new Error("SDK is undefined"); + } + return sdk.crypto().make_key_pair(userKey.keyB64); + }), + ), + ); + + try { + await this.userAsymmetricKeysRegenerationApiService.regenerateUserAsymmetricKeys( + makeKeyPairResponse.userPublicKey, + new EncString(makeKeyPairResponse.userKeyEncryptedPrivateKey), + ); + } catch (error: any) { + if (error?.message === "Key regeneration not supported for this user.") { + this.logService.info( + "[UserAsymmetricKeyRegeneration] Regeneration not supported for this user at this time.", + ); + } else { + this.logService.error( + "[UserAsymmetricKeyRegeneration] Regeneration error when submitting the request to the server: " + + error, + ); + } + return; + } + + await this.keyService.setPrivateKey(makeKeyPairResponse.userKeyEncryptedPrivateKey, userId); + this.logService.info( + "[UserAsymmetricKeyRegeneration] User's asymmetric keys successfully regenerated.", + ); + } + + private async userKeyCanDecrypt(userKey: UserKey): Promise { + const ciphers = await this.cipherService.getAll(); + const cipher = ciphers.find((cipher) => cipher.organizationId == null); + + if (cipher != null) { + try { + await cipher.decrypt(userKey); + return true; + } catch (error) { + this.logService.error( + "[UserAsymmetricKeyRegeneration] User Symmetric Key validation error: " + error, + ); + return false; + } + } + return false; + } +} diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts index 9d58bcbf559..069df8606bf 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.spec.ts @@ -1,5 +1,5 @@ import { mock, MockProxy } from "jest-mock-extended"; -import { BehaviorSubject } from "rxjs"; +import { BehaviorSubject, of } from "rxjs"; import { PinServiceAbstraction } from "@bitwarden/auth/common"; import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -178,8 +178,8 @@ describe("VaultExportService", () => { const activeAccount = { id: userId, ...accountInfo }; accountService.activeAccount$ = new BehaviorSubject(activeAccount); - folderService.getAllDecryptedFromState.mockResolvedValue(UserFolderViews); - folderService.getAllFromState.mockResolvedValue(UserFolders); + folderService.folderViews$.mockReturnValue(of(UserFolderViews)); + folderService.folders$.mockReturnValue(of(UserFolders)); kdfConfigService.getKdfConfig.mockResolvedValue(DEFAULT_KDF_CONFIG); encryptService.encrypt.mockResolvedValue(new EncString("encrypted")); @@ -295,7 +295,7 @@ describe("VaultExportService", () => { it("exported unencrypted object contains folders", async () => { cipherService.getAllDecrypted.mockResolvedValue(UserCipherViews.slice(0, 1)); - await folderService.getAllDecryptedFromState(); + folderService.folderViews$.mockReturnValue(of(UserFolderViews)); const actual = await exportService.getExport("json"); expectEqualFolderViews(UserFolderViews, actual); @@ -303,7 +303,7 @@ describe("VaultExportService", () => { it("exported encrypted json contains folders", async () => { cipherService.getAll.mockResolvedValue(UserCipherDomains.slice(0, 1)); - await folderService.getAllFromState(); + folderService.folders$.mockReturnValue(of(UserFolders)); const actual = await exportService.getExport("encrypted_json"); expectEqualFolders(UserFolders, actual); diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts index dbd2024d3ad..f9df9c7057f 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/individual-vault-export.service.ts @@ -32,6 +32,8 @@ export class IndividualVaultExportService extends BaseVaultExportService implements IndividualVaultExportServiceAbstraction { + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); + constructor( private folderService: FolderService, private cipherService: CipherService, @@ -61,9 +63,10 @@ export class IndividualVaultExportService let decFolders: FolderView[] = []; let decCiphers: CipherView[] = []; const promises = []; + const activeUserId = await firstValueFrom(this.activeUserId$); promises.push( - this.folderService.getAllDecryptedFromState().then((folders) => { + firstValueFrom(this.folderService.folderViews$(activeUserId)).then((folders) => { decFolders = folders; }), ); @@ -87,9 +90,10 @@ export class IndividualVaultExportService let folders: Folder[] = []; let ciphers: Cipher[] = []; const promises = []; + const activeUserId = await firstValueFrom(this.activeUserId$); promises.push( - this.folderService.getAllFromState().then((f) => { + firstValueFrom(this.folderService.folders$(activeUserId)).then((f) => { folders = f; }), ); @@ -102,10 +106,9 @@ export class IndividualVaultExportService await Promise.all(promises); - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), + const userKey = await this.keyService.getUserKeyWithLegacySupport( + await firstValueFrom(this.activeUserId$), ); - const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId); const encKeyValidation = await this.encryptService.encrypt(Utils.newGuid(), userKey); const jsonDoc: BitwardenEncryptedIndividualJsonExport = { diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts b/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts index 6bf05796070..7e04972f8ab 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.spec.ts @@ -1,5 +1,5 @@ import { mock, MockProxy } from "jest-mock-extended"; -import { BehaviorSubject } from "rxjs"; +import { BehaviorSubject, of } from "rxjs"; import { PinServiceAbstraction } from "@bitwarden/auth/common"; import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -168,8 +168,8 @@ describe("VaultExportService", () => { kdfConfigService = mock(); - folderService.getAllDecryptedFromState.mockResolvedValue(UserFolderViews); - folderService.getAllFromState.mockResolvedValue(UserFolders); + folderService.folderViews$.mockReturnValue(of(UserFolderViews)); + folderService.folders$.mockReturnValue(of(UserFolders)); kdfConfigService.getKdfConfig.mockResolvedValue(DEFAULT_KDF_CONFIG); encryptService.encrypt.mockResolvedValue(new EncString("encrypted")); keyService.userKey$.mockReturnValue(new BehaviorSubject("mockOriginalUserKey" as any)); @@ -294,7 +294,7 @@ describe("VaultExportService", () => { it("exported unencrypted object contains folders", async () => { cipherService.getAllDecrypted.mockResolvedValue(UserCipherViews.slice(0, 1)); - await folderService.getAllDecryptedFromState(); + const actual = await exportService.getExport("json"); expectEqualFolderViews(UserFolderViews, actual); @@ -302,7 +302,7 @@ describe("VaultExportService", () => { it("exported encrypted json contains folders", async () => { cipherService.getAll.mockResolvedValue(UserCipherDomains.slice(0, 1)); - await folderService.getAllFromState(); + const actual = await exportService.getExport("encrypted_json"); expectEqualFolders(UserFolders, actual); diff --git a/libs/vault/src/cipher-form/cipher-form.stories.ts b/libs/vault/src/cipher-form/cipher-form.stories.ts index 98ccdec360e..1d44e4542bc 100644 --- a/libs/vault/src/cipher-form/cipher-form.stories.ts +++ b/libs/vault/src/cipher-form/cipher-form.stories.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { importProvidersFrom } from "@angular/core"; import { action } from "@storybook/addon-actions"; import { @@ -234,7 +232,7 @@ export const Edit: Story = { config: { ...defaultConfig, mode: "edit", - originalCipher: defaultConfig.originalCipher, + originalCipher: defaultConfig.originalCipher!, }, }, }; @@ -245,7 +243,7 @@ export const PartialEdit: Story = { config: { ...defaultConfig, mode: "partial-edit", - originalCipher: defaultConfig.originalCipher, + originalCipher: defaultConfig.originalCipher!, }, }, }; @@ -256,7 +254,7 @@ export const Clone: Story = { config: { ...defaultConfig, mode: "clone", - originalCipher: defaultConfig.originalCipher, + originalCipher: defaultConfig.originalCipher!, }, }, }; @@ -269,7 +267,7 @@ export const NoPersonalOwnership: Story = { mode: "add", allowPersonalOwnership: false, originalCipher: defaultConfig.originalCipher, - organizations: defaultConfig.organizations, + organizations: defaultConfig.organizations!, }, }, }; diff --git a/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.spec.ts b/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.spec.ts index 749093902d7..8e0d4f7a665 100644 --- a/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.spec.ts +++ b/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.spec.ts @@ -98,7 +98,7 @@ describe("DeleteAttachmentComponent", () => { expect(showToast).toHaveBeenCalledWith({ variant: "success", - title: null, + title: "", message: "deletedAttachment", }); }); diff --git a/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.ts b/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.ts index d5a7039ad70..b1ada907b1d 100644 --- a/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.ts +++ b/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { CommonModule } from "@angular/common"; import { Component, EventEmitter, Input, Output } from "@angular/core"; @@ -24,10 +22,10 @@ import { }) export class DeleteAttachmentComponent { /** Id of the cipher associated with the attachment */ - @Input({ required: true }) cipherId: string; + @Input({ required: true }) cipherId!: string; /** The attachment that is can be deleted */ - @Input({ required: true }) attachment: AttachmentView; + @Input({ required: true }) attachment!: AttachmentView; /** Emits when the attachment is successfully deleted */ @Output() onDeletionSuccess = new EventEmitter(); @@ -56,7 +54,7 @@ export class DeleteAttachmentComponent { this.toastService.showToast({ variant: "success", - title: null, + title: "", message: this.i18nService.t("deletedAttachment"), }); diff --git a/libs/vault/src/cipher-form/services/default-cipher-form-config.service.ts b/libs/vault/src/cipher-form/services/default-cipher-form-config.service.ts index c2fbd024aa8..93a53345d3a 100644 --- a/libs/vault/src/cipher-form/services/default-cipher-form-config.service.ts +++ b/libs/vault/src/cipher-form/services/default-cipher-form-config.service.ts @@ -7,6 +7,7 @@ import { CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { OrganizationUserStatusType, PolicyType } from "@bitwarden/common/admin-console/enums"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { CipherId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; @@ -31,12 +32,17 @@ export class DefaultCipherFormConfigService implements CipherFormConfigService { private cipherService: CipherService = inject(CipherService); private folderService: FolderService = inject(FolderService); private collectionService: CollectionService = inject(CollectionService); + private accountService = inject(AccountService); + + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); async buildConfig( mode: CipherFormMode, cipherId?: CipherId, cipherType?: CipherType, ): Promise { + const activeUserId = await firstValueFrom(this.activeUserId$); + const [organizations, collections, allowPersonalOwnership, folders, cipher] = await firstValueFrom( combineLatest([ @@ -49,9 +55,9 @@ export class DefaultCipherFormConfigService implements CipherFormConfigService { ), ), this.allowPersonalOwnership$, - this.folderService.folders$.pipe( + this.folderService.folders$(activeUserId).pipe( switchMap((f) => - this.folderService.folderViews$.pipe( + this.folderService.folderViews$(activeUserId).pipe( filter((d) => d.length - 1 === f.length), // -1 for "No Folder" in folderViews$ ), ), diff --git a/libs/vault/src/cipher-view/additional-options/additional-options.component.ts b/libs/vault/src/cipher-view/additional-options/additional-options.component.ts index d590fbf3d50..9f8f034d59b 100644 --- a/libs/vault/src/cipher-view/additional-options/additional-options.component.ts +++ b/libs/vault/src/cipher-view/additional-options/additional-options.component.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { CommonModule } from "@angular/common"; import { Component, Input } from "@angular/core"; @@ -31,5 +29,5 @@ import { ], }) export class AdditionalOptionsComponent { - @Input() notes: string; + @Input() notes: string = ""; } diff --git a/libs/vault/src/cipher-view/cipher-view.component.ts b/libs/vault/src/cipher-view/cipher-view.component.ts index d525b88d924..4bd87a7869d 100644 --- a/libs/vault/src/cipher-view/cipher-view.component.ts +++ b/libs/vault/src/cipher-view/cipher-view.component.ts @@ -1,13 +1,12 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { CommonModule } from "@angular/common"; import { Component, Input, OnChanges, OnDestroy } from "@angular/core"; -import { firstValueFrom, Observable, Subject, takeUntil } from "rxjs"; +import { firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { isCardExpired } from "@bitwarden/common/autofill/utils"; import { CollectionId } from "@bitwarden/common/types/guid"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; @@ -48,19 +47,21 @@ import { ViewIdentitySectionsComponent } from "./view-identity-sections/view-ide ], }) export class CipherViewComponent implements OnChanges, OnDestroy { - @Input({ required: true }) cipher: CipherView; + @Input({ required: true }) cipher: CipherView | null = null; + + private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); /** * Optional list of collections the cipher is assigned to. If none are provided, they will be fetched using the * `CipherService` and the `collectionIds` property of the cipher. */ - @Input() collections: CollectionView[]; + @Input() collections?: CollectionView[]; /** Should be set to true when the component is used within the Admin Console */ @Input() isAdminConsole?: boolean = false; - organization$: Observable; - folder$: Observable; + organization$: Observable | undefined; + folder$: Observable | undefined; private destroyed$: Subject = new Subject(); cardIsExpired: boolean = false; @@ -68,6 +69,7 @@ export class CipherViewComponent implements OnChanges, OnDestroy { private organizationService: OrganizationService, private collectionService: CollectionService, private folderService: FolderService, + private accountService: AccountService, ) {} async ngOnChanges() { @@ -86,24 +88,38 @@ export class CipherViewComponent implements OnChanges, OnDestroy { } get hasCard() { + if (!this.cipher) { + return false; + } + const { cardholderName, code, expMonth, expYear, number } = this.cipher.card; return cardholderName || code || expMonth || expYear || number; } get hasLogin() { + if (!this.cipher) { + return false; + } + const { username, password, totp } = this.cipher.login; return username || password || totp; } get hasAutofill() { - return this.cipher.login?.uris.length > 0; + const uris = this.cipher?.login?.uris.length ?? 0; + + return uris > 0; } get hasSshKey() { - return this.cipher.sshKey?.privateKey; + return !!this.cipher?.sshKey?.privateKey; } async loadCipherData() { + if (!this.cipher) { + return; + } + // Load collections if not provided and the cipher has collectionIds if ( this.cipher.collectionIds && @@ -124,8 +140,14 @@ export class CipherViewComponent implements OnChanges, OnDestroy { } if (this.cipher.folderId) { + const activeUserId = await firstValueFrom(this.activeUserId$); + + if (!activeUserId) { + return; + } + this.folder$ = this.folderService - .getDecrypted$(this.cipher.folderId) + .getDecrypted$(this.cipher.folderId, activeUserId) .pipe(takeUntil(this.destroyed$)); } } diff --git a/libs/vault/src/cipher-view/item-history/item-history-v2.component.ts b/libs/vault/src/cipher-view/item-history/item-history-v2.component.ts index 6ecd95a7b75..f49d7030d77 100644 --- a/libs/vault/src/cipher-view/item-history/item-history-v2.component.ts +++ b/libs/vault/src/cipher-view/item-history/item-history-v2.component.ts @@ -5,7 +5,6 @@ import { Component, Input } from "@angular/core"; import { RouterModule } from "@angular/router"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { CipherId } from "@bitwarden/common/types/guid"; import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions/view-password-history.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -45,6 +44,6 @@ export class ItemHistoryV2Component { * View the password history for the cipher. */ async viewPasswordHistory() { - await this.viewPasswordHistoryService.viewPasswordHistory(this.cipher?.id as CipherId); + await this.viewPasswordHistoryService.viewPasswordHistory(this.cipher); } } diff --git a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html index 5b6b995d095..8503604bf7c 100644 --- a/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html +++ b/libs/vault/src/cipher-view/login-credentials/login-credentials-view.component.html @@ -28,17 +28,34 @@

    {{ "loginCredentials" | i18n }}

    > - {{ "password" | i18n }} + + {{ "password" | i18n }} + + + + + + diff --git a/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-one.component.spec.ts b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-one.component.spec.ts new file mode 100644 index 00000000000..c6eccb78739 --- /dev/null +++ b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-one.component.spec.ts @@ -0,0 +1,173 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; +import { Router } from "@angular/router"; +import { BehaviorSubject } from "rxjs"; + +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; + +import { NewDeviceVerificationNoticeService } from "../../services/new-device-verification-notice.service"; + +import { NewDeviceVerificationNoticePageOneComponent } from "./new-device-verification-notice-page-one.component"; + +describe("NewDeviceVerificationNoticePageOneComponent", () => { + let component: NewDeviceVerificationNoticePageOneComponent; + let fixture: ComponentFixture; + + const activeAccount$ = new BehaviorSubject({ email: "test@example.com", id: "acct-1" }); + const navigate = jest.fn().mockResolvedValue(null); + const updateNewDeviceVerificationNoticeState = jest.fn().mockResolvedValue(null); + const getFeatureFlag = jest.fn().mockResolvedValue(null); + + beforeEach(async () => { + navigate.mockClear(); + updateNewDeviceVerificationNoticeState.mockClear(); + getFeatureFlag.mockClear(); + + await TestBed.configureTestingModule({ + providers: [ + { provide: I18nService, useValue: { t: (...key: string[]) => key.join(" ") } }, + { provide: Router, useValue: { navigate } }, + { provide: AccountService, useValue: { activeAccount$ } }, + { + provide: NewDeviceVerificationNoticeService, + useValue: { updateNewDeviceVerificationNoticeState }, + }, + { provide: PlatformUtilsService, useValue: { getClientType: () => false } }, + { provide: ConfigService, useValue: { getFeatureFlag } }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(NewDeviceVerificationNoticePageOneComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("sets initial properties", () => { + expect(component["currentEmail"]).toBe("test@example.com"); + expect(component["currentUserId"]).toBe("acct-1"); + }); + + describe("temporary flag submission", () => { + beforeEach(() => { + getFeatureFlag.mockImplementation((key) => { + if (key === FeatureFlag.NewDeviceVerificationTemporaryDismiss) { + return Promise.resolve(true); + } + + return Promise.resolve(false); + }); + }); + + describe("no email access", () => { + beforeEach(() => { + component["formGroup"].controls.hasEmailAccess.setValue(0); + fixture.detectChanges(); + + const submit = fixture.debugElement.query(By.css('button[type="submit"]')); + submit.nativeElement.click(); + }); + + it("redirects to step two ", () => { + expect(navigate).toHaveBeenCalledTimes(1); + expect(navigate).toHaveBeenCalledWith(["new-device-notice/setup"]); + }); + + it("does not update notice state", () => { + expect(getFeatureFlag).not.toHaveBeenCalled(); + expect(updateNewDeviceVerificationNoticeState).not.toHaveBeenCalled(); + }); + }); + + describe("has email access", () => { + beforeEach(() => { + component["formGroup"].controls.hasEmailAccess.setValue(1); + fixture.detectChanges(); + + jest.useFakeTimers(); + jest.setSystemTime(new Date("2024-03-03T00:00:00.000Z")); + const submit = fixture.debugElement.query(By.css('button[type="submit"]')); + submit.nativeElement.click(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it("redirects to the vault", () => { + expect(navigate).toHaveBeenCalledTimes(1); + expect(navigate).toHaveBeenCalledWith(["/vault"]); + }); + + it("updates notice state with a new date", () => { + expect(updateNewDeviceVerificationNoticeState).toHaveBeenCalledWith("acct-1", { + last_dismissal: new Date("2024-03-03T00:00:00.000Z"), + permanent_dismissal: false, + }); + }); + }); + }); + + describe("permanent flag submission", () => { + beforeEach(() => { + getFeatureFlag.mockImplementation((key) => { + if (key === FeatureFlag.NewDeviceVerificationPermanentDismiss) { + return Promise.resolve(true); + } + + return Promise.resolve(false); + }); + }); + + describe("no email access", () => { + beforeEach(() => { + component["formGroup"].controls.hasEmailAccess.setValue(0); + fixture.detectChanges(); + + const submit = fixture.debugElement.query(By.css('button[type="submit"]')); + submit.nativeElement.click(); + }); + + it("redirects to step two", () => { + expect(navigate).toHaveBeenCalledTimes(1); + expect(navigate).toHaveBeenCalledWith(["new-device-notice/setup"]); + }); + + it("does not update notice state", () => { + expect(getFeatureFlag).not.toHaveBeenCalled(); + expect(updateNewDeviceVerificationNoticeState).not.toHaveBeenCalled(); + }); + }); + + describe("has email access", () => { + beforeEach(() => { + component["formGroup"].controls.hasEmailAccess.setValue(1); + fixture.detectChanges(); + + jest.useFakeTimers(); + jest.setSystemTime(new Date("2024-04-04T00:00:00.000Z")); + const submit = fixture.debugElement.query(By.css('button[type="submit"]')); + submit.nativeElement.click(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it("redirects to the vault ", () => { + expect(navigate).toHaveBeenCalledTimes(1); + expect(navigate).toHaveBeenCalledWith(["/vault"]); + }); + + it("updates notice state with a new date", () => { + expect(updateNewDeviceVerificationNoticeState).toHaveBeenCalledWith("acct-1", { + last_dismissal: new Date("2024-04-04T00:00:00.000Z"), + permanent_dismissal: true, + }); + }); + }); + }); +}); diff --git a/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-one.component.ts b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-one.component.ts new file mode 100644 index 00000000000..4ae3f50f3cb --- /dev/null +++ b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-one.component.ts @@ -0,0 +1,122 @@ +import { LiveAnnouncer } from "@angular/cdk/a11y"; +import { CommonModule } from "@angular/common"; +import { AfterViewInit, Component, OnInit } from "@angular/core"; +import { FormBuilder, FormControl, ReactiveFormsModule } from "@angular/forms"; +import { Router } from "@angular/router"; +import { firstValueFrom, Observable } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { ClientType } from "@bitwarden/common/enums"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { UserId } from "@bitwarden/common/types/guid"; +import { + AsyncActionsModule, + ButtonModule, + CardComponent, + FormFieldModule, + RadioButtonModule, + TypographyModule, +} from "@bitwarden/components"; + +import { + NewDeviceVerificationNotice, + NewDeviceVerificationNoticeService, +} from "./../../services/new-device-verification-notice.service"; + +@Component({ + standalone: true, + selector: "app-new-device-verification-notice-page-one", + templateUrl: "./new-device-verification-notice-page-one.component.html", + imports: [ + CardComponent, + CommonModule, + JslibModule, + TypographyModule, + ButtonModule, + RadioButtonModule, + FormFieldModule, + AsyncActionsModule, + ReactiveFormsModule, + ], +}) +export class NewDeviceVerificationNoticePageOneComponent implements OnInit, AfterViewInit { + protected formGroup = this.formBuilder.group({ + hasEmailAccess: new FormControl(0), + }); + protected isDesktop: boolean; + readonly currentAcct$: Observable = this.accountService.activeAccount$; + protected currentEmail: string = ""; + private currentUserId: UserId | null = null; + + constructor( + private formBuilder: FormBuilder, + private router: Router, + private accountService: AccountService, + private newDeviceVerificationNoticeService: NewDeviceVerificationNoticeService, + private platformUtilsService: PlatformUtilsService, + private configService: ConfigService, + private liveAnnouncer: LiveAnnouncer, + private i18nService: I18nService, + ) { + this.isDesktop = this.platformUtilsService.getClientType() === ClientType.Desktop; + } + + async ngOnInit() { + const currentAcct = await firstValueFrom(this.currentAcct$); + if (!currentAcct) { + return; + } + this.currentEmail = currentAcct.email; + this.currentUserId = currentAcct.id; + } + + ngAfterViewInit() { + void this.liveAnnouncer.announce(this.i18nService.t("importantNotice"), "polite"); + } + + submit = async () => { + const doesNotHaveEmailAccess = this.formGroup.controls.hasEmailAccess.value === 0; + + if (doesNotHaveEmailAccess) { + await this.router.navigate(["new-device-notice/setup"]); + return; + } + + const tempNoticeFlag = await this.configService.getFeatureFlag( + FeatureFlag.NewDeviceVerificationTemporaryDismiss, + ); + const permNoticeFlag = await this.configService.getFeatureFlag( + FeatureFlag.NewDeviceVerificationPermanentDismiss, + ); + + let newNoticeState: NewDeviceVerificationNotice | null = null; + + // When the temporary flag is enabled, only update the `last_dismissal` + if (tempNoticeFlag) { + newNoticeState = { + last_dismissal: new Date(), + permanent_dismissal: false, + }; + } else if (permNoticeFlag) { + // When the per flag is enabled, only update the `last_dismissal` + newNoticeState = { + last_dismissal: new Date(), + permanent_dismissal: true, + }; + } + + // This shouldn't occur as the user shouldn't get here unless one of the flags is active. + if (newNoticeState) { + await this.newDeviceVerificationNoticeService.updateNewDeviceVerificationNoticeState( + this.currentUserId!, + newNoticeState, + ); + } + + await this.router.navigate(["/vault"]); + }; +} diff --git a/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.html b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.html new file mode 100644 index 00000000000..1129e4750d2 --- /dev/null +++ b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.html @@ -0,0 +1,48 @@ +

    + {{ "newDeviceVerificationNoticeContentPage2" | i18n }} +

    + + + {{ "turnOnTwoStepLogin" | i18n }} + + + + {{ "changeAcctEmail" | i18n }} + + + + diff --git a/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.spec.ts b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.spec.ts new file mode 100644 index 00000000000..92f0494776a --- /dev/null +++ b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.spec.ts @@ -0,0 +1,175 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; +import { Router } from "@angular/router"; +import { BehaviorSubject } from "rxjs"; + +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { ClientType } from "@bitwarden/common/enums"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; + +import { NewDeviceVerificationNoticeService } from "../../services/new-device-verification-notice.service"; + +import { NewDeviceVerificationNoticePageTwoComponent } from "./new-device-verification-notice-page-two.component"; + +describe("NewDeviceVerificationNoticePageTwoComponent", () => { + let component: NewDeviceVerificationNoticePageTwoComponent; + let fixture: ComponentFixture; + + const activeAccount$ = new BehaviorSubject({ email: "test@example.com", id: "acct-1" }); + const environment$ = new BehaviorSubject({ getWebVaultUrl: () => "vault.bitwarden.com" }); + const navigate = jest.fn().mockResolvedValue(null); + const updateNewDeviceVerificationNoticeState = jest.fn().mockResolvedValue(null); + const getFeatureFlag = jest.fn().mockResolvedValue(false); + const getClientType = jest.fn().mockReturnValue(ClientType.Browser); + const launchUri = jest.fn(); + + beforeEach(async () => { + navigate.mockClear(); + updateNewDeviceVerificationNoticeState.mockClear(); + getFeatureFlag.mockClear(); + getClientType.mockClear(); + launchUri.mockClear(); + + await TestBed.configureTestingModule({ + providers: [ + { provide: I18nService, useValue: { t: (...key: string[]) => key.join(" ") } }, + { provide: Router, useValue: { navigate } }, + { provide: AccountService, useValue: { activeAccount$ } }, + { provide: EnvironmentService, useValue: { environment$ } }, + { + provide: NewDeviceVerificationNoticeService, + useValue: { updateNewDeviceVerificationNoticeState }, + }, + { provide: PlatformUtilsService, useValue: { getClientType, launchUri } }, + { provide: ConfigService, useValue: { getFeatureFlag } }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(NewDeviceVerificationNoticePageTwoComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("sets initial properties", () => { + expect(component["currentUserId"]).toBe("acct-1"); + expect(component["permanentFlagEnabled"]).toBe(false); + }); + + describe("change email", () => { + const changeEmailButton = () => + fixture.debugElement.query(By.css('[data-testid="change-email"]')); + + describe("web", () => { + beforeEach(() => { + component["isWeb"] = true; + fixture.detectChanges(); + }); + + it("navigates to settings", () => { + changeEmailButton().nativeElement.click(); + + expect(navigate).toHaveBeenCalledTimes(1); + expect(navigate).toHaveBeenCalledWith(["/settings/account"], { + queryParams: { fromNewDeviceVerification: true }, + }); + expect(launchUri).not.toHaveBeenCalled(); + }); + }); + + describe("browser/desktop", () => { + beforeEach(() => { + component["isWeb"] = false; + fixture.detectChanges(); + }); + + it("launches to settings", () => { + changeEmailButton().nativeElement.click(); + + expect(navigate).not.toHaveBeenCalled(); + expect(launchUri).toHaveBeenCalledWith( + "vault.bitwarden.com/#/settings/account/?fromNewDeviceVerification=true", + ); + }); + }); + }); + + describe("enable 2fa", () => { + const changeEmailButton = () => + fixture.debugElement.query(By.css('[data-testid="two-factor"]')); + + describe("web", () => { + beforeEach(() => { + component["isWeb"] = true; + fixture.detectChanges(); + }); + + it("navigates to two factor settings", () => { + changeEmailButton().nativeElement.click(); + + expect(navigate).toHaveBeenCalledTimes(1); + expect(navigate).toHaveBeenCalledWith(["/settings/security/two-factor"], { + queryParams: { fromNewDeviceVerification: true }, + }); + expect(launchUri).not.toHaveBeenCalled(); + }); + }); + + describe("browser/desktop", () => { + beforeEach(() => { + component["isWeb"] = false; + fixture.detectChanges(); + }); + + it("launches to two factor settings", () => { + changeEmailButton().nativeElement.click(); + + expect(navigate).not.toHaveBeenCalled(); + expect(launchUri).toHaveBeenCalledWith( + "vault.bitwarden.com/#/settings/security/two-factor/?fromNewDeviceVerification=true", + ); + }); + }); + }); + + describe("remind me later", () => { + const remindMeLater = () => + fixture.debugElement.query(By.css('[data-testid="remind-me-later"]')); + + beforeEach(() => { + jest.useFakeTimers(); + jest.setSystemTime(new Date("2024-02-02T00:00:00.000Z")); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it("navigates to the vault", () => { + remindMeLater().nativeElement.click(); + + expect(navigate).toHaveBeenCalledTimes(1); + expect(navigate).toHaveBeenCalledWith(["/vault"]); + }); + + it("updates notice state", () => { + remindMeLater().nativeElement.click(); + + expect(updateNewDeviceVerificationNoticeState).toHaveBeenCalledTimes(1); + expect(updateNewDeviceVerificationNoticeState).toHaveBeenCalledWith("acct-1", { + last_dismissal: new Date("2024-02-02T00:00:00.000Z"), + permanent_dismissal: false, + }); + }); + + it("is hidden when the permanent flag is enabled", async () => { + getFeatureFlag.mockResolvedValueOnce(true); + await component.ngOnInit(); + fixture.detectChanges(); + + expect(remindMeLater()).toBeNull(); + }); + }); +}); diff --git a/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.ts b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.ts new file mode 100644 index 00000000000..a2d958d53ff --- /dev/null +++ b/libs/vault/src/components/new-device-verification-notice/new-device-verification-notice-page-two.component.ts @@ -0,0 +1,111 @@ +import { LiveAnnouncer } from "@angular/cdk/a11y"; +import { CommonModule } from "@angular/common"; +import { AfterViewInit, Component, OnInit } from "@angular/core"; +import { Router } from "@angular/router"; +import { firstValueFrom, Observable } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { ClientType } from "@bitwarden/common/enums"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { + Environment, + EnvironmentService, +} from "@bitwarden/common/platform/abstractions/environment.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { UserId } from "@bitwarden/common/types/guid"; +import { ButtonModule, LinkModule, TypographyModule } from "@bitwarden/components"; + +import { NewDeviceVerificationNoticeService } from "../../services/new-device-verification-notice.service"; + +@Component({ + standalone: true, + selector: "app-new-device-verification-notice-page-two", + templateUrl: "./new-device-verification-notice-page-two.component.html", + imports: [CommonModule, JslibModule, TypographyModule, ButtonModule, LinkModule], +}) +export class NewDeviceVerificationNoticePageTwoComponent implements OnInit, AfterViewInit { + protected isWeb: boolean; + protected isDesktop: boolean; + protected permanentFlagEnabled = false; + readonly currentAcct$: Observable = this.accountService.activeAccount$; + private currentUserId: UserId | null = null; + private env$: Observable = this.environmentService.environment$; + + constructor( + private newDeviceVerificationNoticeService: NewDeviceVerificationNoticeService, + private router: Router, + private accountService: AccountService, + private platformUtilsService: PlatformUtilsService, + private environmentService: EnvironmentService, + private configService: ConfigService, + private liveAnnouncer: LiveAnnouncer, + private i18nService: I18nService, + ) { + this.isWeb = this.platformUtilsService.getClientType() === ClientType.Web; + this.isDesktop = this.platformUtilsService.getClientType() === ClientType.Desktop; + } + + async ngOnInit() { + this.permanentFlagEnabled = await this.configService.getFeatureFlag( + FeatureFlag.NewDeviceVerificationPermanentDismiss, + ); + + const currentAcct = await firstValueFrom(this.currentAcct$); + if (!currentAcct) { + return; + } + this.currentUserId = currentAcct.id; + } + + ngAfterViewInit() { + void this.liveAnnouncer.announce(this.i18nService.t("setupTwoStepLogin"), "polite"); + } + + async navigateToTwoStepLogin(event: Event) { + event.preventDefault(); + + const env = await firstValueFrom(this.env$); + const url = env.getWebVaultUrl(); + + if (this.isWeb) { + await this.router.navigate(["/settings/security/two-factor"], { + queryParams: { fromNewDeviceVerification: true }, + }); + } else { + this.platformUtilsService.launchUri( + url + "/#/settings/security/two-factor/?fromNewDeviceVerification=true", + ); + } + } + + async navigateToChangeAcctEmail(event: Event) { + event.preventDefault(); + + const env = await firstValueFrom(this.env$); + const url = env.getWebVaultUrl(); + if (this.isWeb) { + await this.router.navigate(["/settings/account"], { + queryParams: { fromNewDeviceVerification: true }, + }); + } else { + this.platformUtilsService.launchUri( + url + "/#/settings/account/?fromNewDeviceVerification=true", + ); + } + } + + async remindMeLaterSelect() { + await this.newDeviceVerificationNoticeService.updateNewDeviceVerificationNoticeState( + this.currentUserId!, + { + last_dismissal: new Date(), + permanent_dismissal: false, + }, + ); + + await this.router.navigate(["/vault"]); + } +} diff --git a/libs/vault/src/components/org-icon.directive.ts b/libs/vault/src/components/org-icon.directive.ts index bcf42503760..69a19b46c65 100644 --- a/libs/vault/src/components/org-icon.directive.ts +++ b/libs/vault/src/components/org-icon.directive.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Directive, ElementRef, HostBinding, Input, Renderer2 } from "@angular/core"; import { ProductTierType } from "@bitwarden/common/billing/enums"; @@ -11,7 +9,7 @@ export type OrgIconSize = "default" | "small" | "large"; selector: "[appOrgIcon]", }) export class OrgIconDirective { - @Input({ required: true }) tierType: ProductTierType; + @Input({ required: true }) tierType!: ProductTierType; @Input() size?: OrgIconSize = "default"; constructor( diff --git a/libs/vault/src/components/password-history-view/password-history-view.component.spec.ts b/libs/vault/src/components/password-history-view/password-history-view.component.spec.ts index 3900681f230..461cc734d76 100644 --- a/libs/vault/src/components/password-history-view/password-history-view.component.spec.ts +++ b/libs/vault/src/components/password-history-view/password-history-view.component.spec.ts @@ -46,6 +46,7 @@ describe("PasswordHistoryViewComponent", () => { fixture = TestBed.createComponent(PasswordHistoryViewComponent); component = fixture.componentInstance; + component.cipher = mockCipher; fixture.detectChanges(); }); @@ -60,8 +61,8 @@ describe("PasswordHistoryViewComponent", () => { beforeEach(async () => { mockCipher.passwordHistory = [password1, password2]; - mockCipherService.get.mockResolvedValue({ decrypt: jest.fn().mockResolvedValue(mockCipher) }); - await component.ngOnInit(); + component.cipher = mockCipher; + component.ngOnInit(); fixture.detectChanges(); }); diff --git a/libs/vault/src/components/password-history-view/password-history-view.component.ts b/libs/vault/src/components/password-history-view/password-history-view.component.ts index b038d89c464..0f3c54d9d2b 100644 --- a/libs/vault/src/components/password-history-view/password-history-view.component.ts +++ b/libs/vault/src/components/password-history-view/password-history-view.component.ts @@ -2,13 +2,9 @@ // @ts-strict-ignore import { CommonModule } from "@angular/common"; import { OnInit, Component, Input } from "@angular/core"; -import { firstValueFrom, map } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { CipherId, UserId } from "@bitwarden/common/types/guid"; -import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { PasswordHistoryView } from "@bitwarden/common/vault/models/view/password-history.view"; import { ItemModule, ColorPasswordModule, IconButtonModule } from "@bitwarden/components"; @@ -20,39 +16,14 @@ import { ItemModule, ColorPasswordModule, IconButtonModule } from "@bitwarden/co }) export class PasswordHistoryViewComponent implements OnInit { /** - * The ID of the cipher to display the password history for. + * Optional cipher view. When included `cipherId` is ignored. */ - @Input({ required: true }) cipherId: CipherId; + @Input({ required: true }) cipher: CipherView; /** The password history for the cipher. */ history: PasswordHistoryView[] = []; - constructor( - protected cipherService: CipherService, - protected i18nService: I18nService, - protected accountService: AccountService, - ) {} - - async ngOnInit() { - await this.init(); - } - - /** Retrieve the password history for the given cipher */ - protected async init() { - const cipher = await this.cipherService.get(this.cipherId); - const activeAccount = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a: { id: string | undefined }) => a)), - ); - - if (!activeAccount?.id) { - throw new Error("Active account is not available."); - } - - const activeUserId = activeAccount.id as UserId; - const decCipher = await cipher.decrypt( - await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId), - ); - - this.history = decCipher.passwordHistory == null ? [] : decCipher.passwordHistory; + ngOnInit() { + this.history = this.cipher.passwordHistory == null ? [] : this.cipher.passwordHistory; } } diff --git a/libs/vault/src/components/password-reprompt.component.ts b/libs/vault/src/components/password-reprompt.component.ts index acedcd79b81..abefac58747 100644 --- a/libs/vault/src/components/password-reprompt.component.ts +++ b/libs/vault/src/components/password-reprompt.component.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { DialogRef } from "@angular/cdk/dialog"; import { Component } from "@angular/core"; import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms"; @@ -51,6 +49,12 @@ export class PasswordRepromptComponent { ) {} submit = async () => { + // Exit early when a master password is not provided. + // The form field required error will be shown to users in these cases. + if (!this.formGroup.value.masterPassword) { + return; + } + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id))); if (userId == null) { diff --git a/libs/vault/src/icons/exclamation-triangle.ts b/libs/vault/src/icons/exclamation-triangle.ts new file mode 100644 index 00000000000..6340546d1e1 --- /dev/null +++ b/libs/vault/src/icons/exclamation-triangle.ts @@ -0,0 +1,7 @@ +import { svgIcon } from "@bitwarden/components"; + +export const ExclamationTriangle = svgIcon` + + + +`; diff --git a/libs/vault/src/icons/index.ts b/libs/vault/src/icons/index.ts index c1b69a31ef5..2e106782f53 100644 --- a/libs/vault/src/icons/index.ts +++ b/libs/vault/src/icons/index.ts @@ -2,3 +2,5 @@ export * from "./deactivated-org"; export * from "./no-folders"; export * from "./vault"; export * from "./empty-trash"; +export * from "./exclamation-triangle"; +export * from "./user-lock"; diff --git a/libs/vault/src/icons/user-lock.ts b/libs/vault/src/icons/user-lock.ts new file mode 100644 index 00000000000..c1dc3efde39 --- /dev/null +++ b/libs/vault/src/icons/user-lock.ts @@ -0,0 +1,17 @@ +import { svgIcon } from "@bitwarden/components"; + +export const UserLock = svgIcon` + + + + + + + + + + + + + +`; diff --git a/libs/vault/src/index.ts b/libs/vault/src/index.ts index dca9b2dee79..0112de44241 100644 --- a/libs/vault/src/index.ts +++ b/libs/vault/src/index.ts @@ -14,5 +14,7 @@ export { export { DownloadAttachmentComponent } from "./components/download-attachment/download-attachment.component"; export { PasswordHistoryViewComponent } from "./components/password-history-view/password-history-view.component"; +export { NewDeviceVerificationNoticePageOneComponent } from "./components/new-device-verification-notice/new-device-verification-notice-page-one.component"; +export { NewDeviceVerificationNoticePageTwoComponent } from "./components/new-device-verification-notice/new-device-verification-notice-page-two.component"; export * as VaultIcons from "./icons"; diff --git a/libs/vault/src/services/copy-cipher-field.service.spec.ts b/libs/vault/src/services/copy-cipher-field.service.spec.ts index fa148b0e2e3..48510b2efd9 100644 --- a/libs/vault/src/services/copy-cipher-field.service.spec.ts +++ b/libs/vault/src/services/copy-cipher-field.service.spec.ts @@ -76,7 +76,7 @@ describe("CopyCipherFieldService", () => { expect(toastService.showToast).toHaveBeenCalledWith({ variant: "success", message: "Username copied", - title: null, + title: "", }); expect(i18nService.t).toHaveBeenCalledWith("username"); expect(i18nService.t).toHaveBeenCalledWith("valueCopied", "Username"); diff --git a/libs/vault/src/services/copy-cipher-field.service.ts b/libs/vault/src/services/copy-cipher-field.service.ts index 1b3bb6c0a8c..bfcf3495865 100644 --- a/libs/vault/src/services/copy-cipher-field.service.ts +++ b/libs/vault/src/services/copy-cipher-field.service.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Injectable } from "@angular/core"; import { firstValueFrom } from "rxjs"; @@ -131,7 +129,7 @@ export class CopyCipherFieldService { this.toastService.showToast({ variant: "success", message: this.i18nService.t("valueCopied", this.i18nService.t(action.typeI18nKey)), - title: null, + title: "", }); if (action.event !== undefined) { diff --git a/libs/vault/src/services/new-device-verification-notice.service.ts b/libs/vault/src/services/new-device-verification-notice.service.ts index 6c7df590b50..a925cf09ec3 100644 --- a/libs/vault/src/services/new-device-verification-notice.service.ts +++ b/libs/vault/src/services/new-device-verification-notice.service.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Injectable } from "@angular/core"; import { Observable } from "rxjs"; import { Jsonify } from "type-fest"; @@ -17,8 +15,8 @@ import { UserId } from "@bitwarden/common/types/guid"; // If a user dismisses the notice, use "last_dismissal" to wait 7 days before re-prompting // permanent_dismissal will be checked if the user should never see the notice again export class NewDeviceVerificationNotice { - last_dismissal: Date; - permanent_dismissal: boolean; + last_dismissal: Date | null = null; + permanent_dismissal: boolean | null = null; constructor(obj: Partial) { if (obj == null) { @@ -52,7 +50,7 @@ export class NewDeviceVerificationNoticeService { return this.stateProvider.getUser(userId, NEW_DEVICE_VERIFICATION_NOTICE_KEY); } - noticeState$(userId: UserId): Observable { + noticeState$(userId: UserId): Observable { return this.noticeState(userId).state$; } diff --git a/package-lock.json b/package-lock.json index ff7dac2c461..197b7cb0093 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "@angular/platform-browser": "17.3.12", "@angular/platform-browser-dynamic": "17.3.12", "@angular/router": "17.3.12", - "@bitwarden/sdk-internal": "0.2.0-main.3", + "@bitwarden/sdk-internal": "0.2.0-main.38", "@electron/fuses": "1.8.0", "@koa/multer": "3.0.2", "@koa/router": "13.1.0", @@ -54,7 +54,7 @@ "lunr": "2.3.9", "multer": "1.4.5-lts.1", "ngx-infinite-scroll": "17.0.1", - "ngx-toastr": "18.0.0", + "ngx-toastr": "19.0.0", "node-fetch": "2.6.12", "node-forge": "1.3.1", "nord": "0.2.1", @@ -68,7 +68,7 @@ "qrious": "4.0.2", "rxjs": "7.8.1", "tabbable": "6.2.0", - "tldts": "6.1.66", + "tldts": "6.1.69", "utf-8-validate": "6.0.5", "zone.js": "0.14.10", "zxcvbn": "4.4.2" @@ -111,7 +111,7 @@ "@types/koa-json": "2.0.23", "@types/lowdb": "1.0.15", "@types/lunr": "2.3.7", - "@types/node": "22.10.1", + "@types/node": "22.10.2", "@types/node-fetch": "2.6.4", "@types/node-forge": "1.3.11", "@types/node-ipc": "9.2.3", @@ -127,7 +127,7 @@ "babel-loader": "9.2.1", "base64-loader": "1.0.0", "browserslist": "4.23.2", - "chromatic": "11.20.0", + "chromatic": "11.20.2", "concurrently": "9.1.0", "copy-webpack-plugin": "12.0.2", "cross-env": "7.0.3", @@ -168,10 +168,10 @@ "sass-loader": "16.0.4", "storybook": "8.4.7", "style-loader": "4.0.0", - "tailwindcss": "3.4.16", + "tailwindcss": "3.4.17", "ts-jest": "29.2.2", "ts-loader": "9.5.1", - "tsconfig-paths-webpack-plugin": "4.1.0", + "tsconfig-paths-webpack-plugin": "4.2.0", "type-fest": "2.19.0", "typescript": "5.4.2", "typescript-strict-plugin": "^2.4.4", @@ -180,7 +180,7 @@ "wait-on": "8.0.1", "webpack": "5.97.1", "webpack-cli": "5.1.4", - "webpack-dev-server": "5.0.4", + "webpack-dev-server": "5.2.0", "webpack-node-externals": "3.0.0" }, "engines": { @@ -190,11 +190,11 @@ }, "apps/browser": { "name": "@bitwarden/browser", - "version": "2024.12.0" + "version": "2025.1.0" }, "apps/cli": { "name": "@bitwarden/cli", - "version": "2024.12.0", + "version": "2025.1.0", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@koa/multer": "3.0.2", @@ -221,7 +221,7 @@ "papaparse": "5.4.1", "proper-lockfile": "4.1.2", "rxjs": "7.8.1", - "tldts": "6.1.66", + "tldts": "6.1.69", "zxcvbn": "4.4.2" }, "bin": { @@ -230,7 +230,7 @@ }, "apps/desktop": { "name": "@bitwarden/desktop", - "version": "2024.12.1", + "version": "2025.1.0", "hasInstallScript": true, "license": "GPL-3.0" }, @@ -244,7 +244,7 @@ }, "apps/web": { "name": "@bitwarden/web-vault", - "version": "2024.12.0" + "version": "2025.1.0" }, "libs/admin-console": { "name": "@bitwarden/admin-console", @@ -4298,9 +4298,9 @@ "link": true }, "node_modules/@bitwarden/sdk-internal": { - "version": "0.2.0-main.3", - "resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.3.tgz", - "integrity": "sha512-CYp98uaVMSFp6nr/QLw+Qw8ttnVtWark/bMpw59OhwMVhrCDKmpCgcR9G4oEdVO11IuFcYZieTBmtOEPhCpGaw==", + "version": "0.2.0-main.38", + "resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.38.tgz", + "integrity": "sha512-bkN+BZC0YA4k0To8QiT33UTZX8peKDXud8Gzq3UHNPlU/vMSkP3Wn8q0GezzmYN3UNNIWXfreNCS0mJ+S51j/Q==", "license": "GPL-3.0" }, "node_modules/@bitwarden/vault": { @@ -5541,1525 +5541,1151 @@ "node": "*" } }, - "node_modules/@esbuild/aix-ppc64": { + "node_modules/@esbuild/darwin-arm64": { "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.1.tgz", - "integrity": "sha512-m55cpeupQ2DbuRGQMMZDzbv9J9PgVelPjlcmM5kxHnrBdBx6REaEd7LamYV7Dm8N7rCyR/XwU6rVP8ploKtIkA==", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.1.tgz", + "integrity": "sha512-Ylk6rzgMD8klUklGPzS414UQLa5NPXZD5tf8JmQU8GQrj6BrFA/Ic9tb2zRe1kOZyCbGl+e8VMbDRazCEBqPvA==", "cpu": [ - "ppc64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "aix" + "darwin" ], "engines": { "node": ">=12" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.1.tgz", - "integrity": "sha512-4j0+G27/2ZXGWR5okcJi7pQYhmkVgb4D7UKwxcqrjhvp5TKWx3cUjgB1CGj1mfdmJBQ9VnUGgUhign+FPF2Zgw==", - "cpu": [ - "arm" - ], + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.1.tgz", - "integrity": "sha512-hCnXNF0HM6AjowP+Zou0ZJMWWa1VkD77BXe959zERgGJBBxB+sV+J9f/rcjeg2c5bsukD/n17RKWXGFCO5dD5A==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=12" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.1.tgz", - "integrity": "sha512-MSfZMBoAsnhpS+2yMFYIQUPs8Z19ajwfuaSZx+tSl09xrHZCjbeXXMsUF/0oq7ojxYEpsSo4c0SfjxOYXRbpaA==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.1.tgz", - "integrity": "sha512-Ylk6rzgMD8klUklGPzS414UQLa5NPXZD5tf8JmQU8GQrj6BrFA/Ic9tb2zRe1kOZyCbGl+e8VMbDRazCEBqPvA==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.1.tgz", - "integrity": "sha512-pFIfj7U2w5sMp52wTY1XVOdoxw+GDwy9FsK3OFz4BpMAjvZVs0dT1VXs8aQm22nhwoIWUmIRaE+4xow8xfIDZA==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } + "license": "Python-2.0" }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.1.tgz", - "integrity": "sha512-UyW1WZvHDuM4xDz0jWun4qtQFauNdXjXOtIy7SYdf7pbxSWWVlqhnR/T2TpX6LX5NI62spt0a3ldIIEkPM6RHw==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.1.tgz", - "integrity": "sha512-itPwCw5C+Jh/c624vcDd9kRCCZVpzpQn8dtwoYIt2TJF3S9xJLiRohnnNrKwREvcZYx0n8sCSbvGH349XkcQeg==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "type-fest": "^0.20.2" + }, "engines": { - "node": ">=12" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.1.tgz", - "integrity": "sha512-LojC28v3+IhIbfQ+Vu4Ut5n3wKcgTu6POKIHN9Wpt0HnfgUGlBuyDDQR4jWZUZFyYLiz4RBBBmfU6sNfn6RhLw==", - "cpu": [ - "arm" - ], + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.1.tgz", - "integrity": "sha512-cX8WdlF6Cnvw/DO9/X7XLH2J6CkBnz7Twjpk56cshk9sjYVcuh4sXQBy5bmTwzBjNVZze2yaV1vtcJS04LbN8w==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } + "license": "MIT" }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.1.tgz", - "integrity": "sha512-4H/sQCy1mnnGkUt/xszaLlYJVTz3W9ep52xEefGtd6yXDQbz/5fZE5dFLUgsPdbUOQANcVUa5iO6g3nyy5BJiw==", - "cpu": [ - "ia32" - ], + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=12" + "node": "*" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.1.tgz", - "integrity": "sha512-c0jgtB+sRHCciVXlyjDcWb2FUuzlGVRwGXgI+3WqKOIuoo8AmZAddzeOHeYLtD+dmtHw3B4Xo9wAUdjlfW5yYA==", - "cpu": [ - "loong64" - ], + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=12" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.1.tgz", - "integrity": "sha512-TgFyCfIxSujyuqdZKDZ3yTwWiGv+KnlOeXXitCQ+trDODJ+ZtGOzLkSWngynP0HZnTsDyBbPy7GWVXWaEl6lhA==", - "cpu": [ - "mips64el" - ], + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.1.tgz", - "integrity": "sha512-b+yuD1IUeL+Y93PmFZDZFIElwbmFfIKLKlYI8M6tRyzE6u7oEP7onGk0vZRh8wfVGC2dZoy0EqX1V8qok4qHaw==", - "cpu": [ - "ppc64" - ], + "node_modules/@figspec/components": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@figspec/components/-/components-1.0.3.tgz", + "integrity": "sha512-fBwHzJ4ouuOUJEi+yBZIrOy+0/fAjB3AeTcIHTT1PRxLz8P63xwC7R0EsIJXhScIcc+PljGmqbbVJCjLsnaGYA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "dependencies": { + "lit": "^2.1.3" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.1.tgz", - "integrity": "sha512-wpDlpE0oRKZwX+GfomcALcouqjjV8MIX8DyTrxfyCfXxoKQSDm45CZr9fanJ4F6ckD4yDEPT98SrjvLwIqUCgg==", - "cpu": [ - "riscv64" - ], + "node_modules/@figspec/react": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@figspec/react/-/react-1.0.3.tgz", + "integrity": "sha512-r683qOko+5CbT48Ox280fMx2MNAtaFPgCNJvldOqN3YtmAzlcTT+YSxd3OahA+kjXGGrnzDbUgeTOX1cPLII+g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@figspec/components": "^1.0.1", + "@lit-labs/react": "^1.0.2" + }, + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.1.tgz", - "integrity": "sha512-5BepC2Au80EohQ2dBpyTquqGCES7++p7G+7lXe1bAIvMdXm4YYcEfZtQrP4gaoZ96Wv1Ute61CEHFU7h4FMueQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@hapi/bourne": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-3.0.0.tgz", + "integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.1.tgz", - "integrity": "sha512-5gRPk7pKuaIB+tmH+yKd2aQTRpqlf1E4f/mC+tawIm/CGJemZcHZpp2ic8oD83nKgUPMEd0fNanrnFljiruuyA==", - "cpu": [ - "x64" - ], + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, "engines": { - "node": ">=12" + "node": ">=10.10.0" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.1.tgz", - "integrity": "sha512-4fL68JdrLV2nVW2AaWZBv3XEm3Ae3NZn/7qy2KGAt3dexAgSVT+Hc97JKSZnqezgMlv9x6KV0ZkZY7UO5cNLCg==", - "cpu": [ - "x64" - ], + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.1.tgz", - "integrity": "sha512-GhRuXlvRE+twf2ES+8REbeCb/zeikNqwD3+6S5y5/x+DYbAQUNl0HNBs4RQJqrechS4v4MruEr8ZtAin/hK5iw==", - "cpu": [ - "x64" - ], + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=12" + "node": "*" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.1.tgz", - "integrity": "sha512-ZnWEyCM0G1Ex6JtsygvC3KUUrlDXqOihw8RicRuQAzw+c4f1D66YlPNNV3rkjVW90zXVsHwZYWbJh3v+oQFM9Q==", - "cpu": [ - "x64" - ], + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.1.tgz", - "integrity": "sha512-QZ6gXue0vVQY2Oon9WyLFCdSuYbXSoxaZrPuJ4c20j6ICedfsDilNPYfHLlMH7vGfU5DQR0czHLmJvH4Nzis/A==", - "cpu": [ - "arm64" - ], + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, "engines": { "node": ">=12" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.1.tgz", - "integrity": "sha512-HzcJa1NcSWTAU0MJIxOho8JftNp9YALui3o+Ny7hCh0v5f90nprly1U3Sj1Ldj/CvKKdvvFsCRvDkpsEMp4DNw==", - "cpu": [ - "ia32" - ], + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.1.tgz", - "integrity": "sha512-0MBh53o6XtI6ctDnRMeQ+xoCN8kD2qI1rY1KgF/xdWQwoFeKou7puvDfV8/Wv4Ctx2rRpET/gGdz3YlNtNACSA==", - "cpu": [ - "x64" - ], + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], "engines": { "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.4.3" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=12" }, "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=12" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "engines": { + "node": ">=8" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", "dependencies": { - "type-fest": "^0.20.2" + "p-locate": "^4.1.0" }, "engines": { "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "p-limit": "^2.2.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=8" } }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, - "license": "ISC", + "license": "MIT", + "peer": true, "dependencies": { - "brace-expansion": "^1.1.7" + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" }, "engines": { - "node": "*" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, - "license": "(MIT OR CC0-1.0)", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "node_modules/@jest/core/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", + "peer": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@figspec/components": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@figspec/components/-/components-1.0.3.tgz", - "integrity": "sha512-fBwHzJ4ouuOUJEi+yBZIrOy+0/fAjB3AeTcIHTT1PRxLz8P63xwC7R0EsIJXhScIcc+PljGmqbbVJCjLsnaGYA==", + "node_modules/@jest/core/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "lit": "^2.1.3" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@figspec/react": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@figspec/react/-/react-1.0.3.tgz", - "integrity": "sha512-r683qOko+5CbT48Ox280fMx2MNAtaFPgCNJvldOqN3YtmAzlcTT+YSxd3OahA+kjXGGrnzDbUgeTOX1cPLII+g==", + "node_modules/@jest/core/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, "license": "MIT", "dependencies": { - "@figspec/components": "^1.0.1", - "@lit-labs/react": "^1.0.2" + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" }, - "peerDependencies": { - "react": "^16.14.0 || ^17.0.0 || ^18.0.0" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@hapi/bourne": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-3.0.0.tgz", - "integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==", - "license": "BSD-3-Clause" - }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", + "peer": true, "dependencies": { - "@hapi/hoek": "^9.0.0" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "deprecated": "Use @eslint/config-array instead", + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "jest-get-type": "^29.6.3" }, "engines": { - "node": ">=10.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { - "node": "*" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, - "license": "ISC", + "license": "MIT", + "peer": true, "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" }, "engines": { - "node": ">=12" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "peer": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", "engines": { - "node": ">=12" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/@jest/reporters/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "MIT", + "license": "ISC", + "peer": true, "dependencies": { - "ansi-regex": "^6.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=12" + "node": "*" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", + "peer": true, "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=10" } }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "node_modules/@jest/reporters/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@sinclair/typebox": "^0.27.8" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "p-locate": "^4.1.0" + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "p-try": "^2.0.0" + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/console": { + "node_modules/@jest/test-sequencer": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", "slash": "^3.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/core": { + "node_modules/@jest/transform": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", + "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", + "pirates": "^4.0.4", "slash": "^3.0.0", - "strip-ansi": "^6.0.0" + "write-file-atomic": "^4.0.2" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } } }, - "node_modules/@jest/core/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "node_modules/@jest/transform/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } + "peer": true }, - "node_modules/@jest/core/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/core/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "license": "MIT", "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.0.0" } }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "license": "MIT", - "peer": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.0.0" } }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.0.0" } }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "license": "MIT", - "peer": true, "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, + "license": "Apache-2.0", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10.0" }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "peerDependencies": { + "tslib": "2" } }, - "node_modules/@jest/reporters/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.0.tgz", + "integrity": "sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==", "dev": true, - "license": "MIT", - "peer": true, + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@jsonjoy.com/base64": "^1.1.1", + "@jsonjoy.com/util": "^1.1.2", + "hyperdyperid": "^1.2.0", + "thingies": "^1.20.0" + }, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, - "node_modules/@jest/reporters/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/@jsonjoy.com/util": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.5.0.tgz", + "integrity": "sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==", "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, + "license": "Apache-2.0", "engines": { - "node": "*" + "node": ">=10.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, - "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "peer": true, + "node_modules/@koa/multer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@koa/multer/-/multer-3.0.2.tgz", + "integrity": "sha512-Q6WfPpE06mJWyZD1fzxM6zWywaoo+zocAn2YA9QYz4RsecoASr1h/kSzG0c5seDpFVKCMZM9raEfuM7XfqbRLw==", + "license": "MIT", "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" + "fix-esm": "1.0.1" }, "engines": { - "node": ">=10" + "node": ">= 8" + }, + "peerDependencies": { + "multer": "*" } }, - "node_modules/@jest/reporters/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "peer": true, + "node_modules/@koa/router": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@koa/router/-/router-13.1.0.tgz", + "integrity": "sha512-mNVu1nvkpSd8Q8gMebGbCkDWJ51ODetrFvLKYusej+V0ByD4btqHYnPIzTBLXnQMVUlm/oxVwqmWBY3zQfZilw==", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "http-errors": "^2.0.0", + "koa-compose": "^4.1.0", + "path-to-regexp": "^6.3.0" }, "engines": { - "node": "*" + "node": ">= 18" } }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/@lit-labs/react": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-1.2.1.tgz", + "integrity": "sha512-DiZdJYFU0tBbdQkfwwRSwYyI/mcWkg3sWesKRsHUd4G+NekTmmeq9fzsurvcKTNVa0comNljwtg4Hvi1ds3V+A==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.1.tgz", + "integrity": "sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@lit/reactive-element": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.3.tgz", + "integrity": "sha512-QuTgnG52Poic7uM1AN5yJ09QMe0O28e10XzSvWDz02TJiiKee4stsiownEIadWm8nYzyDAyT+gKzUoZmiWQtsQ==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "@lit-labs/ssr-dom-shim": "^1.0.0" } }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "node_modules/@ljharb/through": { + "version": "2.3.13", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", + "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" + "call-bind": "^1.0.7" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" } }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "node_modules/@malept/cross-spawn-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", + "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", "dev": true, - "license": "MIT", - "peer": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" + "cross-spawn": "^7.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 12.13.0" } }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "node_modules/@malept/flatpak-bundler": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", + "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "tmp-promise": "^3.0.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 10.0.0" } }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" } }, - "node_modules/@jest/transform/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "node_modules/@mdx-js/react": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", + "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@jsonjoy.com/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/json-pack": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.0.tgz", - "integrity": "sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/base64": "^1.1.1", - "@jsonjoy.com/util": "^1.1.2", - "hyperdyperid": "^1.2.0", - "thingies": "^1.20.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/util": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.5.0.tgz", - "integrity": "sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@koa/multer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@koa/multer/-/multer-3.0.2.tgz", - "integrity": "sha512-Q6WfPpE06mJWyZD1fzxM6zWywaoo+zocAn2YA9QYz4RsecoASr1h/kSzG0c5seDpFVKCMZM9raEfuM7XfqbRLw==", - "license": "MIT", - "dependencies": { - "fix-esm": "1.0.1" - }, - "engines": { - "node": ">= 8" - }, - "peerDependencies": { - "multer": "*" - } - }, - "node_modules/@koa/router": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@koa/router/-/router-13.1.0.tgz", - "integrity": "sha512-mNVu1nvkpSd8Q8gMebGbCkDWJ51ODetrFvLKYusej+V0ByD4btqHYnPIzTBLXnQMVUlm/oxVwqmWBY3zQfZilw==", - "license": "MIT", - "dependencies": { - "http-errors": "^2.0.0", - "koa-compose": "^4.1.0", - "path-to-regexp": "^6.3.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@lit-labs/react": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@lit-labs/react/-/react-1.2.1.tgz", - "integrity": "sha512-DiZdJYFU0tBbdQkfwwRSwYyI/mcWkg3sWesKRsHUd4G+NekTmmeq9fzsurvcKTNVa0comNljwtg4Hvi1ds3V+A==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@lit-labs/ssr-dom-shim": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.1.tgz", - "integrity": "sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@lit/reactive-element": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.3.tgz", - "integrity": "sha512-QuTgnG52Poic7uM1AN5yJ09QMe0O28e10XzSvWDz02TJiiKee4stsiownEIadWm8nYzyDAyT+gKzUoZmiWQtsQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@lit-labs/ssr-dom-shim": "^1.0.0" - } - }, - "node_modules/@ljharb/through": { - "version": "2.3.13", - "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", - "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@malept/cross-spawn-promise": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", - "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], - "license": "Apache-2.0", - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/@malept/flatpak-bundler": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", - "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "fs-extra": "^9.0.0", - "lodash": "^4.17.15", - "tmp-promise": "^3.0.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@mdx-js/react": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", - "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mdx": "^2.0.0" + "@types/mdx": "^2.0.0" }, "funding": { "type": "opencollective", @@ -7665,58 +7291,16 @@ "@parcel/watcher-win32-x64": "2.5.0" } }, - "node_modules/@parcel/watcher-android-arm64": { + "node_modules/@parcel/watcher-darwin-arm64": { "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", - "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", - "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", - "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, "os": [ "darwin" ], @@ -7728,216 +7312,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", - "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", - "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-musl": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", - "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", - "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", - "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", - "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", - "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", - "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", - "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", - "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/@parcel/watcher/node_modules/detect-libc": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", @@ -7980,34 +7354,6 @@ "node": ">=14" } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.4.tgz", - "integrity": "sha512-jfUJrFct/hTA0XDM5p/htWKoNNTbDLY0KRwEt6pyOA6k2fmk0WVwl65PdUdJZgzGEHWx+49LilkcSaumQRyNQw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.4.tgz", - "integrity": "sha512-j4nrEO6nHU1nZUuCfRKoCcvh7PIywQPUCBa2UsootTHvTHIoIu2BzueInGJhhvQO/2FTRdNYpf63xsgEqH9IhA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.24.4", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.4.tgz", @@ -8022,237 +7368,27 @@ "darwin" ] }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.4.tgz", - "integrity": "sha512-N6oDBiZCBKlwYcsEPXGDE4g9RoxZLK6vT98M8111cW7VsVJFpNEqvJeIPfsCzbf0XEakPslh72X0gnlMi4Ddgg==", - "cpu": [ - "x64" - ], + "node_modules/@schematics/angular": { + "version": "17.3.11", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.3.11.tgz", + "integrity": "sha512-tvJpTgYC+hCnTyLszYRUZVyNTpPd+C44gh5CPTcG3qkqStzXQwynQAf6X/DjtwXbUiPQF0XfF0+0R489GpdZPA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "dependencies": { + "@angular-devkit/core": "17.3.11", + "@angular-devkit/schematics": "17.3.11", + "jsonc-parser": "3.2.1" + }, + "engines": { + "node": "^18.13.0 || >=20.9.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.4.tgz", - "integrity": "sha512-py5oNShCCjCyjWXCZNrRGRpjWsF0ic8f4ieBNra5buQz0O/U6mMXCpC1LvrHuhJsNPgRt36tSYMidGzZiJF6mw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.4.tgz", - "integrity": "sha512-L7VVVW9FCnTTp4i7KrmHeDsDvjB4++KOBENYtNYAiYl96jeBThFfhP6HVxL74v4SiZEVDH/1ILscR5U9S4ms4g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.4.tgz", - "integrity": "sha512-10ICosOwYChROdQoQo589N5idQIisxjaFE/PAnX2i0Zr84mY0k9zul1ArH0rnJ/fpgiqfu13TFZR5A5YJLOYZA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.4.tgz", - "integrity": "sha512-ySAfWs69LYC7QhRDZNKqNhz2UKN8LDfbKSMAEtoEI0jitwfAG2iZwVqGACJT+kfYvvz3/JgsLlcBP+WWoKCLcw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.4.tgz", - "integrity": "sha512-uHYJ0HNOI6pGEeZ/5mgm5arNVTI0nLlmrbdph+pGXpC9tFHFDQmDMOEqkmUObRfosJqpU8RliYoGz06qSdtcjg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.4.tgz", - "integrity": "sha512-38yiWLemQf7aLHDgTg85fh3hW9stJ0Muk7+s6tIkSUOMmi4Xbv5pH/5Bofnsb6spIwD5FJiR+jg71f0CH5OzoA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.4.tgz", - "integrity": "sha512-q73XUPnkwt9ZNF2xRS4fvneSuaHw2BXuV5rI4cw0fWYVIWIBeDZX7c7FWhFQPNTnE24172K30I+dViWRVD9TwA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.4.tgz", - "integrity": "sha512-Aie/TbmQi6UXokJqDZdmTJuZBCU3QBDA8oTKRGtd4ABi/nHgXICulfg1KI6n9/koDsiDbvHAiQO3YAUNa/7BCw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.4.tgz", - "integrity": "sha512-P8MPErVO/y8ohWSP9JY7lLQ8+YMHfTI4bAdtCi3pC2hTeqFJco2jYspzOzTUB8hwUWIIu1xwOrJE11nP+0JFAQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.4.tgz", - "integrity": "sha512-K03TljaaoPK5FOyNMZAAEmhlyO49LaE4qCsr0lYHUKyb6QacTNF9pnfPpXnFlFD3TXuFbFbz7tJ51FujUXkXYA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.4.tgz", - "integrity": "sha512-VJYl4xSl/wqG2D5xTYncVWW+26ICV4wubwN9Gs5NrqhJtayikwCXzPL8GDsLnaLU3WwhQ8W02IinYSFJfyo34Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.4.tgz", - "integrity": "sha512-ku2GvtPwQfCqoPFIJCqZ8o7bJcj+Y54cZSr43hHca6jLwAiCbZdBUOrqE6y29QFajNAzzpIOwsckaTFmN6/8TA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.4.tgz", - "integrity": "sha512-V3nCe+eTt/W6UYNr/wGvO1fLpHUrnlirlypZfKCT1fG6hWfqhPgQV/K/mRBXBpxc0eKLIF18pIOFVPh0mqHjlg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.4.tgz", - "integrity": "sha512-LTw1Dfd0mBIEqUVCxbvTE/LLo+9ZxVC9k99v1v4ahg9Aak6FpqOfNu5kRkeTAn0wphoC4JU7No1/rL+bBCEwhg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@schematics/angular": { - "version": "17.3.11", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.3.11.tgz", - "integrity": "sha512-tvJpTgYC+hCnTyLszYRUZVyNTpPd+C44gh5CPTcG3qkqStzXQwynQAf6X/DjtwXbUiPQF0XfF0+0R489GpdZPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "17.3.11", - "@angular-devkit/schematics": "17.3.11", - "jsonc-parser": "3.2.1" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@schematics/angular/node_modules/@angular-devkit/core": { - "version": "17.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.11.tgz", - "integrity": "sha512-vTNDYNsLIWpYk2I969LMQFH29GTsLzxNk/0cLw5q56ARF0v5sIWfHYwGTS88jdDqIpuuettcSczbxeA7EuAmqQ==", + "node_modules/@schematics/angular/node_modules/@angular-devkit/core": { + "version": "17.3.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.11.tgz", + "integrity": "sha512-vTNDYNsLIWpYk2I969LMQFH29GTsLzxNk/0cLw5q56ARF0v5sIWfHYwGTS88jdDqIpuuettcSczbxeA7EuAmqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10072,9 +9208,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz", - "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==", + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", "dev": true, "license": "MIT", "dependencies": { @@ -13706,9 +12842,9 @@ } }, "node_modules/chromatic": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-11.20.0.tgz", - "integrity": "sha512-Btdli1qoAI01UKmk3Iqe6vKhAhePRXqNI/2uKKy2R16q7SN/5kLTqhd1JI20LFOZSnH3xSJaUXeJ2xZOJB//3A==", + "version": "11.20.2", + "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-11.20.2.tgz", + "integrity": "sha512-c+M3HVl5Y60c7ipGTZTyeWzWubRW70YsJ7PPDpO1D735ib8+Lu3yGF90j61pvgkXGngpkTPHZyBw83lcu2JMxA==", "dev": true, "license": "MIT", "bin": { @@ -17314,9 +16450,9 @@ "license": "Apache-2.0" }, "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", "dev": true, "license": "MIT", "dependencies": { @@ -17339,7 +16475,7 @@ "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", + "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", @@ -17354,6 +16490,10 @@ }, "engines": { "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/express/node_modules/debug": { @@ -17406,9 +16546,9 @@ } }, "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", - "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "dev": true, "license": "MIT" }, @@ -25217,9 +24357,9 @@ } }, "node_modules/ngx-toastr": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-18.0.0.tgz", - "integrity": "sha512-jZ3rOG6kygl8ittY8OltIMSo47P1VStuS01igm3MZXK6InJwHVvxU7wDHI/HGMlXSyNvWncyOuFHnnMEAifsew==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-19.0.0.tgz", + "integrity": "sha512-6pTnktwwWD+kx342wuMOWB4+bkyX9221pAgGz3SHOJH0/MI9erLucS8PeeJDFwbUYyh75nQ6AzVtolgHxi52dQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -30643,9 +29783,9 @@ "license": "MIT" }, "node_modules/tailwindcss": { - "version": "3.4.16", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.16.tgz", - "integrity": "sha512-TI4Cyx7gDiZ6r44ewaJmt0o6BrMCT5aK5e0rmJ/G9Xq3w7CX/5VXl/zIPEJZFUK5VEqwByyhqNPycPlvcK4ZNw==", + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "dev": true, "license": "MIT", "dependencies": { @@ -31135,21 +30275,21 @@ } }, "node_modules/tldts": { - "version": "6.1.66", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.66.tgz", - "integrity": "sha512-l3ciXsYFel/jSRfESbyKYud1nOw7WfhrBEF9I3UiarYk/qEaOOwu3qXNECHw4fHGHGTEOuhf/VdKgoDX5M/dhQ==", + "version": "6.1.69", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.69.tgz", + "integrity": "sha512-Oh/CqRQ1NXNY7cy9NkTPUauOWiTro0jEYZTioGbOmcQh6EC45oribyIMJp0OJO3677r13tO6SKdWoGZUx2BDFw==", "license": "MIT", "dependencies": { - "tldts-core": "^6.1.66" + "tldts-core": "^6.1.69" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.66", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.66.tgz", - "integrity": "sha512-s07jJruSwndD2X8bVjwioPfqpIc1pDTzszPe9pL1Skbh4bjytL85KNQ3tolqLbCvpQHawIsGfFi9dgerWjqW4g==", + "version": "6.1.69", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.69.tgz", + "integrity": "sha512-nygxy9n2PBUFQUtAXAc122gGo+04/j5qr5TGQFZTHafTKYvmARVXt2cA5rgero2/dnXUfkdPtiJoKmrd3T+wdA==", "license": "MIT" }, "node_modules/tmp": { @@ -31425,14 +30565,15 @@ } }, "node_modules/tsconfig-paths-webpack-plugin": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz", - "integrity": "sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", + "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.7.0", + "tapable": "^2.2.1", "tsconfig-paths": "^4.1.2" }, "engines": { @@ -32387,587 +31528,213 @@ "dev": true, "license": "MIT" }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/validate-npm-package-name": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", - "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/verror": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", - "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vis": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/vis/-/vis-4.21.0.tgz", - "integrity": "sha512-jonDXTGm2mFU/X6Kg9pvkZEQtXh2J6+NlDJD1tDP7TDCFy+qNeKlsTcXKQtv4nAtUIiKo6sphCt4kbRlEKw75A==", - "deprecated": "Please consider using https://github.com/visjs", - "dev": true, - "license": "(Apache-2.0 OR MIT)", - "dependencies": { - "emitter-component": "^1.1.1", - "hammerjs": "^2.0.8", - "keycharm": "^0.2.0", - "moment": "^2.18.1", - "propagating-hammerjs": "^1.4.6" - } - }, - "node_modules/vite": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.8.tgz", - "integrity": "sha512-mB8ToUuSmzODSpENgvpFk2fTiU/YQ1tmcVJJ4WZbq4fPdGJkFNVcmVL5k7iDug6xzWjjuGDKAuSievIsD6H7Xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.19.3", - "postcss": "^8.4.35", - "rollup": "^4.2.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", - "cpu": [ - "loong64" - ], + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=12" + "node": ">= 0.4.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", - "cpu": [ - "mips64el" - ], + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" ], - "engines": { - "node": ">=12" + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" } }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", - "cpu": [ - "ppc64" - ], + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "ISC", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, "engines": { - "node": ">=12" + "node": ">=10.12.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", - "cpu": [ - "riscv64" - ], + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "peer": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", - "cpu": [ - "s390x" - ], + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "ISC", "engines": { - "node": ">=12" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=12" + "node": ">= 0.8" } }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", - "cpu": [ - "x64" - ], + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", "dev": true, "license": "MIT", "optional": true, - "os": [ - "netbsd" - ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, "engines": { - "node": ">=12" + "node": ">=0.6.0" } }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", - "cpu": [ - "x64" - ], + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", - "cpu": [ - "x64" - ], + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", - "cpu": [ - "arm64" - ], + "node_modules/vis": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/vis/-/vis-4.21.0.tgz", + "integrity": "sha512-jonDXTGm2mFU/X6Kg9pvkZEQtXh2J6+NlDJD1tDP7TDCFy+qNeKlsTcXKQtv4nAtUIiKo6sphCt4kbRlEKw75A==", + "deprecated": "Please consider using https://github.com/visjs", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "license": "(Apache-2.0 OR MIT)", + "dependencies": { + "emitter-component": "^1.1.1", + "hammerjs": "^2.0.8", + "keycharm": "^0.2.0", + "moment": "^2.18.1", + "propagating-hammerjs": "^1.4.6" } }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", - "cpu": [ - "ia32" - ], + "node_modules/vite": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.8.tgz", + "integrity": "sha512-mB8ToUuSmzODSpENgvpFk2fTiU/YQ1tmcVJJ4WZbq4fPdGJkFNVcmVL5k7iDug6xzWjjuGDKAuSievIsD6H7Xw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "esbuild": "^0.19.3", + "postcss": "^8.4.35", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" + }, "engines": { - "node": ">=12" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } } }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "win32" + "darwin" ], "engines": { "node": ">=12" @@ -33260,9 +32027,9 @@ } }, "node_modules/webpack-dev-server": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.0.4.tgz", - "integrity": "sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.0.tgz", + "integrity": "sha512-90SqqYXA2SK36KcT6o1bvwvZfJFcmoamqeJY7+boioffX9g9C0wjjJRGUrQIuh43pb0ttX7+ssavmj/WN2RHtA==", "dev": true, "license": "MIT", "dependencies": { @@ -33279,23 +32046,20 @@ "colorette": "^2.0.10", "compression": "^1.7.4", "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", + "express": "^4.21.2", "graceful-fs": "^4.2.6", - "html-entities": "^2.4.0", - "http-proxy-middleware": "^2.0.3", + "http-proxy-middleware": "^2.0.7", "ipaddr.js": "^2.1.0", "launch-editor": "^2.6.1", "open": "^10.0.3", "p-retry": "^6.2.0", - "rimraf": "^5.0.5", "schema-utils": "^4.2.0", "selfsigned": "^2.4.1", "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", - "webpack-dev-middleware": "^7.1.0", - "ws": "^8.16.0" + "webpack-dev-middleware": "^7.4.2", + "ws": "^8.18.0" }, "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" @@ -33359,9 +32123,9 @@ } }, "node_modules/webpack-dev-server/node_modules/memfs": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.14.0.tgz", - "integrity": "sha512-JUeY0F/fQZgIod31Ja1eJgiSxLn7BfQlCnqhwXFBzFHEw63OdLK7VJUJ7bnzNsWgCyoUP5tEp1VRY8rDaYzqOA==", + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.15.0.tgz", + "integrity": "sha512-q9MmZXd2rRWHS6GU3WEm3HyiXZyyoA1DqdOhEq0lxPBmKb5S7IAOwX0RgUCwJfqjelDCySa5h8ujOy24LqsWcw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -33397,22 +32161,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/webpack-dev-server/node_modules/rimraf": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", - "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz", diff --git a/package.json b/package.json index aa567f18df6..907723c3a02 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "@types/koa-json": "2.0.23", "@types/lowdb": "1.0.15", "@types/lunr": "2.3.7", - "@types/node": "22.10.1", + "@types/node": "22.10.2", "@types/node-fetch": "2.6.4", "@types/node-forge": "1.3.11", "@types/node-ipc": "9.2.3", @@ -88,7 +88,7 @@ "babel-loader": "9.2.1", "base64-loader": "1.0.0", "browserslist": "4.23.2", - "chromatic": "11.20.0", + "chromatic": "11.20.2", "concurrently": "9.1.0", "copy-webpack-plugin": "12.0.2", "cross-env": "7.0.3", @@ -129,10 +129,10 @@ "sass-loader": "16.0.4", "storybook": "8.4.7", "style-loader": "4.0.0", - "tailwindcss": "3.4.16", + "tailwindcss": "3.4.17", "ts-jest": "29.2.2", "ts-loader": "9.5.1", - "tsconfig-paths-webpack-plugin": "4.1.0", + "tsconfig-paths-webpack-plugin": "4.2.0", "type-fest": "2.19.0", "typescript": "5.4.2", "typescript-strict-plugin": "^2.4.4", @@ -141,7 +141,7 @@ "wait-on": "8.0.1", "webpack": "5.97.1", "webpack-cli": "5.1.4", - "webpack-dev-server": "5.0.4", + "webpack-dev-server": "5.2.0", "webpack-node-externals": "3.0.0" }, "dependencies": { @@ -154,7 +154,7 @@ "@angular/platform-browser": "17.3.12", "@angular/platform-browser-dynamic": "17.3.12", "@angular/router": "17.3.12", - "@bitwarden/sdk-internal": "0.2.0-main.3", + "@bitwarden/sdk-internal": "0.2.0-main.38", "@electron/fuses": "1.8.0", "@koa/multer": "3.0.2", "@koa/router": "13.1.0", @@ -184,7 +184,7 @@ "lunr": "2.3.9", "multer": "1.4.5-lts.1", "ngx-infinite-scroll": "17.0.1", - "ngx-toastr": "18.0.0", + "ngx-toastr": "19.0.0", "node-fetch": "2.6.12", "node-forge": "1.3.1", "nord": "0.2.1", @@ -198,7 +198,7 @@ "qrious": "4.0.2", "rxjs": "7.8.1", "tabbable": "6.2.0", - "tldts": "6.1.66", + "tldts": "6.1.69", "utf-8-validate": "6.0.5", "zone.js": "0.14.10", "zxcvbn": "4.4.2"