diff --git a/.eslintrc.json b/.eslintrc.json index 6a5fc5b83f3..6bcea9dd045 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -86,7 +86,7 @@ "error", { "zones": [ - // Do not allow angular/node/electron code to be imported into common + // Do not allow angular/node code to be imported into common { "target": "./libs/common/**/*", "from": "./libs/angular/**/*" @@ -94,10 +94,6 @@ { "target": "./libs/common/**/*", "from": "./libs/node/**/*" - }, - { - "target": "./libs/common/**/*", - "from": "./libs/electron/**/*" } ] } @@ -131,12 +127,6 @@ "rules": { "no-restricted-imports": ["error", { "patterns": ["@bitwarden/node/*", "src/**/*"] }] } - }, - { - "files": ["libs/electron/src/**/*.ts"], - "rules": { - "no-restricted-imports": ["error", { "patterns": ["@bitwarden/electron/*", "src/**/*"] }] - } } ] } diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000000..48a87a94361 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# Please sort lines alphabetically, this will ensure we don't accidentally add duplicates. +# +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +bitwarden_license/bit-web/src/app/secrets-manager @bitwarden/pod-sm-dev diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index e1c74a70943..2339c1756bd 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -4,14 +4,10 @@ ./apps/browser/src/safari/desktop/Base.lproj ./apps/browser/src/services/vaultTimeout ./apps/browser/store/windows/Assets -./apps/desktop/src/models/nativeMessaging -./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses ./libs/common/spec/misc/logInStrategies ./libs/common/src/abstractions/fileDownload ./libs/common/src/abstractions/userVerification ./libs/common/src/abstractions/vaultTimeout -./libs/common/src/emailForwarders ./libs/common/src/misc/logInStrategies ./libs/common/src/services/userVerification ./libs/common/src/services/vaultTimeout @@ -64,6 +60,7 @@ ./libs/common/src/misc/nodeUtils.ts ./libs/common/src/misc/linkedFieldOption.decorator.ts ./libs/common/src/misc/serviceUtils.ts +./libs/common/src/misc/serviceUtils.spec.ts ./libs/common/src/types/twoFactorResponse.ts ./libs/common/src/types/authResponse.ts ./libs/common/src/types/syncEventArgs.ts @@ -112,12 +109,6 @@ ./libs/common/src/enums/nativeMessagingVersion.ts ./libs/common/src/enums/cipherRepromptType.ts ./libs/common/src/enums/organizationUserType.ts -./libs/common/src/emailForwarders/fastmailForwarder.ts -./libs/common/src/emailForwarders/duckDuckGoForwarder.ts -./libs/common/src/emailForwarders/firefoxRelayForwarder.ts -./libs/common/src/emailForwarders/anonAddyForwarder.ts -./libs/common/src/emailForwarders/simpleLoginForwarder.ts -./libs/common/src/emailForwarders/forwarderOptions.ts ./libs/common/src/factories/accountFactory.ts ./libs/common/src/factories/globalStateFactory.ts ./libs/common/src/factories/stateFactory.ts @@ -162,27 +153,6 @@ ./libs/common/src/services/bitwardenFileUpload.service.ts ./libs/common/src/services/webCryptoFunction.service.ts ./libs/common/src/interfaces/IEncrypted.ts -./libs/node/spec/cli/consoleLog.service.spec.ts -./libs/node/src/cli/models/response/baseResponse.ts -./libs/node/src/cli/models/response/stringResponse.ts -./libs/node/src/cli/models/response/fileResponse.ts -./libs/node/src/cli/models/response/messageResponse.ts -./libs/node/src/cli/models/response/listResponse.ts -./libs/node/src/cli/baseProgram.ts -./libs/node/src/cli/services/consoleLog.service.ts -./libs/node/src/cli/services/cliPlatformUtils.service.ts -./libs/node/src/services/nodeApi.service.ts -./libs/node/src/services/lowdbStorage.service.ts -./libs/electron/spec/services/electronLog.service.spec.ts -./libs/electron/src/baseMenu.ts -./libs/electron/src/services/electronLog.service.ts -./libs/electron/src/services/electronStorage.service.ts -./libs/electron/src/services/electronRendererMessaging.service.ts -./libs/electron/src/services/electronMainMessaging.service.ts -./libs/electron/src/services/electronPlatformUtils.service.ts -./libs/electron/src/services/electronRendererStorage.service.ts -./libs/electron/src/services/electronCrypto.service.ts -./libs/electron/src/services/electronRendererSecureStorage.service.ts ./README.md ./LICENSE_BITWARDEN.txt ./CONTRIBUTING.md @@ -197,64 +167,11 @@ ./apps/desktop/resources/appx/StoreLogo.png ./apps/desktop/resources/appx/Wide310x150Logo.png ./apps/desktop/resources/appx/Square44x44Logo.png -./apps/desktop/native-messaging-test-runner/src/ipcService.ts -./apps/desktop/native-messaging-test-runner/src/nativeMessageService.ts -./apps/desktop/native-messaging-test-runner/src/logUtils.ts ./apps/desktop/README.md ./apps/desktop/desktop_native/Cargo.toml ./apps/desktop/desktop_native/Cargo.lock -./apps/desktop/src/app/services/desktopFileDownloadService.ts -./apps/desktop/src/models/nativeMessaging/unencryptedCommand.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessage.ts -./apps/desktop/src/models/nativeMessaging/legacyMessageWrapper.ts -./apps/desktop/src/models/nativeMessaging/unencryptedMessage.ts -./apps/desktop/src/models/nativeMessaging/unencryptedMessageResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedCommand.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/successStatusResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/generateResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/failureStatusResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cannotDecryptErrorResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/userStatusErrorResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/accountStatusResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cipherResponse.ts -./apps/desktop/src/models/nativeMessaging/decryptedCommandData.ts -./apps/desktop/src/models/nativeMessaging/messageCommon.ts -./apps/desktop/src/models/nativeMessaging/legacyMessage.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/passwordGeneratePayload.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialCreatePayload.ts -./apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialRetrievePayload.ts -./apps/desktop/src/main/desktopCredentialStorageListener.ts -./apps/desktop/src/main/powerMonitor.main.ts -./apps/desktop/src/main/nativeMessaging.main.ts -./apps/desktop/src/services/passwordReprompt.service.ts -./apps/desktop/src/services/encryptedMessageHandlerService.ts -./apps/desktop/src/services/nativeMessaging.service.ts -./apps/desktop/src/services/nativeMessageHandler.service.ts ./apps/cli/stores/chocolatey/tools/VERIFICATION.txt ./apps/cli/README.md -./apps/cli/src/models/response/sendFileResponse.ts -./apps/cli/src/models/response/organizationCollectionResponse.ts -./apps/cli/src/models/response/collectionResponse.ts -./apps/cli/src/models/response/templateResponse.ts -./apps/cli/src/models/response/passwordHistoryResponse.ts -./apps/cli/src/models/response/folderResponse.ts -./apps/cli/src/models/response/loginResponse.ts -./apps/cli/src/models/response/sendAccessResponse.ts -./apps/cli/src/models/response/sendResponse.ts -./apps/cli/src/models/response/cipherResponse.ts -./apps/cli/src/models/response/organizationResponse.ts -./apps/cli/src/models/response/organizationUserResponse.ts -./apps/cli/src/models/response/sendTextResponse.ts -./apps/cli/src/models/response/attachmentResponse.ts -./apps/cli/src/models/selectionReadOnly.ts -./apps/cli/src/models/request/organizationCollectionRequest.ts -./apps/cli/src/commands/convertToKeyConnector.command.ts -./apps/cli/src/commands/send/removePassword.command.ts -./apps/cli/src/services/lowdbStorage.service.ts -./apps/cli/src/services/nodeEnvSecureStorage.service.ts ./apps/browser/README.md ./apps/browser/store/windows/AppxManifest.xml ./apps/browser/src/background/nativeMessaging.background.ts @@ -291,7 +208,6 @@ ./apps/browser/src/safari/safari/SafariWebExtensionHandler.swift ./apps/browser/src/safari/safari/Info.plist ./apps/browser/src/safari/desktop.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist -./apps/browser/src/commands/autoFillActiveTabCommand.ts ./apps/browser/src/listeners/onCommandListener.ts ./apps/browser/src/listeners/onInstallListener.ts ./apps/browser/src/services/browserFileDownloadService.ts diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index 0a5746ce682..e54dc936eb4 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -63,8 +63,8 @@ jobs: repo_url=https://github.com/$GITHUB_REPOSITORY.git adj_build_num=${GITHUB_SHA:0:7} - echo "::set-output name=repo_url::$repo_url" - echo "::set-output name=adj_build_number::$adj_build_num" + echo "repo_url=$repo_url" >> $GITHUB_OUTPUT + echo "adj_build_number=$adj_build_num" >> $GITHUB_OUTPUT locales-test: @@ -183,7 +183,7 @@ jobs: name: dist-opera-MV3-${{ env._BUILD_NUMBER }}.zip path: apps/browser/dist/dist-opera-mv3.zip if-no-files-found: error - + - name: Upload Chrome artifact uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 with: diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index e32dbedb24c..56ad1fac9fc 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -62,7 +62,7 @@ jobs: id: retrieve-version run: | PKG_VERSION=$(jq -r .version package.json) - echo "::set-output name=package_version::$PKG_VERSION" + echo "package_version=$PKG_VERSION" >> $GITHUB_OUTPUT cli: @@ -72,7 +72,7 @@ jobs: - setup env: _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} - _WIN_PKG_FETCH_VERSION: 16.15.0 + _WIN_PKG_FETCH_VERSION: 16.16.0 _WIN_PKG_VERSION: 3.4 steps: - name: Checkout repo diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index be69b22a8a5..f92ae8a71e4 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -87,29 +87,29 @@ jobs: id: retrieve-version run: | PKG_VERSION=$(jq -r .version src/package.json) - echo "::set-output name=package_version::$PKG_VERSION" + echo "package_version=$PKG_VERSION" >> $GITHUB_OUTPUT - name: Increment Version id: increment-version run: | BUILD_NUMBER=$(expr 3000 + $GITHUB_RUN_NUMBER) echo "Setting build number to $BUILD_NUMBER" - echo "::set-output name=build_number::$BUILD_NUMBER" + echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT - name: Get Version Channel id: release-channel run: | case "${{ steps.retrieve-version.outputs.package_version }}" in *"alpha"*) - echo "::set-output name=channel::alpha" + echo "channel=alpha" >> $GITHUB_OUTPUT echo "[!] We do not yet support 'alpha'" exit 1 ;; *"beta"*) - echo "::set-output name=channel::beta" + echo "channel=beta" >> $GITHUB_OUTPUT ;; *) - echo "::set-output name=channel::latest" + echo "channel=latest" >> $GITHUB_OUTPUT ;; esac @@ -117,15 +117,15 @@ jobs: id: branch-check run: | if [[ $(git ls-remote --heads origin rc) ]]; then - echo "::set-output name=rc_branch_exists::1" + echo "rc_branch_exists=1" >> $GITHUB_OUTPUT else - echo "::set-output name=rc_branch_exists::0" + echo "rc_branch_exists=0" >> $GITHUB_OUTPUT fi if [[ $(git ls-remote --heads origin hotfix-rc-desktop) ]]; then - echo "::set-output name=hotfix_branch_exists::1" + echo "hotfix_branch_exists=1" >> $GITHUB_OUTPUT else - echo "::set-output name=hotfix_branch_exists::0" + echo "hotfix_branch_exists=0" >> $GITHUB_OUTPUT fi diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index f7073ec3c62..b0bd282de6f 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -60,7 +60,7 @@ jobs: - name: Get GitHub sha as version id: version - run: echo "::set-output name=value::${GITHUB_SHA:0:7}" + run: echo "value=${GITHUB_SHA:0:7}" >> $GITHUB_OUTPUT build-artifacts: name: Build artifacts @@ -112,7 +112,7 @@ jobs: if: matrix.name == 'cloud-QA' run: | VERSION=$( jq -r ".version" package.json) - jq --arg version "$VERSION - ${GITHUB_SHA:0:7}" '.version = $version' package.json > package.json.tmp + jq --arg version "$VERSION+${GITHUB_SHA:0:7}" '.version = $version' package.json > package.json.tmp mv package.json.tmp package.json - name: Build ${{ matrix.name }} @@ -303,7 +303,7 @@ jobs: IMAGE_TAG=$IMAGE_TAG-$TAG_EXTENSION fi - echo "::set-output name=value::$IMAGE_TAG" + echo "value=$IMAGE_TAG" >> $GITHUB_OUTPUT - name: Tag image env: diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index 5d0e1f963fe..c01f73677d2 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -266,7 +266,7 @@ jobs: uses: bitwarden/gh-actions/get-keyvault-secrets@c3b3285993151c5af47cefcb3b9134c28ab479af with: keyvault: "bitwarden-prod-kv" - secrets: "cli-npm-api-key" + secrets: "npm-api-key" - name: Download artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} @@ -293,7 +293,7 @@ jobs: echo 'registry="https://registry.npmjs.org/"' > ./.npmrc echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ./.npmrc env: - NPM_TOKEN: ${{ steps.retrieve-secrets.outputs.cli-npm-api-key }} + NPM_TOKEN: ${{ steps.retrieve-secrets.outputs.npm-api-key }} - name: Install Husky run: npm install -g husky diff --git a/.github/workflows/release-desktop-beta.yml b/.github/workflows/release-desktop-beta.yml index a57b600d0ec..fb456de1951 100644 --- a/.github/workflows/release-desktop-beta.yml +++ b/.github/workflows/release-desktop-beta.yml @@ -60,22 +60,22 @@ jobs: run: | BUILD_NUMBER=$(expr 3000 + $GITHUB_RUN_NUMBER) echo "Setting build number to $BUILD_NUMBER" - echo "::set-output name=build_number::$BUILD_NUMBER" + echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT - name: Get Version Channel id: release-channel run: | case "${{ steps.version.outputs.version }}" in *"alpha"*) - echo "::set-output name=channel::alpha" + echo "channel=alpha" >> $GITHUB_OUTPUT echo "[!] We do not yet support 'alpha'" exit 1 ;; *"beta"*) - echo "::set-output name=channel::beta" + echo "channel=beta" >> $GITHUB_OUTPUT ;; *) - echo "::set-output name=channel::latest" + echo "channel=latest" >> $GITHUB_OUTPUT ;; esac @@ -102,7 +102,7 @@ jobs: git push -u origin $branch_name - echo "::set-output name=branch-name::$branch_name" + echo "branch-name=$branch_name" >> $GITHUB_OUTPUT linux: name: Linux Build diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 80c3b71c15d..a4aceac962b 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -29,6 +29,16 @@ on: required: true default: true type: boolean + electron_publish: + description: 'Publish electron to S3 bucket' + required: true + default: true + type: boolean + github_release: + description: 'Publish github release' + required: true + default: true + type: boolean defaults: run: @@ -70,15 +80,15 @@ jobs: run: | case "${{ steps.version.outputs.version }}" in *"alpha"*) - echo "::set-output name=channel::alpha" + echo "channel=alpha" >> $GITHUB_OUTPUT echo "[!] We do not yet support 'alpha'" exit 1 ;; *"beta"*) - echo "::set-output name=channel::beta" + echo "channel=beta" >> $GITHUB_OUTPUT ;; *) - echo "::set-output name=channel::latest" + echo "channel=latest" >> $GITHUB_OUTPUT ;; esac @@ -136,6 +146,7 @@ jobs: run: mv Bitwarden-${{ env.PKG_VERSION }}-universal.pkg Bitwarden-${{ env.PKG_VERSION }}-universal.pkg.archive - name: Set staged rollout percentage + if: ${{ github.event.inputs.electron_publish }} env: RELEASE_CHANNEL: ${{ steps.release-channel.outputs.channel }} ROLLOUT_PCT: ${{ github.event.inputs.rollout_percentage }} @@ -145,7 +156,7 @@ jobs: echo "stagingPercentage: ${ROLLOUT_PCT}" >> apps/desktop/artifacts/${RELEASE_CHANNEL}-mac.yml - name: Publish artifacts to S3 - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ github.event.inputs.release_type != 'Dry Run' && github.event.inputs.electron_publish }} env: AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.aws-electron-access-id }} AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.aws-electron-access-key }} @@ -159,7 +170,7 @@ jobs: --quiet - name: Publish artifacts to R2 - if: ${{ github.event.inputs.release_type != 'Dry Run' }} + if: ${{ github.event.inputs.release_type != 'Dry Run' && github.event.inputs.electron_publish }} env: AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.r2-electron-access-id }} AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.r2-electron-access-key }} @@ -175,7 +186,7 @@ jobs: - name: Create Release uses: ncipollo/release-action@95215a3cb6e6a1908b3c44e00b4fdb15548b1e09 - if: ${{ steps.release-channel.outputs.channel == 'latest' && github.event.inputs.release_type != 'Dry Run' }} + if: ${{ steps.release-channel.outputs.channel == 'latest' && github.event.inputs.release_type != 'Dry Run' && github.event.inputs.github_release }} env: PKG_VERSION: ${{ steps.version.outputs.version }} RELEASE_CHANNEL: ${{ steps.release-channel.outputs.channel }} diff --git a/.github/workflows/staged-rollout-desktop.yml b/.github/workflows/staged-rollout-desktop.yml index d60fa700617..d2c842301a3 100644 --- a/.github/workflows/staged-rollout-desktop.yml +++ b/.github/workflows/staged-rollout-desktop.yml @@ -15,26 +15,9 @@ defaults: shell: bash jobs: - setup: - name: Setup - runs-on: ubuntu-22.04 - steps: - - name: Checkout repo - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b - - - name: Branch check - run: | - if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc-desktop" ]]; then - echo "===================================" - echo "[!] Can only increase rollout from the 'rc' or 'hotfix-rc-desktop' branches" - echo "===================================" - exit 1 - fi - rollout: name: Update Rollout Percentage runs-on: ubuntu-22.04 - needs: setup steps: - name: Login to Azure uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010 @@ -54,20 +37,6 @@ jobs: r2-electron-bucket-name, cf-prod-account" - - name: Download channel update info files from S3 - env: - AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.aws-electron-access-id }} - AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.aws-electron-access-key }} - AWS_DEFAULT_REGION: 'us-west-2' - AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.aws-electron-bucket-name }} - run: | - aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest.yml . \ - --quiet - aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest-linux.yml . \ - --quiet - aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest-mac.yml . \ - --quiet - - name: Download channel update info files from R2 env: AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.r2-electron-access-id }} @@ -99,7 +68,7 @@ jobs: echo echo "If you want to pull a staged release because it hasn’t gone well, you must increment the version \ number higher than your broken release. Because some of your users will be on the broken 1.0.1, \ - releasing a new 1.0.1 would result in them staying on a broken version.” + releasing a new 1.0.1 would result in them staying on a broken version." exit 1 fi @@ -118,10 +87,14 @@ jobs: AWS_DEFAULT_REGION: 'us-west-2' AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.aws-electron-bucket-name }} run: | - aws s3 cp ./ $AWS_S3_BUCKET_NAME/desktop/ \ - --include "latest*.yml" \ - --acl "public-read" \ - --quiet + aws s3 cp latest.yml $AWS_S3_BUCKET_NAME/desktop/ \ + --acl "public-read" + + aws s3 cp latest-linux.yml $AWS_S3_BUCKET_NAME/desktop/ \ + --acl "public-read" + + aws s3 cp latest-mac.yml $AWS_S3_BUCKET_NAME/desktop/ \ + --acl "public-read" - name: Publish channel update info files to R2 env: @@ -131,7 +104,11 @@ jobs: AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.r2-electron-bucket-name }} CF_ACCOUNT: ${{ steps.retrieve-secrets.outputs.cf-prod-account }} run: | - aws s3 cp ./ $AWS_S3_BUCKET_NAME/desktop/ \ - --include "latest*.yml" \ - --quiet \ + aws s3 cp latest.yml $AWS_S3_BUCKET_NAME/desktop/ \ + --endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com + + aws s3 cp latest-linux.yml $AWS_S3_BUCKET_NAME/desktop/ \ + --endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com + + aws s3 cp latest-mac.yml $AWS_S3_BUCKET_NAME/desktop/ \ --endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b0ca237c0cc..44f5325ce02 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -53,8 +53,16 @@ jobs: done - name: Run tests - run: | - npm run test + run: npm run test + + - name: Report test results + uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226 + if: always() + with: + name: Test Results + path: "junit.xml" + reporter: jest-junit + fail-on-error: true rust: name: rust - ${{ matrix.os }} @@ -102,6 +110,4 @@ jobs: - name: Test Windows / macOS if: ${{ matrix.os!='ubuntu-latest' }} working-directory: ./apps/desktop/desktop_native - run: | - cargo test -- --test-threads=1 - + run: cargo test -- --test-threads=1 diff --git a/.github/workflows/version-auto-bump.yml b/.github/workflows/version-auto-bump.yml index 3e5b5fc3c2b..5060156114b 100644 --- a/.github/workflows/version-auto-bump.yml +++ b/.github/workflows/version-auto-bump.yml @@ -25,8 +25,8 @@ jobs: env: RELEASE_TAG: ${{ github.ref }} run: | - CURR_MAJOR=$(echo $RELEASE_TAG | sed -r 's/[a-z]*-v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\1/') - CURR_PATCH=$(echo $RELEASE_TAG | sed -r 's/[a-z]*-v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\2/') + CURR_MAJOR=$(echo $RELEASE_TAG | sed -r 's/refs\/tags\/[a-z]*-v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\1/') + CURR_PATCH=$(echo $RELEASE_TAG | sed -r 's/refs\/tags\/[a-z]*-v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\2/') echo "Current Major: $CURR_MAJOR" echo "Current Patch: $CURR_PATCH" @@ -36,7 +36,7 @@ jobs: NEW_VER=$CURR_MAJOR.$NEW_PATCH echo "New Version: $NEW_VER" - echo "::set-output name=new-version::$NEW_VER" + echo "new-version=$NEW_VER" >> $GITHUB_OUTPUT trigger_version_bump: name: "Trigger desktop version bump workflow" diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index 80d54eef145..f7ba508c6c3 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -56,7 +56,7 @@ jobs: VERSION: ${{ github.event.inputs.version_number }} run: | CLIENT=$(python -c "print('$CLIENT_NAME'.lower())") - echo "::set-output name=client::$CLIENT" + echo "client=$CLIENT" >> $GITHUB_OUTPUT git switch -c ${CLIENT}_version_bump_${VERSION} @@ -131,9 +131,9 @@ jobs: id: version-changed run: | if [ -n "$(git status --porcelain)" ]; then - echo "::set-output name=changes_to_commit::TRUE" + echo "changes_to_commit=TRUE" >> $GITHUB_OUTPUT else - echo "::set-output name=changes_to_commit::FALSE" + echo "changes_to_commit=FALSE" >> $GITHUB_OUTPUT echo "No changes to commit!"; fi @@ -142,8 +142,7 @@ jobs: env: CLIENT: ${{ steps.branch.outputs.client }} VERSION: ${{ github.event.inputs.version_number }} - run: | - git commit -m "Bumped ${CLIENT} version to ${VERSION}" -a + run: git commit -m "Bumped ${CLIENT} version to ${VERSION}" -a - name: Push changes if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }} diff --git a/.gitignore b/.gitignore index d7f237aa071..11a4d4c80ff 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ build # Testing coverage +junit.xml # Misc *.crx diff --git a/.storybook/main.js b/.storybook/main.js index 7c0d2d97a15..3db3964022e 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -6,6 +6,8 @@ module.exports = { "../libs/components/src/**/*.stories.@(js|jsx|ts|tsx)", "../apps/web/src/**/*.stories.mdx", "../apps/web/src/**/*.stories.@(js|jsx|ts|tsx)", + "../bitwarden_license/bit-web/src/**/*.stories.mdx", + "../bitwarden_license/bit-web/src/**/*.stories.@(js|jsx|ts|tsx)", ], addons: [ "@storybook/addon-links", @@ -18,6 +20,12 @@ module.exports = { builder: "webpack5", disableTelemetry: true, }, + env: (config) => ({ + ...config, + FLAGS: JSON.stringify({ + secretsManager: true, + }), + }), webpackFinal: async (config, { configType }) => { config.resolve.plugins = [new TsconfigPathsPlugin()]; return config; diff --git a/README.md b/README.md index c426251905b..c04fbf66062 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Github Workflow web build on master - + gitter chat @@ -25,7 +25,7 @@ This repository houses all Bitwarden client applications except the [Mobile application](https://github.com/bitwarden/mobile). -Please refer to the [Clients section](https://contributing.bitwarden.com/clients/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. +Please refer to the [Clients section](https://contributing.bitwarden.com/getting-started/clients/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. ## Related projects: diff --git a/apps/browser/README.md b/apps/browser/README.md index 3ec5e544547..2cd1615a112 100644 --- a/apps/browser/README.md +++ b/apps/browser/README.md @@ -19,4 +19,4 @@ The Bitwarden browser extension is written using the Web Extension API and Angul ## Documentation -Please refer to the [Browser section](https://contributing.bitwarden.com/clients/browser/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. +Please refer to the [Browser section](https://contributing.bitwarden.com/getting-started/clients/browser/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. diff --git a/apps/browser/package.json b/apps/browser/package.json index 50f71797a69..d3f64632bc8 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/browser", - "version": "2022.10.2", + "version": "2023.1.0", "scripts": { "build": "webpack", "build:mv3": "cross-env MANIFEST_VERSION=3 webpack", diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 6e4208b13f0..d0066f44e6c 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -482,7 +482,7 @@ "message": "الاسم مطلوب." }, "addedFolder": { - "message": "Folder added" + "message": "أُضيف المجلد" }, "changeMasterPass": { "message": "تغيير كلمة المرور الرئيسية" @@ -494,7 +494,7 @@ "message": "تسجيل الدخول بخطوتين يجعل حسابك أكثر أمنا من خلال مطالبتك بالتحقق من تسجيل الدخول باستخدام جهاز آخر مثل مفتاح الأمان، تطبيق المصادقة، الرسائل القصيرة، المكالمة الهاتفية، أو البريد الإلكتروني. يمكن تمكين تسجيل الدخول بخطوتين على خزنة الويب bitwarden.com. هل تريد زيارة الموقع الآن؟" }, "editedFolder": { - "message": "Folder saved" + "message": "حُفظ المجلد" }, "deleteFolderConfirmation": { "message": "هل أنت متأكد من حذف هذا المجلّد؟" @@ -521,7 +521,7 @@ "message": "عنوان الـ URI" }, "uriPosition": { - "message": "URI $POSITION$", + "message": "رابط $POSITION$", "description": "A listing of URIs. Ex: URI 1, URI 2, URI 3, etc.", "placeholders": { "position": { @@ -571,22 +571,22 @@ "description": "This is the folder for uncategorized items" }, "enableAddLoginNotification": { - "message": "Ask to add login" + "message": "اطلب إضافة تسجيل الدخول" }, "addLoginNotificationDesc": { - "message": "Ask to add an item if one isn't found in your vault." + "message": "اطلب إضافة عنصر إذا لم يُعثر عليه في خزنتك." }, "showCardsCurrentTab": { - "message": "Show cards on Tab page" + "message": "أظهر البطاقات في صفحة التبويبات" }, "showCardsCurrentTabDesc": { - "message": "List card items on the Tab page for easy auto-fill." + "message": "قائمة عناصر البطاقة في صفحة التبويب لسهولة التعبئة التلقائية." }, "showIdentitiesCurrentTab": { - "message": "Show identities on Tab page" + "message": "إظهار الهويات على صفحة التبويب" }, "showIdentitiesCurrentTabDesc": { - "message": "List identity items on the Tab page for easy auto-fill." + "message": "قائمة عناصر الهوية في صفحة التبويب لسهولة الملء التلقائي." }, "clearClipboard": { "message": "مسح الحافظة", @@ -615,23 +615,23 @@ "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": "استخدم نقرة ثانوية للوصول إلى توليد كلمة المرور ومطابقة تسجيلات الدخول للموقع. " }, "defaultUriMatchDetection": { - "message": "Default URI match detection", + "message": "الكشف الافتراضي عن تطابق URI", "description": "Default URI match detection for auto-fill." }, "defaultUriMatchDetectionDesc": { - "message": "Choose the default way that URI match detection is handled for logins when performing actions such as auto-fill." + "message": "اختر الطريقة الافتراضية التي يتم التعامل بها مع الكشف عن مطابقة URI لتسجيل الدخول عند تنفيذ إجراءات مثل التعبئة التلقائية." }, "theme": { - "message": "Theme" + "message": "السمة" }, "themeDesc": { - "message": "Change the application's color theme." + "message": "تغيير سمة لون التطبيق." }, "dark": { "message": "داكن", @@ -659,28 +659,28 @@ "message": "تأكيد تصدير الخزنة" }, "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": "يحتوي هذا التصدير على بيانات خزنتك بتنسيق غير مشفر. لا يجب عليك تخزين أو إرسال الملف الذي تم تصديره عبر قنوات غير آمنة (مثل البريد الإلكتروني). احذفه مباشرة بعد انتهائك من استخدامه." }, "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": "يقوم هذا التصدير بتشفير بياناتك باستخدام مفتاح تشفير حسابك. إذا قمت بتدوير مفتاح تشفير حسابك يجب عليك التصدير مرة أخرى لأنك لن تتمكن من فك تشفير ملف التصدير هذا." }, "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": "مفاتيح تشفير الحساب فريدة من نوعها لكل حساب مستخدم Bitwarden، لذلك لا يمكنك استيراد تصدير مشفر إلى حساب آخر." }, "exportMasterPassword": { - "message": "Enter your master password to export your vault data." + "message": "أدخل كلمة المرور الرئيسية لتصدير بيانات خزنتك." }, "shared": { - "message": "Shared" + "message": "مشترك" }, "learnOrg": { - "message": "Learn about organizations" + "message": "تعرف على المؤسسات" }, "learnOrgConfirmation": { - "message": "Bitwarden allows you to share your vault items with others by using an organization. Would you like to visit the bitwarden.com website to learn more?" + "message": "يسمح لك Bitwarden بمشاركة عناصر خزنتك مع الآخرين باستخدام حساب المؤسسة. هل ترغب في زيارة موقع bitwarden.com لمعرفة المزيد؟" }, "moveToOrganization": { - "message": "Move to organization" + "message": "الانتقال إلى مؤسسة" }, "share": { "message": "مشاركة" @@ -711,94 +711,94 @@ "message": "رمز التحقق (TOTP)" }, "copyVerificationCode": { - "message": "Copy verification code" + "message": "نسخ رمز التحقق" }, "attachments": { - "message": "Attachments" + "message": "المرفقات" }, "deleteAttachment": { - "message": "Delete attachment" + "message": "حذف المرفق" }, "deleteAttachmentConfirmation": { - "message": "Are you sure you want to delete this attachment?" + "message": "هل أنت متأكد من أنك تريد حذف هذا المرفق؟" }, "deletedAttachment": { - "message": "Attachment deleted" + "message": "تم حذف المرفق" }, "newAttachment": { - "message": "Add new attachment" + "message": "إضافة مرفق جديد" }, "noAttachments": { - "message": "No attachments." + "message": "لا توجد مرفقات." }, "attachmentSaved": { - "message": "Attachment saved" + "message": "تم حفظ المرفقات" }, "file": { - "message": "File" + "message": "الملف" }, "selectFile": { - "message": "Select a file" + "message": "حدد ملفًا" }, "maxFileSize": { - "message": "Maximum file size is 500 MB." + "message": "الحجم الأقصى للملف هو 500 ميجابايت." }, "featureUnavailable": { - "message": "Feature unavailable" + "message": "الميزة غير متوفرة" }, "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "message": "لا يمكنك استخدام هذه المِيزة حتى تحديث مفتاح التشفير الخاص بك." }, "premiumMembership": { - "message": "Premium membership" + "message": "العضوية المميزة" }, "premiumManage": { - "message": "Manage membership" + "message": "إدارة العضوية" }, "premiumManageAlert": { - "message": "You can manage your membership on the bitwarden.com web vault. Do you want to visit the website now?" + "message": "يمكنك إدارة عضويتك على مقطع ويب bitwarden.com. هل تريد زيارة الموقع الآن؟" }, "premiumRefresh": { - "message": "Refresh membership" + "message": "تحديث العضوية" }, "premiumNotCurrentMember": { - "message": "You are not currently a Premium member." + "message": "أنت لست حاليا عضوا مميزا." }, "premiumSignUpAndGet": { - "message": "Sign up for a Premium membership and get:" + "message": "قم بالتسجيل للحصول على عضوية مميزة واحصل على:" }, "ppremiumSignUpStorage": { - "message": "1 GB encrypted storage for file attachments." + "message": "1 جيغابايت وحدة تخزين مشفرة لمرفقات الملفات." }, "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "message": "خيارات تسجيل الدخول الإضافية من خطوتين مثل YubiKey و FIDO U2F و Duo." }, "ppremiumSignUpReports": { - "message": "Password hygiene, account health, and data breach reports to keep your vault safe." + "message": "نظافة كلمة المرور، صحة الحساب، وتقارير خرق البيانات للحفاظ على سلامة خزنتك." }, "ppremiumSignUpTotp": { - "message": "TOTP verification code (2FA) generator for logins in your vault." + "message": "مورد رمز التحقق (2FA) لتسجيل الدخول في خزنتك." }, "ppremiumSignUpSupport": { - "message": "Priority customer support." + "message": "أولوية دعم العملاء." }, "ppremiumSignUpFuture": { - "message": "All future Premium features. More coming soon!" + "message": "جميع الميزات المميزة المستقبلية. المزيد قادم قريبا!" }, "premiumPurchase": { - "message": "Purchase Premium" + "message": "شراء العضوية المميزة" }, "premiumPurchaseAlert": { - "message": "You can purchase Premium membership on the bitwarden.com web vault. Do you want to visit the website now?" + "message": "يمكنك شراء العضوية المتميزة على bitwarden.com على خزانة الويب. هل تريد زيارة الموقع الآن؟" }, "premiumCurrentMember": { - "message": "You are a Premium member!" + "message": "أنت عضو مميز!" }, "premiumCurrentMemberThanks": { - "message": "Thank you for supporting Bitwarden." + "message": "شكرا لك على دعم Bitwarden." }, "premiumPrice": { - "message": "All for just $PRICE$ /year!", + "message": "الكل فقط بـ $PRICE$ /سنة!", "placeholders": { "price": { "content": "$1", @@ -807,28 +807,28 @@ } }, "refreshComplete": { - "message": "Refresh complete" + "message": "اكتمل التحديث" }, "enableAutoTotpCopy": { - "message": "Copy TOTP automatically" + "message": "نسخ TOTP تلقائياً" }, "disableAutoTotpCopyDesc": { - "message": "If a login has an authenticator key, copy the TOTP verification code to your clip-board when you auto-fill the login." + "message": "إذا كان تسجيل الدخول يحتوي على مفتاح مصادقة، انسخ رمز التحقق TOTP إلى الحافظة الخاصة بك عند ملء تسجيل الدخول تلقائيا." }, "enableAutoBiometricsPrompt": { - "message": "Ask for biometrics on launch" + "message": "اسأل عن القياسات الحيوية عند الإطلاق" }, "premiumRequired": { - "message": "Premium required" + "message": "حساب البريميوم مطلوب" }, "premiumRequiredDesc": { - "message": "A Premium membership is required to use this feature." + "message": "هذه المِيزة متاحة فقط للعضوية المميزة." }, "enterVerificationCodeApp": { - "message": "Enter the 6 digit verification code from your authenticator app." + "message": "أدخل رمز التحقق من 6 أرقام من تطبيق المصادقة الخاص بك." }, "enterVerificationCodeEmail": { - "message": "Enter the 6 digit verification code that was emailed to $EMAIL$.", + "message": "أدخل رمز التحقق المكون من 6 أرقام الذي تم إرساله إلى $EMAIL$.", "placeholders": { "email": { "content": "$1", @@ -837,7 +837,7 @@ } }, "verificationCodeEmailSent": { - "message": "Verification email sent to $EMAIL$.", + "message": "تم إرسال رسالة التحقق إلى $EMAIL$.", "placeholders": { "email": { "content": "$1", @@ -846,115 +846,115 @@ } }, "rememberMe": { - "message": "Remember me" + "message": "تذكرني" }, "sendVerificationCodeEmailAgain": { - "message": "Send verification code email again" + "message": "إرسال رمز التحقق إلى البريد الإلكتروني مرة أخرى" }, "useAnotherTwoStepMethod": { - "message": "Use another two-step login method" + "message": "استخدام طريقة أخرى لتسجيل الدخول بخطوتين" }, "insertYubiKey": { - "message": "Insert your YubiKey into your computer's USB port, then touch its button." + "message": "أدخل YubiKey الخاص بك في منفذ USB في كمبيوترك، ثم المس الزر." }, "insertU2f": { - "message": "Insert your security key into your computer's USB port. If it has a button, touch it." + "message": "أدخل مفتاح الأمان الخاص بك في منفذ USB كمبيوترك، إذا كان يحتوي على زر، إلمسه." }, "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": "لبدء التحقق من WebAuthn 2FA. انقر على الزر أدناه لفتح علامة تبويب جديدة واتبع التعليمات المقدمة في علامة التبويب الجديدة." }, "webAuthnNewTabOpen": { - "message": "Open new tab" + "message": "فتح علامة تبويب جديدة" }, "webAuthnAuthenticate": { - "message": "Authenticate WebAuthn" + "message": "مصادقة WebAuthn" }, "loginUnavailable": { - "message": "Login unavailable" + "message": "تسجيل الدخول غير متاح" }, "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": "تم إعداد تسجيل الدخول من خطوتين لهذا الحساب، ومع ذلك، لا يدعم هذا المتصفح أيًا من موفري تسجيل الدخول من خطوتين." }, "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": "الرجاء استخدام متصفح ويب مدعوم (مثل Chrome) و/أو إضافة موفري إضافيين مدعومين بشكل أفضل عبر متصفحات الويب (مثل تطبيق المصادقة)." }, "twoStepOptions": { - "message": "Two-step login options" + "message": "خيارات تسجيل الدخول بخطوتين" }, "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": "هل تفقد الوصول إلى جميع مزودي التحقق بعاملين؟ استخدم رمز الاسترداد الخاص بك لتعطيل جميع مزودي التحقق بعاملين من حسابك." }, "recoveryCodeTitle": { - "message": "Recovery code" + "message": "رمز الاسترداد" }, "authenticatorAppTitle": { - "message": "Authenticator app" + "message": "تطبيق المصادقة" }, "authenticatorAppDesc": { - "message": "Use an authenticator app (such as Authy or Google Authenticator) to generate time-based verification codes.", + "message": "استخدام تطبيق مصادقة (مثل Authy أو Google Authenticator) لإنشاء رموز تحقق مستندة إلى الوقت.", "description": "'Authy' and 'Google Authenticator' are product names and should not be translated." }, "yubiKeyTitle": { - "message": "YubiKey OTP Security Key" + "message": "مفتاح أمان YubiKey OTP" }, "yubiKeyDesc": { - "message": "Use a YubiKey to access your account. Works with YubiKey 4, 4 Nano, 4C, and NEO devices." + "message": "استخدم YubiKey للوصول إلى حسابك. يعمل مع YubiKey 4 ،4 Nano ،4C، وأجهزة NEO." }, "duoDesc": { - "message": "Verify with Duo Security using the Duo Mobile app, SMS, phone call, or U2F security key.", + "message": "التحقق باستخدام نظام الحماية الثنائي باستخدام تطبيق Duo Mobile أو الرسائل القصيرة أو المكالمة الهاتفية أو مفتاح الأمان U2F.", "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": "تحقق من خلال نظام الحماية الثنائي لمؤسستك باستخدام تطبيق Duo Mobile أو الرسائل القصيرة أو المكالمة الهاتفية أو مفتاح الأمان 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": "استخدم أي مفتاح أمان متوافق مع WebAuthn للوصول إلى حسابك." }, "emailTitle": { - "message": "Email" + "message": "البريد الإلكتروني" }, "emailDesc": { - "message": "Verification codes will be emailed to you." + "message": "سيتم إرسال رمز التحقق إليك بالبريد الإلكتروني." }, "selfHostedEnvironment": { - "message": "Self-hosted environment" + "message": "البيئة المستضافة ذاتيا" }, "selfHostedEnvironmentFooter": { - "message": "Specify the base URL of your on-premises hosted Bitwarden installation." + "message": "حدد عنوان URL الأساسي لتثبيت Bitwarden المستضاف محليًا." }, "customEnvironment": { - "message": "Custom environment" + "message": "بيئة مخصصة" }, "customEnvironmentFooter": { - "message": "For advanced users. You can specify the base URL of each service independently." + "message": "للمستخدمين المتقدمين. يمكنك تحديد عنوان URL الأساسي لكل خدمة بشكل مستقل." }, "baseUrl": { - "message": "Server URL" + "message": "رابط الخادم" }, "apiUrl": { - "message": "API Server URL" + "message": "رابط خادم API" }, "webVaultUrl": { - "message": "Web vault server URL" + "message": "رابط خادم مخزن الويب" }, "identityUrl": { - "message": "Identity server URL" + "message": "رابط خادم الهوية" }, "notificationsUrl": { - "message": "Notifications server URL" + "message": "رابط خادم الإشعارات" }, "iconsUrl": { - "message": "Icons server URL" + "message": "رابط خادم الأيقونات" }, "environmentSaved": { - "message": "Environment URLs saved" + "message": "روابط البيئة المحفوظة" }, "enableAutoFillOnPageLoad": { - "message": "Auto-fill on page load" + "message": "ملء تلقائي عند تحميل الصفحة" }, "enableAutoFillOnPageLoadDesc": { "message": "If a login form is detected, auto-fill when the web page loads." @@ -972,7 +972,7 @@ "message": "Auto-fill on page load (if set up in Options)" }, "autoFillOnPageLoadUseDefault": { - "message": "Use default setting" + "message": "إستخدم الإعداد الإفتراضي" }, "autoFillOnPageLoadYes": { "message": "Auto-fill on page load" @@ -1020,108 +1020,108 @@ "message": "Hidden" }, "cfTypeBoolean": { - "message": "Boolean" + "message": "قيمة منطقية" }, "cfTypeLinked": { - "message": "Linked", + "message": "مرتبط", "description": "This describes a field that is 'linked' (tied) to another field." }, "linkedValue": { - "message": "Linked value", + "message": "القيمة المرتبطة", "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": "النقر خارج النافذة المنبثقة للتحقق من بريدك الإلكتروني للحصول على رمز التحقق الخاص بك سيؤدي إلى إغلاق هذا المنبثق. هل تريد فتح هذا المنبثق في نافذة جديدة حتى لا يغلق؟" }, "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": "لا يمكن لهذا المتصفح معالجة طلبات U2F في هذه النافذة المنبثقة. هل تريد فتح هذا المنبثق في نافذة جديدة بحيث يمكنك تسجيل الدخول باستخدام U2F؟" }, "enableFavicon": { - "message": "Show website icons" + "message": "إظهار أيقونات الموقع" }, "faviconDesc": { - "message": "Show a recognizable image next to each login." + "message": "إظهار صورة قابلة للتعرف بجانب كل تسجيل دخول." }, "enableBadgeCounter": { - "message": "Show badge counter" + "message": "إظهار عداد الشارات" }, "badgeCounterDesc": { - "message": "Indicate how many logins you have for the current web page." + "message": "حدد عدد تسجيلات الدخول الخاصة بك لصفحة الويب الحالية." }, "cardholderName": { - "message": "Cardholder name" + "message": "اسم حامل البطاقة" }, "number": { - "message": "Number" + "message": "الرقم" }, "brand": { - "message": "Brand" + "message": "العلامة" }, "expirationMonth": { - "message": "Expiration month" + "message": "شهر الانتهاء" }, "expirationYear": { - "message": "Expiration year" + "message": "سنة الإنتهاء" }, "expiration": { - "message": "Expiration" + "message": "تاريخ الانتهاء" }, "january": { - "message": "January" + "message": "جانفي" }, "february": { - "message": "February" + "message": "فبراير" }, "march": { - "message": "March" + "message": "مارس" }, "april": { - "message": "April" + "message": "أبريل" }, "may": { - "message": "May" + "message": "مايو" }, "june": { - "message": "June" + "message": "جوان" }, "july": { - "message": "July" + "message": "جويلية" }, "august": { - "message": "August" + "message": "أغسطس" }, "september": { - "message": "September" + "message": "سبتمبر" }, "october": { - "message": "October" + "message": "أكتوبر" }, "november": { - "message": "November" + "message": "نوفمبر" }, "december": { - "message": "December" + "message": "ديسمبر" }, "securityCode": { - "message": "Security code" + "message": "رمز الأمان" }, "ex": { - "message": "ex." + "message": "مثال." }, "title": { - "message": "Title" + "message": "اللقب" }, "mr": { - "message": "Mr" + "message": "السيد" }, "mrs": { - "message": "Mrs" + "message": "السيدة" }, "ms": { - "message": "Ms" + "message": "الآنسة" }, "dr": { - "message": "Dr" + "message": "الدكتور" }, "firstName": { "message": "الاسم الأول" @@ -1136,107 +1136,107 @@ "message": "الاسم الكامل" }, "identityName": { - "message": "Identity name" + "message": "اسم الهوية" }, "company": { - "message": "Company" + "message": "الشركة" }, "ssn": { - "message": "Social Security number" + "message": "رقم الضمان الاجتماعي" }, "passportNumber": { - "message": "Passport number" + "message": "رقم جواز السفر" }, "licenseNumber": { - "message": "License number" + "message": "رقم الرخصة" }, "email": { - "message": "Email" + "message": "البريد الإلكتروني" }, "phone": { - "message": "Phone" + "message": "الهاتف" }, "address": { - "message": "Address" + "message": "العنوان" }, "address1": { - "message": "Address 1" + "message": "العنوان 1" }, "address2": { - "message": "Address 2" + "message": "العنوان 2" }, "address3": { - "message": "Address 3" + "message": "العنوان 3" }, "cityTown": { - "message": "City / Town" + "message": "المدينة / البلدة" }, "stateProvince": { - "message": "State / Province" + "message": "الولاية / المقاطعة" }, "zipPostalCode": { - "message": "Zip / Postal code" + "message": "الرمز البريدي" }, "country": { - "message": "Country" + "message": "البلد" }, "type": { - "message": "Type" + "message": "النوع" }, "typeLogin": { - "message": "Login" + "message": "تسجيل الدخول" }, "typeLogins": { - "message": "Logins" + "message": "تسجيلات الدخول" }, "typeSecureNote": { - "message": "Secure note" + "message": "ملاحظة آمنة" }, "typeCard": { - "message": "Card" + "message": "بطاقة" }, "typeIdentity": { - "message": "Identity" + "message": "الهوية" }, "passwordHistory": { - "message": "Password history" + "message": "سجل كلمة المرور" }, "back": { - "message": "Back" + "message": "رجوع" }, "collections": { - "message": "Collections" + "message": "المجموعات" }, "favorites": { - "message": "Favorites" + "message": "المفضلات" }, "popOutNewWindow": { - "message": "Pop out to a new window" + "message": "انبثق إلى نافذة جديدة" }, "refresh": { - "message": "Refresh" + "message": "تحديث" }, "cards": { - "message": "Cards" + "message": "البطاقات" }, "identities": { - "message": "Identities" + "message": "الهويات" }, "logins": { - "message": "Logins" + "message": "تسجيلات الدخول" }, "secureNotes": { - "message": "Secure notes" + "message": "ملاحظات آمنة" }, "clear": { - "message": "Clear", + "message": "مسح", "description": "To clear something out. example: To clear browser history." }, "checkPassword": { - "message": "Check if password has been exposed." + "message": "تحقق مما إذا تم الكشف عن كلمة المرور." }, "passwordExposed": { - "message": "This password has been exposed $VALUE$ time(s) in data breaches. You should change it.", + "message": "تم الكشف عن كلمة المرور هذه $VALUE$ مرّة (ات) في خروقات البيانات. يجب عليك تغييرها.", "placeholders": { "value": { "content": "$1", @@ -1245,47 +1245,47 @@ } }, "passwordSafe": { - "message": "This password was not found in any known data breaches. It should be safe to use." + "message": "لم يتم العثور على كلمة المرور هذه في أي عمليات اختراق معروفة للبيانات. من المفترض أن تكون آمنة للاستخدام." }, "baseDomain": { - "message": "Base domain", + "message": "النطاق الأساسي", "description": "Domain name. Ex. website.com" }, "domainName": { - "message": "Domain name", + "message": "إسم النطاق", "description": "Domain name. Ex. website.com" }, "host": { - "message": "Host", + "message": "المضيف", "description": "A URL's host value. For example, the host of https://sub.domain.com:443 is 'sub.domain.com:443'." }, "exact": { - "message": "Exact" + "message": "بالضبط" }, "startsWith": { - "message": "Starts with" + "message": "يبدأ بـ" }, "regEx": { - "message": "Regular expression", + "message": "التعبير الاعتيادي", "description": "A programming term, also known as 'RegEx'." }, "matchDetection": { - "message": "Match detection", + "message": "كشف المطابقة", "description": "URI match detection for auto-fill." }, "defaultMatchDetection": { - "message": "Default match detection", + "message": "الكشف الافتراضي عن المطابقة", "description": "Default URI match detection for auto-fill." }, "toggleOptions": { - "message": "Toggle options" + "message": "خيارات التبديل" }, "toggleCurrentUris": { - "message": "Toggle current URIs", + "message": "تبديل URIs الحالية", "description": "Toggle the display of the URIs of the currently open tabs in the browser." }, "currentUri": { - "message": "Current URI", + "message": "URI الحالي", "description": "The URI of one of the current open tabs in the browser." }, "organization": { @@ -1482,7 +1482,7 @@ } }, "masterPasswordPolicyRequirementsNotMet": { - "message": "Your new master password does not meet the policy requirements." + "message": "كلمة المرور الرئيسية الجديدة لا تفي بمتطلبات السياسة العامة." }, "acceptPolicies": { "message": "By checking this box you agree to the following:" @@ -1494,7 +1494,7 @@ "message": "Terms of Service" }, "privacyPolicy": { - "message": "Privacy Policy" + "message": "سياسة الخصوصية" }, "hintEqualsPassword": { "message": "Your password hint cannot be the same as your password." @@ -1695,7 +1695,7 @@ } }, "custom": { - "message": "Custom" + "message": "مُخصّص" }, "maximumAccessCount": { "message": "Maximum Access Count" @@ -2030,13 +2030,13 @@ } }, "loginWithMasterPassword": { - "message": "Log in with master password" + "message": "تسجيل الدخول باستخدام كلمة المرور الرئيسية" }, "loggingInAs": { - "message": "Logging in as" + "message": "تسجيل الدخول كـ" }, "notYou": { - "message": "Not you?" + "message": "ليس حسابك؟" }, "newAroundHere": { "message": "New around here?" diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 843a479d174..1c9b61a7927 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -196,7 +196,7 @@ "message": "Даведка і зваротная сувязь" }, "sync": { - "message": "Сінхранізавана" + "message": "Сінхранізаваць" }, "syncVaultNow": { "message": "Сінхранізаваць сховішча зараз" @@ -642,7 +642,7 @@ "description": "Light color" }, "solarizedDark": { - "message": "Цёмная Solarized", + "message": "Solarized dark", "description": "'Solarized' is a noun and the name of a color scheme. It should not be translated." }, "exportVault": { diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index fcca7309fa6..25b5b1d52b7 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -86,7 +86,7 @@ "message": "Копиране на номера" }, "copySecurityCode": { - "message": "Копиране на кода да сигурност" + "message": "Копиране на кода за сигурност" }, "autoFill": { "message": "Автоматично дописване" @@ -291,7 +291,7 @@ "message": "Парола" }, "passphrase": { - "message": "Парола за преминаване" + "message": "Парола-фраза" }, "favorite": { "message": "Любими" @@ -1923,17 +1923,17 @@ "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": "Използвайте възможностите за под-адресиране на е-поща на своя доставчик." }, "catchallEmail": { - "message": "Catch-all email" + "message": "Хващаща всичко е-поща" }, "catchallEmailDesc": { - "message": "Use your domain's configured catch-all inbox." + "message": "Използвайте конфигурираната входяща кутия за събиране на всичко." }, "random": { "message": "Произволно" @@ -1954,10 +1954,10 @@ "message": "Услуга" }, "forwardedEmail": { - "message": "Forwarded email alias" + "message": "Псевдоним на препратена е-поща" }, "forwardedEmailDesc": { - "message": "Generate an email alias with an external forwarding service." + "message": "Създайте псевдоним на е-поща с външна услуга за препращане." }, "hostname": { "message": "Име на сървъра", diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index b1c6ebd1721..ff44cfaff5d 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -53,19 +53,19 @@ "message": "Tab" }, "vault": { - "message": "Vault" + "message": "Trezor" }, "myVault": { - "message": "My vault" + "message": "Moj trezor" }, "allVaults": { "message": "All vaults" }, "tools": { - "message": "Tools" + "message": "Alati" }, "settings": { - "message": "Settings" + "message": "Postavke" }, "currentTab": { "message": "Current tab" @@ -116,7 +116,7 @@ "message": "Add item" }, "passwordHint": { - "message": "Password hint" + "message": "Nagovještaj lozinke" }, "enterEmailToGetHint": { "message": "Enter your account email address to receive your master password hint." @@ -125,7 +125,7 @@ "message": "Get master password hint" }, "continue": { - "message": "Continue" + "message": "Nastavi" }, "sendVerificationCode": { "message": "Send a verification code to your email" diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 2bcfbed2bb1..4e88a5bf3b2 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -1393,7 +1393,7 @@ "message": "Una o més polítiques d’organització afecten la configuració del generador." }, "vaultTimeoutAction": { - "message": "Acció del temps d'espera de la caixa forta" + "message": "Acció quan acabe el temps d'espera de la caixa forta" }, "lock": { "message": "Bloqueja", @@ -2030,7 +2030,7 @@ } }, "loginWithMasterPassword": { - "message": "Inicia sessió amb la contrasenya mestra" + "message": "Inici de sessió amb contrasenya mestra" }, "loggingInAs": { "message": "Has iniciat sessió com" diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index b3a8a004a29..796db71c2b0 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -149,7 +149,7 @@ "message": "Změnit hlavní heslo" }, "fingerprintPhrase": { - "message": "Fráze otisku účtu", + "message": "Unikátní přístupová fráze", "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": { @@ -606,7 +606,7 @@ "message": "Zeptat se na aktualizaci existujícího přihlášení" }, "changedPasswordNotificationDesc": { - "message": "Ask to update a login's password when a change is detected on a website." + "message": "Dotázat se na aktualizaci hesla pro přihlášení, pokud je na webové stránce zjištěno použití jiného hesla." }, "notificationChangeDesc": { "message": "Chcete aktualizovat toto heslo v Bitwarden?" @@ -618,7 +618,7 @@ "message": "Zobrazit v kontextovém menu" }, "contextMenuItemDesc": { - "message": "Use a secondary click to access password generation and matching logins for the website. " + "message": "Použijte pravé tlačítko pro přístup k vytvoření hesla a odpovídajícímu přihlášení pro tuto stránku. " }, "defaultUriMatchDetection": { "message": "Výchozí zjišťování shody URI", @@ -1043,10 +1043,10 @@ "message": "Zobrazit rozeznatelný obrázek vedle každého přihlášení." }, "enableBadgeCounter": { - "message": "Show badge counter" + "message": "Zobrazovat počet uložených přihlašovacích údajů na stránce" }, "badgeCounterDesc": { - "message": "Zobrazit počet přihlašovacích údajů pro aktuální webovou stránku." + "message": "Zobrazit počet přihlašovacích údajů pro aktuální webovou stránku na ikoně rozšíření prohlížeče." }, "cardholderName": { "message": "Jméno držitele karty" @@ -1632,7 +1632,7 @@ "message": "Odstraněné heslo" }, "deletedSend": { - "message": "Smazaný Send", + "message": "Send odstraněn", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { @@ -1899,10 +1899,10 @@ "message": "Vypršel časový limit relace. Vraťte se prosím zpět a zkuste se znovu přihlásit." }, "exportingPersonalVaultTitle": { - "message": "Exporting individual vault" + "message": "Export mého trezoru" }, "exportingPersonalVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "message": "Budou exportovány pouze položky trezoru spojené s účtem $EMAIL$. Nebudou zahrnuty položky trezoru v organizaci.", "placeholders": { "email": { "content": "$1", @@ -1933,7 +1933,7 @@ "message": "E-mail pro doménový koš" }, "catchallEmailDesc": { - "message": "Use your domain's configured catch-all inbox." + "message": "Použijte nakonfigurovanou univerzální schránku své domény." }, "random": { "message": "Náhodný" @@ -1970,16 +1970,16 @@ "message": "API klíč" }, "ssoKeyConnectorError": { - "message": "Key connector error: make sure key connector is available and working correctly." + "message": "Chyba Key Connector: ujistěte se, že je Key Connector k dispozici a funguje správně." }, "premiumSubcriptionRequired": { "message": "Vyžadováno prémiové předplatné" }, "organizationIsDisabled": { - "message": "Organization suspended." + "message": "Organizace je deaktivována." }, "disabledOrganizationFilterError": { - "message": "Items in suspended Organizations cannot be accessed. Contact your Organization owner for assistance." + "message": "K položkám v deaktivované organizaci nemáte přístup. Požádejte o pomoc vlastníka organizace." }, "cardBrandMir": { "message": "Mir" @@ -2000,19 +2000,19 @@ "message": "Klikněte zde" }, "environmentEditedReset": { - "message": "to reset to pre-configured settings" + "message": "pro obnovení do přednastavených nastavení" }, "serverVersion": { "message": "Verze serveru" }, "selfHosted": { - "message": "Self-hosted" + "message": "Vlastní hosting" }, "thirdParty": { - "message": "Third-party" + "message": "Tretí strana" }, "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": "Připojeno k serveru třetí strany $SERVERNAME$. Ověřte chyby připojením na oficiální server nebo nahlaste problém správci serveru.", "placeholders": { "servername": { "content": "$1", diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index e48ff9db786..642fcec5c7e 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -2030,18 +2030,18 @@ } }, "loginWithMasterPassword": { - "message": "Log in with master password" + "message": "Log ind med hovedadgangskode" }, "loggingInAs": { - "message": "Logging in as" + "message": "Logger ind som" }, "notYou": { - "message": "Not you?" + "message": "Ikke dig?" }, "newAroundHere": { - "message": "New around here?" + "message": "Ny her?" }, "rememberEmail": { - "message": "Remember email" + "message": "Husk e-mail" } } diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 67d70cc04f7..287e7ff857c 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -98,7 +98,7 @@ "message": "Benutzerdefinierten Feldnamen kopieren" }, "noMatchingLogins": { - "message": "Keine passenden Zugangsdaten." + "message": "Keine passenden Zugangsdaten" }, "unlockVaultMenu": { "message": "Entsperre deinen Tresor" @@ -199,7 +199,7 @@ "message": "Synchronisierung" }, "syncVaultNow": { - "message": "Datenspeicher jetzt synchronisieren" + "message": "Tresor jetzt synchronisieren" }, "lastSync": { "message": "Zuletzt synchronisiert:" @@ -491,7 +491,7 @@ "message": "Du kannst dein Master-Passwort im Bitwarden.com Web-Tresor ändern. Möchtest du die Seite jetzt öffnen?" }, "twoStepLoginConfirmation": { - "message": "Mit der Zwei-Faktor-Authentifizierung wird Ihr Account zusätzlich abgesichert, da jede Anmeldung durch einen Sicherheitscode, eine Authentifizierungs-App, eine SMS, einen Anruf oder eine E-Mail verifiziert werden muss. Die Zwei-Faktor-Authentifizierung kann im bitwarden.com Web-Tresor aktiviert werden. Möchten Sie die Seite jetzt öffnen?" + "message": "Mit der Zwei-Faktor-Authentifizierung wird dein Account zusätzlich abgesichert, da jede Anmeldung durch einen Sicherheitscode, eine Authentifizierungs-App, eine SMS, einen Anruf oder eine E-Mail verifiziert werden muss. Die Zwei-Faktor-Authentifizierung kann im bitwarden.com Web-Tresor aktiviert werden. Möchtest du die Seite jetzt öffnen?" }, "editedFolder": { "message": "Ordner bearbeitet" @@ -574,7 +574,7 @@ "message": "Danach fragen Zugangsdaten hinzuzufügen" }, "addLoginNotificationDesc": { - "message": "Die \"Login hinzufügen\" Benachrichtigung fragt Sie automatisch, ob Sie neue Zugangsdaten in Ihrem Tresor speichern möchten, wenn Sie sich zum ersten Mal mit ihnen anmelden." + "message": "Die \"Login hinzufügen\" Benachrichtigung fragt dich automatisch, ob du neue Zugangsdaten im Tresor speichern möchtest, wenn du dich zum ersten Mal mit ihnen anmeldest." }, "showCardsCurrentTab": { "message": "Karten auf Tab Seite anzeigen" @@ -738,7 +738,7 @@ "message": "Datei" }, "selectFile": { - "message": "Wähle eine Datei." + "message": "Wähle eine Datei" }, "maxFileSize": { "message": "Die maximale Dateigröße beträgt 500 MB." @@ -762,7 +762,7 @@ "message": "Mitgliedschaft erneuern" }, "premiumNotCurrentMember": { - "message": "Sie haben derzeit keine Premium-Mitgliedschaft." + "message": "Du bist derzeit kein Premium-Mitglied." }, "premiumSignUpAndGet": { "message": "Werde Premium-Mitglied und erhalte dafür:" @@ -783,7 +783,7 @@ "message": "Vorrangiger Kunden-Support." }, "ppremiumSignUpFuture": { - "message": "Alle zukünftigen Premium-Features. Mehr in Kürze!" + "message": "Alle zukünftigen Premium-Funktionen. Mehr in Kürze!" }, "premiumPurchase": { "message": "Premium-Mitgliedschaft kaufen" @@ -792,7 +792,7 @@ "message": "Du kannst deine Premium-Mitgliedschaft im Bitwarden.com Web-Tresor kaufen. Möchtest du die Webseite jetzt besuchen?" }, "premiumCurrentMember": { - "message": "Sie sind jetzt Premium-Mitglied!" + "message": "Du bist jetzt Premium-Mitglied!" }, "premiumCurrentMemberThanks": { "message": "Vielen Dank, dass du Bitwarden unterstützt." @@ -870,7 +870,7 @@ "message": "Authentifiziere WebAuthn" }, "loginUnavailable": { - "message": "Login nicht verfügbar" + "message": "Anmeldung nicht verfügbar" }, "noTwoStepProviders": { "message": "Dieses Konto hat eine aktive Zwei-Faktor Authentifizierung, allerdings wird keiner der konfigurierten Zwei-Faktor Anbieter von diesem Browser unterstützt." diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 2d637cb8ef4..575eb94c955 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -1312,7 +1312,7 @@ "description": "ex. Date this item was updated" }, "dateCreated": { - "message": "Created", + "message": "Δημιουργήθηκε", "description": "ex. Date this item was created" }, "datePasswordUpdated": { @@ -2006,13 +2006,13 @@ "message": "Έκδοση διακομιστή" }, "selfHosted": { - "message": "Self-hosted" + "message": "Αυτο-φιλοξενείται" }, "thirdParty": { "message": "Third-party" }, "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": "Συνδέθηκε με υλοποίηση διακομιστή τρίτων, $SERVERNAME$. Παρακαλώ επαληθεύστε τα σφάλματα χρησιμοποιώντας τον επίσημο διακομιστή, ή αναφέρετε τα στον διακομιστή τρίτων.", "placeholders": { "servername": { "content": "$1", @@ -2021,7 +2021,7 @@ } }, "lastSeenOn": { - "message": "last seen on: $DATE$", + "message": "ημερομηνία τελευταίας προβολής: $DATE$", "placeholders": { "date": { "content": "$1", @@ -2030,18 +2030,18 @@ } }, "loginWithMasterPassword": { - "message": "Log in with master password" + "message": "Συνδεθείτε με τον κύριο κωδικό πρόσβασης" }, "loggingInAs": { - "message": "Logging in as" + "message": "Σύνδεση ως" }, "notYou": { - "message": "Not you?" + "message": "Δεν είστε εσείς;" }, "newAroundHere": { - "message": "New around here?" + "message": "Νέος/α στα μέρη μας;" }, "rememberEmail": { - "message": "Remember email" + "message": "Απομνημόνευση email" } } diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index c6d58234cf4..8b6c17d0170 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1123,6 +1123,9 @@ "dr": { "message": "Dr" }, + "mx": { + "message": "Mx" + }, "firstName": { "message": "First name" }, @@ -2058,5 +2061,35 @@ }, "origin": { "message": "Origin" + }, + "exposedMasterPassword": { + "message": "Exposed Master Password" + }, + "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?" + }, + "weakAndExposedMasterPassword": { + "message": "Weak and Exposed Master Password" + }, + "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?" + }, + "checkForBreaches": { + "message": "Check known data breaches for this password" + }, + "important": { + "message": "Important:" + }, + "masterPasswordHint": { + "message": "Your master password cannot be recovered if you forget it!" + }, + "characterMinimum": { + "message": "$LENGTH$ character minimum", + "placeholders": { + "length": { + "content": "$1", + "example": "14" + } + } } } diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 293c6e0559c..ddf2067b456 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -59,7 +59,7 @@ "message": "My vault" }, "allVaults": { - "message": "All Vaults" + "message": "All vaults" }, "tools": { "message": "Tools" @@ -92,13 +92,13 @@ "message": "Auto-fill" }, "generatePasswordCopied": { - "message": "Generate Password (and Copy)" + "message": "Generate password (copied)" }, "copyElementIdentifier": { - "message": "Copy Custom Field Name" + "message": "Copy custom field name" }, "noMatchingLogins": { - "message": "No matching logins." + "message": "No matching logins" }, "unlockVaultMenu": { "message": "Unlock your vault" @@ -131,10 +131,10 @@ "message": "Send a verification code to your email" }, "sendCode": { - "message": "Send Code" + "message": "Send code" }, "codeSent": { - "message": "Code Sent" + "message": "Code sent" }, "verificationCode": { "message": "Verification code" @@ -245,7 +245,7 @@ "message": "Numbers (0-9)" }, "specialCharacters": { - "message": "Special Characters (!@#$%^&*)" + "message": "Special characters (!@#$%^&*)" }, "numWords": { "message": "Number of words" @@ -339,7 +339,7 @@ "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, "verifyIdentity": { - "message": "Verify Identity" + "message": "Verify identity" }, "yourVaultIsLocked": { "message": "Your vault is locked. Verify your identity to continue." @@ -482,7 +482,7 @@ "message": "Name is required." }, "addedFolder": { - "message": "Added folder" + "message": "Folder added" }, "changeMasterPass": { "message": "Change master password" @@ -491,16 +491,16 @@ "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" }, "twoStepLoginConfirmation": { - "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be enabled on the bitwarden.com web vault. Do you want to visit the website now?" + "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?" }, "editedFolder": { - "message": "Edited folder" + "message": "Folder saved" }, "deleteFolderConfirmation": { "message": "Are you sure you want to delete this folder?" }, "deletedFolder": { - "message": "Deleted folder" + "message": "Folder deleted" }, "gettingStartedTutorial": { "message": "Getting started tutorial" @@ -534,16 +534,16 @@ "message": "New URI" }, "addedItem": { - "message": "Added item" + "message": "Item added" }, "editedItem": { - "message": "Edited item" + "message": "Item saved" }, "deleteItemConfirmation": { "message": "Do you really want to send to the bin?" }, "deletedItem": { - "message": "Deleted item" + "message": "Item sent to bin" }, "overwritePassword": { "message": "Overwrite password" @@ -552,7 +552,7 @@ "message": "Are you sure you want to overwrite the current password?" }, "overwriteUsername": { - "message": "Overwrite Username" + "message": "Overwrite username" }, "overwriteUsernameConfirmation": { "message": "Are you sure you want to overwrite the current username?" @@ -656,7 +656,7 @@ "description": "WARNING (should stay in capitalized letters if the language permits)" }, "confirmVaultExport": { - "message": "Confirm Vault Export" + "message": "Confirm vault export" }, "exportWarningDesc": { "message": "This export contains your vault data in an unencrypted format. You should not store or send the exported file over insecure channels (such as email). Delete it immediately after you are done using it." @@ -680,7 +680,7 @@ "message": "Bitwarden allows you to share your vault items with others by using an organisation. Would you like to visit the bitwarden.com website to learn more?" }, "moveToOrganization": { - "message": "Move to Organisation" + "message": "Move to organisation" }, "share": { "message": "Share" @@ -723,7 +723,7 @@ "message": "Are you sure you want to delete this attachment?" }, "deletedAttachment": { - "message": "Deleted attachment" + "message": "Attachment deleted" }, "newAttachment": { "message": "Add new attachment" @@ -732,13 +732,13 @@ "message": "No attachments." }, "attachmentSaved": { - "message": "The attachment has been saved." + "message": "Attachment saved" }, "file": { "message": "File" }, "selectFile": { - "message": "Select a file." + "message": "Select a file" }, "maxFileSize": { "message": "Maximum file size is 500 MB." @@ -762,10 +762,10 @@ "message": "Refresh membership" }, "premiumNotCurrentMember": { - "message": "You are not currently a premium member." + "message": "You are not currently a Premium member." }, "premiumSignUpAndGet": { - "message": "Sign up for a premium membership and get:" + "message": "Sign up for a Premium membership and get:" }, "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." @@ -783,16 +783,16 @@ "message": "Priority customer support." }, "ppremiumSignUpFuture": { - "message": "All future premium features. More coming soon!" + "message": "All future Premium features. More coming soon!" }, "premiumPurchase": { "message": "Purchase premium" }, "premiumPurchaseAlert": { - "message": "You can purchase premium membership on the bitwarden.com web vault. Do you want to visit the website now?" + "message": "You can purchase Premium membership on the bitwarden.com web vault. Do you want to visit the website now?" }, "premiumCurrentMember": { - "message": "You are a premium member!" + "message": "You are a Premium member!" }, "premiumCurrentMemberThanks": { "message": "Thank you for supporting Bitwarden." @@ -822,7 +822,7 @@ "message": "Premium required" }, "premiumRequiredDesc": { - "message": "A premium membership is required to use this feature." + "message": "A Premium membership is required to use this feature." }, "enterVerificationCodeApp": { "message": "Enter the 6 digit verification code from your authenticator app." @@ -873,7 +873,7 @@ "message": "Login unavailable" }, "noTwoStepProviders": { - "message": "This account has two-step login enabled. However, none of the configured two-step providers are supported by this web browser." + "message": "This account has two-step login set up; however, none of the configured two-step providers are supported by this web browser." }, "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)." @@ -882,7 +882,7 @@ "message": "Two-step login options" }, "recoveryCodeDesc": { - "message": "Lost access to all of your two-factor providers? Use your recovery code to disable all two-factor providers from your account." + "message": "Lost access to all of your two-factor providers? Use your recovery code to turn off all two-factor providers from your account." }, "recoveryCodeTitle": { "message": "Recovery code" @@ -912,7 +912,7 @@ "message": "FIDO2 WebAuthn" }, "webAuthnDesc": { - "message": "Use any WebAuthn enabled security key to access your account." + "message": "Use any WebAuthn compatible security key to access your account." }, "emailTitle": { "message": "Email" @@ -951,7 +951,7 @@ "message": "Icons server URL" }, "environmentSaved": { - "message": "The environment URLs have been saved." + "message": "Environment URLs saved" }, "enableAutoFillOnPageLoad": { "message": "Auto-fill on page load" @@ -969,7 +969,7 @@ "message": "You can turn off auto-fill on page load for individual login items from the item's Edit view." }, "itemAutoFillOnPageLoad": { - "message": "Auto-fill on page load (if enabled in Options)" + "message": "Auto-fill on page load (if set up in Options)" }, "autoFillOnPageLoadUseDefault": { "message": "Use default setting" @@ -1133,7 +1133,7 @@ "message": "Last name" }, "fullName": { - "message": "Full Name" + "message": "Full name" }, "identityName": { "message": "Identity name" @@ -1375,7 +1375,7 @@ "message": "Awaiting confirmation from desktop" }, "awaitDesktopDesc": { - "message": "Please confirm using biometrics in the Bitwarden Desktop application to enable biometrics for browser." + "message": "Please confirm using biometrics in the Bitwarden desktop application to set up biometrics for browser." }, "lockWithMasterPassOnRestart": { "message": "Lock with master password on browser restart" @@ -1413,7 +1413,7 @@ "message": "Are you sure you want to permanently delete this item?" }, "permanentlyDeletedItem": { - "message": "Permanently deleted item" + "message": "Item permanently deleted" }, "restoreItem": { "message": "Restore item" @@ -1422,7 +1422,7 @@ "message": "Are you sure you want to restore this item?" }, "restoredItem": { - "message": "Restored item" + "message": "Item restored" }, "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?" @@ -1434,10 +1434,10 @@ "message": "Auto-fill and save" }, "autoFillSuccessAndSavedUri": { - "message": "Auto-filled item and saved URI" + "message": "Item auto-filled and URI saved" }, "autoFillSuccess": { - "message": "Auto-filled item" + "message": "Item auto-filled " }, "setMasterPassword": { "message": "Set master password" @@ -1509,19 +1509,19 @@ "message": "Please verify that the desktop application shows this fingerprint: " }, "desktopIntegrationDisabledTitle": { - "message": "Browser integration is not enabled" + "message": "Browser integration is not set up" }, "desktopIntegrationDisabledDesc": { - "message": "Browser integration is not enabled in the Bitwarden Desktop application. Please enable it in the settings within the desktop application." + "message": "Browser integration is not set up in the Bitwarden desktop application. Please set it up in the settings within the desktop application." }, "startDesktopTitle": { - "message": "Start the Bitwarden Desktop application" + "message": "Start the Bitwarden desktop application" }, "startDesktopDesc": { - "message": "The Bitwarden Desktop application needs to be started before unlock with biometrics can be used." + "message": "The Bitwarden desktop application needs to be started before unlock with biometrics can be used." }, "errorEnableBiometricTitle": { - "message": "Unable to enable biometrics" + "message": "Unable to set up biometrics" }, "errorEnableBiometricDesc": { "message": "Action was cancelled by the desktop application" @@ -1539,10 +1539,10 @@ "message": "Account mismatch" }, "biometricsNotEnabledTitle": { - "message": "Biometrics not enabled" + "message": "Biometrics not set up" }, "biometricsNotEnabledDesc": { - "message": "Browser biometrics requires desktop biometric to be enabled in the settings first." + "message": "Browser biometrics requires desktop biometric to be set up in the settings first." }, "biometricsNotSupportedTitle": { "message": "Biometrics not supported" @@ -1563,7 +1563,7 @@ "message": "This action cannot be done in the sidebar, please retry the action in the popup or popout." }, "personalOwnershipSubmitError": { - "message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organisation and choose from available Collections." + "message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organisation and choose from available collections." }, "personalOwnershipPolicyInEffect": { "message": "An organisation policy is affecting your ownership options." @@ -1629,10 +1629,10 @@ "message": "Delete" }, "removedPassword": { - "message": "Removed Password" + "message": "Password removed" }, "deletedSend": { - "message": "Deleted Send", + "message": "Send deleted", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { @@ -1669,14 +1669,14 @@ "message": "The file you want to send." }, "deletionDate": { - "message": "Deletion Date" + "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." }, "expirationDate": { - "message": "Expiration Date" + "message": "Expiration date" }, "expirationDateDesc": { "message": "If set, access to this Send will expire on the specified date and time.", @@ -1713,7 +1713,7 @@ "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.", + "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": { @@ -1728,17 +1728,17 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "currentAccessCount": { - "message": "Current Access Count" + "message": "Current access count" }, "createSend": { - "message": "Create New Send", + "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "newPassword": { - "message": "New Password" + "message": "New password" }, "sendDisabled": { - "message": "Send Disabled", + "message": "Send removed", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { @@ -1746,11 +1746,11 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSend": { - "message": "Created Send", + "message": "Send created", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editedSend": { - "message": "Edited Send", + "message": "Send saved", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinuxChromiumFileWarning": { @@ -1808,22 +1808,22 @@ "message": "This action is protected. To continue, please re-enter your master password to verify your identity." }, "emailVerificationRequired": { - "message": "Email Verification Required" + "message": "Email verification required" }, "emailVerificationRequiredDesc": { "message": "You must verify your email to use this feature. You can verify your email in the web vault." }, "updatedMasterPassword": { - "message": "Updated Master Password" + "message": "Updated master password" }, "updateMasterPassword": { - "message": "Update Master Password" + "message": "Update master password" }, "updateMasterPasswordWarning": { - "message": "Your Master Password was recently changed by an administrator in your organisation. 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": "Your master password was recently changed by an administrator in your organisation. 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." }, "resetPasswordPolicyAutoEnroll": { - "message": "Automatic Enrolment" + "message": "Automatic enrolment" }, "resetPasswordAutoEnrollInviteWarning": { "message": "This organisation has an enterprise policy that will automatically enrol you in password reset. Enrolment will allow organisation administrators to change your master password." @@ -1857,10 +1857,10 @@ "message": "Your vault timeout exceeds the restrictions set by your organisation." }, "vaultExportDisabled": { - "message": "Vault Export Disabled" + "message": "Vault export unavailable" }, "personalVaultExportPolicyInEffect": { - "message": "One or more organisation policies prevents you from exporting your personal vault." + "message": "One or more organisation policies prevents you from exporting your individual vault." }, "copyCustomFieldNameInvalidElement": { "message": "Unable to identify a valid form element. Try inspecting the HTML instead." @@ -1878,13 +1878,13 @@ } }, "leaveOrganization": { - "message": "Leave Organisation" + "message": "Leave organisation" }, "removeMasterPassword": { - "message": "Remove Master Password" + "message": "Remove master password" }, "removedMasterPassword": { - "message": "Master password removed." + "message": "Master password removed" }, "leaveOrganizationConfirmation": { "message": "Are you sure you want to leave this organisation?" @@ -1899,10 +1899,10 @@ "message": "Your session has timed out. Please go back and try logging in again." }, "exportingPersonalVaultTitle": { - "message": "Exporting Personal Vault" + "message": "Exporting individual vault" }, "exportingPersonalVaultDescription": { - "message": "Only the personal vault items associated with $EMAIL$ will be exported. Organisation vault items will not be included.", + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organisation vault items will not be included.", "placeholders": { "email": { "content": "$1", @@ -1914,23 +1914,23 @@ "message": "Error" }, "regenerateUsername": { - "message": "Regenerate Username" + "message": "Regenerate username" }, "generateUsername": { - "message": "Generate Username" + "message": "Generate username" }, "usernameType": { - "message": "Username Type" + "message": "Username type" }, "plusAddressedEmail": { - "message": "Plus Addressed Email", + "message": "Plus addressed email", "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." }, "catchallEmail": { - "message": "Catch-all Email" + "message": "Catch-all email" }, "catchallEmailDesc": { "message": "Use your domain's configured catch-all inbox." @@ -1939,22 +1939,22 @@ "message": "Random" }, "randomWord": { - "message": "Random Word" + "message": "Random word" }, "websiteName": { - "message": "Website Name" + "message": "Website name" }, "whatWouldYouLikeToGenerate": { "message": "What would you like to generate?" }, "passwordType": { - "message": "Password Type" + "message": "Password type" }, "service": { "message": "Service" }, "forwardedEmail": { - "message": "Forwarded Email Alias" + "message": "Forwarded email alias" }, "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." @@ -1970,16 +1970,16 @@ "message": "API Key" }, "ssoKeyConnectorError": { - "message": "Key Connector error: make sure Key Connector is available and working correctly." + "message": "Key connector error: make sure key connector is available and working correctly." }, "premiumSubcriptionRequired": { "message": "Premium subscription required" }, "organizationIsDisabled": { - "message": "Organisation is disabled." + "message": "Organisation suspended." }, "disabledOrganizationFilterError": { - "message": "Items in disabled Organisations cannot be accessed. Contact your Organisation owner for assistance." + "message": "Items in suspended Organisations cannot be accessed. Contact your Organisation owner for assistance." }, "cardBrandMir": { "message": "Mir" @@ -2003,13 +2003,13 @@ "message": "to reset to pre-configured settings" }, "serverVersion": { - "message": "Server Version" + "message": "Server version" }, "selfHosted": { - "message": "Self-Hosted" + "message": "Self-hosted" }, "thirdParty": { - "message": "Third-Party" + "message": "Third-party" }, "thirdPartyServerMessage": { "message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.", @@ -2021,7 +2021,7 @@ } }, "lastSeenOn": { - "message": "last seen on $DATE$", + "message": "last seen on: $DATE$", "placeholders": { "date": { "content": "$1", diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 1adeb72b4b9..c129740c509 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -7,7 +7,7 @@ "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "یک مدیریت کننده کلمه عبور رایگان برای تمامی دستگاههایتان.", + "message": "یک مدیریت کننده کلمه عبور رایگان برای تمامی دستگاه‌هایتان.", "description": "Extension description" }, "loginOrCreateNewAccount": { @@ -32,22 +32,22 @@ "message": "ثبت" }, "emailAddress": { - "message": "آدرس ایمیل" + "message": "نشانی ایمیل" }, "masterPass": { "message": "کلمه عبور اصلی" }, "masterPassDesc": { - "message": "کلمه عبور اصلی کلمه عبوری است که شما برای دسترسی به گاوصندوق خود استفاده میکنید. بیاد داشتن کلمه عبور اصلی بسیار اهمیت دارد. اگر فراموشش کنید هیچ راهی برای بازگردانی آن وجود ندارد." + "message": "کلمه عبور اصلی، کلمه عبوری است که شما برای دسترسی به گاوصندوق خود استفاده می‌کنید. به یاد داشتن کلمه عبور اصلی بسیار اهمیت دارد. اگر فراموشش کنید هیچ راهی برای بازگردانی آن وجود ندارد." }, "masterPassHintDesc": { - "message": "راهنمای کلمه عبور اصلی می تواند در صورت فراموشی آن را بیاد بیارید." + "message": "یادآور کلمه عبور اصلی کمک می‌کند در صورت فراموشی آن را به یاد بیارید." }, "reTypeMasterPass": { - "message": "تایپ دوباره کلمه عبور اصلی" + "message": "نوشتن دوباره کلمه عبور اصلی" }, "masterPassHint": { - "message": "راهنمای کلمه عبور اصلی (اختیاری)" + "message": "یادآور کلمه عبور اصلی (اختیاری)" }, "tab": { "message": "زبانه" @@ -71,13 +71,13 @@ "message": "زبانه فعلی" }, "copyPassword": { - "message": "کپی رمز عبور" + "message": "کپی کلمه عبور" }, "copyNote": { "message": "کپی یادداشت" }, "copyUri": { - "message": "کپی آدرس اینترنتی" + "message": "کپی نشانی اینترنتی" }, "copyUsername": { "message": "کپی نام کاربری" @@ -95,40 +95,40 @@ "message": "ساخت کلمه عبور (کپی شد)" }, "copyElementIdentifier": { - "message": "رونوشت نام فیلد سفارشی" + "message": "کپی نام فیلد سفارشی" }, "noMatchingLogins": { - "message": "ورودیها منتطبق نیست." + "message": "ورودی‌ها منتطبق نیست" }, "unlockVaultMenu": { - "message": "بازکردن گاو‌صندوقتان" + "message": "قفل گاوصندوق خود را باز کنید" }, "loginToVaultMenu": { "message": "وارد شدن به گاو‌صندوقتان" }, "autoFillInfo": { - "message": "پرکردن خودکار برای برگه فعلی مرورگر در دسترس نیست." + "message": "پر کردن خودکار برای برگه فعلی مرورگر در دسترس نیست." }, "addLogin": { - "message": "افزودن ورود جدید" + "message": "افزودن یک ورود" }, "addItem": { "message": "افزودن مورد" }, "passwordHint": { - "message": "راهنمای کلمه عبور" + "message": "یادآور کلمه عبور" }, "enterEmailToGetHint": { - "message": "برای دریافت راهنمایی کلمه عبور اصلی خود آدرس ایملیتان را وارد کنید." + "message": "برای دریافت یادآور کلمه عبور اصلی خود نشانی ایمیل‌تان را وارد کنید." }, "getMasterPasswordHint": { - "message": "دریافت راهنمای کلمه عبور اصلی" + "message": "دریافت یادآور کلمه عبور اصلی" }, "continue": { "message": "ادامه" }, "sendVerificationCode": { - "message": "ارسال یک کد تأیید به ایمیل شما" + "message": "یک کد تأیید به ایمیل خود ارسال کنید" }, "sendCode": { "message": "ارسال کد" @@ -137,10 +137,10 @@ "message": "کد ارسال شد" }, "verificationCode": { - "message": "کد تایید" + "message": "کد تأیید" }, "confirmIdentity": { - "message": "برای ادامه هویت خود را تأیید کنید." + "message": "برای ادامه، هویت خود را تأیید کنید." }, "account": { "message": "حساب" @@ -163,7 +163,7 @@ "message": "خروج" }, "about": { - "message": "درباره ما" + "message": "درباره" }, "version": { "message": "نسخه" @@ -187,38 +187,38 @@ "message": "حذف پوشه" }, "folders": { - "message": "پوشه ها" + "message": "پوشه‌ها" }, "noFolders": { - "message": "هیچ موردی برای نمایش وجود ندارد." + "message": "هیچ پوشه‌ای برای نمایش وجود ندارد." }, "helpFeedback": { "message": "کمک و بازخورد" }, "sync": { - "message": "همگام سازی" + "message": "همگام‌سازی" }, "syncVaultNow": { - "message": "همگام سازی گاوصندوق" + "message": "همگام‌سازی گاوصندوق" }, "lastSync": { - "message": "آخرین همگام سازی:" + "message": "آخرین همگام‌سازی:" }, "passGen": { - "message": "تولید کلمه عبور" + "message": "تولید کننده کلمه عبور" }, "generator": { "message": "تولید کننده", "description": "Short for 'Password Generator'." }, "passGenInfo": { - "message": "به طور خودکار کلمه های عبور قوی و منحصر به فرد برای ورود به سیستم خود ایجاد کنید." + "message": "به طور خودکار کلمه‌های عبور قوی و منحصر به فرد برای ورود به سیستم خود ایجاد کنید." }, "bitWebVault": { "message": "گاوصندوق وب Bitwarden" }, "importItems": { - "message": "واردن کردن موارد" + "message": "درون ریزی موارد" }, "select": { "message": "انتخاب" @@ -230,7 +230,7 @@ "message": "تولید مجدد کلمه عبور" }, "options": { - "message": "گزینه ها" + "message": "گزینه‌ها" }, "length": { "message": "طول" @@ -251,7 +251,7 @@ "message": "تعداد کلمات" }, "wordSeparator": { - "message": "کلمه جداکننده" + "message": "جداکننده کلمات" }, "capitalize": { "message": "بزرگ کردن", @@ -267,7 +267,7 @@ "message": "حداقل حرف خاص" }, "avoidAmbChar": { - "message": "از کاراکترهای مبهم اجتناب شود" + "message": "از کاراکترهای مبهم اجتناب کن" }, "searchVault": { "message": "جستجوی گاوصندوق" @@ -291,13 +291,13 @@ "message": "کلمه عبور" }, "passphrase": { - "message": "کلمه عبور" + "message": "عبارت عبور" }, "favorite": { "message": "مورد علاقه" }, "notes": { - "message": "یادداشت ها" + "message": "یادداشت‌ها" }, "note": { "message": "یادداشت" @@ -318,10 +318,10 @@ "message": "راه اندازی" }, "website": { - "message": "وب سایت" + "message": "وب‌سایت" }, "toggleVisibility": { - "message": "تغییر قابلیت نمایش" + "message": "قابلیت مشاهده را تغییر دهید" }, "manage": { "message": "مدیریت" @@ -336,16 +336,16 @@ "message": "لطفاً با یک بررسی خوب به ما کمک کنید!" }, "browserNotSupportClipboard": { - "message": "مرورگر شما از کپی کلیپ بورد آسان پشتیبانی نمی کند. به جای آن به صورت دستی کپی کنید." + "message": "مرورگر شما از کپی کلیپ بورد آسان پشتیبانی نمی‌کند. به جای آن به صورت دستی کپی کنید." }, "verifyIdentity": { "message": "تأیید هویت" }, "yourVaultIsLocked": { - "message": "گاوصندوق شما قفل است. برای ادامه کلمه عبور اصلی خود را وارد کنید." + "message": "گاوصندوق شما قفل شده است. برای ادامه هویت خود را تأیید کنید." }, "unlock": { - "message": "بازکردن" + "message": "باز کردن قفل" }, "loggedInAsOn": { "message": "وارد شده با $EMAIL$ در $HOSTNAME$.", @@ -403,7 +403,7 @@ "message": "4 ساعت" }, "onLocked": { - "message": "هنگام قفل سیستم" + "message": "در قفل سیستم" }, "onRestart": { "message": "هنگام راه اندازی مجدد" @@ -418,34 +418,34 @@ "message": "خطایی رخ داده است" }, "emailRequired": { - "message": "آدرس ایمیل ضروری است." + "message": "نشانی ایمیل ضروری است." }, "invalidEmail": { - "message": "آدرس ایمیل نامعتبر است." + "message": "نشانی ایمیل نامعتبر است." }, "masterPasswordRequired": { - "message": "گذرواژه اصلی ضروری است." + "message": "کلمه عبور اصلی ضروری است." }, "confirmMasterPasswordRequired": { - "message": "تایپ مجدد گذرواژه اصلی نیاز است." + "message": "نوشتن مجدد کلمه عبور اصلی ضروری است." }, "masterPasswordMinlength": { "message": "طول کلمه عبور اصلی باید حداقل ۸ کاراکتر باشد." }, "masterPassDoesntMatch": { - "message": "کلمه عبور اصلی با تکرار کلمه عبور اصلی مطابقت ندارد." + "message": "کلمه عبور اصلی با تکرار آن مطابقت ندارد." }, "newAccountCreated": { - "message": "حساب شما ساختته شد! حالا میتوانید وارد شوید." + "message": "حساب کاربری جدید شما ساخته شد! حالا می‌توانید وارد شوید." }, "masterPassSent": { "message": "ما یک ایمیل همراه با راهنمای کلمه عبور اصلی برایتان ارسال کردیم." }, "verificationCodeRequired": { - "message": "کد تایید مورد نیاز است." + "message": "کد تأیید مورد نیاز است." }, "invalidVerificationCode": { - "message": "کد تایید نامعتبر" + "message": "کد تأیید نامعتبر است" }, "valueCopied": { "message": " کپی شده", @@ -458,13 +458,13 @@ } }, "autofillError": { - "message": "ناتوان در پرکردن خودکار آیتم انتخاب شده در این صفحه. اطلاعات را کپی و جایگذاری کنید." + "message": "ناتوان در پر کردن خودکار مورد انتخاب شده در این صفحه. اطلاعات را کپی و جای‌گذاری کنید." }, "loggedOut": { - "message": "خارج شده" + "message": "خارج شد" }, "loginExpired": { - "message": "جلسه ورود شما منقضی شده است." + "message": "نشست ورود شما منقضی شده است." }, "logOutConfirmation": { "message": "آیا مطمئنید که می‌خواهید خارج شوید؟" @@ -482,7 +482,7 @@ "message": "نام ضروری است." }, "addedFolder": { - "message": "پوشه اضافه شده" + "message": "پوشه اضافه شد" }, "changeMasterPass": { "message": "تغییر کلمه عبور اصلی" @@ -491,16 +491,16 @@ "message": "شما می‌توانید کلمه عبور اصلی خود را در bitwarden.com تغییر دهید. آیا می‌خواهید از سایت بازدید کنید؟" }, "twoStepLoginConfirmation": { - "message": "ورودی دو مرحله ای باعث می شود که حساب کاربری شما با استفاده از یک دستگاه دیگر مانند کلید امنیتی، برنامه تأیید هویت، پیامک، تماس تلفنی و یا ایمیل، اعتبار خود را با ایمنی بیشتر ثابت کند. ورودی دو مرحله ای می تواند در bitwarden.com فعال شود. آیا می خواهید از سایت بازدید کنید؟" + "message": "ورود دو مرحله ای باعث می‌شود که حساب کاربری شما با استفاده از یک دستگاه دیگر مانند کلید امنیتی، برنامه احراز هویت، پیامک، تماس تلفنی و یا ایمیل، اعتبار خود را با ایمنی بیشتر اثبات کند. ورود دو مرحله ای می تواند در bitwarden.com فعال شود. آیا می‌خواهید از سایت بازدید کنید؟" }, "editedFolder": { - "message": "پوشه ویرایش شده" + "message": "پوشه ذخیره شد" }, "deleteFolderConfirmation": { "message": "آیا از حذف این پوشه اطمینان دارید؟" }, "deletedFolder": { - "message": "پوشه حذف شده" + "message": "پوشه حذف شد" }, "gettingStartedTutorial": { "message": "آغاز نمودن آموزش" @@ -509,10 +509,10 @@ "message": "برای یادگیری بیشتر نحوه استفاده از افزونه مرورگر آموزش شروع به کار را تماشا کنید." }, "syncingComplete": { - "message": "همگام سازی کامل شد" + "message": "همگام‌سازی کامل شد" }, "syncingFailed": { - "message": "همگام سازی شکست خورد" + "message": "همگام‌سازی شکست خورد" }, "passwordCopied": { "message": "کلمه عبور کپی شد" @@ -521,7 +521,7 @@ "message": "نشانی اینترنتی" }, "uriPosition": { - "message": "آدرس اینترنتی $POSITION$", + "message": "نشانی اینترنتی $POSITION$", "description": "A listing of URIs. Ex: URI 1, URI 2, URI 3, etc.", "placeholders": { "position": { @@ -531,19 +531,19 @@ } }, "newUri": { - "message": "آدرس اینترنتی جدید" + "message": "نشانی اینترنتی جدید" }, "addedItem": { - "message": "مورد افزوده شده" + "message": "مورد اضافه شد" }, "editedItem": { - "message": "مورد ویرایش شده" + "message": "مورد ذخیره شد" }, "deleteItemConfirmation": { - "message": "آیا مطمئن هستید می خواهید این مورد را حذف کنید؟" + "message": "واقعاً می‌خواهید این آیتم را به سطل زباله ارسال کنید؟" }, "deletedItem": { - "message": "مورد حذف شده" + "message": "مورد به زباله‌ها فرستاده شد" }, "overwritePassword": { "message": "بازنویسی کلمه عبور" @@ -571,10 +571,10 @@ "description": "This is the folder for uncategorized items" }, "enableAddLoginNotification": { - "message": "درخواست اضافه کردن ورود به سیستم" + "message": "درخواست افزودن ورود به سیستم" }, "addLoginNotificationDesc": { - "message": "\"افزودن اعلانیه ورود\" به صورت خودکار از شما می خواهد هر بار که برای اولین بار وارد سیستم میشوید ورودی های جدید را در گاوصندوق خود ذخیره کنید." + "message": "در صورتی که موردی در گاوصندوق شما یافت نشد، درخواست افزودن کنید." }, "showCardsCurrentTab": { "message": "نمایش کارت‌ها در صفحه برگه" @@ -589,43 +589,43 @@ "message": "موارد هویتی را در صفحه برگه برای پر کردن خودکار آسان فهرست کن." }, "clearClipboard": { - "message": "پاک‌سازی کلیپ‌ برد", + "message": "پاکسازی کلیپ بورد", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "clearClipboardDesc": { - "message": "مقدار های کپی شده را به صورت خودکار از کلیپ برد خود پاک کنید.", + "message": "به صورت خودکار، مقادیر کپی شده را از کلیپ بورد پاک کن.", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "notificationAddDesc": { "message": "آیا Bitwarden باید این کلمه عبور را برایتان بخاطر بسپارد؟" }, "notificationAddSave": { - "message": "بله، ذخیره کن" + "message": "ذخیره" }, "enableChangedPasswordNotification": { - "message": "درخواست برای بروزرسانی ورود به سیستم موجود" + "message": "درخواست برای به‌روزرسانی ورود به سیستم موجود" }, "changedPasswordNotificationDesc": { "message": "هنگامی که تغییری در یک وب‌سایت شناسایی شد، درخواست به‌روزرسانی کلمه عبور ورود کن." }, "notificationChangeDesc": { - "message": "آیا مایل به بروزرسانی این پسورد در Bitwarden هستید؟" + "message": "آیا مایل به به‌روزرسانی این کلمه عبور در Bitwarden هستید؟" }, "notificationChangeSave": { - "message": "بله، بروزرسانی کن" + "message": "به‌روزرسانی" }, "enableContextMenuItem": { - "message": "نمایش گزینه های منوی زمینه" + "message": "نمایش گزینه‌های منوی زمینه" }, "contextMenuItemDesc": { "message": "از یک کلیک ثانویه برای دسترسی به تولید کلمه عبور و ورودهای منطبق برای وب سایت استفاده کن." }, "defaultUriMatchDetection": { - "message": "بازرسی تطابق URL پیشفرض", + "message": "بررسی مطابقت نشانی اینترنتی پیش‌فرض", "description": "Default URI match detection for auto-fill." }, "defaultUriMatchDetectionDesc": { - "message": "روش پیش فرض اعمال بازرسی تطابق ‌‌URL را هنگامی که عملی را مثل پر کردن خودکار انتخاب کنید." + "message": "هنگام انجام دادن کارهایی مانند پر کردن خودکار، روش پیش‌فرضی را که برای شناسایی ورود نشانی اینترنتی انجام می‌شود انتخاب کنید." }, "theme": { "message": "پوسته" @@ -646,38 +646,38 @@ "description": "'Solarized' is a noun and the name of a color scheme. It should not be translated." }, "exportVault": { - "message": "صادر کردن گاوصندوق" + "message": "برون ریزی گاوصندوق" }, "fileFormat": { - "message": "File Format" + "message": "فرمت پرونده" }, "warning": { "message": "اخطار", "description": "WARNING (should stay in capitalized letters if the language permits)" }, "confirmVaultExport": { - "message": "صادرات گاوصندوق را تأیید کنید" + "message": "برون ریزی گاوصندوق را تأیید کنید" }, "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": "این برون ریزی شامل داده‌های گاوصندوق در یک قالب رمزنگاری نشده است. شما نباید آن را از طریق یک راه ارتباطی نا امن (مثل ایمیل) ذخیره یا ارسال کنید. به محض اینکه کارتان با آن تمام شد، آن را حذف کنید." }, "encExportKeyWarningDesc": { - "message": "این صادرات با استفاده از کلید رمزگذاری حساب شما ، اطلاعات شما را رمزگذاری می کند. اگر حتی کلید رمزگذاری حساب خود را بچرخانید ، باید دوباره صادر کنید چون قادر به رمزگشایی این پرونده صادراتی نخواهید بود." + "message": "این برون ریزی با استفاده از کلید رمزگذاری حساب شما، اطلاعاتتان را رمزگذاری می کند. اگر زمانی کلید رمزگذاری حساب خود را بچرخانید، باید دوباره خروجی بگیرید، چون قادر به رمزگشایی این پرونده برون ریزی نخواهید بود." }, "encExportAccountWarningDesc": { - "message": "کلیدهای رمزگذاری حساب برای هر حساب کاربری Bitwarden منحصر به فرد است ، بنابراین نمی توانید صادرات رمزگذاری شده را به حساب دیگری وارد کنید." + "message": "کلیدهای رمزگذاری حساب برای هر حساب کاربری Bitwarden منحصر به فرد است، بنابراین نمی‌توانید برون ریزی رمزگذاری شده را به حساب دیگری وارد کنید." }, "exportMasterPassword": { - "message": "کلمه عبور اصلی خود را برای صادرات داده ها از گاوصندوقتان وارد کنید." + "message": "کلمه عبور اصلی خود را برای برون ریزی داده‌های گاوصندوقتان وارد کنید." }, "shared": { "message": "اشتراک گذاری شد" }, "learnOrg": { - "message": "درباره سازمانها اطلاعات کسب کنید" + "message": "درباره سازمان‌ها اطلاعات کسب کنید" }, "learnOrgConfirmation": { - "message": "Bitwarden به شما اجازه می دهد با استفاده از یک سازمان، موارد گاوصندوق خود را با دیگران به اشتراک بگذارید. آیا مایل به بازدید از وب سایت bitwarden.com برای کسب اطلاعات بیشتر هستید؟" + "message": "Bitwarden به شما اجازه می‌دهد با استفاده از سازماندهی، موارد گاوصندوق خود را با دیگران به اشتراک بگذارید. آیا مایل به بازدید از وب سایت bitwarden.com برای کسب اطلاعات بیشتر هستید؟" }, "moveToOrganization": { "message": "انتقال به سازمان" @@ -699,13 +699,13 @@ } }, "moveToOrgDesc": { - "message": "سازمانی را انتخاب کنید که می خواهید این مورد را به آن منتقل کنید. انتقال به یک سازمان، مالکیت مورد را به آن سازمان منتقل می کند. پس از انتقال این مورد، دیگر مالک مستقیم آن نخواهید بود." + "message": "سازمانی را انتخاب کنید که می‌خواهید این مورد را به آن منتقل کنید. انتقال به یک سازمان، مالکیت مورد را به آن سازمان منتقل می‌کند. پس از انتقال این مورد، دیگر مالک مستقیم آن نخواهید بود." }, "learnMore": { "message": "بیشتر بدانید" }, "authenticatorKeyTotp": { - "message": "کلید تأیید کننده (TOTP)" + "message": "کلید احراز هویت (TOTP)" }, "verificationCodeTotp": { "message": "کد تأیید (TOTP)" @@ -723,7 +723,7 @@ "message": "آیا از پاک کردن این پیوست مطمئن هستید؟" }, "deletedAttachment": { - "message": "حذف پیوست" + "message": "پیوست حذف شد" }, "newAttachment": { "message": "افزودن پیوست جدید" @@ -732,22 +732,22 @@ "message": "بدون پیوست." }, "attachmentSaved": { - "message": "پیوست ذخیره شد." + "message": "پیوست ذخیره شد" }, "file": { - "message": "فایل" + "message": "پرونده" }, "selectFile": { - "message": "انتخاب یک فایل." + "message": "ﺍﻧﺘﺨﺎﺏ یک ﭘﺮﻭﻧﺪﻩ" }, "maxFileSize": { - "message": "بیشترین حجم فایل 500 مگابایت است." + "message": "بیشترین حجم پرونده ۵۰۰ مگابایت است." }, "featureUnavailable": { "message": "ویژگی موجود نیست" }, "updateKey": { - "message": "تا زمانی که کد رمزنگاری را بروز نکنید نمیتوانید از این قابلیت استفاده کنید." + "message": "تا زمانی که کد رمزنگاری را به‌روز نکنید نمی‌توانید از این قابلیت استفاده کنید." }, "premiumMembership": { "message": "عضویت پرمیوم" @@ -756,7 +756,7 @@ "message": "مدیریت عضویت" }, "premiumManageAlert": { - "message": "شما میتوانید عضویت خود را در نسخه وب گاوصندوق در bitwarden.com مدیریت کنید. آیا مایل به دیدن وبسایت هستید؟" + "message": "شما می‌توانید عضویت خود را در نسخه وب گاوصندوق در bitwarden.com مدیریت کنید. آیا مایل به دیدن وب‌سایت هستید؟" }, "premiumRefresh": { "message": "نوسازی عضویت" @@ -765,31 +765,31 @@ "message": "شما در حال حاظر کاربر پرمیوم نیستید." }, "premiumSignUpAndGet": { - "message": "ثبت نام برای عضویت پرمیوم و گرفتن:" + "message": "برای عضویت پرمیوم ثبت نام کنید و دریافت کنید:" }, "ppremiumSignUpStorage": { - "message": "۱ گیگابایت فضای ذخیره سازی رمزنگاری شده." + "message": "۱ گیگابایت فضای ذخیره سازی رمزگذاری شده برای پیوست های پرونده." }, "ppremiumSignUpTwoStep": { - "message": "گزینه های ورود اضافی دو مرحله ای مانند YubiKey, FIDO U2F و Duo." + "message": "گزینه‌های ورود دو مرحله‌ای اضافی مانند YubiKey, FIDO U2F و Duo." }, "ppremiumSignUpReports": { - "message": "بهداشت کلمه عبور، سلامت اکانت و گزارش های نقض اطلاعات تا صندوق شما امن نگه داشته شود." + "message": "گزارش‌های بهداشت رمز عبور، سلامت حساب و نقض داده‌ها برای ایمن نگهداشتن گاوصندوق شما." }, "ppremiumSignUpTotp": { - "message": "تولید کننده کد تایید (2FA) از نوع TOTP برای ورود به گاوصندوقتان." + "message": "تولید کننده کد تأیید (2FA) از نوع TOTP برای ورودهای در گاوصندوقتان." }, "ppremiumSignUpSupport": { "message": "اولویت پشتیبانی از مشتری." }, "ppremiumSignUpFuture": { - "message": "تمام ویژگی های پرمیوم آینده. به زودی بیشتر!" + "message": "تمام ویژگی‌های پرمیوم آینده. به زودی بیشتر!" }, "premiumPurchase": { "message": "خرید پرمیوم" }, "premiumPurchaseAlert": { - "message": "شما میوانید عضویت پرمیوم را از وب گاوصندوق bitwarden.com خریداری کنید. مایلید اکنون از وبسایت بازید کنید؟" + "message": "شما می‌توانید عضویت پرمیوم را از گاوصندوق وب bitwarden.com خریداری کنید. مایلید اکنون از وب‌سایت بازید کنید؟" }, "premiumCurrentMember": { "message": "شما یک عضو پرمیوم هستید!" @@ -813,22 +813,22 @@ "message": "TOTP را به صورت خودکار کپی کن" }, "disableAutoTotpCopyDesc": { - "message": "اگر ورود شما دارای یک کلید تأیید کننده است که به آن متصل شده است، هر زمان که بصورت خودکار وارد سایت شوید کد تأیید TOTP به صورت خودکار به کلیپ بورد شما کپی می شود." + "message": "اگر یک ورود دارای یک کلید احراز هویت باشد، هنگام پر کردن خودکار ورود، کد تأیید TOTP را در کلیپ بورد خود کپی کن." }, "enableAutoBiometricsPrompt": { "message": "درخواست بیومتریک هنگام راه اندازی" }, "premiumRequired": { - "message": "در نسخه پرمیوم کار میکند" + "message": "در نسخه پرمیوم کار می‌کند" }, "premiumRequiredDesc": { "message": "برای استفاده از این ویژگی عضویت پرمیوم لازم است." }, "enterVerificationCodeApp": { - "message": "کد ۶ رقمی تایید را از برنامه تایید کننده وارد کنید." + "message": "کد ۶ رقمی تأیید را از برنامه احراز هویت وارد کنید." }, "enterVerificationCodeEmail": { - "message": "Enter the 6 digit verification code that was emailed to", + "message": "کد ۶ رقمی تأیید را که به $EMAIL$ ایمیل شده را وارد کنید.", "placeholders": { "email": { "content": "$1", @@ -837,7 +837,7 @@ } }, "verificationCodeEmailSent": { - "message": "ایمیل تایید به $EMAIL$ ارسال شد.", + "message": "ایمیل تأیید به $EMAIL$ ارسال شد.", "placeholders": { "email": { "content": "$1", @@ -849,10 +849,10 @@ "message": "مرا به خاطر بسپار" }, "sendVerificationCodeEmailAgain": { - "message": "ارسال دوباره ایمیل کد تایید" + "message": "ارسال دوباره ایمیل کد تأیید" }, "useAnotherTwoStepMethod": { - "message": "استفاده از روش ورود دو مرحله ای دیگر" + "message": "استفاده از روش ورود دو مرحله‌ای دیگر" }, "insertYubiKey": { "message": "YubiKey خود را وارد پورت USB رایانه کنید، بعد دکمه آن را بفشارید." @@ -870,42 +870,42 @@ "message": "تأیید اعتبار در WebAuthn" }, "loginUnavailable": { - "message": "ورود به سیستم موجود نیست" + "message": "ورود به سیستم در دسترس نیست" }, "noTwoStepProviders": { - "message": "این حساب با سیستم دو مرحله ورود فعال است، با این حال، هیچ یک از ارائه دهندگان دو مرحله ای پیکربندی شده توسط این مرورگر وب پشتیبانی نمی شوند." + "message": "ورود دو مرحله‌ای برای این حساب فعال است، با این حال، هیچ یک از ارائه‌دهندگان دو مرحله‌ای پیکربندی شده توسط این مرورگر وب پشتیبانی نمی‌شوند." }, "noTwoStepProviders2": { - "message": "لطفا از یک مرورگر وب پشتیبانی شده (مانند کروم) استفاده کنید و / یا ارائه دهندگان اضافی را که در مرورگر وب بهتر پشتیانی میکنند را اضافه کنید (مانند یک برنامه تأیید کننده هویت)." + "message": "لطفاً از یک مرورگر وب پشتیبانی شده (مانند کروم) استفاده کنید و یا ارائه دهندگان اضافی را که از مرورگر وب بهتر پشتیانی می‌کنند را اضافه کنید (مانند یک برنامه احراز هویت)." }, "twoStepOptions": { - "message": "گزینه های ورود دو مرحله ای" + "message": "گزینه‌های ورود دو مرحله‌ای" }, "recoveryCodeDesc": { - "message": "دسترسی به تمامی ارائه دهندگان دو مرحله ای را از دست داده اید؟ از کد بازیابی خود برای غیرفعال سازی ارائه دهندگان دو مرحله ای از حسابتان استفاده کنید." + "message": "دسترسی به تمامی ارائه‌دهندگان دو مرحله‌ای را از دست داده‌اید؟ از کد بازیابی خود برای غیرفعال‌سازی ارائه‌دهندگان دو مرحله‌ای از حسابتان استفاده کنید." }, "recoveryCodeTitle": { "message": "کد بازیابی" }, "authenticatorAppTitle": { - "message": "برنامه تأیید کننده" + "message": "برنامه احراز هویت" }, "authenticatorAppDesc": { - "message": "از یک اپ تایید کننده (همانند Authy یا Google Authenticator) استفاده کنید تا کدهای تایید بر پایه زمان تولید کنید.", + "message": "از یک برنامه احراز هویت (مانند Authy یا Google Authenticator) استفاده کنید تا کدهای تأیید بر پایه زمان تولید کنید.", "description": "'Authy' and 'Google Authenticator' are product names and should not be translated." }, "yubiKeyTitle": { - "message": "کلید امنیت YubiKey OTP" + "message": "کلید امنیتی YubiKey OTP" }, "yubiKeyDesc": { - "message": "از یک YubiKey برای دسترسی به حسابتان استفاده کنید. همراه با دستگاه های YubiKey 4،4 Nano، NEO کار میکند." + "message": "از یک YubiKey برای دسترسی به حسابتان استفاده کنید. همراه با دستگاه‌های YubiKey 4 ،4 Nano ،NEO کار می‌کند." }, "duoDesc": { - "message": "تایید کنید همراه با Duo Security از اپ موبایل Dou استفاده کنید، پیامک،تماس تلفنی، یا کلید امنیتی U2F.", + "message": "با Duo Security با استفاده از برنامه تلفن همراه، پیامک، تماس تلفنی، یا کلید امنیتی U2F تأیید کنید.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { - "message": "Duo Security را برای سازماندهی خود با استفاده از برنامه Duo Mobile، SMS، تماس تلفنی یا کلید امنیتی U2F تأیید کنید.", + "message": "از Duo Security با استفاده از برنامه تلفن همراه، پیامک، تماس تلفنی یا کلید امنیتی U2F برای تأیید سازمان خود استفاده کنید.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "webAuthnTitle": { @@ -918,79 +918,79 @@ "message": "ایمیل" }, "emailDesc": { - "message": "کد تایید برایتان فرستاده می شود." + "message": "کد تأیید برایتان ارسال می‌شود." }, "selfHostedEnvironment": { "message": "محیط خود میزبان" }, "selfHostedEnvironmentFooter": { - "message": "آدرس اینترنتی پایه فرضی نصب Bitwarden میزبانی شده را مشخص کنید." + "message": "نشانی اینترنتی پایه فرضی نصب Bitwarden میزبانی شده را مشخص کنید." }, "customEnvironment": { - "message": "محیط های سفارشی" + "message": "محیط سفارشی" }, "customEnvironmentFooter": { - "message": "برای کاربران پیشرفته. شما می توانید آدرس پایه هر سرویس را مستقل تعیین کنید." + "message": "برای کاربران پیشرفته. شما می‌توانید نشانی پایه هر سرویس را مستقلاً تعیین کنید." }, "baseUrl": { - "message": "آدرس سرور" + "message": "نشانی اینترنتی سرور" }, "apiUrl": { - "message": "آدرس سرور API" + "message": "نشانی API سرور" }, "webVaultUrl": { - "message": "آدرس سرور گاوصندوق وب" + "message": "نشانی اینترنتی سرور گاوصندوق وب" }, "identityUrl": { - "message": "آدرس سرور شناسایی" + "message": "نشانی سرور شناسایی" }, "notificationsUrl": { - "message": "اعلانهای آدرس سرور" + "message": "نشانی سرور اعلان‌ها" }, "iconsUrl": { - "message": "آدرس سرور آیکون ها" + "message": "نشانی سرور آیکون ها" }, "environmentSaved": { - "message": "آدرس های اینترنتی محیط ذخیره شد." + "message": "نشانی‌های اینترنتی محیط ذخیره شد" }, "enableAutoFillOnPageLoad": { - "message": "فعال کردن پرکردن خودکار هنگام بارگذاری صفحه" + "message": "پر کردن خودکار هنگام بارگذاری صفحه" }, "enableAutoFillOnPageLoadDesc": { - "message": "اگر یک فرم ورودی شناسایی شود، وقتی صفحه وب بارگذاری می شود، به صورت اتوماتیک انجام می شود." + "message": "اگر یک فرم ورودی شناسایی شد، وقتی صفحه وب بارگذاری شد، به صورت خودکار پر شود." }, "experimentalFeature": { "message": "در حال حاضر این یک ویژگی آزمایشی است. با مسئولیت خود استفاده کنید." }, "defaultAutoFillOnPageLoad": { - "message": "تنظیم خودکار پیش فرض برای موارد ورود به سیستم" + "message": "تنظیم تکمیل خودکار پیش‌فرض برای موارد ورود به سیستم" }, "defaultAutoFillOnPageLoadDesc": { - "message": "پس از فعال کردن پرکردن خودکار بارگذاری صفحه، می توانید این ویژگی را برای موارد ورود به سیستم جداگانه فعال یا غیرفعال کنید. این تنظیمات پیش فرض برای موارد ورود به سیستم است که به طور جداگانه پیکربندی نشده اند." + "message": "می‌توانید پر کردن خودکار هنگام بارگیری صفحه را برای موارد ورود به سیستم از نمای ویرایش مورد خاموش کنید." }, "itemAutoFillOnPageLoad": { "message": "پر کردن خودکار بارگذاری صفحه (درصورت فعال بودن در گزینه ها)" }, "autoFillOnPageLoadUseDefault": { - "message": "استفاده از تنظيمات پيشفرض" + "message": "استفاده از تنظیمات پیش‌فرض" }, "autoFillOnPageLoadYes": { - "message": "بارگذاری صفحه را به طور خودکار پر کن" + "message": "پر کردن خودکار هنگام بارگذاری صفحه" }, "autoFillOnPageLoadNo": { - "message": "بارگذاری صفحه را به طور خودکار پر نکن" + "message": "هنگام بارگذاری صفحه پر کردن خودکار انجام نده" }, "commandOpenPopup": { - "message": "بازکردن پنجره گاوصندوق" + "message": "باز کردن پنجره گاوصندوق" }, "commandOpenSidebar": { - "message": "بازکردن گاوصندوق در نوار کناری" + "message": "باز کردن گاوصندوق در نوار کناری" }, "commandAutofillDesc": { - "message": "آخرین ورودی مورد استفاده برای وب سایت فعلی را به صورت خودکار پر کنید." + "message": "آخرین ورودی مورد استفاده برای وب سایت فعلی را به صورت خودکار پر کنید" }, "commandGeneratePasswordDesc": { - "message": "یک کلمه عبور تصادفی جدید ایجاد کنید و آن را در کلیپ بورد کپی کنید." + "message": "یک کلمه عبور تصادفی جدید ایجاد کنید و آن را در کلیپ بورد کپی کنید" }, "commandLockVaultDesc": { "message": "قفل گاوصندوق" @@ -1011,7 +1011,7 @@ "message": "فیلد سفارشی جدید" }, "dragToSort": { - "message": "برای مرتب سازی بکشید" + "message": "برای مرتب‌سازی بکشید" }, "cfTypeText": { "message": "متن" @@ -1020,24 +1020,24 @@ "message": "مخفی" }, "cfTypeBoolean": { - "message": "بولین" + "message": "منطقی" }, "cfTypeLinked": { - "message": "لینک شده", + "message": "پیوند شده", "description": "This describes a field that is 'linked' (tied) to another field." }, "linkedValue": { - "message": "ارزش لینک شده", + "message": "مقدار پیوند شده", "description": "This describes a value that is 'linked' (tied) to another value." }, "popup2faCloseMessage": { - "message": "در خارج از پنجره پاپ آپ کلیک کنید که ایملیتان بررسی شود برای کد تأییدیه که باعث میشود این پنجره بسته شود. آیا می خواهید این پاپ آپ را در یک پنجره جدید باز کنید تا آن را نبندید؟" + "message": "در خارج از پنجره پاپ آپ کلیک کنید که ایمیل‌تان بررسی شود برای کد تأییدیه که باعث می‌شود این پنجره بسته شود. آیا می‌خواهید این پاپ آپ را در یک پنجره جدید باز کنید تا آن را نبندید؟" }, "popupU2fCloseMessage": { - "message": "این مرورگر نمیتواند درخواستهای U2F را در این پنجره پاپ آپ پردازش کند. آیا می خواهید این پنجره را در یک پنجره جدید باز کنید تا بتوانید با استفاده از U2F وارد شوید؟" + "message": "این مرورگر نمی‌تواند درخواستهای U2F را در این پنجره پاپ آپ پردازش کند. آیا می‌خواهید این پنجره را در یک پنجره جدید باز کنید تا بتوانید با استفاده از U2F وارد شوید؟" }, "enableFavicon": { - "message": "نمایش نمادهای وب سایت" + "message": "نمایش نمادهای وب‌سایت" }, "faviconDesc": { "message": "یک تصویر قابل تشخیص در کنار هر ورود نشان دهید." @@ -1106,7 +1106,7 @@ "message": "کد امنیتی" }, "ex": { - "message": "سابق." + "message": "مثال." }, "title": { "message": "عنوان" @@ -1118,7 +1118,7 @@ "message": "خانم" }, "ms": { - "message": "خانم" + "message": "بانو" }, "dr": { "message": "دکتر" @@ -1136,19 +1136,19 @@ "message": "نام کامل" }, "identityName": { - "message": "نام شناسایی" + "message": "نام هویت" }, "company": { "message": "شرکت" }, "ssn": { - "message": "شماره امنیتی اجتماعی" + "message": "کد ملی" }, "passportNumber": { - "message": "شماره پاسپورت" + "message": "شماره گذرنامه" }, "licenseNumber": { - "message": "شماره مجوز" + "message": "شماره گواهی‌نامه" }, "email": { "message": "ایمیل" @@ -1157,7 +1157,7 @@ "message": "تلفن" }, "address": { - "message": "آدرس" + "message": "نشانی" }, "address1": { "message": "نشانی ۱" @@ -1187,7 +1187,7 @@ "message": "ورود" }, "typeLogins": { - "message": "ورود" + "message": "ورودها" }, "typeSecureNote": { "message": "یادداشت امن" @@ -1196,7 +1196,7 @@ "message": "کارت" }, "typeIdentity": { - "message": "مشخصات" + "message": "هویت" }, "passwordHistory": { "message": "تاریخچه کلمه عبور" @@ -1205,38 +1205,38 @@ "message": "بازگشت" }, "collections": { - "message": "مجموعه ها" + "message": "مجموعه‌ها" }, "favorites": { - "message": "مورد علاقه" + "message": "مورد علاقه‌ها" }, "popOutNewWindow": { - "message": "به یک پنجره جدید پاپ بزنید" + "message": "به یک پنجره جدید پاپ بزن" }, "refresh": { "message": "تازه کردن" }, "cards": { - "message": "کارتها" + "message": "کارت‌ها" }, "identities": { - "message": "مشخصات" + "message": "هویت‌ها" }, "logins": { - "message": "ورود" + "message": "ورودها" }, "secureNotes": { - "message": "یادداشت های امن" + "message": "یادداشت‌های امن" }, "clear": { "message": "پاک کردن", "description": "To clear something out. example: To clear browser history." }, "checkPassword": { - "message": "بررسی اینکه که آیا کلمه عبور افشا شده است." + "message": "بررسی کنید که آیا کلمه عبور افشا شده است." }, "passwordExposed": { - "message": "This password has been exposed in data breaches. You should change it.", + "message": "این کلمه عبور $VALUE$ بار در رخنه داده‌ها افشا شده است. باید آن را تغییر دهید.", "placeholders": { "value": { "content": "$1", @@ -1245,7 +1245,7 @@ } }, "passwordSafe": { - "message": "این کلمه عبور در هیچ رخنه داده ای شناخته نشده است. باید برای امنیت از آن استفاده کنید." + "message": "این کلمه عبور در هیچ رخنه داده ای شناخته نشده است. استفاده از آن باید ایمن باشد." }, "baseDomain": { "message": "دامنه پایه", @@ -1263,18 +1263,18 @@ "message": "دقیق" }, "startsWith": { - "message": "شروع می شود با" + "message": "شروع می‌شود با" }, "regEx": { "message": "عبارت منظم", "description": "A programming term, also known as 'RegEx'." }, "matchDetection": { - "message": "بازرسی تطابق", + "message": "تشخیص مطابقت", "description": "URI match detection for auto-fill." }, "defaultMatchDetection": { - "message": "بازرسی تطابق پیشفرض", + "message": "بررسی مطابقت پیش‌فرض", "description": "Default URI match detection for auto-fill." }, "toggleOptions": { @@ -1289,7 +1289,7 @@ "description": "The URI of one of the current open tabs in the browser." }, "organization": { - "message": "سازماندهی", + "message": "سازمان", "description": "An entity of multiple related people (ex. a team or business organization)." }, "types": { @@ -1299,31 +1299,31 @@ "message": "تمام موارد" }, "noPasswordsInList": { - "message": "هیچ کلمه عبوری در لیست وجود ندارد." + "message": "هیچ کلمه عبوری برای فهرست کردن وجود ندارد." }, "remove": { "message": "حذف" }, "default": { - "message": "پیش فرض" + "message": "پیش‌فرض" }, "dateUpdated": { - "message": "بروزرسانی شد", + "message": "به‌روزرسانی شد", "description": "ex. Date this item was updated" }, "dateCreated": { - "message": "ایجاد شده", + "message": "ایجاد شد", "description": "ex. Date this item was created" }, "datePasswordUpdated": { - "message": "کلمه عبور بروزرسانی شد", + "message": "کلمه عبور به‌روزرسانی شد", "description": "ex. Date this password was updated" }, "neverLockWarning": { - "message": "آیا جدا میخواهید از گزینه \"هرگز\" استفاده کنید؟ تنظیم کردن کردن گزینه قفل به \"هرگز\" کلیدهای رمزنگاری گاوصندوقتان را بر روی دستگاه شما ذخیره خواهد کرد. اگر از این گزینه استفاده میکنید باید اطمینان داشته باشید که دستگاه شما کاملا محافظت شده است." + "message": "آیا جداً می‌خواهید از گزینه \"هرگز\" استفاده کنید؟ تنظیم کردن گزینه قفل به \"هرگز\" کلیدهای رمزنگاری گاوصندوقتان را بر روی دستگاه شما ذخیره خواهد کرد. اگر از این گزینه استفاده می‌کنید باید اطمینان داشته باشید که دستگاه شما کاملا محافظت شده است." }, "noOrganizationsList": { - "message": "You do not belong to any organizations. Organizations allow you to securely share items with other users." + "message": "شما به هیچ سازمانی تعلق ندارید. سازمان‌ها به شما اجازه می‌دهند تا داده‌های خود را با کاربران دیگر به صورت امن به اشتراک بگذارید." }, "noCollectionsInList": { "message": "هیچ مجموعه ای برای لیست کردن وجود ندارد." @@ -1357,16 +1357,16 @@ "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." }, "unlockWithPin": { - "message": "بازکردن با پین" + "message": "باز کردن با پین" }, "setYourPinCode": { - "message": "پین کد خود را برای باز کردن بیت واردن خود استفاده کنید. اگر به صورت کامل از حساب خود خارج شوید تنظیمات پین شما ریست می شود." + "message": "کد پین خود را برای باز کردن Bitwarden تنظیم کنید. اگر به طور کامل از برنامه خارج شوید، تنظیمات پین شما از بین می‌رود." }, "pinRequired": { "message": "کد پین الزامیست." }, "invalidPin": { - "message": "کد پین معتبر نیست. " + "message": "کد پین معتبر نیست." }, "unlockWithBiometrics": { "message": "با استفاده از بیومتریک باز کنید" @@ -1378,7 +1378,7 @@ "message": "لطفاً استفاده از بیومتریک را در برنامه دسکتاپ Bitwarden تأیید کنید تا بیومتریک را برای مرورگر فعال کنید." }, "lockWithMasterPassOnRestart": { - "message": "در زمان شروع مجدد مرورگر، با رمز اصلی قفل کن" + "message": "در زمان شروع مجدد، با کلمه عبور اصلی قفل کن" }, "selectOneCollection": { "message": "شما باید حداقل یک مجموعه را انتخاب کنید." @@ -1390,7 +1390,7 @@ "message": "شبیه سازی" }, "passwordGeneratorPolicyInEffect": { - "message": "یک یا چند خط مشی سازمان بر تنظیمات تولیدکننده شما تأثیر می گذارد." + "message": "یک یا چند سیاست سازمان بر تنظیمات تولید کننده شما تأثیر می‌گذارد." }, "vaultTimeoutAction": { "message": "عمل متوقف شدن گاو‌صندوق" @@ -1410,7 +1410,7 @@ "message": "حذف دائمی مورد" }, "permanentlyDeleteItemConfirmation": { - "message": "آیا مطمئن هستید که می خواهید این مورد را برای همیشه حذف کنید؟" + "message": "مطمئن هستید که می‌خواهید این مورد را برای همیشه پاک کنید؟" }, "permanentlyDeletedItem": { "message": "مورد برای همیشه حذف شد" @@ -1419,22 +1419,22 @@ "message": "بازیابی مورد" }, "restoreItemConfirmation": { - "message": "آیا مطمئن هستید می خواهید این مورد را بازیابی کنید؟" + "message": "آیا مطمئن هستید که می‌خواهید این مورد را بازیابی کنید؟" }, "restoredItem": { "message": "مورد بازیابی شد" }, "vaultTimeoutLogOutConfirmation": { - "message": "خروج از سیستم تمام دسترسی ها به گاو‌صندوق شما را از بین می برد و نیاز به احراز هویت آنلاین پس از مدت زمان توقف دارد. آیا مطمئن هستید که می خواهید از این تنظیمات استفاده کنید؟" + "message": "خروج از سیستم، تمام دسترسی ها به گاو‌صندوق شما را از بین می‌برد و نیاز به احراز هویت آنلاین پس از مدت زمان توقف دارد. آیا مطمئن هستید که می‌خواهید از این تنظیمات استفاده کنید؟" }, "vaultTimeoutLogOutConfirmationTitle": { - "message": "تایید عمل توقف" + "message": "تأیید عمل توقف" }, "autoFillAndSave": { - "message": "پرکردن خودکار و ذخیره" + "message": "پر کردن خودکار و ذخیره" }, "autoFillSuccessAndSavedUri": { - "message": "مورد خودکار پر شد و آدرس اینترنتی ذخیره شد" + "message": "مورد خودکار پر شد و نشانی اینترنتی ذخیره شد" }, "autoFillSuccess": { "message": "مورد خودکار پر شد" @@ -1482,10 +1482,10 @@ } }, "masterPasswordPolicyRequirementsNotMet": { - "message": "کلمه عبور اصلی جدید شما از شرایط سیاست پپیروی نمی کند." + "message": "کلمه عبور اصلی جدید شما از شرایط سیاست پیروی نمی‌کند." }, "acceptPolicies": { - "message": "با علامت زدن این کادر با موارد زیر موافقت می کنید:" + "message": "با علامت زدن این کادر با موارد زیر موافقت می‌کنید:" }, "acceptPoliciesRequired": { "message": "شرایط خدمات و سیاست حفظ حریم خصوصی تأیید نشده است." @@ -1497,16 +1497,16 @@ "message": "سیاست حفظ حریم خصوصی" }, "hintEqualsPassword": { - "message": "نکته کلمه عبور شما نمی تواند همان کلمه عبور شما باشد." + "message": "اشاره به کلمه عبور شما نمی‌تواند همان کلمه عبور شما باشد." }, "ok": { - "message": "تایید" + "message": "تأیید" }, "desktopSyncVerificationTitle": { - "message": "تأیید همگام سازی دسکتاپ" + "message": "تأیید همگام‌سازی دسکتاپ" }, "desktopIntegrationVerificationText": { - "message": "لطفاً تأیید کنید که برنامه دسکتاپ این اثر انگشت را نشان می دهد:" + "message": "لطفاً تأیید کنید که برنامه دسکتاپ این اثر انگشت را نشان می‌دهد:" }, "desktopIntegrationDisabledTitle": { "message": "ادغام مرورگر فعال نیست" @@ -1536,37 +1536,37 @@ "message": "برنامه دسکتاپ به یک حساب دیگر وارد شده است. لطفاً اطمینان حاصل کنید که هر دو برنامه به یک حساب وارد شده اند." }, "nativeMessagingWrongUserTitle": { - "message": "عدم تطابق حساب" + "message": "عدم مطابقت حساب کاربری" }, "biometricsNotEnabledTitle": { - "message": "بیومتریک فعال نیست" + "message": "بیومتریک برپا نشده" }, "biometricsNotEnabledDesc": { "message": "بیومتریک مرورگر ابتدا نیاز به فعالسازی بیومتریک دسکتاپ در تنظیمات دارد." }, "biometricsNotSupportedTitle": { - "message": "بیومتریک پشتیبانی نمی شود" + "message": "بیومتریک پشتیبانی نمی‌شود" }, "biometricsNotSupportedDesc": { - "message": "بیومتریک مرورگر در این دستگاه پشتیبانی نمی شود." + "message": "بیومتریک مرورگر در این دستگاه پشتیبانی نمی‌شود." }, "nativeMessaginPermissionErrorTitle": { "message": "مجوز ارائه نشده است" }, "nativeMessaginPermissionErrorDesc": { - "message": "بدون اجازه ارتباط با برنامه دسکتاپ Bitwarden ، نمی توانیم بیومتریک را در افزونه مرورگر ارائه دهیم. لطفا دوباره تلاش کنید." + "message": "بدون اجازه برای ارتباط با برنامه دسکتاپ Bitwarden، نمی‌توانیم بیومتریک را در افزونه مرورگر ارائه دهیم. لطفاً دوباره تلاش کنید." }, "nativeMessaginPermissionSidebarTitle": { "message": "خطای درخواست مجوز" }, "nativeMessaginPermissionSidebarDesc": { - "message": "این عمل را نمی توان در نوار کناری انجام داد، لطفاً اقدام را در پنجره بازشو یا پنجره بازخوانی کنید." + "message": "این عمل را نمی‌توان در نوار کناری انجام داد، لطفاً اقدام را در پنجره بازشو بازخوانی کنید." }, "personalOwnershipSubmitError": { - "message": "به دلیل خط مشی Enterprise ، برای ذخیره موارد در گاوصندوق شخصی خود محدود شده اید. گزینه مالکیت را به یک سازمان تغییر دهید و مجموعه های موجود را انتخاب کنید." + "message": "به دلیل سیاست پرمیوم، برای ذخیره موارد در گاوصندوق شخصی خود محدود شده اید. گزینه مالکیت را به یک سازمان تغییر دهید و مجموعه های موجود را انتخاب کنید." }, "personalOwnershipPolicyInEffect": { - "message": "خط مشی سازمانی بر تنظیمات مالکیت شما تأثیر می گذارد." + "message": "سیاست سازمانی بر تنظیمات مالکیت شما تأثیر می‌گذارد." }, "excludedDomains": { "message": "دامنه های مستثنی" @@ -1588,7 +1588,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "searchSends": { - "message": "ارسال ها را جستجو کن", + "message": "جستجوی ارسال‌ها", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "addSend": { @@ -1599,7 +1599,7 @@ "message": "متن" }, "sendTypeFile": { - "message": "فایل" + "message": "پرونده" }, "allSends": { "message": "همه ارسال ها", @@ -1632,7 +1632,7 @@ "message": "کلمه عبور حذف شد" }, "deletedSend": { - "message": "ارسال پاک شد", + "message": "ارسال حذف شد", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { @@ -1643,14 +1643,14 @@ "message": "غیرفعال شد" }, "removePasswordConfirmation": { - "message": "مطمئنید می‌خواهید این کلمه عبور حذف شود؟" + "message": "مطمئنید که می‌خواهید کلمه عبور حذف شود؟" }, "deleteSend": { - "message": "ارسال پاک شد", + "message": "ارسال حذف شد", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendConfirmation": { - "message": "آیا مطمئن هستید می خواهید این ارسال را حذف کنید؟", + "message": "آیا مطمئن هستید که می‌خواهید این ارسال را حذف کنید؟", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editSend": { @@ -1666,7 +1666,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFileDesc": { - "message": "فایلی که می خواهید ارسال کنید." + "message": "پرونده ای که می‌خواهید ارسال کنید." }, "deletionDate": { "message": "تاریخ حذف" @@ -1679,7 +1679,7 @@ "message": "تاريخ انقضاء" }, "expirationDateDesc": { - "message": "در صورت تنظیم، دسترسی به این ارسال در تاریخ و ساعت مشخص شده منقضی می شود.", + "message": "در صورت تنظیم، دسترسی به این ارسال در تاریخ و ساعت مشخص شده منقضی می‌شود.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "oneDay": { @@ -1701,7 +1701,7 @@ "message": "تعداد دسترسی حداکثر" }, "maximumAccessCountDesc": { - "message": "در صورت تنظیم، با رسیدن به حداکثر تعداد دسترسی، کاربران دیگر نمی توانند به این ارسال دسترسی پیدا کنند.", + "message": "در صورت تنظیم، با رسیدن به حداکثر تعداد دسترسی، کاربران دیگر نمی‌توانند به این ارسال دسترسی پیدا کنند.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendPasswordDesc": { @@ -1721,7 +1721,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTextDesc": { - "message": "متنی که می خواهید ارسال کنید." + "message": "متنی که می‌خواهید ارسال کنید." }, "sendHideText": { "message": "متن این ارسال را به طور پیش فرض پنهان کن.", @@ -1731,36 +1731,36 @@ "message": "تعداد دسترسی فعلی" }, "createSend": { - "message": "ساختن ارسال جدید", + "message": "ارسال جدید", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "newPassword": { "message": "کلمه عبور جدید" }, "sendDisabled": { - "message": "ارسال غیرفعال شد", + "message": "ارسال حذف شد", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { - "message": "به دلیل خط مشی سازمانی، شما فقط می توانید ارسال موجود را حذف کنید.", + "message": "به دلیل سیاست سازمانی، شما فقط می‌توانید ارسال موجود را حذف کنید.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSend": { - "message": "ارسال جدید ساخته شد", + "message": "ارسال ساخته شد", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editedSend": { - "message": "ارسال جدید ویرایش شد", + "message": "ارسال ذخیره شد", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinuxChromiumFileWarning": { - "message": "برای انتخاب پرونده، پسوند را در نوار کناری باز کنید (در صورت امکان) یا با کلیک بر روی این آگهی به پنجره جدیدی باز شوید." + "message": "برای انتخاب پرونده، پسوند را در نوار کناری باز کنید (در صورت امکان) یا با کلیک بر روی این بنر پنجره جدیدی باز کنید." }, "sendFirefoxFileWarning": { - "message": "برای انتخاب یک فایل با استفاده از Firefox، افزونه را در نوار کناری باز کنید یا با کلیک بر روی این بنر به پنجره جدید باز شوید." + "message": "برای انتخاب یک پرونده با استفاده از Firefox، افزونه را در نوار کناری باز کنید یا با کلیک بر روی این بنر پنجره جدیدی باز کنید." }, "sendSafariFileWarning": { - "message": "برای انتخاب فایلی با استفاده از Safari، با کلیک روی این بنر به پنجره جدیدی باز شوید." + "message": "برای انتخاب پرونده ای با استفاده از Safari، با کلیک روی این بنر پنجره جدیدی باز کنید." }, "sendFileCalloutHeader": { "message": "قبل از اینکه شروع کنی" @@ -1778,61 +1778,61 @@ "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": "تاریخ انقضا ارائه شده معتبر نیست." + "message": "تاریخ انقضاء ارائه شده معتبر نیست." }, "deletionDateIsInvalid": { "message": "تاریخ حذف ارائه شده معتبر نیست." }, "expirationDateAndTimeRequired": { - "message": "تاریخ انقضا و زمان لازم است." + "message": "تاریخ انقضاء و زمان لازم است." }, "deletionDateAndTimeRequired": { "message": "تاریخ و زمان حذف لازم است." }, "dateParsingError": { - "message": "هنگام ذخیره حذف و تاریخ انقضا شما خطایی روی داد." + "message": "هنگام ذخیره حذف و تاریخ انقضاء شما خطایی روی داد." }, "hideEmail": { - "message": "آدرس ایمیلم را از گیرندگان مخفی کن." + "message": "نشانی ایمیلم را از گیرندگان مخفی کن." }, "sendOptionsPolicyInEffect": { - "message": "یک یا چند سیاست سازمان بر گزینه های ارسال شما تأثیر می گذارد." + "message": "یک یا چند سیاست سازمان بر گزینه های ارسال شما تأثیر می‌گذارد." }, "passwordPrompt": { - "message": "کلمه عبور اصلی دوباره تولید می شود" + "message": "درخواست مجدد کلمه عبور اصلی" }, "passwordConfirmation": { "message": "تأیید کلمه عبور اصلی" }, "passwordConfirmationDesc": { - "message": "این عمل محافظت می شود. برای ادامه، لطفاً کلمه ورود اصلی خود را دوباره وارد کنید تا هویتان را تأیید کنید." + "message": "این عمل محافظت می‌شود. برای ادامه، لطفاً کلمه عبور اصلی خود را دوباره وارد کنید تا هویت‌تان را تأیید کنید." }, "emailVerificationRequired": { - "message": "تایید ایمیل لازم است" + "message": "تأیید ایمیل لازم است" }, "emailVerificationRequiredDesc": { - "message": "برای استفاده از این ویژگی باید ایمیل خود را تأیید کنید. می توانید ایمیل خود را در گاوصندوق وب تأیید کنید." + "message": "برای استفاده از این ویژگی باید ایمیل خود را تأیید کنید. می‌توانید ایمیل خود را در گاوصندوق وب تأیید کنید." }, "updatedMasterPassword": { - "message": "کلمه عبور اصلی بروز شد" + "message": "کلمه عبور اصلی به‌روز شد" }, "updateMasterPassword": { - "message": "بروزرسانی کلمه عبور اصلی" + "message": "به‌روزرسانی کلمه عبور اصلی" }, "updateMasterPasswordWarning": { - "message": "کلمه عبور اصلی شما اخیراً توسط سرپرست سازمانتان تغییر کرده است. برای دسترسی به گاوصندوق، باید همین حالا کلمه عبور اصلی خود را به روز کنید. در صورت ادامه، شما از نشست فعلی خود خارج می شوید و باید دوباره وارد سیستم شوید. نشست فعال در دستگاه های دیگر ممکن است تا یک ساعت همچنان فعال باقی بمانند." + "message": "کلمه عبور اصلی شما اخیراً توسط سرپرست سازمان‌تان تغییر کرده است. برای دسترسی به گاوصندوق، باید همین حالا کلمه عبور اصلی خود را به‌روز کنید. در صورت ادامه، شما از نشست فعلی خود خارج می‌شوید و باید دوباره وارد سیستم شوید. نشست فعال در دستگاه های دیگر ممکن است تا یک ساعت همچنان فعال باقی بمانند." }, "resetPasswordPolicyAutoEnroll": { "message": "ثبت نام خودکار" }, "resetPasswordAutoEnrollInviteWarning": { - "message": "این سازمان دارای خط مشی سازمانی ای است که به طور خودکار شما را در بازنشانی کلمه عبور ثبت نام می کند. این ثبت نام به مدیران سازمان اجازه می دهد تا کلمه عبور اصلی شما را تغییر دهند." + "message": "این سازمان دارای سیاست سازمانی ای است که به طور خودکار شما را در بازنشانی کلمه عبور ثبت نام می‌کند. این ثبت نام به مدیران سازمان اجازه می‌دهد تا کلمه عبور اصلی شما را تغییر دهند." }, "selectFolder": { "message": "پوشه را انتخاب کنید..." }, "ssoCompleteRegistration": { - "message": "برای تکمیل ورود به سیستم با SSO ، لطفاً یک کلمه عبور اصلی برای دسترسی و محافظت از گاوصندوق خود تنظیم کنید." + "message": "برای پر کردن ورود به سیستم با SSO، لطفاً یک کلمه عبور اصلی برای دسترسی و محافظت از گاوصندوق خود تنظیم کنید." }, "hours": { "message": "ساعت" @@ -1841,7 +1841,7 @@ "message": "دقیقه" }, "vaultTimeoutPolicyInEffect": { - "message": "خط مشی های سازمانتان بر مهلت زمانی گاوصندوق شما تأثیر می گذارد. حداکثر زمان مجاز گاوصندوق $HOURS$ ساعت و $MINUTES$ دقیقه است", + "message": "سیاست‌های سازمانتان بر مهلت زمانی گاوصندوق شما تأثیر می‌گذارد. حداکثر زمان مجاز گاوصندوق $HOURS$ ساعت و $MINUTES$ دقیقه است", "placeholders": { "hours": { "content": "$1", @@ -1857,10 +1857,10 @@ "message": "مهلت زمانی شما بیش از محدودیت های تعیین شده توسط سازمانتان است." }, "vaultExportDisabled": { - "message": "صادرات گاوصندوق غیرفعال شده است" + "message": "برون ریزی گاوصندوق غیرفعال شده است" }, "personalVaultExportPolicyInEffect": { - "message": "یک یا چند خط مشی سازمان از صادرات گاوصندوق شخصی شما جلوگیری می کند." + "message": "یک یا چند سیاست سازمان از برون ریزی گاوصندوق شخصی شما جلوگیری می‌کند." }, "copyCustomFieldNameInvalidElement": { "message": "شناسایی عنصر فرم معتبر امکان پذیر نیست. به جای آن سعی کنید HTML را بررسی کنید." @@ -1881,16 +1881,16 @@ "message": "ترک سازمان" }, "removeMasterPassword": { - "message": "پاک کردن کلمه عبور اصلی" + "message": "حذف کلمه عبور اصلی" }, "removedMasterPassword": { - "message": "کلمه عبور اصلی پاک شد." + "message": "کلمه عبور اصلی حذف شد" }, "leaveOrganizationConfirmation": { "message": "آيا مطمئن هستيد که می خواهيد سازمان های انتخاب شده را ترک کنيد؟" }, "leftOrganization": { - "message": "شما از سازمان ها خارج شده اید." + "message": "شما از سازمان خارج شده اید." }, "toggleCharacterCount": { "message": "تغییر تعداد کاراکترها" @@ -1899,10 +1899,10 @@ "message": "زمان نشست شما به پایان رسید. لطفاً برگردید و دوباره وارد سیستم شوید." }, "exportingPersonalVaultTitle": { - "message": "صادرات گاو‌صندوق شخصی" + "message": "برون ریزی گاو‌صندوق شخصی" }, "exportingPersonalVaultDescription": { - "message": "فقط موارد گاو‌صندوق شخصی مرتبط با $EMAIL$ صادر خواهد شد. موارد گاو‌صندوق سازمان شامل نخواهد شد.", + "message": "فقط موارد گاو‌صندوق شخصی مرتبط با $EMAIL$ برون ریزی خواهد شد. موارد گاو‌صندوق سازمان شامل نخواهد شد.", "placeholders": { "email": { "content": "$1", @@ -1923,14 +1923,14 @@ "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": "از قابلیت های آدرس دهی فرعی ارائه دهنده ایمیل خود استفاده کنید." }, "catchallEmail": { - "message": "رایانامه همه‌گیر" + "message": "دریافت همه ایمیل‌ها" }, "catchallEmailDesc": { "message": "از صندوق ورودی پیکربندی شده دامنه خود استفاده کنید." @@ -1942,22 +1942,22 @@ "message": "کلمه تصادفی" }, "websiteName": { - "message": "نام وب سایت" + "message": "نام وب‌سایت" }, "whatWouldYouLikeToGenerate": { "message": "چه چیزی دوست دارید تولید کنید؟" }, "passwordType": { - "message": "نوع گذرواژه" + "message": "نوع کلمه عبور" }, "service": { - "message": "خدمت" + "message": "سرویس" }, "forwardedEmail": { "message": "نام مستعار ایمیل فوروارد شده" }, "forwardedEmailDesc": { - "message": "یک نام مستعار ایمیل با یک سرویس فروارد خارجی ایجاد کنید." + "message": "یک نام مستعار ایمیل با یک سرویس ارسال خارجی ایجاد کنید." }, "hostname": { "message": "نام میزبان", @@ -1970,7 +1970,7 @@ "message": "کلید API" }, "ssoKeyConnectorError": { - "message": "خطای Key Connector: مطمئن شوید که Key Connector در دسترس است و به درستی کار می کند." + "message": "خطای رابط کلید: مطمئن شوید که رابط کلید در دسترس است و به درستی کار می‌کند." }, "premiumSubcriptionRequired": { "message": "اشتراک پرمیوم نیاز است" @@ -2006,7 +2006,7 @@ "message": "نسخه سرور" }, "selfHosted": { - "message": "میزبانی خود" + "message": "خود میزبان" }, "thirdParty": { "message": "شخص ثالث" diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 9981bf49cae..5c2a7f656ff 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -3,7 +3,7 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden - Free Password Manager", + "message": "Bitwarden - Libreng Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { @@ -20,7 +20,7 @@ "message": "Mag-login" }, "enterpriseSingleSignOn": { - "message": "Enterprise single sign-on" + "message": "Enterprise Single Sign-On sa Filipino ay Isang Sign-On na Enterprise" }, "cancel": { "message": "Kanselahin" @@ -47,19 +47,19 @@ "message": "Muling i-type ang Master Password" }, "masterPassHint": { - "message": "Master password hint (optional)" + "message": "Mungkahi sa Master Password (opsyonal)" }, "tab": { "message": "Tab" }, "vault": { - "message": "Vault" + "message": "Ayos" }, "myVault": { "message": "Aking Kahadeyero" }, "allVaults": { - "message": "All vaults" + "message": "Lahat ng Vault" }, "tools": { "message": "Mga Kagamitan" @@ -80,34 +80,34 @@ "message": "Kopyahin ang URI" }, "copyUsername": { - "message": "Copy username" + "message": "Kopyahin ang pangalan ng gumagamit" }, "copyNumber": { - "message": "Copy number" + "message": "Pamamagitan ng Kopya ng Bilang" }, "copySecurityCode": { - "message": "Copy security code" + "message": "Kopyahin ang code ng seguridad" }, "autoFill": { - "message": "Auto-fill" + "message": "Auto-fill sa Filipino ay Awtomatikong Pagpuno" }, "generatePasswordCopied": { - "message": "Generate password (copied)" + "message": "Maglagay ng Password" }, "copyElementIdentifier": { - "message": "Copy custom field name" + "message": "Salin ang Pangalan ng Pasadyang Field" }, "noMatchingLogins": { - "message": "No matching logins" + "message": "Walang tumutugmang mga login" }, "unlockVaultMenu": { "message": "Buksan ang iyong kahadeyero" }, "loginToVaultMenu": { - "message": "Log in to your vault" + "message": "Ag-log in sa iyong bangko" }, "autoFillInfo": { - "message": "There are no logins available to auto-fill for the current browser tab." + "message": "Walang mga login na magagamit para i-auto-fill para sa kasalukuyang tab ng browser." }, "addLogin": { "message": "Magdagdag ng Login" @@ -116,28 +116,28 @@ "message": "Magdagdag ng Item" }, "passwordHint": { - "message": "Password hint" + "message": "Mungkahi sa Password" }, "enterEmailToGetHint": { - "message": "Enter your account email address to receive your master password hint." + "message": "Ipasok ang iyong email address ng account para makatanggap ng hint ng iyong master password." }, "getMasterPasswordHint": { - "message": "Get master password hint" + "message": "Makuha ang Punong Password na Hint" }, "continue": { "message": "Magpatuloy" }, "sendVerificationCode": { - "message": "Send a verification code to your email" + "message": "Magpadala ng isang verification code sa iyong email" }, "sendCode": { - "message": "Send code" + "message": "Magpadala ng Code" }, "codeSent": { "message": "Ipinadala ang Code" }, "verificationCode": { - "message": "Verification code" + "message": "VerificationCode sa Filipino ay Pagsasagot sa Tanong" }, "confirmIdentity": { "message": "Kumpirmahin ang iyong identididad upang magpatuloy." @@ -146,112 +146,112 @@ "message": "Account" }, "changeMasterPassword": { - "message": "Change master password" + "message": "Baguhin ang Master Password" }, "fingerprintPhrase": { - "message": "Fingerprint phrase", + "message": "Hulmabig ng Hilik ng Dako", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." }, "yourAccountsFingerprint": { - "message": "Your account's fingerprint phrase", + "message": "Ang fingerprint pala ng iyong account", "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": { - "message": "Two-step login" + "message": "Dalawahang-hakbang na Pag-login" }, "logOut": { - "message": "Log out" + "message": "Mag-Log Out" }, "about": { - "message": "About" + "message": "Tungkol" }, "version": { - "message": "Version" + "message": "Bersyon" }, "save": { - "message": "Save" + "message": "I-save" }, "move": { - "message": "Move" + "message": "Lumipat" }, "addFolder": { - "message": "Add folder" + "message": "Magdagdag ng folder" }, "name": { - "message": "Name" + "message": "Pangalan" }, "editFolder": { - "message": "Edit folder" + "message": "I-edit ang folder" }, "deleteFolder": { - "message": "Delete folder" + "message": "Burahin ang folder" }, "folders": { - "message": "Folders" + "message": "Mga Folder" }, "noFolders": { - "message": "There are no folders to list." + "message": "Walang mga folder na listahan." }, "helpFeedback": { - "message": "Help & feedback" + "message": "Tulong at Mga Feedback" }, "sync": { - "message": "Sync" + "message": "Ikintal" }, "syncVaultNow": { - "message": "Sync vault now" + "message": "Isingit ang Vault ngayon" }, "lastSync": { - "message": "Last sync:" + "message": "Huling sinkronisasyon:" }, "passGen": { - "message": "Password generator" + "message": "Tagapaglikha ng Password" }, "generator": { - "message": "Generator", + "message": "Magmamana", "description": "Short for 'Password Generator'." }, "passGenInfo": { - "message": "Automatically generate strong, unique passwords for your logins." + "message": "Automatiko na gumawa ng mga malakas at natatanging mga password para sa iyong mga logins." }, "bitWebVault": { "message": "Bitwarden web vault" }, "importItems": { - "message": "Import items" + "message": "Isingit ang Vault ngayon" }, "select": { - "message": "Select" + "message": "Piliin" }, "generatePassword": { - "message": "Generate password" + "message": "Magtatag ng Password" }, "regeneratePassword": { - "message": "Regenerate password" + "message": "Muling I-generate ang Password" }, "options": { - "message": "Options" + "message": "Mga Pagpipilian" }, "length": { - "message": "Length" + "message": "Kahabaan" }, "uppercase": { - "message": "Uppercase (A-Z)" + "message": "2. Mga letra na malalaki (A-Z)" }, "lowercase": { - "message": "Lowercase (a-z)" + "message": "Lowercase (a-t) sa Filipino ay mababang-letra (a-t)" }, "numbers": { "message": "Numbers (0-9)" }, "specialCharacters": { - "message": "Special characters (!@#$%^&*)" + "message": "Mga espesyal na character (!@#$%^&*) sa Filipino ay tinatawag na mga simbolong pambihira" }, "numWords": { - "message": "Number of words" + "message": "Ang bilang ng mga salita\n\nNumero ng mga salita" }, "wordSeparator": { - "message": "Word separator" + "message": "Separador ng salita" }, "capitalize": { "message": "Capitalize", @@ -267,16 +267,16 @@ "message": "Minimum special" }, "avoidAmbChar": { - "message": "Avoid ambiguous characters" + "message": "Iwasan ang mga hindi malinaw na character" }, "searchVault": { "message": "Search vault" }, "edit": { - "message": "Edit" + "message": "I-edit" }, "view": { - "message": "View" + "message": "Tanaw" }, "noItemsInList": { "message": "There are no items to list." @@ -285,16 +285,16 @@ "message": "Item information" }, "username": { - "message": "Username" + "message": "Ang pangalan ng tagagamit" }, "password": { - "message": "Password" + "message": "Ang Password" }, "passphrase": { - "message": "Passphrase" + "message": "Pasa salita" }, "favorite": { - "message": "Favorite" + "message": "Ang Paborito" }, "notes": { "message": "Notes" @@ -303,19 +303,19 @@ "message": "Note" }, "editItem": { - "message": "Edit item" + "message": "Baguhin ang Item" }, "folder": { - "message": "Folder" + "message": "Folder/Direktoryo" }, "deleteItem": { - "message": "Delete item" + "message": "Tanggalin ang Item" }, "viewItem": { - "message": "View item" + "message": "Tingnan ang Item" }, "launch": { - "message": "Launch" + "message": "Paglulunsad" }, "website": { "message": "Website" @@ -339,13 +339,13 @@ "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." }, "verifyIdentity": { - "message": "Verify identity" + "message": "I-verify ang pagkakakilanlan" }, "yourVaultIsLocked": { "message": "Your vault is locked. Verify your identity to continue." }, "unlock": { - "message": "Unlock" + "message": "Buksan" }, "loggedInAsOn": { "message": "Logged in as $EMAIL$ on $HOSTNAME$.", @@ -367,7 +367,7 @@ "message": "Vault timeout" }, "lockNow": { - "message": "Lock now" + "message": "Mag-kandado Na" }, "immediately": { "message": "Immediately" @@ -467,7 +467,7 @@ "message": "Your login session has expired." }, "logOutConfirmation": { - "message": "Are you sure you want to log out?" + "message": "Sigurado ka bang gusto mong mag-log out?" }, "yes": { "message": "Yes" diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index c2d133c853f..601600325cc 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -32,22 +32,22 @@ "message": "Soumettre" }, "emailAddress": { - "message": "Adresse e-mail" + "message": "Adresse électronique" }, "masterPass": { - "message": "Mot de passe maître" + "message": "Mot de passe principal" }, "masterPassDesc": { - "message": "Le mot de passe maître est le mot de passe que vous utilisez pour accéder à votre coffre. Il est très important de ne pas l'oublier. Il n'existe aucun moyen de le récupérer si vous le perdez." + "message": "Le mot de passe principal est le mot de passe que vous utilisez pour accéder à votre coffre. Il est très important de ne pas oublier votre mot de passe principal. Il n'existe aucun moyen de récupérer le mot de passe si vous l'oubliez." }, "masterPassHintDesc": { - "message": "Un indice de mot de passe maître peut vous aider à vous rappeler de votre mot de passe en cas d'oubli." + "message": "Un indice de mot de passe principal peut vous aider à vous souvenir de votre mot de passe si vous l'oubliez." }, "reTypeMasterPass": { - "message": "Saisir à nouveau le mot de passe maître" + "message": "Ressaisir le mot de passe principal" }, "masterPassHint": { - "message": "Indice du mot de passe maître (facultatif)" + "message": "Indice du mot de passe principal (facultatif)" }, "tab": { "message": "Onglet" @@ -107,7 +107,7 @@ "message": "Connectez-vous à votre coffre" }, "autoFillInfo": { - "message": "Il n'existe aucun site disponible pour le remplissage automatique pour l'onglet actuel du navigateur." + "message": "Il n'y a pas d'identifiants disponibles à saisir automatiquement pour l'onglet actuel du navigateur." }, "addLogin": { "message": "Ajouter un site" @@ -119,10 +119,10 @@ "message": "Indice mot de passe" }, "enterEmailToGetHint": { - "message": "Saisissez l'adresse e-mail de votre compte pour recevoir l'indice de votre mot de passe maître." + "message": "Saisissez l'adresse électronique de votre compte pour recevoir l'indice de votre mot de passe principal." }, "getMasterPasswordHint": { - "message": "Obtenir l'indice du mot de passe maître" + "message": "Obtenir l'indice du mot de passe principal" }, "continue": { "message": "Continuer" @@ -146,7 +146,7 @@ "message": "Compte" }, "changeMasterPassword": { - "message": "Changer le mot de passe maître" + "message": "Changer le mot de passe principal" }, "fingerprintPhrase": { "message": "Phrase d'empreinte", @@ -157,10 +157,10 @@ "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": { - "message": "Identification à deux facteurs" + "message": "Authentification à deux facteurs" }, "logOut": { - "message": "Déconnexion" + "message": "Se déconnecter" }, "about": { "message": "À propos" @@ -212,13 +212,13 @@ "description": "Short for 'Password Generator'." }, "passGenInfo": { - "message": "Générer automatiquement des mots de passe forts et uniques pour vos identifiants." + "message": "Générer automatiquement des mots de passe robustes et uniques pour vos identifiants." }, "bitWebVault": { "message": "Coffre en ligne de bitwarden" }, "importItems": { - "message": "Importer des identifiants" + "message": "Importer des éléments" }, "select": { "message": "Sélectionner" @@ -261,10 +261,10 @@ "message": "Inclure le numéro" }, "minNumbers": { - "message": "Nombre minimum de chiffres" + "message": "Minimum de chiffres" }, "minSpecial": { - "message": "Nombre minimum de caractères spéciaux" + "message": "Minimum de caractères spéciaux" }, "avoidAmbChar": { "message": "Éviter les caractères ambigus" @@ -282,7 +282,7 @@ "message": "Aucun identifiant à afficher." }, "itemInformation": { - "message": "Information sur l'élément" + "message": "Informations sur l'élément" }, "username": { "message": "Nom d'utilisateur" @@ -303,16 +303,16 @@ "message": "Note" }, "editItem": { - "message": "Modifier l'identifiant" + "message": "Éditer l'élément" }, "folder": { "message": "Dossier" }, "deleteItem": { - "message": "Supprimer l'identifiant" + "message": "Supprimer l'élément" }, "viewItem": { - "message": "Voir l'élément" + "message": "Afficher l'élément" }, "launch": { "message": "Ouvrir" @@ -361,7 +361,7 @@ } }, "invalidMasterPassword": { - "message": "Mot de passe maître invalide" + "message": "Mot de passe principal invalide" }, "vaultTimeout": { "message": "Délai d'expiration du coffre" @@ -424,22 +424,22 @@ "message": "Adresse e-mail invalide." }, "masterPasswordRequired": { - "message": "Le mot de passe maître est requis." + "message": "Le mot de passe principal est requis." }, "confirmMasterPasswordRequired": { - "message": "Le mot de passe maître doit être entré de nouveau." + "message": "Une nouvelle saisie du mot de passe principal est nécessaire." }, "masterPasswordMinlength": { - "message": "Le mot de passe maître doit au moins contenir 8 caractères." + "message": "Le mot de passe principal doit comporter au moins 8 caractères." }, "masterPassDoesntMatch": { - "message": "La confirmation du mot de passe maître ne correspond pas." + "message": "La confirmation du mot de passe principal ne correspond pas." }, "newAccountCreated": { "message": "Votre nouveau compte a été créé ! Vous pouvez maintenant vous authentifier." }, "masterPassSent": { - "message": "Nous vous avons envoyé un e-mail contenant votre indice de mot de passe maître." + "message": "Nous vous avons envoyé un courriel avec votre indice de mot de passe principal." }, "verificationCodeRequired": { "message": "Le code de vérification est requis." @@ -485,13 +485,13 @@ "message": "Dossier ajouté" }, "changeMasterPass": { - "message": "Modifier le mot de passe maître" + "message": "Changer le mot de passe principal" }, "changeMasterPasswordConfirmation": { - "message": "Vous pouvez modifier votre mot de passe maître depuis le coffre web sur bitwarden.com. Souhaitez-vous visiter le site maintenant ?" + "message": "Vous pouvez changer votre mot de passe principal depuis le coffre web de bitwarden.com. Voulez-vous visiter le site web maintenant ?" }, "twoStepLoginConfirmation": { - "message": "L'identification en deux étapes rend votre compte plus sécurisé en vous demandant de saisir un code de sécurité depuis une application d'authentification à chaque fois que vous vous identifiez. L'identification en deux étapes peut être activée depuis le coffre web sur bitwarden.com. Souhaitez-vous visiter le site maintenant ?" + "message": "L'authentification à deux facteurs rend votre compte plus sûr en vous demandant de vérifier votre connexion avec un autre dispositif tel qu'une clé de sécurité, une application d'authentification, un SMS, un appel téléphonique ou un courriel. L'authentification à deux facteurs peut être configurée sur le coffre web de bitwarden.com. Voulez-vous visiter le site web maintenant ?" }, "editedFolder": { "message": "Dossier modifié" @@ -534,16 +534,16 @@ "message": "Nouvel URI" }, "addedItem": { - "message": "Identifiant ajouté" + "message": "Élément ajouté" }, "editedItem": { - "message": "Identifiant modifié" + "message": "Élément enregistré" }, "deleteItemConfirmation": { "message": "Êtes-vous sûr de vouloir supprimer cet identifiant ?" }, "deletedItem": { - "message": "L'élément a été envoyé dans la corbeille" + "message": "Élément envoyé à la corbeille" }, "overwritePassword": { "message": "Écraser le mot de passe" @@ -574,19 +574,19 @@ "message": "Demander à ajouter un identifiant" }, "addLoginNotificationDesc": { - "message": "La \"Notification de demande d'ajout d'identifiant\" apparait automatiquement pour vous demander d'enregistrer dans votre coffre les identifiants que vous utilisez pour la première fois." + "message": "Demander à ajouter un élément si aucun n'est trouvé dans votre coffre." }, "showCardsCurrentTab": { - "message": "Show cards on Tab page" + "message": "Afficher les cartes sur la page de l'onglet" }, "showCardsCurrentTabDesc": { - "message": "List card items on the Tab page for easy auto-fill." + "message": "Lister les éléments de la carte sur la page de l'onglet pour faciliter la saisie automatique." }, "showIdentitiesCurrentTab": { - "message": "Show identities on Tab page" + "message": "Afficher les identités sur la page Onglet courant" }, "showIdentitiesCurrentTabDesc": { - "message": "List identity items on the Tab page for easy auto-fill." + "message": "Lister les éléments d'identité sur la page de l'onglet pour faciliter la saisie automatique." }, "clearClipboard": { "message": "Effacer le presse-papiers", @@ -618,7 +618,7 @@ "message": "Afficher les options du menu contextuel" }, "contextMenuItemDesc": { - "message": "Use a secondary click to access password generation and matching logins for the website. " + "message": "Utilisez un clic secondaire pour accéder à la génération de mots de passe et les identifiants correspondants pour le site Web. " }, "defaultUriMatchDetection": { "message": "Détection de correspondance URI par défaut", @@ -668,7 +668,7 @@ "message": "Les clés de chiffrement du compte sont spécifiques à chaque utilisateur Bitwarden. Vous ne pouvez donc pas importer d'export chiffré dans un compte différent." }, "exportMasterPassword": { - "message": "Saisissez votre mot de passe maître pour exporter les données de votre coffre." + "message": "Saisissez votre mot de passe principal pour exporter les données de votre coffre." }, "shared": { "message": "Partagé" @@ -756,49 +756,49 @@ "message": "Gérer l'adhésion" }, "premiumManageAlert": { - "message": "Vous pouvez gérer votre adhésion depuis le coffre web sur bitwarden.com. Souhaitez-vous visiter le site web maintenant ?" + "message": "Vous pouvez gérer votre adhésion sur le coffre web de bitwarden.com. Voulez-vous visiter le site web maintenant ?" }, "premiumRefresh": { "message": "Actualiser l'adhésion" }, "premiumNotCurrentMember": { - "message": "Vous n'êtes actuellement pas un membre premium." + "message": "Vous n'êtes pas actuellement un membre Premium." }, "premiumSignUpAndGet": { - "message": "Devenez un membre premium et obtenez :" + "message": "Inscrivez-vous pour une adhésion Premium et obtenez :" }, "ppremiumSignUpStorage": { - "message": "1 Go de stockage de fichiers chiffrés." + "message": "1 Go de stockage chiffré pour les fichiers joints." }, "ppremiumSignUpTwoStep": { - "message": "Options d'identification en deux étapes additionnelles comme YubiKey, FIDO U2F et Duo." + "message": "Options additionnelles d'identification à deux étapes telles que YubiKey, FIDO U2F et Duo." }, "ppremiumSignUpReports": { - "message": "Rapports sur l'hygiène des mots de passe, la santé des comptes et les fuites de données pour assurer la sécurité de votre coffre." + "message": "Hygiène du mot de passe, santé du compte et rapports sur les brèches de données pour assurer la sécurité de votre coffre." }, "ppremiumSignUpTotp": { - "message": "Génération d'un code de vérification TOTP (2FA) pour les identifiants de votre coffre." + "message": "Générateur de code de vérification TOTP (2FA) pour les identifiants dans votre coffre." }, "ppremiumSignUpSupport": { - "message": "Support client prioritaire." + "message": "Assistance client prioritaire." }, "ppremiumSignUpFuture": { - "message": "Toutes les futures options premium. Prochainement !" + "message": "Toutes les futures fonctionnalités Premium. Plus à venir prochainement !" }, "premiumPurchase": { "message": "Acheter Premium" }, "premiumPurchaseAlert": { - "message": "Vous pouvez opter pour une adhésion premium depuis le coffre web sur bitwarden.com. Souhaitez-vous consulter le site web maintenant ?" + "message": "Vous pouvez acheter une adhésion Premium sur le coffre web de bitwarden.com. Voulez-vous visiter le site web maintenant ?" }, "premiumCurrentMember": { - "message": "Vous êtes un adhérent premium !" + "message": "Vous êtes un membre Premium !" }, "premiumCurrentMemberThanks": { - "message": "Merci de supporter Bitwarden." + "message": "Merci de soutenir Bitwarden." }, "premiumPrice": { - "message": "Tout pour seulement $PRICE$ /an !", + "message": "Tout pour seulement $PRICE$/an !", "placeholders": { "price": { "content": "$1", @@ -816,13 +816,13 @@ "message": "Si une clé d'authentification est rattachée à votre identifiant, alors le code de vérification TOTP est automatiquement copié dans le presse-papiers lorsque vous renseignez l'identifiant." }, "enableAutoBiometricsPrompt": { - "message": "Ask for biometrics on launch" + "message": "Demander la biométrie au lancement" }, "premiumRequired": { - "message": "Version Premium requise" + "message": "Premium requis" }, "premiumRequiredDesc": { - "message": "Une adhésion premium est requise pour utiliser cette fonctionnalité." + "message": "Une adhésion Premium est requise pour utiliser cette fonctionnalité." }, "enterVerificationCodeApp": { "message": "Saisissez le code de vérification à 6 chiffres depuis votre application d'authentification." @@ -873,13 +873,13 @@ "message": "Identifiant non disponible" }, "noTwoStepProviders": { - "message": "Ce compte dispose d'une authentification à double facteurs, cependant, aucun service d'authentification à double facteurs n'est supporté par ce navigateur web." + "message": "Ce compte dispose d'une authentification à deux facteurs mise en place, mais aucun des fournisseurs d'authentification à deux facteurs configurés ne sont pris en charge par ce navigateur web." }, "noTwoStepProviders2": { "message": "Merci d'utiliser un navigateur web compatible (comme Chrome) et/ou d'ajouter des services additionnels d'identification en deux étapes qui sont mieux supportés par les navigateurs web (comme par exemple une application d'authentification)." }, "twoStepOptions": { - "message": "Options d'identification à double facteurs" + "message": "Options d'authentification à feux facteurs" }, "recoveryCodeDesc": { "message": "Accès perdu à tous vos services d'authentification à double facteurs ? Utilisez votre code de récupération pour désactiver tous les services de double authentifications sur votre compte." @@ -966,7 +966,7 @@ "message": "Paramètre de saisie automatique par défaut pour les identifiants" }, "defaultAutoFillOnPageLoadDesc": { - "message": "Après avoir activé le remplissage automatique au chargement de la page, vous pourrez activer ou désactiver la fonctionnalité pour chaque identifiant. Ceci est le paramètre par défaut pour les identifiants qui ne sont pas configurés individuellement." + "message": "Vous pouvez désactiver la saisie automatique au chargement de la page pour les éléments de connexion individuels à partir de la vue Éditer l'élément." }, "itemAutoFillOnPageLoad": { "message": "Remplissage automatique au chargement de la page (si activé dans les options)" @@ -1034,7 +1034,7 @@ "message": "Le fait de cliquer à l'extérieur de la fenêtre pop-up pour vérifier votre e-mail avec votre code de vérification fermera cette pop-up. Voulez-vous ouvrir cette pop-up dans une nouvelle fenêtre pour qu'elle ne soit pas fermée ?" }, "popupU2fCloseMessage": { - "message": "Ce navigateur ne peut pas traiter les requêtes U2F dans cette popup. Voulez-vous ouvrir cette popup dans une nouvelle fenêtre pour que vous puissiez vous connecter en utilisant U2F ?" + "message": "Ce navigateur ne peut pas traiter les demandes U2F dans cette fenêtre popup. Voulez-vous ouvrir cette popup dans une nouvelle fenêtre afin de pouvoir vous connecter à l'aide de l'U2F ?" }, "enableFavicon": { "message": "Afficher les icônes du site web" @@ -1043,10 +1043,10 @@ "message": "Afficher une image reconnaissable à côté de chaque identifiant." }, "enableBadgeCounter": { - "message": "Show badge counter" + "message": "Afficher le compteur de badge" }, "badgeCounterDesc": { - "message": "Indicate how many logins you have for the current web page." + "message": "Indique le nombre d'identifiants que vous avez pour la page web actuelle." }, "cardholderName": { "message": "Nom du titulaire de la carte" @@ -1347,10 +1347,10 @@ "description": "ex. A weak password. Scale: Weak -> Good -> Strong" }, "weakMasterPassword": { - "message": "Mot de passe maître faible" + "message": "Mot de passe principal faible" }, "weakMasterPasswordDesc": { - "message": "Le mot de passe maître que vous avez choisi est faible. Vous devriez utiliser un mot de passe (ou une phrase de passe) fort(e) pour protéger correctement votre compte Bitwarden. Êtes-vous sûr de vouloir utiliser ce mot de passe maître ?" + "message": "Le mot de passe principal que vous avez choisi est faible. Vous devriez utiliser un mot de passe principal fort (ou une phrase de passe) pour protéger correctement votre compte Bitwarden. Êtes-vous sûr de vouloir utiliser ce mot de passe principal ?" }, "pin": { "message": "Code PIN", @@ -1378,13 +1378,13 @@ "message": "Veuillez confirmer l'utilisation de la biométrie dans l'application Bitwarden de bureau pour activer la biométrie dans le navigateur." }, "lockWithMasterPassOnRestart": { - "message": "Verrouiller avec le mot de passe maître lors du redémarrage du navigateur." + "message": "Verrouiller avec le mot de passe principal au redémarrage du navigateur" }, "selectOneCollection": { "message": "Vous devez sélectionner au moins une collection." }, "cloneItem": { - "message": "Cloner l’élément" + "message": "Cloner l'élément" }, "clone": { "message": "Cloner" @@ -1393,7 +1393,7 @@ "message": "Une ou plusieurs politiques d'organisation affectent les paramètres de votre générateur." }, "vaultTimeoutAction": { - "message": "Action lors de l'expiration du délai du coffre" + "message": "Action après délai d'expiration du coffre" }, "lock": { "message": "Verrouiller", @@ -1413,7 +1413,7 @@ "message": "Êtes-vous sûr de vouloir supprimer définitivement cet élément ?" }, "permanentlyDeletedItem": { - "message": "Élément supprimé définitivement" + "message": "Élément définitivement supprimé" }, "restoreItem": { "message": "Restaurer l'élément" @@ -1425,7 +1425,7 @@ "message": "Élément restauré" }, "vaultTimeoutLogOutConfirmation": { - "message": "La déconnexion supprimera tous les accès à votre coffre et nécessite une authentification en ligne après la période d'expiration. Êtes-vous sûr de vouloir utiliser ce paramètre?" + "message": "La déconnexion supprimera tout accès à votre coffre et nécessitera une authentification en ligne après la période d'expiration. Êtes-vous sûr de vouloir utiliser ce paramètre ?" }, "vaultTimeoutLogOutConfirmationTitle": { "message": "Confirmation de l'action lors de l'expiration du délai" @@ -1434,16 +1434,16 @@ "message": "Remplir automatiquement et enregistrer" }, "autoFillSuccessAndSavedUri": { - "message": "Élément rempli automatiquement et URI sauvegardée" + "message": "Élément saisi automatiquement et URI sauvegardé" }, "autoFillSuccess": { - "message": "Élément rempli automatiquement" + "message": "Élément saisi automatiquement" }, "setMasterPassword": { - "message": "Définir le mot de passe maître" + "message": "Définir le mot de passe principal" }, "masterPasswordPolicyInEffect": { - "message": "Une ou plusieurs politiques de l'organisation exigent que votre mot de passe maître réponde aux exigences suivantes :" + "message": "Une ou plusieurs politiques de l'organisation exigent que votre mot de passe principal réponde aux exigences suivantes :" }, "policyInEffectMinComplexity": { "message": "Score de complexité minimum de $SCORE$", @@ -1482,7 +1482,7 @@ } }, "masterPasswordPolicyRequirementsNotMet": { - "message": "Votre nouveau mot de passe maître ne répond pas aux exigences de la politique." + "message": "Votre nouveau mot de passe principal ne répond pas aux exigences en matière de politique de sécurité." }, "acceptPolicies": { "message": "En cochant cette case, vous acceptez les éléments suivants :" @@ -1563,7 +1563,7 @@ "message": "Cette action ne peut pas être effectuée dans la barre latérale, veuillez réessayer l'action dans la popup ou la nouvelle fenêtre." }, "personalOwnershipSubmitError": { - "message": "En raison d'une politique d'entreprise, il vous est interdit d'enregistrer des éléments dans votre coffre personnel. Sélectionnez une organisation dans l'option Propriété et choisissez parmi les collections disponibles." + "message": "En raison d'une politique d'entreprise, il vous est interdit d'enregistrer des éléments dans votre coffre personnel. Changez l'option Propriété au profit d'une organisation et choisissez parmi les collections disponibles." }, "personalOwnershipPolicyInEffect": { "message": "Une politique d'organisation affecte vos options de propriété." @@ -1724,7 +1724,7 @@ "message": "Le texte que vous voulez envoyer." }, "sendHideText": { - "message": "Cacher par défaut le texte de ce Send.", + "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": { @@ -1738,7 +1738,7 @@ "message": "Nouveau mot de passe" }, "sendDisabled": { - "message": "Send désactivé", + "message": "Send supprimé", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { @@ -1793,19 +1793,19 @@ "message": "Une erreur s'est produite lors de l'enregistrement de vos dates de suppression et d'expiration." }, "hideEmail": { - "message": "Cacher mon adresse e-mail aux destinataires." + "message": "Masquer mon adresse électronique aux destinataires." }, "sendOptionsPolicyInEffect": { "message": "Une ou plusieurs politiques d'organisation affectent vos options Send." }, "passwordPrompt": { - "message": "Ressaisie du mot de passe maître" + "message": "Ressaisir le mot de passe principal" }, "passwordConfirmation": { - "message": "Confirmation du mot de passe maître" + "message": "Confirmation du mot de passe principal" }, "passwordConfirmationDesc": { - "message": "Cette action est protégée. Pour continuer, veuillez ressaisir votre mot de passe maître pour vérifier votre identité." + "message": "Cette action est protégée. Pour continuer, veuillez saisir à nouveau votre mot de passe principal pour vérifier votre identité." }, "emailVerificationRequired": { "message": "Vérification de l'adresse e-mail nécessaire" @@ -1814,25 +1814,25 @@ "message": "Vous devez vérifier votre adresse e-mail pour utiliser cette fonctionnalité. Vous pouvez vérifier votre adresse e-mail dans le coffre web." }, "updatedMasterPassword": { - "message": "Mot de passe maître mis à jour" + "message": "Mot de passe principal mis à jour" }, "updateMasterPassword": { - "message": "Mettre à jour le mot de passe maître" + "message": "Mettre à jour le mot de passe principal" }, "updateMasterPasswordWarning": { - "message": "Votre mot de passe maître a récemment été modifié par un administrateur de votre organisation. Pour pouvoir accéder au coffre-fort, vous devez mettre à jour votre mot de passe maître maintenant. Poursuivre vous déconnectera de votre session actuelle, vous obligeant à vous reconnecter. Les sessions actives sur d'autres appareils peuvent rester actives jusqu'à une heure." + "message": "Votre mot de passe principal a été récemment changé par un administrateur de votre organisation. Pour pouvoir accéder au coffre, vous devez le mettre à jour maintenant. En poursuivant, vous serez déconnecté de votre session actuelle et vous devrez vous reconnecter. Les sessions actives sur d'autres appareils peuvent rester actives pendant encore une heure." }, "resetPasswordPolicyAutoEnroll": { "message": "Inscription automatique" }, "resetPasswordAutoEnrollInviteWarning": { - "message": "Cette organisation a une politique d'entreprise qui vous inscrira automatiquement à la réinitialisation du mot de passe. L'inscription permettra aux administrateurs de l'organisation de changer votre mot de passe maître." + "message": "Cette organisation dispose d'une politique d'entreprise qui vous inscrira automatiquement à la réinitialisation du mot de passe. L'inscription permettra aux administrateurs de l'organisation de changer votre mot de passe principal." }, "selectFolder": { "message": "Sélectionnez un dossier..." }, "ssoCompleteRegistration": { - "message": "Afin de terminer la connexion avec SSO, veuillez définir un mot de passe maître pour accéder à votre coffre et le protéger." + "message": "Afin de finaliser la connexion avec SSO, veuillez définir un mot de passe principal pour accéder et protéger votre coffre." }, "hours": { "message": "Heures" @@ -1869,7 +1869,7 @@ "message": "Aucun identifiant unique trouvé." }, "convertOrganizationEncryptionDesc": { - "message": "$ORGANIZATION$ utilise SSO avec un serveur de clés auto-hébergé. Un mot de passe maître n'est plus nécessaire aux membres de cette organisation pour se connecter.", + "message": "$ORGANIZATION$ utilise le SSO avec un serveur de clés auto-hébergé. Un mot de passe principal n'est plus nécessaire aux membres de cette organisation pour se connecter.", "placeholders": { "organization": { "content": "$1", @@ -1881,10 +1881,10 @@ "message": "Quitter l'organisation" }, "removeMasterPassword": { - "message": "Supprimer le mot de passe maître" + "message": "Supprimer le mot de passe principal" }, "removedMasterPassword": { - "message": "Mot de passe maître supprimé." + "message": "Mot de passe principal supprimé" }, "leaveOrganizationConfirmation": { "message": "Êtes-vous sûr·e de vouloir quitter cette organisation ?" @@ -1902,7 +1902,7 @@ "message": "Export du coffre personnel" }, "exportingPersonalVaultDescription": { - "message": "Seuls les éléments du coffre personnel associé à l'adresse e-mail $EMAIL$ seront exportés. Les éléments du coffre de l'organisation ne seront pas inclus.", + "message": "Seuls les éléments individuels du coffre associés à $EMAIL$ seront exportés. Les éléments du coffre de l'organisation ne seront pas inclus.", "placeholders": { "email": { "content": "$1", @@ -1939,10 +1939,10 @@ "message": "Aléatoire" }, "randomWord": { - "message": "Mots aléatoire" + "message": "Mot aléatoire" }, "websiteName": { - "message": "Nom du site Web" + "message": "Nom du site web" }, "whatWouldYouLikeToGenerate": { "message": "Que souhaitez-vous générer ?" @@ -1970,16 +1970,16 @@ "message": "Clé d'API" }, "ssoKeyConnectorError": { - "message": "Erreur du connecteur de clé: veuillez vérifier que le connecteur de clé est disponible et qu'il fonctionne correctement." + "message": "Erreur Key Connector : vérifiez que Key Connector est disponible et fonctionne correctement." }, "premiumSubcriptionRequired": { - "message": "Un abonnement Premium est requis" + "message": "Abonnement Premium requis" }, "organizationIsDisabled": { "message": "L'organisation est désactivée." }, "disabledOrganizationFilterError": { - "message": "Les éléments des Organisations désactivées ne sont pas accessibles. Contactez le propriétaire de votre Organisation pour obtenir de l'aide." + "message": "Les éléments des organisations suspendues ne sont pas accessibles. Contactez le propriétaire de votre organisation pour obtenir de l'aide." }, "cardBrandMir": { "message": "Mir" @@ -2000,19 +2000,19 @@ "message": "Cliquez ici" }, "environmentEditedReset": { - "message": "to reset to pre-configured settings" + "message": "pour réinitialiser aux paramètres par défaut" }, "serverVersion": { - "message": "Server version" + "message": "Version du serveur" }, "selfHosted": { "message": "Auto-hébergé" }, "thirdParty": { - "message": "Third-party" + "message": "Tierce partie" }, "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": "Connecté à l'implémentation du serveur tiers, $SERVERNAME$. Veuillez contrôler les bugs en utilisant le serveur officiel, ou rapportez-les au serveur tiers.", "placeholders": { "servername": { "content": "$1", @@ -2021,7 +2021,7 @@ } }, "lastSeenOn": { - "message": "last seen on: $DATE$", + "message": "vu pour la dernière fois le : $DATE$", "placeholders": { "date": { "content": "$1", @@ -2030,18 +2030,18 @@ } }, "loginWithMasterPassword": { - "message": "Log in with master password" + "message": "Se connecter avec le mot de passe principal" }, "loggingInAs": { - "message": "Logging in as" + "message": "Connexion en tant que" }, "notYou": { "message": "Ce n'est pas vous ?" }, "newAroundHere": { - "message": "Êtes-vous nouveau ici ?" + "message": "Nouveau par ici ?" }, "rememberEmail": { - "message": "Remember email" + "message": "Se souvenir du courriel" } } diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index b36a6f82840..760d9bd3813 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -104,7 +104,7 @@ "message": "Unlock your vault" }, "loginToVaultMenu": { - "message": "Log in to your vault" + "message": "अपने अकाउंट में लॉगिन करें" }, "autoFillInfo": { "message": "इस ब्राउज़र टैब के लिए स्वत: भरण लॉगिन उपलब्ध नहीं है।" @@ -131,10 +131,10 @@ "message": "Send a verification code to your email" }, "sendCode": { - "message": "Send code" + "message": "कोड भेजें" }, "codeSent": { - "message": "Code sent" + "message": "कोड भेजा गया है" }, "verificationCode": { "message": "Verification Code" @@ -339,7 +339,7 @@ "message": "आपका वेब ब्राउज़र आसान क्लिपबोर्ड कॉपीिंग का समर्थन नहीं करता है। इसके बजाय इसे मैन्युअल रूप से कॉपी करें।" }, "verifyIdentity": { - "message": "Verify identity" + "message": "पहचान सत्यापित करें" }, "yourVaultIsLocked": { "message": "आपकी वॉल्ट लॉक हो गई है। जारी रखने के लिए अपने मास्टर पासवर्ड को सत्यापित करें।" @@ -1133,10 +1133,10 @@ "message": "Last Name" }, "fullName": { - "message": "Full name" + "message": "पूरा नाम" }, "identityName": { - "message": "Identity Name" + "message": "पहचान का नाम" }, "company": { "message": "कंपनी" @@ -1887,7 +1887,7 @@ "message": "Master password removed" }, "leaveOrganizationConfirmation": { - "message": "Are you sure you want to leave this organization?" + "message": "क्या आप सुनिश्चित हैं कि आप इस संगठन को छोड़ना चाहते हैं?" }, "leftOrganization": { "message": "You have left the organization." @@ -1911,13 +1911,13 @@ } }, "error": { - "message": "Error" + "message": "एरर" }, "regenerateUsername": { "message": "Regenerate username" }, "generateUsername": { - "message": "Generate username" + "message": "उपयोगकर्ता नाम बनाएँ" }, "usernameType": { "message": "Username type" @@ -1942,7 +1942,7 @@ "message": "Random word" }, "websiteName": { - "message": "Website name" + "message": "वेबसाइट का नाम" }, "whatWouldYouLikeToGenerate": { "message": "What would you like to generate?" @@ -1997,7 +1997,7 @@ "message": "Settings have been edited" }, "environmentEditedClick": { - "message": "Click here" + "message": "यहाँ क्लिक करें" }, "environmentEditedReset": { "message": "to reset to pre-configured settings" @@ -2042,6 +2042,6 @@ "message": "New around here?" }, "rememberEmail": { - "message": "Remember email" + "message": "ईमेल याद रखें" } } diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 94d8f2cc9eb..8f5c4282e9b 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -2039,7 +2039,7 @@ "message": "Nisi ti?" }, "newAroundHere": { - "message": "New around here?" + "message": "Novi korisnik?" }, "rememberEmail": { "message": "Zapamti adresu e-pošte" diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index b23d1e55d6b..fddd275872b 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -47,19 +47,19 @@ "message": "Ketik ulang Kata Sandi Utama" }, "masterPassHint": { - "message": "Petunjuk Kata Sandi Utama (pilihan)" + "message": "Petunjuk Kata Sandi Utama (opsional)" }, "tab": { "message": "Tab" }, "vault": { - "message": "Vault" + "message": "Brankas" }, "myVault": { "message": "Brankas Saya" }, "allVaults": { - "message": "All vaults" + "message": "Semua brankas" }, "tools": { "message": "Alat" @@ -134,7 +134,7 @@ "message": "Kirim Kode" }, "codeSent": { - "message": "Kode Terkirim!" + "message": "Kode sudah dikirim" }, "verificationCode": { "message": "Kode Verifikasi" @@ -424,13 +424,13 @@ "message": "Alamat email tidak valid." }, "masterPasswordRequired": { - "message": "Master password is required." + "message": "Kata sandi utama diperlukan." }, "confirmMasterPasswordRequired": { "message": "Master password retype is required." }, "masterPasswordMinlength": { - "message": "Master password must be at least 8 characters long." + "message": "Kata sandi utama harus memiliki panjang setidaknya 8 karakter." }, "masterPassDoesntMatch": { "message": "Konfirmasi sandi utama tidak cocok." @@ -810,7 +810,7 @@ "message": "Penyegaran selesai" }, "enableAutoTotpCopy": { - "message": "Copy TOTP automatically" + "message": "Salin TOTP secara otomatis" }, "disableAutoTotpCopyDesc": { "message": "Jika info masuk Anda memiliki kunci autentikasi yang menyertainya, kode verifikasi TOTP akan disalin secara otomatis ke clipboard Anda setiap kali Anda mengisi info masuk secara otomatis." @@ -1312,7 +1312,7 @@ "description": "ex. Date this item was updated" }, "dateCreated": { - "message": "Created", + "message": "Dibuat", "description": "ex. Date this item was created" }, "datePasswordUpdated": { @@ -1488,7 +1488,7 @@ "message": "Dengan mencentang kotak ini, Anda menyetujui yang berikut:" }, "acceptPoliciesRequired": { - "message": "Terms of Service and Privacy Policy have not been acknowledged." + "message": "Persyaratan Layanan dan Kebijakan Privasi belum disetujui." }, "termsOfService": { "message": "Persyaratan Layanan" @@ -1911,16 +1911,16 @@ } }, "error": { - "message": "Error" + "message": "Galat" }, "regenerateUsername": { - "message": "Regenerate username" + "message": "Buat nama pengguna baru" }, "generateUsername": { - "message": "Generate username" + "message": "Buat nama pengguna baru" }, "usernameType": { - "message": "Username type" + "message": "Jenis nama pengguna" }, "plusAddressedEmail": { "message": "Plus addressed email", @@ -1936,19 +1936,19 @@ "message": "Use your domain's configured catch-all inbox." }, "random": { - "message": "Random" + "message": "Acak" }, "randomWord": { - "message": "Random word" + "message": "Kata acak" }, "websiteName": { - "message": "Website name" + "message": "Nama situs web" }, "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" + "message": "Apa yang ingin Anda buat?" }, "passwordType": { - "message": "Password type" + "message": "Jenis kata sandi" }, "service": { "message": "Service" @@ -2042,6 +2042,6 @@ "message": "New around here?" }, "rememberEmail": { - "message": "Remember email" + "message": "Ingat email" } } diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 1867971ba64..5279633cd06 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -403,7 +403,7 @@ "message": "4 ore" }, "onLocked": { - "message": "Al Blocco Computer" + "message": "Al blocco del computer" }, "onRestart": { "message": "Al riavvio del browser" diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json new file mode 100644 index 00000000000..ba083bf1e36 --- /dev/null +++ b/apps/browser/src/_locales/ne/messages.json @@ -0,0 +1,2047 @@ +{ + "appName": { + "message": "Bitwarden" + }, + "extName": { + "message": "Bitwarden - Free Password Manager", + "description": "Extension name, MUST be less than 40 characters (Safari restriction)" + }, + "extDesc": { + "message": "A secure and free password manager for all of your devices.", + "description": "Extension description" + }, + "loginOrCreateNewAccount": { + "message": "Log in or create a new account to access your secure vault." + }, + "createAccount": { + "message": "Create account" + }, + "login": { + "message": "Log in" + }, + "enterpriseSingleSignOn": { + "message": "Enterprise single sign-on" + }, + "cancel": { + "message": "Cancel" + }, + "close": { + "message": "Close" + }, + "submit": { + "message": "Submit" + }, + "emailAddress": { + "message": "Email address" + }, + "masterPass": { + "message": "Master password" + }, + "masterPassDesc": { + "message": "The master password is the password you use to access your vault. It is very important that you do not forget your master password. There is no way to recover the password in the event that you forget it." + }, + "masterPassHintDesc": { + "message": "A master password hint can help you remember your password if you forget it." + }, + "reTypeMasterPass": { + "message": "Re-type master password" + }, + "masterPassHint": { + "message": "Master password hint (optional)" + }, + "tab": { + "message": "Tab" + }, + "vault": { + "message": "Vault" + }, + "myVault": { + "message": "My vault" + }, + "allVaults": { + "message": "All vaults" + }, + "tools": { + "message": "Tools" + }, + "settings": { + "message": "Settings" + }, + "currentTab": { + "message": "Current tab" + }, + "copyPassword": { + "message": "Copy password" + }, + "copyNote": { + "message": "Copy note" + }, + "copyUri": { + "message": "Copy URI" + }, + "copyUsername": { + "message": "Copy username" + }, + "copyNumber": { + "message": "Copy number" + }, + "copySecurityCode": { + "message": "Copy security code" + }, + "autoFill": { + "message": "Auto-fill" + }, + "generatePasswordCopied": { + "message": "Generate password (copied)" + }, + "copyElementIdentifier": { + "message": "Copy custom field name" + }, + "noMatchingLogins": { + "message": "No matching logins" + }, + "unlockVaultMenu": { + "message": "Unlock your vault" + }, + "loginToVaultMenu": { + "message": "Log in to your vault" + }, + "autoFillInfo": { + "message": "There are no logins available to auto-fill for the current browser tab." + }, + "addLogin": { + "message": "Add a login" + }, + "addItem": { + "message": "Add item" + }, + "passwordHint": { + "message": "Password hint" + }, + "enterEmailToGetHint": { + "message": "Enter your account email address to receive your master password hint." + }, + "getMasterPasswordHint": { + "message": "Get master password hint" + }, + "continue": { + "message": "Continue" + }, + "sendVerificationCode": { + "message": "Send a verification code to your email" + }, + "sendCode": { + "message": "Send code" + }, + "codeSent": { + "message": "Code sent" + }, + "verificationCode": { + "message": "Verification code" + }, + "confirmIdentity": { + "message": "Confirm your identity to continue." + }, + "account": { + "message": "Account" + }, + "changeMasterPassword": { + "message": "Change master password" + }, + "fingerprintPhrase": { + "message": "Fingerprint phrase", + "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." + }, + "yourAccountsFingerprint": { + "message": "Your account's fingerprint phrase", + "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." + }, + "twoStepLogin": { + "message": "Two-step login" + }, + "logOut": { + "message": "Log out" + }, + "about": { + "message": "About" + }, + "version": { + "message": "Version" + }, + "save": { + "message": "Save" + }, + "move": { + "message": "Move" + }, + "addFolder": { + "message": "Add folder" + }, + "name": { + "message": "Name" + }, + "editFolder": { + "message": "Edit folder" + }, + "deleteFolder": { + "message": "Delete folder" + }, + "folders": { + "message": "Folders" + }, + "noFolders": { + "message": "There are no folders to list." + }, + "helpFeedback": { + "message": "Help & feedback" + }, + "sync": { + "message": "Sync" + }, + "syncVaultNow": { + "message": "Sync vault now" + }, + "lastSync": { + "message": "Last sync:" + }, + "passGen": { + "message": "Password generator" + }, + "generator": { + "message": "Generator", + "description": "Short for 'Password Generator'." + }, + "passGenInfo": { + "message": "Automatically generate strong, unique passwords for your logins." + }, + "bitWebVault": { + "message": "Bitwarden web vault" + }, + "importItems": { + "message": "Import items" + }, + "select": { + "message": "Select" + }, + "generatePassword": { + "message": "Generate password" + }, + "regeneratePassword": { + "message": "Regenerate password" + }, + "options": { + "message": "Options" + }, + "length": { + "message": "Length" + }, + "uppercase": { + "message": "Uppercase (A-Z)" + }, + "lowercase": { + "message": "Lowercase (a-z)" + }, + "numbers": { + "message": "Numbers (0-9)" + }, + "specialCharacters": { + "message": "Special characters (!@#$%^&*)" + }, + "numWords": { + "message": "Number of words" + }, + "wordSeparator": { + "message": "Word separator" + }, + "capitalize": { + "message": "Capitalize", + "description": "Make the first letter of a work uppercase." + }, + "includeNumber": { + "message": "Include number" + }, + "minNumbers": { + "message": "Minimum numbers" + }, + "minSpecial": { + "message": "Minimum special" + }, + "avoidAmbChar": { + "message": "Avoid ambiguous characters" + }, + "searchVault": { + "message": "Search vault" + }, + "edit": { + "message": "Edit" + }, + "view": { + "message": "View" + }, + "noItemsInList": { + "message": "There are no items to list." + }, + "itemInformation": { + "message": "Item information" + }, + "username": { + "message": "Username" + }, + "password": { + "message": "Password" + }, + "passphrase": { + "message": "Passphrase" + }, + "favorite": { + "message": "Favorite" + }, + "notes": { + "message": "Notes" + }, + "note": { + "message": "Note" + }, + "editItem": { + "message": "Edit item" + }, + "folder": { + "message": "Folder" + }, + "deleteItem": { + "message": "Delete item" + }, + "viewItem": { + "message": "View item" + }, + "launch": { + "message": "Launch" + }, + "website": { + "message": "Website" + }, + "toggleVisibility": { + "message": "Toggle visibility" + }, + "manage": { + "message": "Manage" + }, + "other": { + "message": "Other" + }, + "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." + }, + "verifyIdentity": { + "message": "Verify identity" + }, + "yourVaultIsLocked": { + "message": "Your vault is locked. Verify your identity to continue." + }, + "unlock": { + "message": "Unlock" + }, + "loggedInAsOn": { + "message": "Logged in as $EMAIL$ on $HOSTNAME$.", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + }, + "hostname": { + "content": "$2", + "example": "bitwarden.com" + } + } + }, + "invalidMasterPassword": { + "message": "Invalid master password" + }, + "vaultTimeout": { + "message": "Vault timeout" + }, + "lockNow": { + "message": "Lock now" + }, + "immediately": { + "message": "Immediately" + }, + "tenSeconds": { + "message": "10 seconds" + }, + "twentySeconds": { + "message": "20 seconds" + }, + "thirtySeconds": { + "message": "30 seconds" + }, + "oneMinute": { + "message": "1 minute" + }, + "twoMinutes": { + "message": "2 minutes" + }, + "fiveMinutes": { + "message": "5 minutes" + }, + "fifteenMinutes": { + "message": "15 minutes" + }, + "thirtyMinutes": { + "message": "30 minutes" + }, + "oneHour": { + "message": "1 hour" + }, + "fourHours": { + "message": "4 hours" + }, + "onLocked": { + "message": "On system lock" + }, + "onRestart": { + "message": "On browser restart" + }, + "never": { + "message": "Never" + }, + "security": { + "message": "Security" + }, + "errorOccurred": { + "message": "An error has occurred" + }, + "emailRequired": { + "message": "Email address is required." + }, + "invalidEmail": { + "message": "Invalid email address." + }, + "masterPasswordRequired": { + "message": "Master password is required." + }, + "confirmMasterPasswordRequired": { + "message": "Master password retype is required." + }, + "masterPasswordMinlength": { + "message": "Master password must be at least 8 characters long." + }, + "masterPassDoesntMatch": { + "message": "Master password confirmation does not match." + }, + "newAccountCreated": { + "message": "Your new account has been created! You may now log in." + }, + "masterPassSent": { + "message": "We've sent you an email with your master password hint." + }, + "verificationCodeRequired": { + "message": "Verification code is required." + }, + "invalidVerificationCode": { + "message": "Invalid verification code" + }, + "valueCopied": { + "message": "$VALUE$ copied", + "description": "Value has been copied to the clipboard.", + "placeholders": { + "value": { + "content": "$1", + "example": "Password" + } + } + }, + "autofillError": { + "message": "Unable to auto-fill the selected item on this page. Copy and paste the information instead." + }, + "loggedOut": { + "message": "Logged out" + }, + "loginExpired": { + "message": "Your login session has expired." + }, + "logOutConfirmation": { + "message": "Are you sure you want to log out?" + }, + "yes": { + "message": "Yes" + }, + "no": { + "message": "No" + }, + "unexpectedError": { + "message": "An unexpected error has occurred." + }, + "nameRequired": { + "message": "Name is required." + }, + "addedFolder": { + "message": "Folder added" + }, + "changeMasterPass": { + "message": "Change master password" + }, + "changeMasterPasswordConfirmation": { + "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "twoStepLoginConfirmation": { + "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "editedFolder": { + "message": "Folder saved" + }, + "deleteFolderConfirmation": { + "message": "Are you sure you want to delete this folder?" + }, + "deletedFolder": { + "message": "Folder deleted" + }, + "gettingStartedTutorial": { + "message": "Getting started tutorial" + }, + "gettingStartedTutorialVideo": { + "message": "Watch our getting started tutorial to learn how to get the most out of the browser extension." + }, + "syncingComplete": { + "message": "Syncing complete" + }, + "syncingFailed": { + "message": "Syncing failed" + }, + "passwordCopied": { + "message": "Password copied" + }, + "uri": { + "message": "URI" + }, + "uriPosition": { + "message": "URI $POSITION$", + "description": "A listing of URIs. Ex: URI 1, URI 2, URI 3, etc.", + "placeholders": { + "position": { + "content": "$1", + "example": "2" + } + } + }, + "newUri": { + "message": "New URI" + }, + "addedItem": { + "message": "Item added" + }, + "editedItem": { + "message": "Item saved" + }, + "deleteItemConfirmation": { + "message": "Do you really want to send to the trash?" + }, + "deletedItem": { + "message": "Item sent to trash" + }, + "overwritePassword": { + "message": "Overwrite password" + }, + "overwritePasswordConfirmation": { + "message": "Are you sure you want to overwrite the current password?" + }, + "overwriteUsername": { + "message": "Overwrite username" + }, + "overwriteUsernameConfirmation": { + "message": "Are you sure you want to overwrite the current username?" + }, + "searchFolder": { + "message": "Search folder" + }, + "searchCollection": { + "message": "Search collection" + }, + "searchType": { + "message": "Search type" + }, + "noneFolder": { + "message": "No folder", + "description": "This is the folder for uncategorized items" + }, + "enableAddLoginNotification": { + "message": "Ask to add login" + }, + "addLoginNotificationDesc": { + "message": "Ask to add an item if one isn't found in your vault." + }, + "showCardsCurrentTab": { + "message": "Show cards on Tab page" + }, + "showCardsCurrentTabDesc": { + "message": "List card items on the Tab page for easy auto-fill." + }, + "showIdentitiesCurrentTab": { + "message": "Show identities on Tab page" + }, + "showIdentitiesCurrentTabDesc": { + "message": "List identity items on the Tab page for easy auto-fill." + }, + "clearClipboard": { + "message": "Clear clipboard", + "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.", + "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?" + }, + "notificationAddSave": { + "message": "Save" + }, + "enableChangedPasswordNotification": { + "message": "Ask to update existing login" + }, + "changedPasswordNotificationDesc": { + "message": "Ask to update a login's password when a change is detected on a website." + }, + "notificationChangeDesc": { + "message": "Do you want to update this password in Bitwarden?" + }, + "notificationChangeSave": { + "message": "Update" + }, + "enableContextMenuItem": { + "message": "Show context menu options" + }, + "contextMenuItemDesc": { + "message": "Use a secondary click to access password generation and matching logins for the website. " + }, + "defaultUriMatchDetection": { + "message": "Default URI match detection", + "description": "Default URI match detection for auto-fill." + }, + "defaultUriMatchDetectionDesc": { + "message": "Choose the default way that URI match detection is handled for logins when performing actions such as auto-fill." + }, + "theme": { + "message": "Theme" + }, + "themeDesc": { + "message": "Change the application's color theme." + }, + "dark": { + "message": "Dark", + "description": "Dark color" + }, + "light": { + "message": "Light", + "description": "Light color" + }, + "solarizedDark": { + "message": "Solarized dark", + "description": "'Solarized' is a noun and the name of a color scheme. It should not be translated." + }, + "exportVault": { + "message": "Export vault" + }, + "fileFormat": { + "message": "File format" + }, + "warning": { + "message": "WARNING", + "description": "WARNING (should stay in capitalized letters if the language permits)" + }, + "confirmVaultExport": { + "message": "Confirm vault export" + }, + "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." + }, + "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." + }, + "encExportAccountWarningDesc": { + "message": "Account encryption keys are unique to each Bitwarden user account, so you can't import an encrypted export into a different account." + }, + "exportMasterPassword": { + "message": "Enter your master password to export your vault data." + }, + "shared": { + "message": "Shared" + }, + "learnOrg": { + "message": "Learn about organizations" + }, + "learnOrgConfirmation": { + "message": "Bitwarden allows you to share your vault items with others by using an organization. Would you like to visit the bitwarden.com website to learn more?" + }, + "moveToOrganization": { + "message": "Move to organization" + }, + "share": { + "message": "Share" + }, + "movedItemToOrg": { + "message": "$ITEMNAME$ moved to $ORGNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret Item" + }, + "orgname": { + "content": "$2", + "example": "Company Name" + } + } + }, + "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." + }, + "learnMore": { + "message": "Learn more" + }, + "authenticatorKeyTotp": { + "message": "Authenticator key (TOTP)" + }, + "verificationCodeTotp": { + "message": "Verification code (TOTP)" + }, + "copyVerificationCode": { + "message": "Copy verification code" + }, + "attachments": { + "message": "Attachments" + }, + "deleteAttachment": { + "message": "Delete attachment" + }, + "deleteAttachmentConfirmation": { + "message": "Are you sure you want to delete this attachment?" + }, + "deletedAttachment": { + "message": "Attachment deleted" + }, + "newAttachment": { + "message": "Add new attachment" + }, + "noAttachments": { + "message": "No attachments." + }, + "attachmentSaved": { + "message": "Attachment saved" + }, + "file": { + "message": "File" + }, + "selectFile": { + "message": "Select a file" + }, + "maxFileSize": { + "message": "Maximum file size is 500 MB." + }, + "featureUnavailable": { + "message": "Feature unavailable" + }, + "updateKey": { + "message": "You cannot use this feature until you update your encryption key." + }, + "premiumMembership": { + "message": "Premium membership" + }, + "premiumManage": { + "message": "Manage membership" + }, + "premiumManageAlert": { + "message": "You can manage your membership on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "premiumRefresh": { + "message": "Refresh membership" + }, + "premiumNotCurrentMember": { + "message": "You are not currently a Premium member." + }, + "premiumSignUpAndGet": { + "message": "Sign up for a Premium membership and get:" + }, + "ppremiumSignUpStorage": { + "message": "1 GB encrypted storage for file attachments." + }, + "ppremiumSignUpTwoStep": { + "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + }, + "ppremiumSignUpReports": { + "message": "Password hygiene, account health, and data breach reports to keep your vault safe." + }, + "ppremiumSignUpTotp": { + "message": "TOTP verification code (2FA) generator for logins in your vault." + }, + "ppremiumSignUpSupport": { + "message": "Priority customer support." + }, + "ppremiumSignUpFuture": { + "message": "All future Premium features. More coming soon!" + }, + "premiumPurchase": { + "message": "Purchase Premium" + }, + "premiumPurchaseAlert": { + "message": "You can purchase Premium membership on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "premiumCurrentMember": { + "message": "You are a Premium member!" + }, + "premiumCurrentMemberThanks": { + "message": "Thank you for supporting Bitwarden." + }, + "premiumPrice": { + "message": "All for just $PRICE$ /year!", + "placeholders": { + "price": { + "content": "$1", + "example": "$10" + } + } + }, + "refreshComplete": { + "message": "Refresh complete" + }, + "enableAutoTotpCopy": { + "message": "Copy TOTP automatically" + }, + "disableAutoTotpCopyDesc": { + "message": "If a login has an authenticator key, copy the TOTP verification code to your clip-board when you auto-fill the login." + }, + "enableAutoBiometricsPrompt": { + "message": "Ask for biometrics on launch" + }, + "premiumRequired": { + "message": "Premium required" + }, + "premiumRequiredDesc": { + "message": "A Premium membership is required to use this feature." + }, + "enterVerificationCodeApp": { + "message": "Enter the 6 digit verification code from your authenticator app." + }, + "enterVerificationCodeEmail": { + "message": "Enter the 6 digit verification code that was emailed to $EMAIL$.", + "placeholders": { + "email": { + "content": "$1", + "example": "example@gmail.com" + } + } + }, + "verificationCodeEmailSent": { + "message": "Verification email sent to $EMAIL$.", + "placeholders": { + "email": { + "content": "$1", + "example": "example@gmail.com" + } + } + }, + "rememberMe": { + "message": "Remember me" + }, + "sendVerificationCodeEmailAgain": { + "message": "Send verification code email again" + }, + "useAnotherTwoStepMethod": { + "message": "Use another two-step login method" + }, + "insertYubiKey": { + "message": "Insert your YubiKey into your computer's USB port, then touch its button." + }, + "insertU2f": { + "message": "Insert your security key into your computer's USB port. If it has a button, touch it." + }, + "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." + }, + "webAuthnNewTabOpen": { + "message": "Open new tab" + }, + "webAuthnAuthenticate": { + "message": "Authenticate WebAuthn" + }, + "loginUnavailable": { + "message": "Login unavailable" + }, + "noTwoStepProviders": { + "message": "This account has two-step login set up, however, none of the configured two-step providers are supported by this web browser." + }, + "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)." + }, + "twoStepOptions": { + "message": "Two-step login options" + }, + "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." + }, + "recoveryCodeTitle": { + "message": "Recovery code" + }, + "authenticatorAppTitle": { + "message": "Authenticator app" + }, + "authenticatorAppDesc": { + "message": "Use an authenticator app (such as Authy or Google Authenticator) to generate time-based verification codes.", + "description": "'Authy' and 'Google Authenticator' are product names and should not be translated." + }, + "yubiKeyTitle": { + "message": "YubiKey OTP Security Key" + }, + "yubiKeyDesc": { + "message": "Use a YubiKey to access your account. Works with YubiKey 4, 4 Nano, 4C, and NEO devices." + }, + "duoDesc": { + "message": "Verify with Duo Security using the Duo Mobile app, SMS, phone call, or U2F security key.", + "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.", + "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." + }, + "emailTitle": { + "message": "Email" + }, + "emailDesc": { + "message": "Verification codes will be emailed to you." + }, + "selfHostedEnvironment": { + "message": "Self-hosted environment" + }, + "selfHostedEnvironmentFooter": { + "message": "Specify the base URL of your on-premises hosted Bitwarden installation." + }, + "customEnvironment": { + "message": "Custom environment" + }, + "customEnvironmentFooter": { + "message": "For advanced users. You can specify the base URL of each service independently." + }, + "baseUrl": { + "message": "Server URL" + }, + "apiUrl": { + "message": "API Server URL" + }, + "webVaultUrl": { + "message": "Web vault server URL" + }, + "identityUrl": { + "message": "Identity server URL" + }, + "notificationsUrl": { + "message": "Notifications server URL" + }, + "iconsUrl": { + "message": "Icons server URL" + }, + "environmentSaved": { + "message": "Environment URLs saved" + }, + "enableAutoFillOnPageLoad": { + "message": "Auto-fill on page load" + }, + "enableAutoFillOnPageLoadDesc": { + "message": "If a login form is detected, auto-fill when the web page loads." + }, + "experimentalFeature": { + "message": "This is currently an experimental feature. Use at your own risk." + }, + "defaultAutoFillOnPageLoad": { + "message": "Default autofill setting for login items" + }, + "defaultAutoFillOnPageLoadDesc": { + "message": "You can turn off auto-fill on page load for individual login items from the item's Edit view." + }, + "itemAutoFillOnPageLoad": { + "message": "Auto-fill on page load (if set up in Options)" + }, + "autoFillOnPageLoadUseDefault": { + "message": "Use default setting" + }, + "autoFillOnPageLoadYes": { + "message": "Auto-fill on page load" + }, + "autoFillOnPageLoadNo": { + "message": "Do not auto-fill on page load" + }, + "commandOpenPopup": { + "message": "Open vault popup" + }, + "commandOpenSidebar": { + "message": "Open vault in sidebar" + }, + "commandAutofillDesc": { + "message": "Auto-fill the last used login for the current website" + }, + "commandGeneratePasswordDesc": { + "message": "Generate and copy a new random password to the clipboard" + }, + "commandLockVaultDesc": { + "message": "Lock the vault" + }, + "privateModeWarning": { + "message": "Private mode support is experimental and some features are limited." + }, + "customFields": { + "message": "Custom fields" + }, + "copyValue": { + "message": "Copy value" + }, + "value": { + "message": "Value" + }, + "newCustomField": { + "message": "New custom field" + }, + "dragToSort": { + "message": "Drag to sort" + }, + "cfTypeText": { + "message": "Text" + }, + "cfTypeHidden": { + "message": "Hidden" + }, + "cfTypeBoolean": { + "message": "Boolean" + }, + "cfTypeLinked": { + "message": "Linked", + "description": "This describes a field that is 'linked' (tied) to another field." + }, + "linkedValue": { + "message": "Linked value", + "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?" + }, + "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?" + }, + "enableFavicon": { + "message": "Show website icons" + }, + "faviconDesc": { + "message": "Show a recognizable image next to each login." + }, + "enableBadgeCounter": { + "message": "Show badge counter" + }, + "badgeCounterDesc": { + "message": "Indicate how many logins you have for the current web page." + }, + "cardholderName": { + "message": "Cardholder name" + }, + "number": { + "message": "Number" + }, + "brand": { + "message": "Brand" + }, + "expirationMonth": { + "message": "Expiration month" + }, + "expirationYear": { + "message": "Expiration year" + }, + "expiration": { + "message": "Expiration" + }, + "january": { + "message": "January" + }, + "february": { + "message": "February" + }, + "march": { + "message": "March" + }, + "april": { + "message": "April" + }, + "may": { + "message": "May" + }, + "june": { + "message": "June" + }, + "july": { + "message": "July" + }, + "august": { + "message": "August" + }, + "september": { + "message": "September" + }, + "october": { + "message": "October" + }, + "november": { + "message": "November" + }, + "december": { + "message": "December" + }, + "securityCode": { + "message": "Security code" + }, + "ex": { + "message": "ex." + }, + "title": { + "message": "Title" + }, + "mr": { + "message": "Mr" + }, + "mrs": { + "message": "Mrs" + }, + "ms": { + "message": "Ms" + }, + "dr": { + "message": "Dr" + }, + "firstName": { + "message": "First name" + }, + "middleName": { + "message": "Middle name" + }, + "lastName": { + "message": "Last name" + }, + "fullName": { + "message": "Full name" + }, + "identityName": { + "message": "Identity name" + }, + "company": { + "message": "Company" + }, + "ssn": { + "message": "Social Security number" + }, + "passportNumber": { + "message": "Passport number" + }, + "licenseNumber": { + "message": "License number" + }, + "email": { + "message": "Email" + }, + "phone": { + "message": "Phone" + }, + "address": { + "message": "Address" + }, + "address1": { + "message": "Address 1" + }, + "address2": { + "message": "Address 2" + }, + "address3": { + "message": "Address 3" + }, + "cityTown": { + "message": "City / Town" + }, + "stateProvince": { + "message": "State / Province" + }, + "zipPostalCode": { + "message": "Zip / Postal code" + }, + "country": { + "message": "Country" + }, + "type": { + "message": "Type" + }, + "typeLogin": { + "message": "Login" + }, + "typeLogins": { + "message": "Logins" + }, + "typeSecureNote": { + "message": "Secure note" + }, + "typeCard": { + "message": "Card" + }, + "typeIdentity": { + "message": "Identity" + }, + "passwordHistory": { + "message": "Password history" + }, + "back": { + "message": "Back" + }, + "collections": { + "message": "Collections" + }, + "favorites": { + "message": "Favorites" + }, + "popOutNewWindow": { + "message": "Pop out to a new window" + }, + "refresh": { + "message": "Refresh" + }, + "cards": { + "message": "Cards" + }, + "identities": { + "message": "Identities" + }, + "logins": { + "message": "Logins" + }, + "secureNotes": { + "message": "Secure notes" + }, + "clear": { + "message": "Clear", + "description": "To clear something out. example: To clear browser history." + }, + "checkPassword": { + "message": "Check if password has been exposed." + }, + "passwordExposed": { + "message": "This password has been exposed $VALUE$ time(s) in data breaches. You should change it.", + "placeholders": { + "value": { + "content": "$1", + "example": "2" + } + } + }, + "passwordSafe": { + "message": "This password was not found in any known data breaches. It should be safe to use." + }, + "baseDomain": { + "message": "Base domain", + "description": "Domain name. Ex. website.com" + }, + "domainName": { + "message": "Domain name", + "description": "Domain name. Ex. website.com" + }, + "host": { + "message": "Host", + "description": "A URL's host value. For example, the host of https://sub.domain.com:443 is 'sub.domain.com:443'." + }, + "exact": { + "message": "Exact" + }, + "startsWith": { + "message": "Starts with" + }, + "regEx": { + "message": "Regular expression", + "description": "A programming term, also known as 'RegEx'." + }, + "matchDetection": { + "message": "Match detection", + "description": "URI match detection for auto-fill." + }, + "defaultMatchDetection": { + "message": "Default match detection", + "description": "Default URI match detection for auto-fill." + }, + "toggleOptions": { + "message": "Toggle options" + }, + "toggleCurrentUris": { + "message": "Toggle current URIs", + "description": "Toggle the display of the URIs of the currently open tabs in the browser." + }, + "currentUri": { + "message": "Current URI", + "description": "The URI of one of the current open tabs in the browser." + }, + "organization": { + "message": "Organization", + "description": "An entity of multiple related people (ex. a team or business organization)." + }, + "types": { + "message": "Types" + }, + "allItems": { + "message": "All items" + }, + "noPasswordsInList": { + "message": "There are no passwords to list." + }, + "remove": { + "message": "Remove" + }, + "default": { + "message": "Default" + }, + "dateUpdated": { + "message": "Updated", + "description": "ex. Date this item was updated" + }, + "dateCreated": { + "message": "Created", + "description": "ex. Date this item was created" + }, + "datePasswordUpdated": { + "message": "Password updated", + "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." + }, + "noOrganizationsList": { + "message": "You do not belong to any organizations. Organizations allow you to securely share items with other users." + }, + "noCollectionsInList": { + "message": "There are no collections to list." + }, + "ownership": { + "message": "Ownership" + }, + "whoOwnsThisItem": { + "message": "Who owns this item?" + }, + "strong": { + "message": "Strong", + "description": "ex. A strong password. Scale: Weak -> Good -> Strong" + }, + "good": { + "message": "Good", + "description": "ex. A good password. Scale: Weak -> Good -> Strong" + }, + "weak": { + "message": "Weak", + "description": "ex. A weak password. Scale: Weak -> Good -> Strong" + }, + "weakMasterPassword": { + "message": "Weak master password" + }, + "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?" + }, + "pin": { + "message": "PIN", + "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." + }, + "unlockWithPin": { + "message": "Unlock with 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." + }, + "pinRequired": { + "message": "PIN code is required." + }, + "invalidPin": { + "message": "Invalid PIN code." + }, + "unlockWithBiometrics": { + "message": "Unlock with biometrics" + }, + "awaitDesktop": { + "message": "Awaiting confirmation from desktop" + }, + "awaitDesktopDesc": { + "message": "Please confirm using biometrics in the Bitwarden desktop application to set up biometrics for browser." + }, + "lockWithMasterPassOnRestart": { + "message": "Lock with master password on browser restart" + }, + "selectOneCollection": { + "message": "You must select at least one collection." + }, + "cloneItem": { + "message": "Clone item" + }, + "clone": { + "message": "Clone" + }, + "passwordGeneratorPolicyInEffect": { + "message": "One or more organization policies are affecting your generator settings." + }, + "vaultTimeoutAction": { + "message": "Vault timeout action" + }, + "lock": { + "message": "Lock", + "description": "Verb form: to make secure or inaccesible by" + }, + "trash": { + "message": "Trash", + "description": "Noun: a special folder to hold deleted items" + }, + "searchTrash": { + "message": "Search trash" + }, + "permanentlyDeleteItem": { + "message": "Permanently delete item" + }, + "permanentlyDeleteItemConfirmation": { + "message": "Are you sure you want to permanently delete this item?" + }, + "permanentlyDeletedItem": { + "message": "Item permanently deleted" + }, + "restoreItem": { + "message": "Restore item" + }, + "restoreItemConfirmation": { + "message": "Are you sure you want to restore this item?" + }, + "restoredItem": { + "message": "Item restored" + }, + "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?" + }, + "vaultTimeoutLogOutConfirmationTitle": { + "message": "Timeout action confirmation" + }, + "autoFillAndSave": { + "message": "Auto-fill and save" + }, + "autoFillSuccessAndSavedUri": { + "message": "Item auto-filled and URI saved" + }, + "autoFillSuccess": { + "message": "Item auto-filled " + }, + "setMasterPassword": { + "message": "Set master password" + }, + "masterPasswordPolicyInEffect": { + "message": "One or more organization policies require your master password to meet the following requirements:" + }, + "policyInEffectMinComplexity": { + "message": "Minimum complexity score of $SCORE$", + "placeholders": { + "score": { + "content": "$1", + "example": "4" + } + } + }, + "policyInEffectMinLength": { + "message": "Minimum length of $LENGTH$", + "placeholders": { + "length": { + "content": "$1", + "example": "14" + } + } + }, + "policyInEffectUppercase": { + "message": "Contain one or more uppercase characters" + }, + "policyInEffectLowercase": { + "message": "Contain one or more lowercase characters" + }, + "policyInEffectNumbers": { + "message": "Contain one or more numbers" + }, + "policyInEffectSpecial": { + "message": "Contain one or more of the following special characters $CHARS$", + "placeholders": { + "chars": { + "content": "$1", + "example": "!@#$%^&*" + } + } + }, + "masterPasswordPolicyRequirementsNotMet": { + "message": "Your new master password does not meet the policy requirements." + }, + "acceptPolicies": { + "message": "By checking this box you agree to the following:" + }, + "acceptPoliciesRequired": { + "message": "Terms of Service and Privacy Policy have not been acknowledged." + }, + "termsOfService": { + "message": "Terms of Service" + }, + "privacyPolicy": { + "message": "Privacy Policy" + }, + "hintEqualsPassword": { + "message": "Your password hint cannot be the same as your password." + }, + "ok": { + "message": "Ok" + }, + "desktopSyncVerificationTitle": { + "message": "Desktop sync verification" + }, + "desktopIntegrationVerificationText": { + "message": "Please verify that the desktop application shows this fingerprint: " + }, + "desktopIntegrationDisabledTitle": { + "message": "Browser integration is not set up" + }, + "desktopIntegrationDisabledDesc": { + "message": "Browser integration is not set up in the Bitwarden desktop application. Please set it up in the settings within the desktop application." + }, + "startDesktopTitle": { + "message": "Start the Bitwarden desktop application" + }, + "startDesktopDesc": { + "message": "The Bitwarden desktop application needs to be started before unlock with biometrics can be used." + }, + "errorEnableBiometricTitle": { + "message": "Unable to set up biometrics" + }, + "errorEnableBiometricDesc": { + "message": "Action was canceled by the desktop application" + }, + "nativeMessagingInvalidEncryptionDesc": { + "message": "Desktop application invalidated the secure communication channel. Please retry this operation" + }, + "nativeMessagingInvalidEncryptionTitle": { + "message": "Desktop communication interrupted" + }, + "nativeMessagingWrongUserDesc": { + "message": "The desktop application is logged into a different account. Please ensure both applications are logged into the same account." + }, + "nativeMessagingWrongUserTitle": { + "message": "Account missmatch" + }, + "biometricsNotEnabledTitle": { + "message": "Biometrics not set up" + }, + "biometricsNotEnabledDesc": { + "message": "Browser biometrics requires desktop biometric to be set up in the settings first." + }, + "biometricsNotSupportedTitle": { + "message": "Biometrics not supported" + }, + "biometricsNotSupportedDesc": { + "message": "Browser biometrics is not supported on this device." + }, + "nativeMessaginPermissionErrorTitle": { + "message": "Permission not provided" + }, + "nativeMessaginPermissionErrorDesc": { + "message": "Without permission to communicate with the Bitwarden Desktop Application we cannot provide biometrics in the browser extension. Please try again." + }, + "nativeMessaginPermissionSidebarTitle": { + "message": "Permission request error" + }, + "nativeMessaginPermissionSidebarDesc": { + "message": "This action cannot be done in the sidebar, please retry the action in the popup or popout." + }, + "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." + }, + "personalOwnershipPolicyInEffect": { + "message": "An organization policy is affecting your ownership options." + }, + "excludedDomains": { + "message": "Excluded domains" + }, + "excludedDomainsDesc": { + "message": "Bitwarden will not ask to save login details for these domains. You must refresh the page for changes to take effect." + }, + "excludedDomainsInvalidDomain": { + "message": "$DOMAIN$ is not a valid domain", + "placeholders": { + "domain": { + "content": "$1", + "example": "googlecom" + } + } + }, + "send": { + "message": "Send", + "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" + }, + "sendTypeFile": { + "message": "File" + }, + "allSends": { + "message": "All Sends", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "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" + }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "removePassword": { + "message": "Remove Password" + }, + "delete": { + "message": "Delete" + }, + "removedPassword": { + "message": "Password removed" + }, + "deletedSend": { + "message": "Send deleted", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendLink": { + "message": "Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "disabled": { + "message": "Disabled" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, + "deleteSend": { + "message": "Delete 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?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "editSend": { + "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." + }, + "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" + }, + "days": { + "message": "$DAYS$ days", + "placeholders": { + "days": { + "content": "$1", + "example": "2" + } + } + }, + "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." + }, + "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." + }, + "newPassword": { + "message": "New password" + }, + "sendDisabled": { + "message": "Send removed", + "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.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "createdSend": { + "message": "Send created", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "editedSend": { + "message": "Send saved", + "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." + }, + "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." + }, + "sendSafariFileWarning": { + "message": "In order to choose a file using Safari, pop out to a new window by clicking this banner." + }, + "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." + }, + "deletionDateIsInvalid": { + "message": "The deletion date provided is not valid." + }, + "expirationDateAndTimeRequired": { + "message": "An expiration date and time are required." + }, + "deletionDateAndTimeRequired": { + "message": "A deletion date and time are required." + }, + "dateParsingError": { + "message": "There was an error saving your deletion and expiration dates." + }, + "hideEmail": { + "message": "Hide my email address from recipients." + }, + "sendOptionsPolicyInEffect": { + "message": "One or more organization policies are affecting your Send options." + }, + "passwordPrompt": { + "message": "Master password re-prompt" + }, + "passwordConfirmation": { + "message": "Master password confirmation" + }, + "passwordConfirmationDesc": { + "message": "This action is protected. To continue, please re-enter your master password to verify your identity." + }, + "emailVerificationRequired": { + "message": "Email verification required" + }, + "emailVerificationRequiredDesc": { + "message": "You must verify your email to use this feature. You can verify your email in the web vault." + }, + "updatedMasterPassword": { + "message": "Updated master password" + }, + "updateMasterPassword": { + "message": "Update master password" + }, + "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." + }, + "resetPasswordPolicyAutoEnroll": { + "message": "Automatic enrollment" + }, + "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." + }, + "selectFolder": { + "message": "Select folder..." + }, + "ssoCompleteRegistration": { + "message": "In order to complete logging in with SSO, please set a master password to access and protect your vault." + }, + "hours": { + "message": "Hours" + }, + "minutes": { + "message": "Minutes" + }, + "vaultTimeoutPolicyInEffect": { + "message": "Your organization policies are affecting your vault timeout. Maximum allowed Vault Timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "vaultExportDisabled": { + "message": "Vault export unavailable" + }, + "personalVaultExportPolicyInEffect": { + "message": "One or more organization policies prevents you from exporting your individual vault." + }, + "copyCustomFieldNameInvalidElement": { + "message": "Unable to identify a valid form element. Try inspecting the HTML instead." + }, + "copyCustomFieldNameNotUnique": { + "message": "No unique identifier found." + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "leaveOrganization": { + "message": "Leave organization" + }, + "removeMasterPassword": { + "message": "Remove master password" + }, + "removedMasterPassword": { + "message": "Master password removed" + }, + "leaveOrganizationConfirmation": { + "message": "Are you sure you want to leave this organization?" + }, + "leftOrganization": { + "message": "You have left the organization." + }, + "toggleCharacterCount": { + "message": "Toggle character count" + }, + "sessionTimeout": { + "message": "Your session has timed out. Please go back and try logging in again." + }, + "exportingPersonalVaultTitle": { + "message": "Exporting individual vault" + }, + "exportingPersonalVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, + "error": { + "message": "Error" + }, + "regenerateUsername": { + "message": "Regenerate username" + }, + "generateUsername": { + "message": "Generate username" + }, + "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" + }, + "plusAddressedEmailDesc": { + "message": "Use your email provider's sub-addressing capabilities." + }, + "catchallEmail": { + "message": "Catch-all email" + }, + "catchallEmailDesc": { + "message": "Use your domain's configured catch-all inbox." + }, + "random": { + "message": "Random" + }, + "randomWord": { + "message": "Random word" + }, + "websiteName": { + "message": "Website name" + }, + "whatWouldYouLikeToGenerate": { + "message": "What would you like to generate?" + }, + "passwordType": { + "message": "Password type" + }, + "service": { + "message": "Service" + }, + "forwardedEmail": { + "message": "Forwarded email alias" + }, + "forwardedEmailDesc": { + "message": "Generate an email alias with an external forwarding service." + }, + "hostname": { + "message": "Hostname", + "description": "Part of a URL." + }, + "apiAccessToken": { + "message": "API Access Token" + }, + "apiKey": { + "message": "API Key" + }, + "ssoKeyConnectorError": { + "message": "Key connector error: make sure key connector is available and working correctly." + }, + "premiumSubcriptionRequired": { + "message": "Premium subscription required" + }, + "organizationIsDisabled": { + "message": "Organization suspended." + }, + "disabledOrganizationFilterError": { + "message": "Items in suspended Organizations cannot be accessed. Contact your Organization owner for assistance." + }, + "cardBrandMir": { + "message": "Mir" + }, + "loggingInTo": { + "message": "Logging in to $DOMAIN$", + "placeholders": { + "domain": { + "content": "$1", + "example": "example.com" + } + } + }, + "settingsEdited": { + "message": "Settings have been edited" + }, + "environmentEditedClick": { + "message": "Click here" + }, + "environmentEditedReset": { + "message": "to reset to pre-configured settings" + }, + "serverVersion": { + "message": "Server version" + }, + "selfHosted": { + "message": "Self-hosted" + }, + "thirdParty": { + "message": "Third-party" + }, + "thirdPartyServerMessage": { + "message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.", + "placeholders": { + "servername": { + "content": "$1", + "example": "ThirdPartyServerName" + } + } + }, + "lastSeenOn": { + "message": "last seen on: $DATE$", + "placeholders": { + "date": { + "content": "$1", + "example": "Jun 15, 2015" + } + } + }, + "loginWithMasterPassword": { + "message": "Log in with master password" + }, + "loggingInAs": { + "message": "Logging in as" + }, + "notYou": { + "message": "Not you?" + }, + "newAroundHere": { + "message": "New around here?" + }, + "rememberEmail": { + "message": "Remember email" + } +} diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index fa4aed52cda..a1525ed64b0 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -193,7 +193,7 @@ "message": "Er zijn geen mappen om weer te geven." }, "helpFeedback": { - "message": "Hulp en reacties" + "message": "Help & reacties" }, "sync": { "message": "Synchroniseren" diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 5682bb08e91..aca73bd37b1 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -98,7 +98,7 @@ "message": "Kopiuj nazwę pola niestandardowego" }, "noMatchingLogins": { - "message": "Brak pasujących danych logowania." + "message": "Brak pasujących danych logowania" }, "unlockVaultMenu": { "message": "Odblokuj sejf" @@ -936,7 +936,7 @@ "message": "Adres URL serwera" }, "apiUrl": { - "message": "Adres URL serwera interfejsu API" + "message": "Adres URL serwera API" }, "webVaultUrl": { "message": "Adres URL serwera sejfu internetowego" @@ -1040,7 +1040,7 @@ "message": "Pokaż ikony witryn" }, "faviconDesc": { - "message": "Pokaż rozpoznawalny obraz obok każdych danych logowania." + "message": "Pokaż rozpoznawalny obraz obok danych logowania." }, "enableBadgeCounter": { "message": "Pokaż licznik na ikonie" @@ -1316,7 +1316,7 @@ "description": "ex. Date this item was created" }, "datePasswordUpdated": { - "message": "Aktualizacja hasła", + "message": "Hasło zostało zaktualizowane", "description": "ex. Date this password was updated" }, "neverLockWarning": { @@ -1488,7 +1488,7 @@ "message": "Zaznaczając tę opcję, akceptujesz:" }, "acceptPoliciesRequired": { - "message": "Warunki użytkowania i polityka prywatności nie zostały zaakceptowane." + "message": "Regulamin i polityka prywatności nie zostały zaakceptowane." }, "termsOfService": { "message": "Regulamin" @@ -1820,7 +1820,7 @@ "message": "Zaktualizuj hasło główne" }, "updateMasterPasswordWarning": { - "message": "Twoje hasło główne zostało ostatnio zmienione przez administratora Twojej organizacji. Musisz je teraz zaktualizować, aby uzyskać dostęp do sejfu. W przypadku kontynuacji nastąpi wylogowanie z bieżącej sesji, przez co konieczne będzie ponowne zalogowanie się. Aktywne sesje na innych urządzeniach mogą pozostać aktywne przez maksymalnie jedną godzinę." + "message": "Hasło główne zostało zmienione przez administratora Twojej organizacji. Musisz je zaktualizować, aby uzyskać dostęp do sejfu. Ta czynność spowoduje wylogowanie z bieżącej sesji, przez co konieczne będzie ponowne zalogowanie się. Aktywne sesje na innych urządzeniach mogą pozostać aktywne przez maksymalnie godzinę." }, "resetPasswordPolicyAutoEnroll": { "message": "Automatyczne rejestrowanie użytkowników" @@ -1857,10 +1857,10 @@ "message": "Czas blokowania sejfu przekracza limit określony przez organizację." }, "vaultExportDisabled": { - "message": "Eksport sejfu wyłączony" + "message": "Eksportowanie sejfu jest niedostępne" }, "personalVaultExportPolicyInEffect": { - "message": "Co najmniej jedna zasada organizacji uniemożliwia wyeksportowanie Twojego sejfu." + "message": "Co najmniej jedna zasada organizacji uniemożliwia wyeksportowanie osobistego sejfu." }, "copyCustomFieldNameInvalidElement": { "message": "Nie można zidentyfikować poprawnego elementu formularza. Spróbuj sprawdzić kod HTML." @@ -1942,7 +1942,7 @@ "message": "Losowe słowo" }, "websiteName": { - "message": "Nazwa witryny" + "message": "Nazwa strony" }, "whatWouldYouLikeToGenerate": { "message": "Co chcesz wygenerować?" @@ -1954,10 +1954,10 @@ "message": "Usługa" }, "forwardedEmail": { - "message": "Alias przekazywanego e-maila" + "message": "Alias przekierowania" }, "forwardedEmailDesc": { - "message": "Wygeneruj alias adresu e-mail z zewnętrznej usługi przekazywania." + "message": "Wygeneruj alias adresu e-mail z zewnętrznej usługi przekierowania." }, "hostname": { "message": "Nazwa hosta", @@ -1967,7 +1967,7 @@ "message": "Token dostępu API" }, "apiKey": { - "message": "Klucz interfejsu API" + "message": "Klucz API" }, "ssoKeyConnectorError": { "message": "Błąd serwera Key Connector: upewnij się, że serwer Key Connector jest dostępny i działa poprawnie." @@ -1976,10 +1976,10 @@ "message": "Wymagana jest subskrypcja Premium" }, "organizationIsDisabled": { - "message": "Organizacja jest wyłączona." + "message": "Organizacja została zawieszona." }, "disabledOrganizationFilterError": { - "message": "Nie można uzyskać dostępu do elementów w wyłączonych organizacjach. Skontaktuj się z właścicielem organizacji, aby uzyskać pomoc." + "message": "Nie można uzyskać dostępu do elementów w zawieszonych organizacjach. Skontaktuj się z właścicielem organizacji, aby uzyskać pomoc." }, "cardBrandMir": { "message": "Mir" @@ -1994,7 +1994,7 @@ } }, "settingsEdited": { - "message": "Ustawienia zostały edytowane" + "message": "Ustawienia zostały zmienione" }, "environmentEditedClick": { "message": "Kliknij tutaj" @@ -2009,7 +2009,7 @@ "message": "Samodzielnie hostowany" }, "thirdParty": { - "message": "Innego dostawcy" + "message": "Inny dostawca" }, "thirdPartyServerMessage": { "message": "Połączono z implementacją serwera innego dostawcy, $SERVERNAME$. Zweryfikuj błędy za pomocą oficjalnego serwera lub zgłoś je serwerowi innego dostawcy.", @@ -2021,7 +2021,7 @@ } }, "lastSeenOn": { - "message": "ostatnio widziano $DATE$", + "message": "ostatnio widziany $DATE$", "placeholders": { "date": { "content": "$1", @@ -2039,9 +2039,9 @@ "message": "To nie Ty?" }, "newAroundHere": { - "message": "Jesteś tu nowy(a)?" + "message": "Nowy użytkownik?" }, "rememberEmail": { - "message": "Zapamiętaj email" + "message": "Zapamiętaj adres e-mail" } } diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 7b20d2151d3..39c7c700972 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -642,7 +642,7 @@ "description": "Light color" }, "solarizedDark": { - "message": "Солнечная темная", + "message": "Solarized dark", "description": "'Solarized' is a noun and the name of a color scheme. It should not be translated." }, "exportVault": { @@ -921,7 +921,7 @@ "message": "Коды подтверждения будут отправлены вам по электронной почте." }, "selfHostedEnvironment": { - "message": "Окружение собственного хостинга" + "message": "Окружение пользовательского хостинга" }, "selfHostedEnvironmentFooter": { "message": "Укажите URL Bitwarden на вашем сервере." @@ -1396,7 +1396,7 @@ "message": "Действие по тайм-ауту хранилища" }, "lock": { - "message": "Заблокировать", + "message": "Блокировка", "description": "Verb form: to make secure or inaccesible by" }, "trash": { diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 50cfa01b8b4..3b37ab27813 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -642,7 +642,7 @@ "description": "Light color" }, "solarizedDark": { - "message": "Tmavá –⁠ Solarized", + "message": "Solarized –⁠ tmavý", "description": "'Solarized' is a noun and the name of a color scheme. It should not be translated." }, "exportVault": { diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 746ec5540a9..35a1aa7a8fe 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -53,13 +53,13 @@ "message": "Zavihek" }, "vault": { - "message": "Sef" + "message": "Trezor" }, "myVault": { "message": "Moj trezor" }, "allVaults": { - "message": "All vaults" + "message": "Vsi trezorji" }, "tools": { "message": "Orodja" @@ -101,7 +101,7 @@ "message": "Nobenih ujemajočih prijav." }, "unlockVaultMenu": { - "message": "Unlock your vault" + "message": "Odkleni svoj trezor" }, "loginToVaultMenu": { "message": "Log in to your vault" @@ -131,10 +131,10 @@ "message": "Send a verification code to your email" }, "sendCode": { - "message": "Send code" + "message": "Pošlji kodo" }, "codeSent": { - "message": "Code sent" + "message": "Koda poslana" }, "verificationCode": { "message": "Verifikacijska koda" @@ -242,10 +242,10 @@ "message": "Lowercase (a-z)" }, "numbers": { - "message": "Numbers (0-9)" + "message": "Številke (0-9)" }, "specialCharacters": { - "message": "Special characters (!@#$%^&*)" + "message": "Posebni znaki (!@#$%^&*)" }, "numWords": { "message": "Število besed" @@ -864,7 +864,7 @@ "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." }, "webAuthnNewTabOpen": { - "message": "Open new tab" + "message": "Odpri nov zavihek" }, "webAuthnAuthenticate": { "message": "Authenticate WebAuthn" @@ -879,10 +879,10 @@ "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)." }, "twoStepOptions": { - "message": "Two-step login options" + "message": "Možnosti dvostopenjske prijave" }, "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": "Ste izgubili dostop do vseh vaših ponudnikov dvostopenjske prijave? Uporabite svojo kodo za obnovitev in tako onemogočite dvostopenjsko prijavo v svoj račun." }, "recoveryCodeTitle": { "message": "Koda za obnovitev" @@ -927,7 +927,7 @@ "message": "Specify the base URL of your on-premises hosted Bitwarden installation." }, "customEnvironment": { - "message": "Custom environment" + "message": "Okolje po meri" }, "customEnvironmentFooter": { "message": "For advanced users. You can specify the base URL of each service independently." @@ -993,7 +993,7 @@ "message": "Generate and copy a new random password to the clipboard" }, "commandLockVaultDesc": { - "message": "Lock the vault" + "message": "Zakleni trezor" }, "privateModeWarning": { "message": "Private mode support is experimental and some features are limited." @@ -1002,10 +1002,10 @@ "message": "Custom fields" }, "copyValue": { - "message": "Copy value" + "message": "Kopiraj vrednost" }, "value": { - "message": "Value" + "message": "Vrednost" }, "newCustomField": { "message": "New custom field" @@ -1020,7 +1020,7 @@ "message": "Skrito" }, "cfTypeBoolean": { - "message": "Boolean" + "message": "Logična vrednost" }, "cfTypeLinked": { "message": "Linked", @@ -1133,7 +1133,7 @@ "message": "Priimek" }, "fullName": { - "message": "Full name" + "message": "Polno ime" }, "identityName": { "message": "Ime identitete" @@ -1190,22 +1190,22 @@ "message": "Prijave" }, "typeSecureNote": { - "message": "Secure note" + "message": "Varni zapisek" }, "typeCard": { - "message": "Card" + "message": "Kartica" }, "typeIdentity": { - "message": "Identity" + "message": "Identiteta" }, "passwordHistory": { - "message": "Password history" + "message": "Zgodovina gesel" }, "back": { - "message": "Back" + "message": "Nazaj" }, "collections": { - "message": "Collections" + "message": "Zbirke" }, "favorites": { "message": "Priljubljeno" @@ -1226,7 +1226,7 @@ "message": "Prijave" }, "secureNotes": { - "message": "Secure notes" + "message": "Varni zapiski" }, "clear": { "message": "Počisti", @@ -1285,15 +1285,15 @@ "description": "Toggle the display of the URIs of the currently open tabs in the browser." }, "currentUri": { - "message": "Current URI", + "message": "Trenutni URI", "description": "The URI of one of the current open tabs in the browser." }, "organization": { - "message": "Organization", + "message": "Organizacija", "description": "An entity of multiple related people (ex. a team or business organization)." }, "types": { - "message": "Types" + "message": "Tipi" }, "allItems": { "message": "All items" @@ -1302,13 +1302,13 @@ "message": "There are no passwords to list." }, "remove": { - "message": "Remove" + "message": "Odstrani" }, "default": { - "message": "Default" + "message": "Privzeto" }, "dateUpdated": { - "message": "Updated", + "message": "Posodobljeno", "description": "ex. Date this item was updated" }, "dateCreated": { @@ -1329,25 +1329,25 @@ "message": "There are no collections to list." }, "ownership": { - "message": "Ownership" + "message": "Lastništvo" }, "whoOwnsThisItem": { "message": "Who owns this item?" }, "strong": { - "message": "Strong", + "message": "Močno", "description": "ex. A strong password. Scale: Weak -> Good -> Strong" }, "good": { - "message": "Good", + "message": "Dobro", "description": "ex. A good password. Scale: Weak -> Good -> Strong" }, "weak": { - "message": "Weak", + "message": "Šibko", "description": "ex. A weak password. Scale: Weak -> Good -> Strong" }, "weakMasterPassword": { - "message": "Weak master password" + "message": "Šibko glavno geslo" }, "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?" @@ -1357,19 +1357,19 @@ "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." }, "unlockWithPin": { - "message": "Unlock with PIN" + "message": "Odkleni s PIN kodo" }, "setYourPinCode": { "message": "Set your PIN code for unlocking Bitwarden. Your PIN settings will be reset if you ever fully log out of the application." }, "pinRequired": { - "message": "PIN code is required." + "message": "Potrebna je PIN koda." }, "invalidPin": { - "message": "Invalid PIN code." + "message": "Nepravilna PIN koda." }, "unlockWithBiometrics": { - "message": "Unlock with biometrics" + "message": "Prijava z biometriko" }, "awaitDesktop": { "message": "Awaiting confirmation from desktop" @@ -1387,7 +1387,7 @@ "message": "Clone item" }, "clone": { - "message": "Clone" + "message": "Kloniraj" }, "passwordGeneratorPolicyInEffect": { "message": "One or more organization policies are affecting your generator settings." @@ -1396,15 +1396,15 @@ "message": "Vault timeout action" }, "lock": { - "message": "Lock", + "message": "Zakleni", "description": "Verb form: to make secure or inaccesible by" }, "trash": { - "message": "Trash", + "message": "Koš", "description": "Noun: a special folder to hold deleted items" }, "searchTrash": { - "message": "Search trash" + "message": "Preišči koš" }, "permanentlyDeleteItem": { "message": "Permanently delete item" @@ -1440,7 +1440,7 @@ "message": "Item auto-filled " }, "setMasterPassword": { - "message": "Set master password" + "message": "Nastavi glavno geslo" }, "masterPasswordPolicyInEffect": { "message": "One or more organization policies require your master password to meet the following requirements:" @@ -1494,13 +1494,13 @@ "message": "Terms of Service" }, "privacyPolicy": { - "message": "Privacy Policy" + "message": "Pravilnik o zasebnosti" }, "hintEqualsPassword": { "message": "Your password hint cannot be the same as your password." }, "ok": { - "message": "Ok" + "message": "V redu" }, "desktopSyncVerificationTitle": { "message": "Desktop sync verification" @@ -1584,7 +1584,7 @@ } }, "send": { - "message": "Send", + "message": "Pošlji", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "searchSends": { @@ -1596,10 +1596,10 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTypeText": { - "message": "Text" + "message": "Besedilo" }, "sendTypeFile": { - "message": "File" + "message": "Datoteka" }, "allSends": { "message": "All Sends", @@ -1610,7 +1610,7 @@ "description": "This text will be displayed after a Send has been accessed the maximum amount of times." }, "expired": { - "message": "Expired" + "message": "Poteklo" }, "pendingDeletion": { "message": "Pending deletion" @@ -1626,7 +1626,7 @@ "message": "Remove Password" }, "delete": { - "message": "Delete" + "message": "Izbriši" }, "removedPassword": { "message": "Password removed" @@ -1636,11 +1636,11 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { - "message": "Send link", + "message": "Pošlji povezavo", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "disabled": { - "message": "Disabled" + "message": "Onemogočeno" }, "removePasswordConfirmation": { "message": "Are you sure you want to remove the password?" @@ -1683,10 +1683,10 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "oneDay": { - "message": "1 day" + "message": "1 dan" }, "days": { - "message": "$DAYS$ days", + "message": "$DAYS$ dni", "placeholders": { "days": { "content": "$1", @@ -1835,10 +1835,10 @@ "message": "In order to complete logging in with SSO, please set a master password to access and protect your vault." }, "hours": { - "message": "Hours" + "message": "Ur" }, "minutes": { - "message": "Minutes" + "message": "Minut" }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies are affecting your vault timeout. Maximum allowed Vault Timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)", @@ -1911,7 +1911,7 @@ } }, "error": { - "message": "Error" + "message": "Napaka" }, "regenerateUsername": { "message": "Regenerate username" @@ -1936,13 +1936,13 @@ "message": "Use your domain's configured catch-all inbox." }, "random": { - "message": "Random" + "message": "Naključno" }, "randomWord": { - "message": "Random word" + "message": "Naključna beseda" }, "websiteName": { - "message": "Website name" + "message": "Ime spletne strani" }, "whatWouldYouLikeToGenerate": { "message": "What would you like to generate?" @@ -1997,13 +1997,13 @@ "message": "Settings have been edited" }, "environmentEditedClick": { - "message": "Click here" + "message": "Kliknite tukaj" }, "environmentEditedReset": { "message": "to reset to pre-configured settings" }, "serverVersion": { - "message": "Server version" + "message": "Verzija strežnika" }, "selfHosted": { "message": "Self-hosted" @@ -2036,12 +2036,12 @@ "message": "Logging in as" }, "notYou": { - "message": "Not you?" + "message": "Niste vi?" }, "newAroundHere": { "message": "New around here?" }, "rememberEmail": { - "message": "Remember email" + "message": "Zapomni si e-pošto" } } diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 37020aa2a7e..fc595643ba0 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -2012,7 +2012,7 @@ "message": "Трећа страна" }, "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": "Повезан са имплементацијом сервера треће стране, $SERVERNAME$. Проверите грешке користећи званични сервер или их пријавите серверу треће стране.", "placeholders": { "servername": { "content": "$1", diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index a4c107152af..af7f3cc090c 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -131,7 +131,7 @@ "message": "Skicka en verifieringskod till din e-postadress" }, "sendCode": { - "message": "Send-kod" + "message": "Skicka kod" }, "codeSent": { "message": "Kod har skickats" @@ -318,7 +318,7 @@ "message": "Öppna" }, "website": { - "message": "Webbsida" + "message": "Webbplats" }, "toggleVisibility": { "message": "Växla synlighet" @@ -436,7 +436,7 @@ "message": "Bekräftelsen för huvudlösenordet stämde ej." }, "newAccountCreated": { - "message": "Ditt nya konto har blivit skapat! Du kan nu logga in." + "message": "Ditt nya konto har skapats! Du kan logga in nu." }, "masterPassSent": { "message": "Vi har skickat ett e-postmeddelande till dig med din huvudlösenordsledtråd." @@ -448,7 +448,7 @@ "message": "Ogiltig verifieringskod" }, "valueCopied": { - "message": "$VALUE$ kopierat", + "message": "$VALUE$ har kopierats", "description": "Value has been copied to the clipboard.", "placeholders": { "value": { @@ -461,10 +461,10 @@ "message": "Kunde inte automatiskt fylla i det valda objektet på den här webbsidan. Klipp/klistra informationen istället." }, "loggedOut": { - "message": "Loggade ut" + "message": "Utloggad" }, "loginExpired": { - "message": "Din inloggningssession har utgått." + "message": "Din inloggningssession har upphört." }, "logOutConfirmation": { "message": "Är du säker på att du vill logga ut?" @@ -497,7 +497,7 @@ "message": "Mapp sparad" }, "deleteFolderConfirmation": { - "message": "Är du säker på att du vill ta bort den här mappen?" + "message": "Är du säker på att du vill radera denna mapp?" }, "deletedFolder": { "message": "Mapp raderad" @@ -717,10 +717,10 @@ "message": "Bifogade filer" }, "deleteAttachment": { - "message": "Ta bort bilaga" + "message": "Radera bilaga" }, "deleteAttachmentConfirmation": { - "message": "Är du säker på att du vill ta bort bilagan?" + "message": "Är du säker på att du vill radera denna bilaga?" }, "deletedAttachment": { "message": "Raderade bilaga" @@ -1400,7 +1400,7 @@ "description": "Verb form: to make secure or inaccesible by" }, "trash": { - "message": "Papperskorgen", + "message": "Papperskorg", "description": "Noun: a special folder to hold deleted items" }, "searchTrash": { @@ -1731,7 +1731,7 @@ "message": "Nuvarande antal åtkomster" }, "createSend": { - "message": "Skapa ny Send", + "message": "Ny Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "newPassword": { diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json new file mode 100644 index 00000000000..ba083bf1e36 --- /dev/null +++ b/apps/browser/src/_locales/te/messages.json @@ -0,0 +1,2047 @@ +{ + "appName": { + "message": "Bitwarden" + }, + "extName": { + "message": "Bitwarden - Free Password Manager", + "description": "Extension name, MUST be less than 40 characters (Safari restriction)" + }, + "extDesc": { + "message": "A secure and free password manager for all of your devices.", + "description": "Extension description" + }, + "loginOrCreateNewAccount": { + "message": "Log in or create a new account to access your secure vault." + }, + "createAccount": { + "message": "Create account" + }, + "login": { + "message": "Log in" + }, + "enterpriseSingleSignOn": { + "message": "Enterprise single sign-on" + }, + "cancel": { + "message": "Cancel" + }, + "close": { + "message": "Close" + }, + "submit": { + "message": "Submit" + }, + "emailAddress": { + "message": "Email address" + }, + "masterPass": { + "message": "Master password" + }, + "masterPassDesc": { + "message": "The master password is the password you use to access your vault. It is very important that you do not forget your master password. There is no way to recover the password in the event that you forget it." + }, + "masterPassHintDesc": { + "message": "A master password hint can help you remember your password if you forget it." + }, + "reTypeMasterPass": { + "message": "Re-type master password" + }, + "masterPassHint": { + "message": "Master password hint (optional)" + }, + "tab": { + "message": "Tab" + }, + "vault": { + "message": "Vault" + }, + "myVault": { + "message": "My vault" + }, + "allVaults": { + "message": "All vaults" + }, + "tools": { + "message": "Tools" + }, + "settings": { + "message": "Settings" + }, + "currentTab": { + "message": "Current tab" + }, + "copyPassword": { + "message": "Copy password" + }, + "copyNote": { + "message": "Copy note" + }, + "copyUri": { + "message": "Copy URI" + }, + "copyUsername": { + "message": "Copy username" + }, + "copyNumber": { + "message": "Copy number" + }, + "copySecurityCode": { + "message": "Copy security code" + }, + "autoFill": { + "message": "Auto-fill" + }, + "generatePasswordCopied": { + "message": "Generate password (copied)" + }, + "copyElementIdentifier": { + "message": "Copy custom field name" + }, + "noMatchingLogins": { + "message": "No matching logins" + }, + "unlockVaultMenu": { + "message": "Unlock your vault" + }, + "loginToVaultMenu": { + "message": "Log in to your vault" + }, + "autoFillInfo": { + "message": "There are no logins available to auto-fill for the current browser tab." + }, + "addLogin": { + "message": "Add a login" + }, + "addItem": { + "message": "Add item" + }, + "passwordHint": { + "message": "Password hint" + }, + "enterEmailToGetHint": { + "message": "Enter your account email address to receive your master password hint." + }, + "getMasterPasswordHint": { + "message": "Get master password hint" + }, + "continue": { + "message": "Continue" + }, + "sendVerificationCode": { + "message": "Send a verification code to your email" + }, + "sendCode": { + "message": "Send code" + }, + "codeSent": { + "message": "Code sent" + }, + "verificationCode": { + "message": "Verification code" + }, + "confirmIdentity": { + "message": "Confirm your identity to continue." + }, + "account": { + "message": "Account" + }, + "changeMasterPassword": { + "message": "Change master password" + }, + "fingerprintPhrase": { + "message": "Fingerprint phrase", + "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." + }, + "yourAccountsFingerprint": { + "message": "Your account's fingerprint phrase", + "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." + }, + "twoStepLogin": { + "message": "Two-step login" + }, + "logOut": { + "message": "Log out" + }, + "about": { + "message": "About" + }, + "version": { + "message": "Version" + }, + "save": { + "message": "Save" + }, + "move": { + "message": "Move" + }, + "addFolder": { + "message": "Add folder" + }, + "name": { + "message": "Name" + }, + "editFolder": { + "message": "Edit folder" + }, + "deleteFolder": { + "message": "Delete folder" + }, + "folders": { + "message": "Folders" + }, + "noFolders": { + "message": "There are no folders to list." + }, + "helpFeedback": { + "message": "Help & feedback" + }, + "sync": { + "message": "Sync" + }, + "syncVaultNow": { + "message": "Sync vault now" + }, + "lastSync": { + "message": "Last sync:" + }, + "passGen": { + "message": "Password generator" + }, + "generator": { + "message": "Generator", + "description": "Short for 'Password Generator'." + }, + "passGenInfo": { + "message": "Automatically generate strong, unique passwords for your logins." + }, + "bitWebVault": { + "message": "Bitwarden web vault" + }, + "importItems": { + "message": "Import items" + }, + "select": { + "message": "Select" + }, + "generatePassword": { + "message": "Generate password" + }, + "regeneratePassword": { + "message": "Regenerate password" + }, + "options": { + "message": "Options" + }, + "length": { + "message": "Length" + }, + "uppercase": { + "message": "Uppercase (A-Z)" + }, + "lowercase": { + "message": "Lowercase (a-z)" + }, + "numbers": { + "message": "Numbers (0-9)" + }, + "specialCharacters": { + "message": "Special characters (!@#$%^&*)" + }, + "numWords": { + "message": "Number of words" + }, + "wordSeparator": { + "message": "Word separator" + }, + "capitalize": { + "message": "Capitalize", + "description": "Make the first letter of a work uppercase." + }, + "includeNumber": { + "message": "Include number" + }, + "minNumbers": { + "message": "Minimum numbers" + }, + "minSpecial": { + "message": "Minimum special" + }, + "avoidAmbChar": { + "message": "Avoid ambiguous characters" + }, + "searchVault": { + "message": "Search vault" + }, + "edit": { + "message": "Edit" + }, + "view": { + "message": "View" + }, + "noItemsInList": { + "message": "There are no items to list." + }, + "itemInformation": { + "message": "Item information" + }, + "username": { + "message": "Username" + }, + "password": { + "message": "Password" + }, + "passphrase": { + "message": "Passphrase" + }, + "favorite": { + "message": "Favorite" + }, + "notes": { + "message": "Notes" + }, + "note": { + "message": "Note" + }, + "editItem": { + "message": "Edit item" + }, + "folder": { + "message": "Folder" + }, + "deleteItem": { + "message": "Delete item" + }, + "viewItem": { + "message": "View item" + }, + "launch": { + "message": "Launch" + }, + "website": { + "message": "Website" + }, + "toggleVisibility": { + "message": "Toggle visibility" + }, + "manage": { + "message": "Manage" + }, + "other": { + "message": "Other" + }, + "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." + }, + "verifyIdentity": { + "message": "Verify identity" + }, + "yourVaultIsLocked": { + "message": "Your vault is locked. Verify your identity to continue." + }, + "unlock": { + "message": "Unlock" + }, + "loggedInAsOn": { + "message": "Logged in as $EMAIL$ on $HOSTNAME$.", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + }, + "hostname": { + "content": "$2", + "example": "bitwarden.com" + } + } + }, + "invalidMasterPassword": { + "message": "Invalid master password" + }, + "vaultTimeout": { + "message": "Vault timeout" + }, + "lockNow": { + "message": "Lock now" + }, + "immediately": { + "message": "Immediately" + }, + "tenSeconds": { + "message": "10 seconds" + }, + "twentySeconds": { + "message": "20 seconds" + }, + "thirtySeconds": { + "message": "30 seconds" + }, + "oneMinute": { + "message": "1 minute" + }, + "twoMinutes": { + "message": "2 minutes" + }, + "fiveMinutes": { + "message": "5 minutes" + }, + "fifteenMinutes": { + "message": "15 minutes" + }, + "thirtyMinutes": { + "message": "30 minutes" + }, + "oneHour": { + "message": "1 hour" + }, + "fourHours": { + "message": "4 hours" + }, + "onLocked": { + "message": "On system lock" + }, + "onRestart": { + "message": "On browser restart" + }, + "never": { + "message": "Never" + }, + "security": { + "message": "Security" + }, + "errorOccurred": { + "message": "An error has occurred" + }, + "emailRequired": { + "message": "Email address is required." + }, + "invalidEmail": { + "message": "Invalid email address." + }, + "masterPasswordRequired": { + "message": "Master password is required." + }, + "confirmMasterPasswordRequired": { + "message": "Master password retype is required." + }, + "masterPasswordMinlength": { + "message": "Master password must be at least 8 characters long." + }, + "masterPassDoesntMatch": { + "message": "Master password confirmation does not match." + }, + "newAccountCreated": { + "message": "Your new account has been created! You may now log in." + }, + "masterPassSent": { + "message": "We've sent you an email with your master password hint." + }, + "verificationCodeRequired": { + "message": "Verification code is required." + }, + "invalidVerificationCode": { + "message": "Invalid verification code" + }, + "valueCopied": { + "message": "$VALUE$ copied", + "description": "Value has been copied to the clipboard.", + "placeholders": { + "value": { + "content": "$1", + "example": "Password" + } + } + }, + "autofillError": { + "message": "Unable to auto-fill the selected item on this page. Copy and paste the information instead." + }, + "loggedOut": { + "message": "Logged out" + }, + "loginExpired": { + "message": "Your login session has expired." + }, + "logOutConfirmation": { + "message": "Are you sure you want to log out?" + }, + "yes": { + "message": "Yes" + }, + "no": { + "message": "No" + }, + "unexpectedError": { + "message": "An unexpected error has occurred." + }, + "nameRequired": { + "message": "Name is required." + }, + "addedFolder": { + "message": "Folder added" + }, + "changeMasterPass": { + "message": "Change master password" + }, + "changeMasterPasswordConfirmation": { + "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "twoStepLoginConfirmation": { + "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be set up on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "editedFolder": { + "message": "Folder saved" + }, + "deleteFolderConfirmation": { + "message": "Are you sure you want to delete this folder?" + }, + "deletedFolder": { + "message": "Folder deleted" + }, + "gettingStartedTutorial": { + "message": "Getting started tutorial" + }, + "gettingStartedTutorialVideo": { + "message": "Watch our getting started tutorial to learn how to get the most out of the browser extension." + }, + "syncingComplete": { + "message": "Syncing complete" + }, + "syncingFailed": { + "message": "Syncing failed" + }, + "passwordCopied": { + "message": "Password copied" + }, + "uri": { + "message": "URI" + }, + "uriPosition": { + "message": "URI $POSITION$", + "description": "A listing of URIs. Ex: URI 1, URI 2, URI 3, etc.", + "placeholders": { + "position": { + "content": "$1", + "example": "2" + } + } + }, + "newUri": { + "message": "New URI" + }, + "addedItem": { + "message": "Item added" + }, + "editedItem": { + "message": "Item saved" + }, + "deleteItemConfirmation": { + "message": "Do you really want to send to the trash?" + }, + "deletedItem": { + "message": "Item sent to trash" + }, + "overwritePassword": { + "message": "Overwrite password" + }, + "overwritePasswordConfirmation": { + "message": "Are you sure you want to overwrite the current password?" + }, + "overwriteUsername": { + "message": "Overwrite username" + }, + "overwriteUsernameConfirmation": { + "message": "Are you sure you want to overwrite the current username?" + }, + "searchFolder": { + "message": "Search folder" + }, + "searchCollection": { + "message": "Search collection" + }, + "searchType": { + "message": "Search type" + }, + "noneFolder": { + "message": "No folder", + "description": "This is the folder for uncategorized items" + }, + "enableAddLoginNotification": { + "message": "Ask to add login" + }, + "addLoginNotificationDesc": { + "message": "Ask to add an item if one isn't found in your vault." + }, + "showCardsCurrentTab": { + "message": "Show cards on Tab page" + }, + "showCardsCurrentTabDesc": { + "message": "List card items on the Tab page for easy auto-fill." + }, + "showIdentitiesCurrentTab": { + "message": "Show identities on Tab page" + }, + "showIdentitiesCurrentTabDesc": { + "message": "List identity items on the Tab page for easy auto-fill." + }, + "clearClipboard": { + "message": "Clear clipboard", + "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.", + "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?" + }, + "notificationAddSave": { + "message": "Save" + }, + "enableChangedPasswordNotification": { + "message": "Ask to update existing login" + }, + "changedPasswordNotificationDesc": { + "message": "Ask to update a login's password when a change is detected on a website." + }, + "notificationChangeDesc": { + "message": "Do you want to update this password in Bitwarden?" + }, + "notificationChangeSave": { + "message": "Update" + }, + "enableContextMenuItem": { + "message": "Show context menu options" + }, + "contextMenuItemDesc": { + "message": "Use a secondary click to access password generation and matching logins for the website. " + }, + "defaultUriMatchDetection": { + "message": "Default URI match detection", + "description": "Default URI match detection for auto-fill." + }, + "defaultUriMatchDetectionDesc": { + "message": "Choose the default way that URI match detection is handled for logins when performing actions such as auto-fill." + }, + "theme": { + "message": "Theme" + }, + "themeDesc": { + "message": "Change the application's color theme." + }, + "dark": { + "message": "Dark", + "description": "Dark color" + }, + "light": { + "message": "Light", + "description": "Light color" + }, + "solarizedDark": { + "message": "Solarized dark", + "description": "'Solarized' is a noun and the name of a color scheme. It should not be translated." + }, + "exportVault": { + "message": "Export vault" + }, + "fileFormat": { + "message": "File format" + }, + "warning": { + "message": "WARNING", + "description": "WARNING (should stay in capitalized letters if the language permits)" + }, + "confirmVaultExport": { + "message": "Confirm vault export" + }, + "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." + }, + "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." + }, + "encExportAccountWarningDesc": { + "message": "Account encryption keys are unique to each Bitwarden user account, so you can't import an encrypted export into a different account." + }, + "exportMasterPassword": { + "message": "Enter your master password to export your vault data." + }, + "shared": { + "message": "Shared" + }, + "learnOrg": { + "message": "Learn about organizations" + }, + "learnOrgConfirmation": { + "message": "Bitwarden allows you to share your vault items with others by using an organization. Would you like to visit the bitwarden.com website to learn more?" + }, + "moveToOrganization": { + "message": "Move to organization" + }, + "share": { + "message": "Share" + }, + "movedItemToOrg": { + "message": "$ITEMNAME$ moved to $ORGNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret Item" + }, + "orgname": { + "content": "$2", + "example": "Company Name" + } + } + }, + "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." + }, + "learnMore": { + "message": "Learn more" + }, + "authenticatorKeyTotp": { + "message": "Authenticator key (TOTP)" + }, + "verificationCodeTotp": { + "message": "Verification code (TOTP)" + }, + "copyVerificationCode": { + "message": "Copy verification code" + }, + "attachments": { + "message": "Attachments" + }, + "deleteAttachment": { + "message": "Delete attachment" + }, + "deleteAttachmentConfirmation": { + "message": "Are you sure you want to delete this attachment?" + }, + "deletedAttachment": { + "message": "Attachment deleted" + }, + "newAttachment": { + "message": "Add new attachment" + }, + "noAttachments": { + "message": "No attachments." + }, + "attachmentSaved": { + "message": "Attachment saved" + }, + "file": { + "message": "File" + }, + "selectFile": { + "message": "Select a file" + }, + "maxFileSize": { + "message": "Maximum file size is 500 MB." + }, + "featureUnavailable": { + "message": "Feature unavailable" + }, + "updateKey": { + "message": "You cannot use this feature until you update your encryption key." + }, + "premiumMembership": { + "message": "Premium membership" + }, + "premiumManage": { + "message": "Manage membership" + }, + "premiumManageAlert": { + "message": "You can manage your membership on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "premiumRefresh": { + "message": "Refresh membership" + }, + "premiumNotCurrentMember": { + "message": "You are not currently a Premium member." + }, + "premiumSignUpAndGet": { + "message": "Sign up for a Premium membership and get:" + }, + "ppremiumSignUpStorage": { + "message": "1 GB encrypted storage for file attachments." + }, + "ppremiumSignUpTwoStep": { + "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + }, + "ppremiumSignUpReports": { + "message": "Password hygiene, account health, and data breach reports to keep your vault safe." + }, + "ppremiumSignUpTotp": { + "message": "TOTP verification code (2FA) generator for logins in your vault." + }, + "ppremiumSignUpSupport": { + "message": "Priority customer support." + }, + "ppremiumSignUpFuture": { + "message": "All future Premium features. More coming soon!" + }, + "premiumPurchase": { + "message": "Purchase Premium" + }, + "premiumPurchaseAlert": { + "message": "You can purchase Premium membership on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "premiumCurrentMember": { + "message": "You are a Premium member!" + }, + "premiumCurrentMemberThanks": { + "message": "Thank you for supporting Bitwarden." + }, + "premiumPrice": { + "message": "All for just $PRICE$ /year!", + "placeholders": { + "price": { + "content": "$1", + "example": "$10" + } + } + }, + "refreshComplete": { + "message": "Refresh complete" + }, + "enableAutoTotpCopy": { + "message": "Copy TOTP automatically" + }, + "disableAutoTotpCopyDesc": { + "message": "If a login has an authenticator key, copy the TOTP verification code to your clip-board when you auto-fill the login." + }, + "enableAutoBiometricsPrompt": { + "message": "Ask for biometrics on launch" + }, + "premiumRequired": { + "message": "Premium required" + }, + "premiumRequiredDesc": { + "message": "A Premium membership is required to use this feature." + }, + "enterVerificationCodeApp": { + "message": "Enter the 6 digit verification code from your authenticator app." + }, + "enterVerificationCodeEmail": { + "message": "Enter the 6 digit verification code that was emailed to $EMAIL$.", + "placeholders": { + "email": { + "content": "$1", + "example": "example@gmail.com" + } + } + }, + "verificationCodeEmailSent": { + "message": "Verification email sent to $EMAIL$.", + "placeholders": { + "email": { + "content": "$1", + "example": "example@gmail.com" + } + } + }, + "rememberMe": { + "message": "Remember me" + }, + "sendVerificationCodeEmailAgain": { + "message": "Send verification code email again" + }, + "useAnotherTwoStepMethod": { + "message": "Use another two-step login method" + }, + "insertYubiKey": { + "message": "Insert your YubiKey into your computer's USB port, then touch its button." + }, + "insertU2f": { + "message": "Insert your security key into your computer's USB port. If it has a button, touch it." + }, + "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." + }, + "webAuthnNewTabOpen": { + "message": "Open new tab" + }, + "webAuthnAuthenticate": { + "message": "Authenticate WebAuthn" + }, + "loginUnavailable": { + "message": "Login unavailable" + }, + "noTwoStepProviders": { + "message": "This account has two-step login set up, however, none of the configured two-step providers are supported by this web browser." + }, + "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)." + }, + "twoStepOptions": { + "message": "Two-step login options" + }, + "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." + }, + "recoveryCodeTitle": { + "message": "Recovery code" + }, + "authenticatorAppTitle": { + "message": "Authenticator app" + }, + "authenticatorAppDesc": { + "message": "Use an authenticator app (such as Authy or Google Authenticator) to generate time-based verification codes.", + "description": "'Authy' and 'Google Authenticator' are product names and should not be translated." + }, + "yubiKeyTitle": { + "message": "YubiKey OTP Security Key" + }, + "yubiKeyDesc": { + "message": "Use a YubiKey to access your account. Works with YubiKey 4, 4 Nano, 4C, and NEO devices." + }, + "duoDesc": { + "message": "Verify with Duo Security using the Duo Mobile app, SMS, phone call, or U2F security key.", + "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.", + "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." + }, + "emailTitle": { + "message": "Email" + }, + "emailDesc": { + "message": "Verification codes will be emailed to you." + }, + "selfHostedEnvironment": { + "message": "Self-hosted environment" + }, + "selfHostedEnvironmentFooter": { + "message": "Specify the base URL of your on-premises hosted Bitwarden installation." + }, + "customEnvironment": { + "message": "Custom environment" + }, + "customEnvironmentFooter": { + "message": "For advanced users. You can specify the base URL of each service independently." + }, + "baseUrl": { + "message": "Server URL" + }, + "apiUrl": { + "message": "API Server URL" + }, + "webVaultUrl": { + "message": "Web vault server URL" + }, + "identityUrl": { + "message": "Identity server URL" + }, + "notificationsUrl": { + "message": "Notifications server URL" + }, + "iconsUrl": { + "message": "Icons server URL" + }, + "environmentSaved": { + "message": "Environment URLs saved" + }, + "enableAutoFillOnPageLoad": { + "message": "Auto-fill on page load" + }, + "enableAutoFillOnPageLoadDesc": { + "message": "If a login form is detected, auto-fill when the web page loads." + }, + "experimentalFeature": { + "message": "This is currently an experimental feature. Use at your own risk." + }, + "defaultAutoFillOnPageLoad": { + "message": "Default autofill setting for login items" + }, + "defaultAutoFillOnPageLoadDesc": { + "message": "You can turn off auto-fill on page load for individual login items from the item's Edit view." + }, + "itemAutoFillOnPageLoad": { + "message": "Auto-fill on page load (if set up in Options)" + }, + "autoFillOnPageLoadUseDefault": { + "message": "Use default setting" + }, + "autoFillOnPageLoadYes": { + "message": "Auto-fill on page load" + }, + "autoFillOnPageLoadNo": { + "message": "Do not auto-fill on page load" + }, + "commandOpenPopup": { + "message": "Open vault popup" + }, + "commandOpenSidebar": { + "message": "Open vault in sidebar" + }, + "commandAutofillDesc": { + "message": "Auto-fill the last used login for the current website" + }, + "commandGeneratePasswordDesc": { + "message": "Generate and copy a new random password to the clipboard" + }, + "commandLockVaultDesc": { + "message": "Lock the vault" + }, + "privateModeWarning": { + "message": "Private mode support is experimental and some features are limited." + }, + "customFields": { + "message": "Custom fields" + }, + "copyValue": { + "message": "Copy value" + }, + "value": { + "message": "Value" + }, + "newCustomField": { + "message": "New custom field" + }, + "dragToSort": { + "message": "Drag to sort" + }, + "cfTypeText": { + "message": "Text" + }, + "cfTypeHidden": { + "message": "Hidden" + }, + "cfTypeBoolean": { + "message": "Boolean" + }, + "cfTypeLinked": { + "message": "Linked", + "description": "This describes a field that is 'linked' (tied) to another field." + }, + "linkedValue": { + "message": "Linked value", + "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?" + }, + "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?" + }, + "enableFavicon": { + "message": "Show website icons" + }, + "faviconDesc": { + "message": "Show a recognizable image next to each login." + }, + "enableBadgeCounter": { + "message": "Show badge counter" + }, + "badgeCounterDesc": { + "message": "Indicate how many logins you have for the current web page." + }, + "cardholderName": { + "message": "Cardholder name" + }, + "number": { + "message": "Number" + }, + "brand": { + "message": "Brand" + }, + "expirationMonth": { + "message": "Expiration month" + }, + "expirationYear": { + "message": "Expiration year" + }, + "expiration": { + "message": "Expiration" + }, + "january": { + "message": "January" + }, + "february": { + "message": "February" + }, + "march": { + "message": "March" + }, + "april": { + "message": "April" + }, + "may": { + "message": "May" + }, + "june": { + "message": "June" + }, + "july": { + "message": "July" + }, + "august": { + "message": "August" + }, + "september": { + "message": "September" + }, + "october": { + "message": "October" + }, + "november": { + "message": "November" + }, + "december": { + "message": "December" + }, + "securityCode": { + "message": "Security code" + }, + "ex": { + "message": "ex." + }, + "title": { + "message": "Title" + }, + "mr": { + "message": "Mr" + }, + "mrs": { + "message": "Mrs" + }, + "ms": { + "message": "Ms" + }, + "dr": { + "message": "Dr" + }, + "firstName": { + "message": "First name" + }, + "middleName": { + "message": "Middle name" + }, + "lastName": { + "message": "Last name" + }, + "fullName": { + "message": "Full name" + }, + "identityName": { + "message": "Identity name" + }, + "company": { + "message": "Company" + }, + "ssn": { + "message": "Social Security number" + }, + "passportNumber": { + "message": "Passport number" + }, + "licenseNumber": { + "message": "License number" + }, + "email": { + "message": "Email" + }, + "phone": { + "message": "Phone" + }, + "address": { + "message": "Address" + }, + "address1": { + "message": "Address 1" + }, + "address2": { + "message": "Address 2" + }, + "address3": { + "message": "Address 3" + }, + "cityTown": { + "message": "City / Town" + }, + "stateProvince": { + "message": "State / Province" + }, + "zipPostalCode": { + "message": "Zip / Postal code" + }, + "country": { + "message": "Country" + }, + "type": { + "message": "Type" + }, + "typeLogin": { + "message": "Login" + }, + "typeLogins": { + "message": "Logins" + }, + "typeSecureNote": { + "message": "Secure note" + }, + "typeCard": { + "message": "Card" + }, + "typeIdentity": { + "message": "Identity" + }, + "passwordHistory": { + "message": "Password history" + }, + "back": { + "message": "Back" + }, + "collections": { + "message": "Collections" + }, + "favorites": { + "message": "Favorites" + }, + "popOutNewWindow": { + "message": "Pop out to a new window" + }, + "refresh": { + "message": "Refresh" + }, + "cards": { + "message": "Cards" + }, + "identities": { + "message": "Identities" + }, + "logins": { + "message": "Logins" + }, + "secureNotes": { + "message": "Secure notes" + }, + "clear": { + "message": "Clear", + "description": "To clear something out. example: To clear browser history." + }, + "checkPassword": { + "message": "Check if password has been exposed." + }, + "passwordExposed": { + "message": "This password has been exposed $VALUE$ time(s) in data breaches. You should change it.", + "placeholders": { + "value": { + "content": "$1", + "example": "2" + } + } + }, + "passwordSafe": { + "message": "This password was not found in any known data breaches. It should be safe to use." + }, + "baseDomain": { + "message": "Base domain", + "description": "Domain name. Ex. website.com" + }, + "domainName": { + "message": "Domain name", + "description": "Domain name. Ex. website.com" + }, + "host": { + "message": "Host", + "description": "A URL's host value. For example, the host of https://sub.domain.com:443 is 'sub.domain.com:443'." + }, + "exact": { + "message": "Exact" + }, + "startsWith": { + "message": "Starts with" + }, + "regEx": { + "message": "Regular expression", + "description": "A programming term, also known as 'RegEx'." + }, + "matchDetection": { + "message": "Match detection", + "description": "URI match detection for auto-fill." + }, + "defaultMatchDetection": { + "message": "Default match detection", + "description": "Default URI match detection for auto-fill." + }, + "toggleOptions": { + "message": "Toggle options" + }, + "toggleCurrentUris": { + "message": "Toggle current URIs", + "description": "Toggle the display of the URIs of the currently open tabs in the browser." + }, + "currentUri": { + "message": "Current URI", + "description": "The URI of one of the current open tabs in the browser." + }, + "organization": { + "message": "Organization", + "description": "An entity of multiple related people (ex. a team or business organization)." + }, + "types": { + "message": "Types" + }, + "allItems": { + "message": "All items" + }, + "noPasswordsInList": { + "message": "There are no passwords to list." + }, + "remove": { + "message": "Remove" + }, + "default": { + "message": "Default" + }, + "dateUpdated": { + "message": "Updated", + "description": "ex. Date this item was updated" + }, + "dateCreated": { + "message": "Created", + "description": "ex. Date this item was created" + }, + "datePasswordUpdated": { + "message": "Password updated", + "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." + }, + "noOrganizationsList": { + "message": "You do not belong to any organizations. Organizations allow you to securely share items with other users." + }, + "noCollectionsInList": { + "message": "There are no collections to list." + }, + "ownership": { + "message": "Ownership" + }, + "whoOwnsThisItem": { + "message": "Who owns this item?" + }, + "strong": { + "message": "Strong", + "description": "ex. A strong password. Scale: Weak -> Good -> Strong" + }, + "good": { + "message": "Good", + "description": "ex. A good password. Scale: Weak -> Good -> Strong" + }, + "weak": { + "message": "Weak", + "description": "ex. A weak password. Scale: Weak -> Good -> Strong" + }, + "weakMasterPassword": { + "message": "Weak master password" + }, + "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?" + }, + "pin": { + "message": "PIN", + "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." + }, + "unlockWithPin": { + "message": "Unlock with 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." + }, + "pinRequired": { + "message": "PIN code is required." + }, + "invalidPin": { + "message": "Invalid PIN code." + }, + "unlockWithBiometrics": { + "message": "Unlock with biometrics" + }, + "awaitDesktop": { + "message": "Awaiting confirmation from desktop" + }, + "awaitDesktopDesc": { + "message": "Please confirm using biometrics in the Bitwarden desktop application to set up biometrics for browser." + }, + "lockWithMasterPassOnRestart": { + "message": "Lock with master password on browser restart" + }, + "selectOneCollection": { + "message": "You must select at least one collection." + }, + "cloneItem": { + "message": "Clone item" + }, + "clone": { + "message": "Clone" + }, + "passwordGeneratorPolicyInEffect": { + "message": "One or more organization policies are affecting your generator settings." + }, + "vaultTimeoutAction": { + "message": "Vault timeout action" + }, + "lock": { + "message": "Lock", + "description": "Verb form: to make secure or inaccesible by" + }, + "trash": { + "message": "Trash", + "description": "Noun: a special folder to hold deleted items" + }, + "searchTrash": { + "message": "Search trash" + }, + "permanentlyDeleteItem": { + "message": "Permanently delete item" + }, + "permanentlyDeleteItemConfirmation": { + "message": "Are you sure you want to permanently delete this item?" + }, + "permanentlyDeletedItem": { + "message": "Item permanently deleted" + }, + "restoreItem": { + "message": "Restore item" + }, + "restoreItemConfirmation": { + "message": "Are you sure you want to restore this item?" + }, + "restoredItem": { + "message": "Item restored" + }, + "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?" + }, + "vaultTimeoutLogOutConfirmationTitle": { + "message": "Timeout action confirmation" + }, + "autoFillAndSave": { + "message": "Auto-fill and save" + }, + "autoFillSuccessAndSavedUri": { + "message": "Item auto-filled and URI saved" + }, + "autoFillSuccess": { + "message": "Item auto-filled " + }, + "setMasterPassword": { + "message": "Set master password" + }, + "masterPasswordPolicyInEffect": { + "message": "One or more organization policies require your master password to meet the following requirements:" + }, + "policyInEffectMinComplexity": { + "message": "Minimum complexity score of $SCORE$", + "placeholders": { + "score": { + "content": "$1", + "example": "4" + } + } + }, + "policyInEffectMinLength": { + "message": "Minimum length of $LENGTH$", + "placeholders": { + "length": { + "content": "$1", + "example": "14" + } + } + }, + "policyInEffectUppercase": { + "message": "Contain one or more uppercase characters" + }, + "policyInEffectLowercase": { + "message": "Contain one or more lowercase characters" + }, + "policyInEffectNumbers": { + "message": "Contain one or more numbers" + }, + "policyInEffectSpecial": { + "message": "Contain one or more of the following special characters $CHARS$", + "placeholders": { + "chars": { + "content": "$1", + "example": "!@#$%^&*" + } + } + }, + "masterPasswordPolicyRequirementsNotMet": { + "message": "Your new master password does not meet the policy requirements." + }, + "acceptPolicies": { + "message": "By checking this box you agree to the following:" + }, + "acceptPoliciesRequired": { + "message": "Terms of Service and Privacy Policy have not been acknowledged." + }, + "termsOfService": { + "message": "Terms of Service" + }, + "privacyPolicy": { + "message": "Privacy Policy" + }, + "hintEqualsPassword": { + "message": "Your password hint cannot be the same as your password." + }, + "ok": { + "message": "Ok" + }, + "desktopSyncVerificationTitle": { + "message": "Desktop sync verification" + }, + "desktopIntegrationVerificationText": { + "message": "Please verify that the desktop application shows this fingerprint: " + }, + "desktopIntegrationDisabledTitle": { + "message": "Browser integration is not set up" + }, + "desktopIntegrationDisabledDesc": { + "message": "Browser integration is not set up in the Bitwarden desktop application. Please set it up in the settings within the desktop application." + }, + "startDesktopTitle": { + "message": "Start the Bitwarden desktop application" + }, + "startDesktopDesc": { + "message": "The Bitwarden desktop application needs to be started before unlock with biometrics can be used." + }, + "errorEnableBiometricTitle": { + "message": "Unable to set up biometrics" + }, + "errorEnableBiometricDesc": { + "message": "Action was canceled by the desktop application" + }, + "nativeMessagingInvalidEncryptionDesc": { + "message": "Desktop application invalidated the secure communication channel. Please retry this operation" + }, + "nativeMessagingInvalidEncryptionTitle": { + "message": "Desktop communication interrupted" + }, + "nativeMessagingWrongUserDesc": { + "message": "The desktop application is logged into a different account. Please ensure both applications are logged into the same account." + }, + "nativeMessagingWrongUserTitle": { + "message": "Account missmatch" + }, + "biometricsNotEnabledTitle": { + "message": "Biometrics not set up" + }, + "biometricsNotEnabledDesc": { + "message": "Browser biometrics requires desktop biometric to be set up in the settings first." + }, + "biometricsNotSupportedTitle": { + "message": "Biometrics not supported" + }, + "biometricsNotSupportedDesc": { + "message": "Browser biometrics is not supported on this device." + }, + "nativeMessaginPermissionErrorTitle": { + "message": "Permission not provided" + }, + "nativeMessaginPermissionErrorDesc": { + "message": "Without permission to communicate with the Bitwarden Desktop Application we cannot provide biometrics in the browser extension. Please try again." + }, + "nativeMessaginPermissionSidebarTitle": { + "message": "Permission request error" + }, + "nativeMessaginPermissionSidebarDesc": { + "message": "This action cannot be done in the sidebar, please retry the action in the popup or popout." + }, + "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." + }, + "personalOwnershipPolicyInEffect": { + "message": "An organization policy is affecting your ownership options." + }, + "excludedDomains": { + "message": "Excluded domains" + }, + "excludedDomainsDesc": { + "message": "Bitwarden will not ask to save login details for these domains. You must refresh the page for changes to take effect." + }, + "excludedDomainsInvalidDomain": { + "message": "$DOMAIN$ is not a valid domain", + "placeholders": { + "domain": { + "content": "$1", + "example": "googlecom" + } + } + }, + "send": { + "message": "Send", + "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" + }, + "sendTypeFile": { + "message": "File" + }, + "allSends": { + "message": "All Sends", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "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" + }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "removePassword": { + "message": "Remove Password" + }, + "delete": { + "message": "Delete" + }, + "removedPassword": { + "message": "Password removed" + }, + "deletedSend": { + "message": "Send deleted", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendLink": { + "message": "Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "disabled": { + "message": "Disabled" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, + "deleteSend": { + "message": "Delete 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?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "editSend": { + "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." + }, + "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" + }, + "days": { + "message": "$DAYS$ days", + "placeholders": { + "days": { + "content": "$1", + "example": "2" + } + } + }, + "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." + }, + "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." + }, + "newPassword": { + "message": "New password" + }, + "sendDisabled": { + "message": "Send removed", + "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.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "createdSend": { + "message": "Send created", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "editedSend": { + "message": "Send saved", + "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." + }, + "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." + }, + "sendSafariFileWarning": { + "message": "In order to choose a file using Safari, pop out to a new window by clicking this banner." + }, + "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." + }, + "deletionDateIsInvalid": { + "message": "The deletion date provided is not valid." + }, + "expirationDateAndTimeRequired": { + "message": "An expiration date and time are required." + }, + "deletionDateAndTimeRequired": { + "message": "A deletion date and time are required." + }, + "dateParsingError": { + "message": "There was an error saving your deletion and expiration dates." + }, + "hideEmail": { + "message": "Hide my email address from recipients." + }, + "sendOptionsPolicyInEffect": { + "message": "One or more organization policies are affecting your Send options." + }, + "passwordPrompt": { + "message": "Master password re-prompt" + }, + "passwordConfirmation": { + "message": "Master password confirmation" + }, + "passwordConfirmationDesc": { + "message": "This action is protected. To continue, please re-enter your master password to verify your identity." + }, + "emailVerificationRequired": { + "message": "Email verification required" + }, + "emailVerificationRequiredDesc": { + "message": "You must verify your email to use this feature. You can verify your email in the web vault." + }, + "updatedMasterPassword": { + "message": "Updated master password" + }, + "updateMasterPassword": { + "message": "Update master password" + }, + "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." + }, + "resetPasswordPolicyAutoEnroll": { + "message": "Automatic enrollment" + }, + "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." + }, + "selectFolder": { + "message": "Select folder..." + }, + "ssoCompleteRegistration": { + "message": "In order to complete logging in with SSO, please set a master password to access and protect your vault." + }, + "hours": { + "message": "Hours" + }, + "minutes": { + "message": "Minutes" + }, + "vaultTimeoutPolicyInEffect": { + "message": "Your organization policies are affecting your vault timeout. Maximum allowed Vault Timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "vaultExportDisabled": { + "message": "Vault export unavailable" + }, + "personalVaultExportPolicyInEffect": { + "message": "One or more organization policies prevents you from exporting your individual vault." + }, + "copyCustomFieldNameInvalidElement": { + "message": "Unable to identify a valid form element. Try inspecting the HTML instead." + }, + "copyCustomFieldNameNotUnique": { + "message": "No unique identifier found." + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "leaveOrganization": { + "message": "Leave organization" + }, + "removeMasterPassword": { + "message": "Remove master password" + }, + "removedMasterPassword": { + "message": "Master password removed" + }, + "leaveOrganizationConfirmation": { + "message": "Are you sure you want to leave this organization?" + }, + "leftOrganization": { + "message": "You have left the organization." + }, + "toggleCharacterCount": { + "message": "Toggle character count" + }, + "sessionTimeout": { + "message": "Your session has timed out. Please go back and try logging in again." + }, + "exportingPersonalVaultTitle": { + "message": "Exporting individual vault" + }, + "exportingPersonalVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, + "error": { + "message": "Error" + }, + "regenerateUsername": { + "message": "Regenerate username" + }, + "generateUsername": { + "message": "Generate username" + }, + "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" + }, + "plusAddressedEmailDesc": { + "message": "Use your email provider's sub-addressing capabilities." + }, + "catchallEmail": { + "message": "Catch-all email" + }, + "catchallEmailDesc": { + "message": "Use your domain's configured catch-all inbox." + }, + "random": { + "message": "Random" + }, + "randomWord": { + "message": "Random word" + }, + "websiteName": { + "message": "Website name" + }, + "whatWouldYouLikeToGenerate": { + "message": "What would you like to generate?" + }, + "passwordType": { + "message": "Password type" + }, + "service": { + "message": "Service" + }, + "forwardedEmail": { + "message": "Forwarded email alias" + }, + "forwardedEmailDesc": { + "message": "Generate an email alias with an external forwarding service." + }, + "hostname": { + "message": "Hostname", + "description": "Part of a URL." + }, + "apiAccessToken": { + "message": "API Access Token" + }, + "apiKey": { + "message": "API Key" + }, + "ssoKeyConnectorError": { + "message": "Key connector error: make sure key connector is available and working correctly." + }, + "premiumSubcriptionRequired": { + "message": "Premium subscription required" + }, + "organizationIsDisabled": { + "message": "Organization suspended." + }, + "disabledOrganizationFilterError": { + "message": "Items in suspended Organizations cannot be accessed. Contact your Organization owner for assistance." + }, + "cardBrandMir": { + "message": "Mir" + }, + "loggingInTo": { + "message": "Logging in to $DOMAIN$", + "placeholders": { + "domain": { + "content": "$1", + "example": "example.com" + } + } + }, + "settingsEdited": { + "message": "Settings have been edited" + }, + "environmentEditedClick": { + "message": "Click here" + }, + "environmentEditedReset": { + "message": "to reset to pre-configured settings" + }, + "serverVersion": { + "message": "Server version" + }, + "selfHosted": { + "message": "Self-hosted" + }, + "thirdParty": { + "message": "Third-party" + }, + "thirdPartyServerMessage": { + "message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.", + "placeholders": { + "servername": { + "content": "$1", + "example": "ThirdPartyServerName" + } + } + }, + "lastSeenOn": { + "message": "last seen on: $DATE$", + "placeholders": { + "date": { + "content": "$1", + "example": "Jun 15, 2015" + } + } + }, + "loginWithMasterPassword": { + "message": "Log in with master password" + }, + "loggingInAs": { + "message": "Logging in as" + }, + "notYou": { + "message": "Not you?" + }, + "newAroundHere": { + "message": "New around here?" + }, + "rememberEmail": { + "message": "Remember email" + } +} diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 52062854b6d..d13d2f63b80 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -53,13 +53,13 @@ "message": "แท็บ" }, "vault": { - "message": "Vault" + "message": "ตู้นิรภัย" }, "myVault": { "message": "My Vault" }, "allVaults": { - "message": "All vaults" + "message": "ตู้นิรภัยทั้งหมด" }, "tools": { "message": "เครื่องมือ" @@ -95,16 +95,16 @@ "message": "Generate Password (copied)" }, "copyElementIdentifier": { - "message": "Copy custom field name" + "message": "คัดลอกชื่อของช่องที่กำหนดเอง" }, "noMatchingLogins": { "message": "ไม่พบข้อมูลล็อกอินที่ตรงกัน" }, "unlockVaultMenu": { - "message": "Unlock your vault" + "message": "ปลดล็อกกตู้นิรภัยของคุณ" }, "loginToVaultMenu": { - "message": "Log in to your vault" + "message": "ลงชื่อเข้าใช้ตู้นิรภัยของคุณ" }, "autoFillInfo": { "message": "ไม่พบข้อมูลล็อกอินเพื่อใช้กรอกข้อมูลอัตโนมัติ สำหรับแท็บปัจจุบันของเบราว์เซอร์" @@ -153,7 +153,7 @@ "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": "Your account's fingerprint phrase", + "message": "ข้อความลายนิ้วมือของบัญชีของคุณ", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." }, "twoStepLogin": { @@ -212,7 +212,7 @@ "description": "Short for 'Password Generator'." }, "passGenInfo": { - "message": "Automatically generate strong, unique passwords for your logins." + "message": "สร้างรหัสผ่านที่รัดกุมและไม่ซ้ำใครโดยอัตโนมัติสำหรับการเข้าสู่ระบบของคุณ" }, "bitWebVault": { "message": "bitwarden Web Vault" @@ -291,7 +291,7 @@ "message": "รหัสผ่าน" }, "passphrase": { - "message": "Passphrase" + "message": "ข้อความรหัสผ่าน" }, "favorite": { "message": "รายการโปรด" @@ -333,10 +333,10 @@ "message": "Rate the Extension" }, "rateExtensionDesc": { - "message": "Please consider helping us out with a good review!" + "message": "โปรดพิจารณา ช่วยเราด้วยการตรวจสอบที่ดี!" }, "browserNotSupportClipboard": { - "message": "Your web browser does not support easy clipboard copying. Copy it manually instead." + "message": "เว็บเบราว์เซอร์ของคุณไม่รองรับการคัดลอกคลิปบอร์ดอย่างง่าย คัดลอกด้วยตนเองแทน" }, "verifyIdentity": { "message": "ยืนยันตัวตน" @@ -424,22 +424,22 @@ "message": "ที่อยู่อีเมลไม่ถูกต้อง" }, "masterPasswordRequired": { - "message": "Master password is required." + "message": "ต้องใช้รหัสผ่านหลัก" }, "confirmMasterPasswordRequired": { - "message": "Master password retype is required." + "message": "ต้องพิมพ์รหัสผ่านหลักอีกครั้ง" }, "masterPasswordMinlength": { - "message": "Master password must be at least 8 characters long." + "message": "รหัสผ่านหลักต้องมีความยาวอย่างน้อย 8 ตัวอักษร" }, "masterPassDoesntMatch": { - "message": "Master password confirmation does not match." + "message": "การยืนยันรหัสผ่านหลักไม่ตรงกัน" }, "newAccountCreated": { - "message": "Your new account has been created! You may now log in." + "message": "บัญชีใหม่ของคุณถูกสร้างขึ้นแล้ว! ตอนนี้คุณสามารถเข้าสู่ระบบ" }, "masterPassSent": { - "message": "We've sent you an email with your master password hint." + "message": "เราได้ส่งอีเมลพร้อมคำใบ้รหัสผ่านหลักของคุณออกไปแล้ว" }, "verificationCodeRequired": { "message": "ต้องระบุโค้ดยืนยัน" @@ -497,7 +497,7 @@ "message": "Edited Folder" }, "deleteFolderConfirmation": { - "message": "Are you sure you want to delete this folder?" + "message": "คุณแน่ใจหรือไม่ว่าต้องการลบโฟลเดอร์นี้" }, "deletedFolder": { "message": "ลบโฟลเดอร์แล้ว" @@ -506,7 +506,7 @@ "message": "Getting Started Tutorial" }, "gettingStartedTutorialVideo": { - "message": "Watch our getting started tutorial to learn how to get the most out of the browser extension." + "message": "ดูบทช่วยสอนการเริ่มต้นของเราเพื่อเรียนรู้วิธีใช้ประโยชน์สูงสุดจากส่วนขยายเบราว์เซอร์" }, "syncingComplete": { "message": "การซิงก์เสร็จสมบูรณ์" @@ -552,48 +552,48 @@ "message": "คุณต้องการเขียนทับรหัสผ่านปัจจุบันใช่หรือไม่?" }, "overwriteUsername": { - "message": "Overwrite username" + "message": "เขียนทับชื่อผู้ใช้" }, "overwriteUsernameConfirmation": { - "message": "Are you sure you want to overwrite the current username?" + "message": "คุณแน่ใจหรือไม่ว่าต้องการเขียนทับชื่อผู้ใช้ปัจจุบัน" }, "searchFolder": { "message": "ค้นหาในโพลเดอร์" }, "searchCollection": { - "message": "Search collection" + "message": "คอลเลกชันการค้นหา" }, "searchType": { - "message": "Search type" + "message": "ประเภทการค้นหา" }, "noneFolder": { "message": "No Folder", "description": "This is the folder for uncategorized items" }, "enableAddLoginNotification": { - "message": "Ask to add login" + "message": "ถามเพื่อให้เพิ่มการเข้าสู่ระบบ" }, "addLoginNotificationDesc": { "message": "The \"Add Login Notification\" automatically prompts you to save new logins to your vault whenever you log into them for the first time." }, "showCardsCurrentTab": { - "message": "Show cards on Tab page" + "message": "แสดงการ์ดบนหน้าแท็บ" }, "showCardsCurrentTabDesc": { - "message": "List card items on the Tab page for easy auto-fill." + "message": "บัตรรายการในหน้าแท็บเพื่อให้ป้อนอัตโนมัติได้ง่าย" }, "showIdentitiesCurrentTab": { - "message": "Show identities on Tab page" + "message": "แสดงตัวตนบนหน้าแท็บ" }, "showIdentitiesCurrentTabDesc": { - "message": "List identity items on the Tab page for easy auto-fill." + "message": "แสดงรายการข้อมูลประจำตัวในหน้าแท็บเพื่อให้ป้อนอัตโนมัติได้ง่าย" }, "clearClipboard": { "message": "ล้างคลิปบอร์ด", "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": "ล้างค่าที่คัดลอกโดยอัตโนมัติจากคลิปบอร์ดของคุณ", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "notificationAddDesc": { @@ -603,7 +603,7 @@ "message": "Yes, Save Now" }, "enableChangedPasswordNotification": { - "message": "Ask to update existing login" + "message": "ขอให้ปรับปรุงการเข้าสู่ระบบที่มีอยู่" }, "changedPasswordNotificationDesc": { "message": "Ask to update a login's password when a change is detected on a website." @@ -615,17 +615,17 @@ "message": "Yes, Update Now" }, "enableContextMenuItem": { - "message": "Show context menu options" + "message": "แสดงตัวเลือกเมนูบริบท" }, "contextMenuItemDesc": { - "message": "Use a secondary click to access password generation and matching logins for the website. " + "message": "ใช้การคลิกสำรองเพื่อเข้าถึงการสร้างรหัสผ่านและการเข้าสู่ระบบที่ตรงกันสำหรับเว็บไซต์ " }, "defaultUriMatchDetection": { - "message": "Default URI match detection", + "message": "การตรวจจับการจับคู่ URI เริ่มต้น", "description": "Default URI match detection for auto-fill." }, "defaultUriMatchDetectionDesc": { - "message": "Choose the default way that URI match detection is handled for logins when performing actions such as auto-fill." + "message": "เลือกวิธีเริ่มต้นในการจัดการการตรวจหาการจับคู่ URI สำหรับการเข้าสู่ระบบเมื่อดำเนินการต่างๆ เช่น การป้อนอัตโนมัติ" }, "theme": { "message": "ธีม" @@ -656,31 +656,31 @@ "description": "WARNING (should stay in capitalized letters if the language permits)" }, "confirmVaultExport": { - "message": "Confirm vault export" + "message": "ยืนยันการส่งออกตู้นิรภัย" }, "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." }, "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": "การส่งออกนี้เข้ารหัสข้อมูลของคุณโดยใช้คีย์เข้ารหัสของบัญชีของคุณ หากคุณเคยหมุนเวียนคีย์เข้ารหัสของบัญชี คุณควรส่งออกอีกครั้ง เนื่องจากคุณจะไม่สามารถถอดรหัสไฟล์ส่งออกนี้ได้" }, "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": "คีย์การเข้ารหัสบัญชีจะไม่ซ้ำกันสำหรับบัญชีผู้ใช้ Bitwarden แต่ละบัญชี ดังนั้นคุณจึงไม่สามารถนำเข้าการส่งออกที่เข้ารหัสไปยังบัญชีอื่นได้" }, "exportMasterPassword": { - "message": "Enter your master password to export your vault data." + "message": "ป้อนรหัสผ่านหลักของคุณเพื่อส่งออกข้อมูลตู้นิรภัยของคุณ" }, "shared": { "message": "แชร์แล้ว" }, "learnOrg": { - "message": "Learn about organizations" + "message": "เรียนรู้เกี่ยวกับองค์กร" }, "learnOrgConfirmation": { - "message": "Bitwarden allows you to share your vault items with others by using an organization. Would you like to visit the bitwarden.com website to learn more?" + "message": "Bitwarden อนุญาตให้คุณแชร์รายการตู้นิรภัยของคุณกับผู้อื่นโดยใช้องค์กร คุณต้องการเยี่ยมชมเว็บไซต์ bitwarden.com เพื่อเรียนรู้เพิ่มเติมหรือไม่?" }, "moveToOrganization": { - "message": "Move to organization" + "message": "ย้ายไปยังแบบองค์กร" }, "share": { "message": "แชร์" @@ -699,10 +699,10 @@ } }, "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": "เลือกองค์กรที่คุณต้องการย้ายรายการนี้ไป การย้ายไปยังองค์กรจะโอนความเป็นเจ้าของรายการไปยังองค์กรนั้น คุณจะไม่ได้เป็นเจ้าของโดยตรงของรายการนี้อีกต่อไปเมื่อมีการย้ายแล้ว" }, "learnMore": { - "message": "Learn more" + "message": "เรียนรู้เพิ่มเติม" }, "authenticatorKeyTotp": { "message": "Authenticator Key (TOTP)" @@ -747,7 +747,7 @@ "message": "Feature Unavailable" }, "updateKey": { - "message": "You cannot use this feature until you update your encryption key." + "message": "คุณไม่สามารถใช้คุณลักษณะนี้ได้จนกว่าคุณจะปรับปรุงคีย์การเข้ารหัสลับของคุณ" }, "premiumMembership": { "message": "Premium Membership" @@ -756,28 +756,28 @@ "message": "Manage Membership" }, "premiumManageAlert": { - "message": "You can manage your membership on the bitwarden.com web vault. Do you want to visit the website now?" + "message": "คุณสามารถจัดการการเป็นสมาชิกของคุณได้ที่ bitwarden.com web vault คุณต้องการเข้าชมเว็บไซต์ตอนนี้หรือไม่?" }, "premiumRefresh": { "message": "Refresh Membership" }, "premiumNotCurrentMember": { - "message": "You are not currently a Premium member." + "message": "คุณยังไม่ได้เป็นสมาชิกพรีเมียม" }, "premiumSignUpAndGet": { - "message": "Sign up for a Premium membership and get:" + "message": "สมัครสมาชิกพรีเมี่ยมและรับ:" }, "ppremiumSignUpStorage": { "message": "1 GB of encrypted file storage." }, "ppremiumSignUpTwoStep": { - "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + "message": "ตัวเลือกการเข้าสู่ระบบแบบสองขั้นตอนเพิ่มเติม เช่น YubiKey, FIDO U2F และ Duo" }, "ppremiumSignUpReports": { - "message": "Password hygiene, account health, and data breach reports to keep your vault safe." + "message": "สุขอนามัยของรหัสผ่าน ความสมบูรณ์ของบัญชี และรายงานการละเมิดข้อมูลเพื่อให้ตู้นิรภัยของคุณปลอดภัย" }, "ppremiumSignUpTotp": { - "message": "TOTP verification code (2FA) generator for logins in your vault." + "message": "ตัวสร้างรหัสยืนยัน TOTP (2FA) สำหรับการเข้าสู่ระบบในตู้นิรภัยของคุณ" }, "ppremiumSignUpSupport": { "message": "Priority customer support." @@ -1055,7 +1055,7 @@ "message": "หมายเลข" }, "brand": { - "message": "Brand" + "message": "แบรนด์" }, "expirationMonth": { "message": "Expiration Month" @@ -1133,7 +1133,7 @@ "message": "Last Name" }, "fullName": { - "message": "Full name" + "message": "ชื่อเต็ม" }, "identityName": { "message": "Identity Name" @@ -1181,7 +1181,7 @@ "message": "ประเทศ" }, "type": { - "message": "Type" + "message": "ชนิด" }, "typeLogin": { "message": "ล็อกอิน" @@ -1205,35 +1205,35 @@ "message": "ย้อนกลับ" }, "collections": { - "message": "Collections" + "message": "คอลเลกชัน" }, "favorites": { "message": "รายการโปรด" }, "popOutNewWindow": { - "message": "Pop out to a new window" + "message": "เปิดหน้าต่างใหม่" }, "refresh": { - "message": "Refresh" + "message": "รีเฟรช" }, "cards": { - "message": "Cards" + "message": "บัตร" }, "identities": { - "message": "Identities" + "message": "ข้อมูลระบุตัวตน" }, "logins": { - "message": "Logins" + "message": "เข้าสู่ระบบ" }, "secureNotes": { "message": "Secure Notes" }, "clear": { - "message": "Clear", + "message": "ลบทิ้ง", "description": "To clear something out. example: To clear browser history." }, "checkPassword": { - "message": "Check if password has been exposed." + "message": "ตรวจสอบว่ารหัสผ่านถูกเปิดเผยหรือไม่" }, "passwordExposed": { "message": "This password has been exposed in data breaches. You should change it.", @@ -1245,28 +1245,28 @@ } }, "passwordSafe": { - "message": "This password was not found in any known data breaches. It should be safe to use." + "message": "ไม่พบรหัสผ่านนี้ในการละเมิดข้อมูลที่มี ควรใช้อย่างปลอดภัย" }, "baseDomain": { - "message": "Base domain", + "message": "โดเมนพื้นฐาน", "description": "Domain name. Ex. website.com" }, "domainName": { - "message": "Domain name", + "message": "ชื่อโดเมน", "description": "Domain name. Ex. website.com" }, "host": { - "message": "Host", + "message": "โฮสต์", "description": "A URL's host value. For example, the host of https://sub.domain.com:443 is 'sub.domain.com:443'." }, "exact": { - "message": "Exact" + "message": "ถูกต้อง" }, "startsWith": { - "message": "Starts with" + "message": "เริ่มต้นด้วย" }, "regEx": { - "message": "Regular expression", + "message": "นิพจน์ทั่วไป", "description": "A programming term, also known as 'RegEx'." }, "matchDetection": { @@ -1274,14 +1274,14 @@ "description": "URI match detection for auto-fill." }, "defaultMatchDetection": { - "message": "Default match detection", + "message": "การตรวจจับการจับคู่เริ่มต้น", "description": "Default URI match detection for auto-fill." }, "toggleOptions": { "message": "Toggle Options" }, "toggleCurrentUris": { - "message": "Toggle current URIs", + "message": "สลับ URI ปัจจุบัน", "description": "Toggle the display of the URIs of the currently open tabs in the browser." }, "currentUri": { @@ -1293,26 +1293,26 @@ "description": "An entity of multiple related people (ex. a team or business organization)." }, "types": { - "message": "Types" + "message": "ชนิด" }, "allItems": { "message": "รายการทั้งหมด" }, "noPasswordsInList": { - "message": "There are no passwords to list." + "message": "ไม่มีรหัสผ่านที่จะแสดง" }, "remove": { "message": "ลบ" }, "default": { - "message": "Default" + "message": "ค่าเริ่มต้น" }, "dateUpdated": { "message": "อัปเดตแล้ว", "description": "ex. Date this item was updated" }, "dateCreated": { - "message": "Created", + "message": "สร้างเมื่อ", "description": "ex. Date this item was created" }, "datePasswordUpdated": { @@ -1320,13 +1320,13 @@ "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": "คุณแน่ใจหรือไม่ว่าต้องการใช้ตัวเลือก \"ไม่เคย\" การตั้งค่าตัวเลือกการล็อกเป็น \"ไม่\" จะเก็บคีย์เข้ารหัสของห้องนิรภัยไว้ในอุปกรณ์ของคุณ หากคุณใช้ตัวเลือกนี้ คุณควรตรวจสอบให้แน่ใจว่าคุณปกป้องอุปกรณ์ของคุณอย่างเหมาะสม" }, "noOrganizationsList": { "message": "You do not belong to any organizations. Organizations allow you to securely share items with other users." }, "noCollectionsInList": { - "message": "There are no collections to list." + "message": "ไม่มีคอลเลกชันที่จะแสดง" }, "ownership": { "message": "เจ้าของ" @@ -1350,7 +1350,7 @@ "message": "Weak Master Password" }, "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": "รหัสผ่านหลักที่คุณเลือกนั้นไม่รัดกุม คุณควรใช้รหัสผ่านหลักที่รัดกุม (หรือวลีรหัสผ่าน) เพื่อปกป้องบัญชี Bitwarden ของคุณอย่างเหมาะสม คุณแน่ใจหรือไม่ว่าต้องการใช้รหัสผ่านหลักนี้" }, "pin": { "message": "PIN", @@ -1369,31 +1369,31 @@ "message": "PIN ไม่ถูกต้อง" }, "unlockWithBiometrics": { - "message": "Unlock with biometrics" + "message": "ปลดล็อกด้วยไบโอเมตริก" }, "awaitDesktop": { "message": "Awaiting confirmation from desktop" }, "awaitDesktopDesc": { - "message": "Please confirm using biometrics in the Bitwarden desktop application to set up biometrics for browser." + "message": "โปรดยืนยันการใช้ไบโอเมตริกในแอปพลิเคชันเดสก์ท็อป Bitwarden เพื่อตั้งค่าไบโอเมตริกสำหรับเบราว์เซอร์" }, "lockWithMasterPassOnRestart": { - "message": "Lock with master password on browser restart" + "message": "ล็อคด้วยรหัสผ่านหลักเมื่อรีสตาร์ทเบราว์เซอร์" }, "selectOneCollection": { - "message": "You must select at least one collection." + "message": "คุณต้องเลือกอย่างน้อยหนึ่งคอลเลกชัน" }, "cloneItem": { - "message": "Clone item" + "message": "โคลนรายการ" }, "clone": { - "message": "Clone" + "message": "โคลน" }, "passwordGeneratorPolicyInEffect": { - "message": "One or more organization policies are affecting your generator settings." + "message": "นโยบายองค์กรอย่างน้อยหนึ่งนโยบายส่งผลต่อการตั้งค่าตัวสร้างของคุณ" }, "vaultTimeoutAction": { - "message": "Vault timeout action" + "message": "การดำเนินการหลังหมดเวลาล็อคตู้เซฟ" }, "lock": { "message": "ล็อก", @@ -1407,46 +1407,46 @@ "message": "ค้นหาในถังขยะ" }, "permanentlyDeleteItem": { - "message": "Permanently delete item" + "message": "ลบรายการอย่างถาวร" }, "permanentlyDeleteItemConfirmation": { - "message": "Are you sure you want to permanently delete this item?" + "message": "คุณแน่ใจหรือไม่ว่าต้องการลบรายการนี้อย่างถาวร?" }, "permanentlyDeletedItem": { - "message": "Item permanently deleted" + "message": "ลบรายการอย่างถาวรแล้ว" }, "restoreItem": { - "message": "Restore item" + "message": "กู้คืนรายการ" }, "restoreItemConfirmation": { - "message": "Are you sure you want to restore this item?" + "message": "คุณแน่ใจหรือไม่ว่าต้องการกู้คืนรายการนี้" }, "restoredItem": { - "message": "Item restored" + "message": "คืนค่ารายการแล้ว" }, "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": "การออกจากระบบจะลบการเข้าถึงตู้นิรภัยของคุณทั้งหมด และต้องมีการตรวจสอบสิทธิ์ออนไลน์หลังจากหมดเวลา คุณแน่ใจหรือไม่ว่าต้องการใช้การตั้งค่านี้" }, "vaultTimeoutLogOutConfirmationTitle": { - "message": "Timeout action confirmation" + "message": "การยืนยันการดำเนินการหมดเวลา" }, "autoFillAndSave": { - "message": "Auto-fill and save" + "message": "กรอกอัตโนมัติและบันทึก" }, "autoFillSuccessAndSavedUri": { - "message": "Item auto-filled and URI saved" + "message": "เติมรายการอัตโนมัติและบันทึก URI แล้ว" }, "autoFillSuccess": { - "message": "Item auto-filled " + "message": "รายการเติมอัตโนมัติ " }, "setMasterPassword": { "message": "ตั้งรหัสผ่านหลัก" }, "masterPasswordPolicyInEffect": { - "message": "One or more organization policies require your master password to meet the following requirements:" + "message": "นโยบายองค์กรอย่างน้อยหนึ่งนโยบายกำหนดให้รหัสผ่านหลักของคุณเป็นไปตามข้อกำหนดต่อไปนี้:" }, "policyInEffectMinComplexity": { - "message": "Minimum complexity score of $SCORE$", + "message": "คะแนนความซับซ้อนขั้นต่ำ $SCORE$", "placeholders": { "score": { "content": "$1", @@ -1482,7 +1482,7 @@ } }, "masterPasswordPolicyRequirementsNotMet": { - "message": "Your new master password does not meet the policy requirements." + "message": "รหัสผ่านหลักใหม่ของคุณไม่เป็นไปตามข้อกำหนดของนโยบาย" }, "acceptPolicies": { "message": "By checking this box you agree to the following:" diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index aa7672f69e4..78152d21889 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -3,24 +3,24 @@ "message": "Bitwarden" }, "extName": { - "message": "Bitwarden", + "message": "Bitwarden - Quản lý mật khẩu miễn phí", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { - "message": "Một trình quản lý mật khẩu an toàn và miễn phí cho mọi thiết bị của bạn.", + "message": "Trình quản lý mật khẩu an toàn và miễn phí cho mọi thiết bị của bạn.", "description": "Extension description" }, "loginOrCreateNewAccount": { - "message": "Đăng nhập hoặc tạo tài khoản mới để truy cập kho mật khẩu của bạn." + "message": "Đăng nhập hoặc tạo tài khoản mới để truy cập kho lưu trữ của bạn." }, "createAccount": { - "message": "Tạo Tài Khoản" + "message": "Tạo tài khoản" }, "login": { - "message": "Đăng Nhập" + "message": "Đăng nhập" }, "enterpriseSingleSignOn": { - "message": "Đăng nhập theo tài khoản tổ chức" + "message": "Đăng nhập bằng tài khoản tổ chức" }, "cancel": { "message": "Hủy bỏ" @@ -32,16 +32,16 @@ "message": "Gửi" }, "emailAddress": { - "message": "Địa chỉ Email" + "message": "Địa chỉ email" }, "masterPass": { "message": "Mật khẩu chính" }, "masterPassDesc": { - "message": "Mật khẩu chủ là mật khẩu bạn sử dụng để truy cập kho mật khẩu của bạn. Nó rất quan trọng nên bạn không được quên mật khẩu chủ của mình. Không thể khôi phục lại mật khẩu chủ nếu bạn quên nó." + "message": "Mật khẩu chính là mật khẩu bạn dùng để truy cập vào kho lưu trữ của bạn. Mật khẩu này rất quan trọng và bạn đừng nên quên mật khẩu chính này. Bạn sẽ không thể khôi phục mật khẩu chính trong trường hợp bạn quên nó." }, "masterPassHintDesc": { - "message": "Một gợi ý mật khẩu có thể giúp bạn nhớ lại mật khẩu chủ của bạn nếu bạn quên nó." + "message": "Gợi ý mật khẩu chính có thể giúp bạn nhớ lại mật khẩu của mình nếu bạn quên nó." }, "reTypeMasterPass": { "message": "Nhập lại mật khẩu chính" @@ -50,16 +50,16 @@ "message": "Gợi ý mật khẩu chính (tùy chọn)" }, "tab": { - "message": "Thẻ" + "message": "Tab" }, "vault": { - "message": "Vault" + "message": "Kho lưu trữ" }, "myVault": { - "message": "Hầm của tôi" + "message": "Kho lưu trữ của tôi" }, "allVaults": { - "message": "All vaults" + "message": "Tất cả kho lưu trữ" }, "tools": { "message": "Công cụ" @@ -68,25 +68,25 @@ "message": "Cài đặt" }, "currentTab": { - "message": "Thẻ hiện tại" + "message": "Tab hiện tại" }, "copyPassword": { - "message": "Sao chép Mật khẩu" + "message": "Sao chép mật khẩu" }, "copyNote": { - "message": "Sao chép Ghi chú" + "message": "Sao chép ghi chú" }, "copyUri": { - "message": "Sao chép URL" + "message": "Sao chép URI" }, "copyUsername": { - "message": "Sao chép tên đăng nhập" + "message": "Sao chép tên người dùng" }, "copyNumber": { - "message": "Sao chép Số" + "message": "Sao chép số" }, "copySecurityCode": { - "message": "Sao chép Mã bảo mật" + "message": "Sao chép mã bảo mật" }, "autoFill": { "message": "Tự động điền" @@ -101,28 +101,28 @@ "message": "Không có thông tin đăng nhập phù hợp." }, "unlockVaultMenu": { - "message": "Mở khóa bảo mật" + "message": "Mở khoá kho lưu trữ của bạn" }, "loginToVaultMenu": { - "message": "Đăng nhập tài khoản bảo mật" + "message": "Đăng nhập vào kho lưu trữ của bạn" }, "autoFillInfo": { "message": "Không có thông tin đăng nhập nào sẵn có để tự động điền vào tab hiện tại." }, "addLogin": { - "message": "Thêm một Đăng nhập" + "message": "Thêm một đăng nhập" }, "addItem": { - "message": "Thêm Mục" + "message": "Thêm mục" }, "passwordHint": { "message": "Gợi ý mật khẩu" }, "enterEmailToGetHint": { - "message": "Nhập địa chỉ email tài khoản của bạn để nhận gợi ý mật khẩu chủ." + "message": "Nhập địa chỉ email tài khoản của bạn để nhận gợi ý mật khẩu chính." }, "getMasterPasswordHint": { - "message": "Nhận gợi ý mật khẩu chủ" + "message": "Nhận gợi ý mật khẩu chính" }, "continue": { "message": "Tiếp tục" @@ -140,7 +140,7 @@ "message": "Mã xác nhận" }, "confirmIdentity": { - "message": "Xác nhận nhận dạng của bạn để tiếp tục" + "message": "Xác nhận danh tính của bạn để tiếp tục." }, "account": { "message": "Tài khoản" @@ -157,7 +157,7 @@ "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": { - "message": "Xác thực hai lớp" + "message": "Đăng nhập hai bước" }, "logOut": { "message": "Đăng xuất" @@ -172,13 +172,13 @@ "message": "Lưu" }, "move": { - "message": "Chuyển" + "message": "Di chuyển" }, "addFolder": { "message": "Thêm thư mục" }, "name": { - "message": "Tên mục" + "message": "Tên" }, "editFolder": { "message": "Chỉnh sửa thư mục" @@ -193,16 +193,16 @@ "message": "Không có thư mục để liệt kê." }, "helpFeedback": { - "message": "Trợ giúp & Phản hồi" + "message": "Trợ giúp & phản hồi" }, "sync": { "message": "Đồng bộ" }, "syncVaultNow": { - "message": "Đồng bộ Kho mật khẩu ngay" + "message": "Đồng bộ kho lưu trữ ngay" }, "lastSync": { - "message": "Lần đồng bộ gần nhất:" + "message": "Đồng bộ lần cuối:" }, "passGen": { "message": "Tạo mật khẩu" @@ -212,10 +212,10 @@ "description": "Short for 'Password Generator'." }, "passGenInfo": { - "message": "Tự động tạo mật khẩu mạnh mẽ, duy nhất cho đăng nhập của bạn." + "message": "Tự động tạo mật khẩu mạnh mẽ, độc nhất cho đăng nhập của bạn." }, "bitWebVault": { - "message": "Hầm bitwarden trên Web" + "message": "Trang web kho lưu trữ Bitwarden" }, "importItems": { "message": "Nhập mục" @@ -248,7 +248,7 @@ "message": "Ký tự đặc biệt (!@#$%^&*)" }, "numWords": { - "message": "Number of Words" + "message": "Số từ" }, "wordSeparator": { "message": "Word Separator" @@ -270,7 +270,7 @@ "message": "Tránh các ký tự không rõ ràng" }, "searchVault": { - "message": "Tìm kiếm trong kho" + "message": "Tìm kiếm trong kho lưu trữ" }, "edit": { "message": "Sửa" @@ -339,10 +339,10 @@ "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ế." }, "verifyIdentity": { - "message": "Mã xác minh" + "message": "Xác minh danh tính" }, "yourVaultIsLocked": { - "message": "Kho của bạn đã bị khóa. Xác minh mật khẩu chính của bạn để mở." + "message": "Kho lưu trữ của bạn đã bị khóa. Hãy xác minh danh tính của bạn để mở khoá." }, "unlock": { "message": "Mở khóa" @@ -361,16 +361,16 @@ } }, "invalidMasterPassword": { - "message": "Mật khẩu chủ không hợp lệ" + "message": "Mật khẩu chính không hợp lệ" }, "vaultTimeout": { - "message": "Thời Gian Chờ Của Kho" + "message": "Thời gian chờ của kho lưu trữ" }, "lockNow": { "message": "Khóa ngay" }, "immediately": { - "message": "Tức thì" + "message": "Ngay lập tức" }, "tenSeconds": { "message": "10 giây" @@ -415,31 +415,31 @@ "message": "Bảo mật" }, "errorOccurred": { - "message": "Đã xảy ra lỗi chưa xác định" + "message": "Đã xảy ra lỗi" }, "emailRequired": { - "message": "Địa chỉ email là bắt buộc." + "message": "Yêu cầu địa chỉ email." }, "invalidEmail": { "message": "Địa chỉ email không hợp lệ." }, "masterPasswordRequired": { - "message": "Master password is required." + "message": "Yêu cầu mật khẩu chính." }, "confirmMasterPasswordRequired": { - "message": "Master password retype is required." + "message": "Yêu cầu nhập lại mật khẩu chính." }, "masterPasswordMinlength": { - "message": "Master password must be at least 8 characters long." + "message": "Mật khẩu chính phải có ít nhất 8 kí tự." }, "masterPassDoesntMatch": { - "message": "Xác nhận mật khẩu chủ không khớp." + "message": "Xác nhận mật khẩu chính không khớp." }, "newAccountCreated": { - "message": "Tài khoản của bạn đã được tạo. Bạn có thể đăng nhập bây giờ." + "message": "Tài khoản mới của bạn đã được tạo! Bạn có thể đăng nhập từ bây giờ." }, "masterPassSent": { - "message": "Chúng tôi đã gửi cho bạn email có chứa gợi ý mật khẩu chủ của bạn." + "message": "Chúng tôi đã gửi cho bạn email có chứa gợi ý mật khẩu chính của bạn." }, "verificationCodeRequired": { "message": "Yêu cầu mã xác nhận." @@ -448,7 +448,7 @@ "message": "Mã xác minh không đúng" }, "valueCopied": { - "message": "$VALUE$ đã sao chép", + "message": "Đã sao chép $VALUE$", "description": "Value has been copied to the clipboard.", "placeholders": { "value": { @@ -458,10 +458,10 @@ } }, "autofillError": { - "message": "Tự động điền thông tin đăng nhập trong hoạt động trên trang này. Bạn có thể thực hiện thủ công bằng cách sao chép/dán thông tin đăng nhập." + "message": "Không thể tự động điền mục đã chọn trên trang này. Hãy thực hiện sao chép và dán thông tin một cách thủ công." }, "loggedOut": { - "message": "Đăng xuất" + "message": "Đã đăng xuất" }, "loginExpired": { "message": "Phiên đăng nhập của bạn đã hết hạn." @@ -482,19 +482,19 @@ "message": "Tên là bắt buộc." }, "addedFolder": { - "message": "Thêm thư mục" + "message": "Đã thêm thư mục" }, "changeMasterPass": { "message": "Thay đổi mật khẩu chính" }, "changeMasterPasswordConfirmation": { - "message": "Bạn có thể thay đổi mật khẩu chủ trong trang web Bitwarden. Bạn có muốn truy cập bitwarden.com bây giờ?" + "message": "Bạn có thể thay đổi mật khẩu chính trong trang web kho lưu trữ của Bitwarden. Bạn có muốn truy cập trang web ngay bây giờ không?" }, "twoStepLoginConfirmation": { "message": "Xác thực hai lớp giúp cho tài khoản của bạn an toàn hơn bằng cách yêu cầu bạn xác minh thông tin đăng nhập của bạn bằng một thiết bị khác như khóa bảo mật, ứng dụng xác thực, SMS, cuộc gọi điện thoại hoặc email. Bạn có thể bật xác thực hai lớp trong kho bitwarden nền web. Bạn có muốn ghé thăm trang web bây giờ?" }, "editedFolder": { - "message": "Chỉnh sửa Thư mục" + "message": "Đã lưu thư mục" }, "deleteFolderConfirmation": { "message": "Bạn có chắc chắn muốn xóa thư mục này không?" @@ -518,10 +518,10 @@ "message": "Đã sao chép mật khẩu" }, "uri": { - "message": "Đường dẫn" + "message": "URI" }, "uriPosition": { - "message": "URL $POSITION$", + "message": "URI $POSITION$", "description": "A listing of URIs. Ex: URI 1, URI 2, URI 3, etc.", "placeholders": { "position": { @@ -531,7 +531,7 @@ } }, "newUri": { - "message": "URL Mới" + "message": "URI mới" }, "addedItem": { "message": "Đã thêm mục" @@ -546,16 +546,16 @@ "message": "Đã xóa mục" }, "overwritePassword": { - "message": "Ghi đè lên mật khẩu" + "message": "Ghi đè mật khẩu" }, "overwritePasswordConfirmation": { "message": "Bạn có chắc chắn muốn ghi đè mật khẩu hiện tại không?" }, "overwriteUsername": { - "message": "Overwrite username" + "message": "Ghi đè tên người dùng" }, "overwriteUsernameConfirmation": { - "message": "Are you sure you want to overwrite the current username?" + "message": "Bạn có chắc chắn muốn ghi đè tên người dùng hiện tại không?" }, "searchFolder": { "message": "Tìm kiếm thư mục" @@ -564,29 +564,29 @@ "message": "Tìm kiếm bộ sưu tập" }, "searchType": { - "message": "Tìm mục" + "message": "Tìm loại" }, "noneFolder": { "message": "Không có thư mục", "description": "This is the folder for uncategorized items" }, "enableAddLoginNotification": { - "message": "Ask to add login" + "message": "Hỏi để thêm đăng nhập" }, "addLoginNotificationDesc": { "message": "'Thông báo Thêm đăng nhập' sẽ tự động nhắc bạn lưu các đăng nhập mới vào hầm an toàn của bạn bất cứ khi nào bạn đăng nhập trang web lần đầu tiên." }, "showCardsCurrentTab": { - "message": "Show cards on Tab page" + "message": "Hiển thị thẻ trên trang Tab" }, "showCardsCurrentTabDesc": { - "message": "List card items on the Tab page for easy auto-fill." + "message": "Liệt kê các mục thẻ trên trang Tab để dễ dàng tự động điền." }, "showIdentitiesCurrentTab": { - "message": "Show identities on Tab page" + "message": "Hiển thị danh tính trên trang Tab" }, "showIdentitiesCurrentTabDesc": { - "message": "List identity items on the Tab page for easy auto-fill." + "message": "Liệt kê các mục danh tính trên trang Tab để dễ dàng tự động điền." }, "clearClipboard": { "message": "Dọn dẹp khay nhớ tạm", @@ -600,10 +600,10 @@ "message": "Bạn có cần Bitwarden nhớ giúp bạn mật khẩu này không?" }, "notificationAddSave": { - "message": "Vâng, Lưu Ngay" + "message": "Lưu" }, "enableChangedPasswordNotification": { - "message": "Ask to update existing login" + "message": "Hỏi để cập nhật đăng nhập hiện có" }, "changedPasswordNotificationDesc": { "message": "Ask to update a login's password when a change is detected on a website." @@ -612,10 +612,10 @@ "message": "Bạn có muốn cập nhật mật khẩu này trên Bitwarden không?" }, "notificationChangeSave": { - "message": "Yes, Update Now" + "message": "Cập nhật" }, "enableContextMenuItem": { - "message": "Show context menu options" + "message": "Hiển thị tuỳ chọn menu ngữ cảnh" }, "contextMenuItemDesc": { "message": "Use a secondary click to access password generation and matching logins for the website. " @@ -628,7 +628,7 @@ "message": "Chọn phương thức mặc định để kiểm tra so sánh URI cho các đăng nhập khi xử lí các hành động như là tự động điền." }, "theme": { - "message": "Giao diện" + "message": "Chủ đề" }, "themeDesc": { "message": "Thay đổi màu sắc ứng dụng." @@ -646,7 +646,7 @@ "description": "'Solarized' is a noun and the name of a color scheme. It should not be translated." }, "exportVault": { - "message": "Xuất Kho" + "message": "Xuất kho lưu trữ" }, "fileFormat": { "message": "File Format" @@ -656,7 +656,7 @@ "description": "WARNING (should stay in capitalized letters if the language permits)" }, "confirmVaultExport": { - "message": "Xác nhận xuất mật khẩu" + "message": "Xác nhận xuất kho lưu trữ" }, "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." @@ -668,7 +668,7 @@ "message": "Account encryption keys are unique to each Bitwarden user account, so you can't import an encrypted export into a different account." }, "exportMasterPassword": { - "message": "Nhập mật khẩu chủ để xuất kho dữ liệu của bạn." + "message": "Nhập mật khẩu chính để xuất kho lưu trữ của bạn." }, "shared": { "message": "Đã chia sẻ" @@ -680,13 +680,13 @@ "message": "Bitwarden allows you to share your vault items with others by using an organization. Would you like to visit the bitwarden.com website to learn more?" }, "moveToOrganization": { - "message": "Chuyển tới Tổ chức" + "message": "Di chuyển đến tổ chức" }, "share": { "message": "Chia sẻ" }, "movedItemToOrg": { - "message": "$ITEMNAME$ chuyển tới $ORGNAME$", + "message": "$ITEMNAME$ đã được di chuyển đến $ORGNAME$", "placeholders": { "itemname": { "content": "$1", @@ -738,7 +738,7 @@ "message": "Tập tin" }, "selectFile": { - "message": "Chọn 1 tập tin." + "message": "Chọn tập tin" }, "maxFileSize": { "message": "Kích thước tối đa của tệp tin là 500MB." @@ -756,7 +756,7 @@ "message": "Quản lý Thành viên" }, "premiumManageAlert": { - "message": "Bạn có thể quản lý các thành viên trong trang web Bitwarden. Bạn có muốn truy cập bitwarden.com bây giờ?" + "message": "Bạn có thể quản lí tư cách thành viên của mình trên trang web kho lưu trữ bitwarden.com. Bạn có muốn truy cập trang web ngay bây giờ không?" }, "premiumRefresh": { "message": "Làm mới thành viên" @@ -771,16 +771,16 @@ "message": "1GB bộ nhớ lưu trữ tập tin được mã hóa." }, "ppremiumSignUpTwoStep": { - "message": "Các tùy chọn xác thực hai lớp bổ sung như YubiKey, FIDO U2F và Duo." + "message": "Tuỳ chọn đăng nhập 2 bước bổ sung như YubiKey, FIDO U2F, và Duo." }, "ppremiumSignUpReports": { "message": "Thanh lọc mật khẩu, kiểm tra an toàn tài khoản và các báo cáo rò rĩ dữ liệu là để giữ cho kho của bạn an toàn." }, "ppremiumSignUpTotp": { - "message": "Mã xác nhận TOTP (2FA) để đăng nhập vào kho mật khẩu của bạn." + "message": "Trình tạo mã xác nhận TOTP (2FA) để đăng nhập vào kho lưu trữ của bạn." }, "ppremiumSignUpSupport": { - "message": "Hỗ trợ khách hàng ưu tiên." + "message": "Ưu tiên hỗ trợ khách hàng." }, "ppremiumSignUpFuture": { "message": "Tất cả các tính năng cao cấp trong tương lai. Nó sẽ sớm xuất hiện!" @@ -795,10 +795,10 @@ "message": "Bạn là một thành viên cao cấp!" }, "premiumCurrentMemberThanks": { - "message": "Cảm ơn bạn đã hỗ trợ Bitwarden." + "message": "Cảm ơn bạn vì đã hỗ trợ Bitwarden." }, "premiumPrice": { - "message": "Tất cả chỉ với $PRICE$ /năm!", + "message": "Tất cả chỉ với $PRICE$/năm!", "placeholders": { "price": { "content": "$1", @@ -810,13 +810,13 @@ "message": "Làm mới hoàn tất" }, "enableAutoTotpCopy": { - "message": "Copy TOTP automatically" + "message": "Tự động sao chép TOTP" }, "disableAutoTotpCopyDesc": { "message": "Nếu đăng nhập của bạn có một khóa xác thực gắn liền với nó, mã xác nhận TOTP sẽ được tự động sao chép vào bộ nhớ tạm của bạn bất cứ khi nào bạn tự động điền thông tin đăng nhập." }, "enableAutoBiometricsPrompt": { - "message": "Ask for biometrics on launch" + "message": "Yêu cầu sinh trắc học khi khởi chạy" }, "premiumRequired": { "message": "Cần có tài khoản cao cấp" @@ -828,7 +828,7 @@ "message": "Nhập mã xác nhận 6 chữ số từ ứng dụng xác thực của bạn." }, "enterVerificationCodeEmail": { - "message": "Nhập mã xác nhận 6 chữ số đã được gửi tới $EMAIL$.", + "message": "Nhập mã xác nhận 6 chữ số đã được gửi tới email", "placeholders": { "email": { "content": "$1", @@ -837,7 +837,7 @@ } }, "verificationCodeEmailSent": { - "message": "Email xác minh được gửi tới $EMAIL$.", + "message": "Email xác minh đã được gửi tới $EMAIL$.", "placeholders": { "email": { "content": "$1", @@ -852,13 +852,13 @@ "message": "Gửi lại email chứa mã xác nhận" }, "useAnotherTwoStepMethod": { - "message": "Sử dụng phương pháp xác thực hai lớp khác" + "message": "Sử dụng phương pháp đăng nhập 2 bước khác" }, "insertYubiKey": { "message": "Lắp YubiKey vào cổng USB máy tính của bạn, sau đó chạm vào nút trên nó." }, "insertU2f": { - "message": "Lắp khóa bảo mật vào cổng USB của máy tính. Nếu nó có một nút, nhấn vào nó." + "message": "Lắp khóa bảo mật vào cổng USB máy tính của bạn. Nếu nó có một nút, hãy nhấn vào nó." }, "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." @@ -870,13 +870,13 @@ "message": "Xác thực WebAuthn" }, "loginUnavailable": { - "message": "Đăng nhập không sẵn có" + "message": "Đăng nhập không có sẵn" }, "noTwoStepProviders": { "message": "Tài khoản này đã kích hoạt xác thực hai lớp, tuy nhiên, trình duyệt này không hỗ trợ cấu hình dịch vụ xác thực hai lớp đang sử dụng." }, "noTwoStepProviders2": { - "message": "Vui lòng sử dụng trình duyệt web được hỗ trợ (chẳng hạn như Chrome) và/hoặc thêm dịch vụ bổ sung được hỗ trợ tốt hơn trên các trình duyệt web (chẳng hạn như một ứng dụng xác thực)." + "message": "Hãy sử dụng trình duyệt web được hỗ trợ (chẳng hạn như Chrome) và/hoặc thêm dịch vụ bổ sung được hỗ trợ tốt hơn trên các trình duyệt web (chẳng hạn như một ứng dụng xác thực)." }, "twoStepOptions": { "message": "Tùy chọn xác thực hai lớp" @@ -898,10 +898,10 @@ "message": "Khóa bảo mật YubiKey OTP" }, "yubiKeyDesc": { - "message": "Sử dụng YubiKey để truy cập tài khoản của bạn. Làm việc với thiết bị YubiKey 4, 4 Nano, 4C và NEO." + "message": "Sử dụng YubiKey để truy cập tài khoản của bạn. Hoạt động với thiết bị YubiKey 4, 4 Nano, 4C và NEO." }, "duoDesc": { - "message": "Xác minh với Duo Security sử dụng ứng dụng Duo Mobile, SMS, cuộc gọi điện thoại, hoặc khoá bảo mật U2F.", + "message": "Xác minh với Duo Security bằng ứng dụng Duo Mobile, SMS, cuộc gọi điện thoại, hoặc khoá bảo mật U2F.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { @@ -921,7 +921,7 @@ "message": "Mã xác thực sẽ được gửi qua email cho bạn." }, "selfHostedEnvironment": { - "message": "Môi trường độc lập" + "message": "Môi trường tự lưu trữ" }, "selfHostedEnvironmentFooter": { "message": "Chỉ định liên kết cơ bản của cài đặt bitwarden tại chỗ của bạn." @@ -930,19 +930,19 @@ "message": "Môi trường tùy chỉnh" }, "customEnvironmentFooter": { - "message": "Đối với người dùng nâng cao. Bạn có thể chỉ định liên kết cơ bản của mỗi dịch vụ một cách độc lập." + "message": "Đối với người dùng nâng cao. Bạn có thể chỉ định URL cơ bản của mỗi dịch vụ một cách độc lập." }, "baseUrl": { - "message": "Địa chỉ máy chủ" + "message": "URL máy chủ" }, "apiUrl": { "message": "Địa chỉ API máy chủ" }, "webVaultUrl": { - "message": "Địa chỉ máy chủ kho web" + "message": "URL máy chủ của trang web kho lưu trữ" }, "identityUrl": { - "message": "Địa chỉ nhận dạng máy chủ" + "message": "URL máy chủ nhận dạng" }, "notificationsUrl": { "message": "Notifications Server URL" @@ -954,22 +954,22 @@ "message": "Địa chỉ môi trường đã được lưu." }, "enableAutoFillOnPageLoad": { - "message": "Bật Tự động điền vào Tải trang" + "message": "Tự động điền khi tải trang" }, "enableAutoFillOnPageLoadDesc": { - "message": "Nếu có một biểu mẫu đăng nhập được phát hiện, thực hiện tự động điền khi trang web tải xong." + "message": "Nếu phát hiện biểu mẫu đăng nhập, thực hiện tự động điền khi trang web tải xong." }, "experimentalFeature": { "message": "Đây là một tính năng thử nghiệm. Sử dụng nó có thể gây ra rủi ro cho bạn." }, "defaultAutoFillOnPageLoad": { - "message": "Default autofill setting for login items" + "message": "Cài đặt tự động điền mặc định cho mục đăng nhập" }, "defaultAutoFillOnPageLoadDesc": { - "message": "You can turn off auto-fill on page load for individual login items from the item's Edit view." + "message": "Bạn có thể tắt tự động điền khi tải trang cho mục đăng nhập riêng lẻ từ chế độ xem Chỉnh sửa mục." }, "itemAutoFillOnPageLoad": { - "message": "Tự động điền khi tải trang (nếu được kích hoạt trong Tùy chọn)" + "message": "Tự động điền khi tải trang (nếu được thiết lập trong Tùy chọn)" }, "autoFillOnPageLoadUseDefault": { "message": "Sử dụng thiết lập mặc định" @@ -990,10 +990,10 @@ "message": "Tự động điền thông tin đăng nhập người dùng cho trang web hiện tại." }, "commandGeneratePasswordDesc": { - "message": "Tạo và sao chép một mật khẩu ngẫu nhiên mới vào bộ nhớ tạm." + "message": "Tạo và sao chép một mật khẩu ngẫu nhiên mới vào khay nhớ tạm" }, "commandLockVaultDesc": { - "message": "Khóa bảo mật" + "message": "Khoá kho lưu trữ" }, "privateModeWarning": { "message": "Hỗ trợ cho chế độ riêng tư đang được thử nghiệm và hạn chế một số tính năng." @@ -1017,7 +1017,7 @@ "message": "Văn bản" }, "cfTypeHidden": { - "message": "Ẩn" + "message": "Đã ẩn đi" }, "cfTypeBoolean": { "message": "Đúng/Sai" @@ -1037,13 +1037,13 @@ "message": "Trình duyệt này không thể xử lý các yêu cầu U2F trong cửa sổ popup này. Bạn có muốn mở popup này trong cửa sổ mới để bạn có thể đăng nhập thông qua U2F?" }, "enableFavicon": { - "message": "Show website icons" + "message": "Hiển thị biểu tượng trang web" }, "faviconDesc": { - "message": "Show a recognizable image next to each login." + "message": "Hiển thị một ảnh nhận dạng bên cạnh mỗi lần đăng nhập." }, "enableBadgeCounter": { - "message": "Show badge counter" + "message": "Hiển thị biểu tượng bộ đếm" }, "badgeCounterDesc": { "message": "Indicate how many logins you have for the current web page." @@ -1178,7 +1178,7 @@ "message": "Mã bưu chính" }, "country": { - "message": "Quốc Gia" + "message": "Quốc gia" }, "type": { "message": "Loại" @@ -1190,7 +1190,7 @@ "message": "Đăng nhập" }, "typeSecureNote": { - "message": "Lưu ý an toàn" + "message": "Ghi chú bảo mật" }, "typeCard": { "message": "Thẻ" @@ -1199,13 +1199,13 @@ "message": "Danh tính" }, "passwordHistory": { - "message": "Lịch sử Mật khẩu" + "message": "Lịch sử mật khẩu" }, "back": { "message": "Quay lại" }, "collections": { - "message": "Các Bộ Sưu Tập" + "message": "Bộ sưu tập" }, "favorites": { "message": "Yêu thích" @@ -1236,7 +1236,7 @@ "message": "Kiểm tra xem mật khẩu có bị lộ không." }, "passwordExposed": { - "message": "Mật khẩu này đã bị lộ $VALUE$ trong các dữ liệu vi phạm. Bạn nên thay đổi nó.", + "message": "Mật khẩu này đã bị lộ $VALUE$ lần trong các báo cáo lộ lọt dữ liệu. Bạn nên thay đổi nó.", "placeholders": { "value": { "content": "$1", @@ -1245,18 +1245,18 @@ } }, "passwordSafe": { - "message": "Mật khẩu này không được tìm thấy trong bất kỳ dữ liệu vi phạm nào được biết đến. Nó an toàn để sử dụng." + "message": "Mật khẩu này không được tìm thấy trong bất kỳ báo cáo lộ lọt dữ liệu nào được biết đến. Bạn có thể tiếp tục sử dụng nó." }, "baseDomain": { "message": "Tên miền cơ sở", "description": "Domain name. Ex. website.com" }, "domainName": { - "message": "Domain name", + "message": "Tên miền", "description": "Domain name. Ex. website.com" }, "host": { - "message": "Máy chủ", + "message": "Máy chủ lưu trữ", "description": "A URL's host value. For example, the host of https://sub.domain.com:443 is 'sub.domain.com:443'." }, "exact": { @@ -1293,7 +1293,7 @@ "description": "An entity of multiple related people (ex. a team or business organization)." }, "types": { - "message": "Các loại" + "message": "Loại" }, "allItems": { "message": "Tất cả các mục" @@ -1312,11 +1312,11 @@ "description": "ex. Date this item was updated" }, "dateCreated": { - "message": "Created", + "message": "Ngày tạo", "description": "ex. Date this item was created" }, "datePasswordUpdated": { - "message": "Password Updated", + "message": "Ngày cập nhật mật khẩu", "description": "ex. Date this password was updated" }, "neverLockWarning": { @@ -1357,7 +1357,7 @@ "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." }, "unlockWithPin": { - "message": "Mở khóa với mã PIN" + "message": "Mở khóa bằng mã PIN" }, "setYourPinCode": { "message": "Đặt mã PIN của bạn để mở khóa Bitwarden. Cài đặt mã PIN của bạn sẽ bị xóa nếu bạn hoàn toàn đăng xuất khỏi ứng dụng." @@ -1393,7 +1393,7 @@ "message": "Có một hoặc vài chính sách của tổ chức đang làm ảnh hưởng đến cài đặt tạo mật khẩu của bạn." }, "vaultTimeoutAction": { - "message": "Hành Động Khi Hết Thời Gian Chờ" + "message": "Hành động khi hết thời gian chờ của kho lưu trữ" }, "lock": { "message": "Khóa", @@ -1425,7 +1425,7 @@ "message": "Mục đã được khôi phục" }, "vaultTimeoutLogOutConfirmation": { - "message": "Đăng xuất sẽ xóa tất các truy cập vào kho của bạn và yêu cầu xác thực trực tuyến sau khi khoảng thời gian chờ hết. Bạn có chắc bạn muốn dùng cài đặt này?" + "message": "Việc đăng xuất sẽ loại bỏ tất cả truy cập vào kho lưu trữ của bạn và yêu cầu xác minh trực tuyến sau khi hết giai đoạn thời gian chờ. Bạn có chắc chắn muốn dùng cài đặt này không?" }, "vaultTimeoutLogOutConfirmationTitle": { "message": "Xác nhận hành động khi hết thời gian chờ" @@ -1434,10 +1434,10 @@ "message": "Tự động điền và Lưu" }, "autoFillSuccessAndSavedUri": { - "message": "Tự động điền và Lưu URI" + "message": "Đã tự động điền mục và lưu URI" }, "autoFillSuccess": { - "message": "Tự động điền" + "message": "Đã tự động điền mục " }, "setMasterPassword": { "message": "Đặt mật khẩu chính" @@ -1446,7 +1446,7 @@ "message": "Tổ chức của bạn yêu cầu mật khẩu chính của bạn phải đáp ứng các yêu cầu sau:" }, "policyInEffectMinComplexity": { - "message": "Điểm số phức tạp là", + "message": "Minimum complexity score of $SCORE$", "placeholders": { "score": { "content": "$1", @@ -1539,10 +1539,10 @@ "message": "Tài khoản không đúng" }, "biometricsNotEnabledTitle": { - "message": "Nhận dạng sinh trắc học chưa được bật" + "message": "Sinh trắc học chưa được cài đặt" }, "biometricsNotEnabledDesc": { - "message": "Browser biometrics requires desktop biometric to be set up in the settings first." + "message": "Sinh trắc học trên trình duyệt yêu cầu sinh trắc học trên máy tính phải được cài đặt trước." }, "biometricsNotSupportedTitle": { "message": "Nhận dạng sinh trắc học không được hỗ trợ" @@ -1569,13 +1569,13 @@ "message": "An organization policy is affecting your ownership options." }, "excludedDomains": { - "message": "Excluded domains" + "message": "Tên miền đã loại trừ" }, "excludedDomainsDesc": { "message": "Bitwarden will not ask to save login details for these domains. You must refresh the page for changes to take effect." }, "excludedDomainsInvalidDomain": { - "message": "$DOMAIN$ is not a valid domain", + "message": "$DOMAIN$ không phải là tên miền hợp lệ", "placeholders": { "domain": { "content": "$1", @@ -1623,13 +1623,13 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "removePassword": { - "message": "Đã xóa mật khẩu" + "message": "Xóa mật khẩu" }, "delete": { "message": "Xóa" }, "removedPassword": { - "message": "Xóa mật khẩu" + "message": "Đã xóa mật khẩu" }, "deletedSend": { "message": "Đã xóa Send", @@ -1643,7 +1643,7 @@ "message": "Đã tắt" }, "removePasswordConfirmation": { - "message": "Bạn có chắc chắn muốn xóa mật khẩu này?" + "message": "Bạn có chắc chắn muốn xóa mật khẩu này không?" }, "deleteSend": { "message": "Xóa Send", @@ -1662,7 +1662,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNameDesc": { - "message": "Một tên gợi nhớ để mô tả về Send này.", + "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": { @@ -1686,7 +1686,7 @@ "message": "1 ngày" }, "days": { - "message": "#DAYS# ngày", + "message": "$DAYS$ ngày", "placeholders": { "days": { "content": "$1", @@ -1709,7 +1709,7 @@ "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.", + "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": { @@ -1717,28 +1717,28 @@ "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.", + "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": "Hide this Send's text by default.", + "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": "Current access count" + "message": "Số lượng truy cập hiện tại" }, "createSend": { - "message": "New Send", + "message": "Send mới", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "newPassword": { "message": "Mật khẩu mới" }, "sendDisabled": { - "message": "Đã tắt gửi", + "message": "Đã loại bỏ Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { @@ -1805,7 +1805,7 @@ "message": "Xác nhận mật khẩu chính" }, "passwordConfirmationDesc": { - "message": "Hành động này được bảo vệ. Để tiếp tục, vui lòng nhập lại mật khẩu chính của bạn." + "message": "Hành động này được bảo vệ. Để tiếp tục, hãy nhập lại mật khẩu chính của bạn để xác minh danh tính." }, "emailVerificationRequired": { "message": "Yêu cầu xác nhận danh tính qua email" @@ -1814,7 +1814,7 @@ "message": "Bạn phải xác nhận email để sử dụng tính năng này. Bạn có thể xác minh email trên web." }, "updatedMasterPassword": { - "message": "Mật khẩu chính đã cập nhật" + "message": "Đã cập nhật mật khẩu chính" }, "updateMasterPassword": { "message": "Cập nhật mật khẩu chính" @@ -1829,7 +1829,7 @@ "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." }, "selectFolder": { - "message": "Chọn thư mục" + "message": "Chọn thư mục..." }, "ssoCompleteRegistration": { "message": "In order to complete logging in with SSO, please set a master password to access and protect your vault." @@ -1857,7 +1857,7 @@ "message": "Your vault timeout exceeds the restrictions set by your organization." }, "vaultExportDisabled": { - "message": "Xuất mật khẩu đăng tắt" + "message": "Xuất kho lưu trữ không có sẵn" }, "personalVaultExportPolicyInEffect": { "message": "One or more organization policies prevents you from exporting your individual vault." @@ -1869,7 +1869,7 @@ "message": "No unique identifier found." }, "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$ hiện đang dùng SSO với khoá máy chủ tự lưu trữ. Từ giờ không cần mật khẩu chính để đăng nhập vào tổ chức này nữa.", "placeholders": { "organization": { "content": "$1", @@ -1887,13 +1887,13 @@ "message": "Đã xóa mật khẩu chính" }, "leaveOrganizationConfirmation": { - "message": "Are you sure you want to leave this organization?" + "message": "Bạn có chắc chắn muốn rời tổ chức này không?" }, "leftOrganization": { - "message": "You have left the organization." + "message": "Bạn đã rời khỏi tổ chức." }, "toggleCharacterCount": { - "message": "Toggle character count" + "message": "Bật tắt đếm kí tự" }, "sessionTimeout": { "message": "Your session has timed out. Please go back and try logging in again." @@ -1911,47 +1911,47 @@ } }, "error": { - "message": "Error" + "message": "Lỗi" }, "regenerateUsername": { - "message": "Regenerate username" + "message": "Tạo lại tên người dùng" }, "generateUsername": { - "message": "Generate username" + "message": "Tạo tên người dùng" }, "usernameType": { - "message": "Username type" + "message": "Loại tên người dùng" }, "plusAddressedEmail": { - "message": "Plus addressed email", + "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" }, "plusAddressedEmailDesc": { "message": "Use your email provider's sub-addressing capabilities." }, "catchallEmail": { - "message": "Catch-all email" + "message": "Email Catch-all" }, "catchallEmailDesc": { "message": "Use your domain's configured catch-all inbox." }, "random": { - "message": "Random" + "message": "Ngẫu nhiên" }, "randomWord": { - "message": "Random word" + "message": "Từ ngẫu nhiên" }, "websiteName": { - "message": "Website name" + "message": "Tên website" }, "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" + "message": "Bạn muốn tạo gì?" }, "passwordType": { - "message": "Password type" + "message": "Loại mật khẩu" }, "service": { - "message": "Service" + "message": "Dịch vụ" }, "forwardedEmail": { "message": "Forwarded email alias" @@ -1960,11 +1960,11 @@ "message": "Generate an email alias with an external forwarding service." }, "hostname": { - "message": "Hostname", + "message": "Tên máy chủ", "description": "Part of a URL." }, "apiAccessToken": { - "message": "API Access Token" + "message": "Mã thông báo truy cập API" }, "apiKey": { "message": "Khóa API" @@ -2003,13 +2003,13 @@ "message": "để đặt lại cài đặt đã thiết đặt từ trước" }, "serverVersion": { - "message": "Server version" + "message": "Phiên bản máy chủ" }, "selfHosted": { - "message": "Self-hosted" + "message": "Tự lưu trữ" }, "thirdParty": { - "message": "Third-party" + "message": "Bên thứ ba" }, "thirdPartyServerMessage": { "message": "Connected to third-party server implementation, $SERVERNAME$. Please verify bugs using the official server, or report them to the third-party server.", @@ -2021,7 +2021,7 @@ } }, "lastSeenOn": { - "message": "last seen on: $DATE$", + "message": "nhìn thấy lần cuối: $DATE$", "placeholders": { "date": { "content": "$1", @@ -2030,18 +2030,18 @@ } }, "loginWithMasterPassword": { - "message": "Log in with master password" + "message": "Đăng nhập bằng mật khẩu chính" }, "loggingInAs": { - "message": "Logging in as" + "message": "Đang đăng nhập với tên" }, "notYou": { - "message": "Not you?" + "message": "Không phải bạn sao?" }, "newAroundHere": { - "message": "New around here?" + "message": "Bạn mới tới đây sao?" }, "rememberEmail": { - "message": "Remember email" + "message": "Ghi nhớ email" } } diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index b05d505d21d..b4e2bd0304f 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -2039,7 +2039,7 @@ "message": "不是你?" }, "newAroundHere": { - "message": "新建在这里?" + "message": "初来乍到吗?" }, "rememberEmail": { "message": "记住电子邮件地址" diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index a4ff34345c1..0fcb389a61f 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -2039,7 +2039,7 @@ "message": "不是您嗎?" }, "newAroundHere": { - "message": "New around here?" + "message": "第一次使用?" }, "rememberEmail": { "message": "記住電子郵件地址" diff --git a/apps/browser/src/alarms/alarm-state.ts b/apps/browser/src/alarms/alarm-state.ts new file mode 100644 index 00000000000..774bcfe1c03 --- /dev/null +++ b/apps/browser/src/alarms/alarm-state.ts @@ -0,0 +1,66 @@ +import { BrowserApi } from "../browser/browserApi"; +import { clearClipboardAlarmName } from "../clipboard"; + +export const alarmKeys = [clearClipboardAlarmName] as const; +export type AlarmKeys = typeof alarmKeys[number]; + +type AlarmState = { [T in AlarmKeys]: number | undefined }; + +const alarmState: AlarmState = { + clearClipboard: null, + //TODO once implemented vaultTimeout: null; + //TODO once implemented checkNotifications: null; + //TODO once implemented (if necessary) processReload: null; +}; + +/** + * Retrieves the set alarm time (planned execution) for a give an commandName {@link AlarmState} + * @param commandName A command that has been previously registered with {@link AlarmState} + * @returns {Promise} null or Unix epoch timestamp when the alarm action is supposed to execute + * @example + * // getAlarmTime(clearClipboard) + */ +export async function getAlarmTime(commandName: AlarmKeys): Promise { + let alarmTime: number; + if (BrowserApi.manifestVersion == 3) { + const fromSessionStore = await chrome.storage.session.get(commandName); + alarmTime = fromSessionStore[commandName]; + } else { + alarmTime = alarmState[commandName]; + } + + return alarmTime; +} + +/** + * Registers an action that should execute after the given time has passed + * @param commandName A command that has been previously registered with {@link AlarmState} + * @param delay_ms The number of ms from now in which the command should execute from + * @example + * // setAlarmTime(clearClipboard, 5000) register the clearClipboard action which will execute when at least 5 seconds from now have passed + */ +export async function setAlarmTime(commandName: AlarmKeys, delay_ms: number): Promise { + if (!delay_ms || delay_ms === 0) { + await this.clearAlarmTime(commandName); + return; + } + + const time = Date.now() + delay_ms; + await setAlarmTimeInternal(commandName, time); +} + +/** + * Clears the time currently set for a given command + * @param commandName A command that has been previously registered with {@link AlarmState} + */ +export async function clearAlarmTime(commandName: AlarmKeys): Promise { + await setAlarmTimeInternal(commandName, null); +} + +async function setAlarmTimeInternal(commandName: AlarmKeys, time: number): Promise { + if (BrowserApi.manifestVersion == 3) { + await chrome.storage.session.set({ [commandName]: time }); + } else { + alarmState[commandName] = time; + } +} diff --git a/apps/browser/src/alarms/on-alarm-listener.ts b/apps/browser/src/alarms/on-alarm-listener.ts new file mode 100644 index 00000000000..4b246908778 --- /dev/null +++ b/apps/browser/src/alarms/on-alarm-listener.ts @@ -0,0 +1,26 @@ +import { ClearClipboard, clearClipboardAlarmName } from "../clipboard"; + +import { alarmKeys, clearAlarmTime, getAlarmTime } from "./alarm-state"; + +export const onAlarmListener = async (alarm: chrome.alarms.Alarm) => { + alarmKeys.forEach(async (key) => { + const executionTime = await getAlarmTime(key); + if (!executionTime) { + return; + } + + const currentDate = Date.now(); + if (executionTime > currentDate) { + return; + } + + await clearAlarmTime(key); + + switch (key) { + case clearClipboardAlarmName: + ClearClipboard.run(); + break; + default: + } + }); +}; diff --git a/apps/browser/src/alarms/register-alarms.ts b/apps/browser/src/alarms/register-alarms.ts new file mode 100644 index 00000000000..c4da2fc27bc --- /dev/null +++ b/apps/browser/src/alarms/register-alarms.ts @@ -0,0 +1,29 @@ +const NUMBER_OF_ALARMS = 6; + +export function registerAlarms() { + alarmsToBeCreated(NUMBER_OF_ALARMS); +} + +/** + * Creates staggered alarms that periodically (1min) raise OnAlarm events. The staggering is calculated based on the numnber of alarms passed in. + * @param numberOfAlarms Number of named alarms, that shall be registered + * @example + * // alarmsToBeCreated(2) results in 2 alarms separated by 30 seconds + * @example + * // alarmsToBeCreated(4) results in 4 alarms separated by 15 seconds + * @example + * // alarmsToBeCreated(6) results in 6 alarms separated by 10 seconds + * @example + * // alarmsToBeCreated(60) results in 60 alarms separated by 1 second + */ +function alarmsToBeCreated(numberOfAlarms: number): void { + const oneMinuteInMs = 60 * 1000; + const offset = oneMinuteInMs / numberOfAlarms; + + let calculatedWhen: number = Date.now() + offset; + + for (let index = 0; index < numberOfAlarms; index++) { + chrome.alarms.create(`bw_alarm${index}`, { periodInMinutes: 1, when: calculatedWhen }); + calculatedWhen += offset; + } +} diff --git a/apps/browser/src/background.ts b/apps/browser/src/background.ts index 504b5afb5c0..2c888cfd4b4 100644 --- a/apps/browser/src/background.ts +++ b/apps/browser/src/background.ts @@ -1,39 +1,32 @@ +import { onAlarmListener } from "./alarms/on-alarm-listener"; +import { registerAlarms } from "./alarms/register-alarms"; import MainBackground from "./background/main.background"; import { BrowserApi } from "./browser/browserApi"; -import { ClearClipboard } from "./clipboard"; -import { onCommandListener } from "./listeners/onCommandListener"; -import { onInstallListener } from "./listeners/onInstallListener"; -import { UpdateBadge } from "./listeners/update-badge"; - -const manifestV3MessageListeners: (( - serviceCache: Record, - message: { command: string } -) => void | Promise)[] = [UpdateBadge.messageListener]; -type AlarmAction = (executionTime: Date, serviceCache: Record) => void; - -const AlarmActions: AlarmAction[] = [ClearClipboard.run]; +import { + contextMenusClickedListener, + onCommandListener, + onInstallListener, + runtimeMessageListener, + tabsOnActivatedListener, + tabsOnReplacedListener, + tabsOnUpdatedListener, +} from "./listeners"; if (BrowserApi.manifestVersion === 3) { chrome.commands.onCommand.addListener(onCommandListener); chrome.runtime.onInstalled.addListener(onInstallListener); - chrome.tabs.onActivated.addListener(UpdateBadge.tabsOnActivatedListener); - chrome.tabs.onReplaced.addListener(UpdateBadge.tabsOnReplacedListener); - chrome.tabs.onUpdated.addListener(UpdateBadge.tabsOnUpdatedListener); - BrowserApi.messageListener("runtime.background", (message) => { - const serviceCache = {}; - - manifestV3MessageListeners.forEach((listener) => { - listener(serviceCache, message); - }); - }); - chrome.alarms.onAlarm.addListener((_alarm) => { - const executionTime = new Date(); - const serviceCache = {}; - - for (const alarmAction of AlarmActions) { - alarmAction(executionTime, serviceCache); + chrome.alarms.onAlarm.addListener(onAlarmListener); + registerAlarms(); + chrome.tabs.onActivated.addListener(tabsOnActivatedListener); + chrome.tabs.onReplaced.addListener(tabsOnReplacedListener); + chrome.tabs.onUpdated.addListener(tabsOnUpdatedListener); + chrome.contextMenus.onClicked.addListener(contextMenusClickedListener); + BrowserApi.messageListener( + "runtime.background", + (message: { command: string }, sender, sendResponse) => { + runtimeMessageListener(message, sender); } - }); + ); } else { const bitwardenMain = ((window as any).bitwardenMain = new MainBackground()); bitwardenMain.bootstrap().then(() => { diff --git a/apps/browser/src/background/contextMenus.background.ts b/apps/browser/src/background/contextMenus.background.ts index 76fbfd78a97..9a9ba0f5140 100644 --- a/apps/browser/src/background/contextMenus.background.ts +++ b/apps/browser/src/background/contextMenus.background.ts @@ -1,146 +1,38 @@ -import { AuthService } from "@bitwarden/common/abstractions/auth.service"; -import { CipherService } from "@bitwarden/common/abstractions/cipher.service"; -import { EventService } from "@bitwarden/common/abstractions/event.service"; -import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; -import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; -import { TotpService } from "@bitwarden/common/abstractions/totp.service"; -import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus"; -import { CipherRepromptType } from "@bitwarden/common/enums/cipherRepromptType"; -import { EventType } from "@bitwarden/common/enums/eventType"; -import { CipherView } from "@bitwarden/common/models/view/cipher.view"; - import { BrowserApi } from "../browser/browserApi"; +import { ContextMenuClickedHandler } from "../browser/context-menu-clicked-handler"; -import MainBackground from "./main.background"; import LockedVaultPendingNotificationsItem from "./models/lockedVaultPendingNotificationsItem"; export default class ContextMenusBackground { - private readonly noopCommandSuffix = "noop"; - private contextMenus: any; + private contextMenus: typeof chrome.contextMenus; - constructor( - private main: MainBackground, - private cipherService: CipherService, - private passwordGenerationService: PasswordGenerationService, - private platformUtilsService: PlatformUtilsService, - private authService: AuthService, - private eventService: EventService, - private totpService: TotpService - ) { + constructor(private contextMenuClickedHandler: ContextMenuClickedHandler) { this.contextMenus = chrome.contextMenus; } - async init() { + init() { if (!this.contextMenus) { return; } - this.contextMenus.onClicked.addListener( - async (info: chrome.contextMenus.OnClickData, tab: chrome.tabs.Tab) => { - if (info.menuItemId === "generate-password") { - await this.generatePasswordToClipboard(); - } else if (info.menuItemId === "copy-identifier") { - await this.getClickedElement(tab, info.frameId); - } else if ( - info.parentMenuItemId === "autofill" || - info.parentMenuItemId === "copy-username" || - info.parentMenuItemId === "copy-password" || - info.parentMenuItemId === "copy-totp" - ) { - await this.cipherAction(tab, info); - } - } + this.contextMenus.onClicked.addListener((info, tab) => + this.contextMenuClickedHandler.run(info, tab) ); BrowserApi.messageListener( "contextmenus.background", - async (msg: any, sender: chrome.runtime.MessageSender, sendResponse: any) => { + async ( + msg: { command: string; data: LockedVaultPendingNotificationsItem }, + sender: chrome.runtime.MessageSender, + sendResponse: any + ) => { if (msg.command === "unlockCompleted" && msg.data.target === "contextmenus.background") { - await this.cipherAction( - msg.data.commandToRetry.sender.tab, - msg.data.commandToRetry.msg.data + await this.contextMenuClickedHandler.cipherAction( + msg.data.commandToRetry.msg.data, + msg.data.commandToRetry.sender.tab ); } } ); } - - private async generatePasswordToClipboard() { - const options = (await this.passwordGenerationService.getOptions())?.[0] ?? {}; - const password = await this.passwordGenerationService.generatePassword(options); - this.platformUtilsService.copyToClipboard(password, { window: window }); - this.passwordGenerationService.addHistory(password); - } - - private async getClickedElement(tab: chrome.tabs.Tab, frameId: number) { - if (tab == null) { - return; - } - - BrowserApi.tabSendMessage(tab, { command: "getClickedElement" }, { frameId: frameId }); - } - - private async cipherAction(tab: chrome.tabs.Tab, info: chrome.contextMenus.OnClickData) { - if (typeof info.menuItemId !== "string") { - return; - } - - const id = info.menuItemId.split("_")[1]; - - if ((await this.authService.getAuthStatus()) < AuthenticationStatus.Unlocked) { - const retryMessage: LockedVaultPendingNotificationsItem = { - commandToRetry: { - msg: { command: this.noopCommandSuffix, data: info }, - sender: { tab: tab }, - }, - target: "contextmenus.background", - }; - await BrowserApi.tabSendMessageData( - tab, - "addToLockedVaultPendingNotifications", - retryMessage - ); - - BrowserApi.tabSendMessageData(tab, "promptForLogin"); - return; - } - - let cipher: CipherView; - if (id === this.noopCommandSuffix) { - const ciphers = await this.cipherService.getAllDecryptedForUrl(tab.url); - cipher = ciphers.find((c) => c.reprompt === CipherRepromptType.None); - } else { - const ciphers = await this.cipherService.getAllDecrypted(); - cipher = ciphers.find((c) => c.id === id); - } - - if (cipher == null) { - return; - } - - if (info.parentMenuItemId === "autofill") { - await this.startAutofillPage(tab, cipher); - } else if (info.parentMenuItemId === "copy-username") { - this.platformUtilsService.copyToClipboard(cipher.login.username, { window: window }); - } else if (info.parentMenuItemId === "copy-password") { - this.platformUtilsService.copyToClipboard(cipher.login.password, { window: window }); - this.eventService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id); - } else if (info.parentMenuItemId === "copy-totp") { - const totpValue = await this.totpService.getCode(cipher.login.totp); - this.platformUtilsService.copyToClipboard(totpValue, { window: window }); - } - } - - private async startAutofillPage(tab: chrome.tabs.Tab, cipher: CipherView) { - this.main.loginToAutoFill = cipher; - if (tab == null) { - return; - } - - BrowserApi.tabSendMessage(tab, { - command: "collectPageDetails", - tab: tab, - sender: "contextMenu", - }); - } } diff --git a/apps/browser/src/background/idle.background.ts b/apps/browser/src/background/idle.background.ts index 56e2e2dd7e1..1a8c5ae5c50 100644 --- a/apps/browser/src/background/idle.background.ts +++ b/apps/browser/src/background/idle.background.ts @@ -1,7 +1,7 @@ import { NotificationsService } from "@bitwarden/common/abstractions/notifications.service"; import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; const IdleInterval = 60 * 5; // 5 minutes @@ -12,7 +12,7 @@ export default class IdleBackground { constructor( private vaultTimeoutService: VaultTimeoutService, - private stateService: StateService, + private stateService: BrowserStateService, private notificationsService: NotificationsService ) { this.idle = chrome.idle || (browser != null ? browser.idle : null); diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index f02546f3926..a10098049ee 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -1,3 +1,4 @@ +import { AvatarUpdateService as AvatarUpdateServiceAbstraction } from "@bitwarden/common/abstractions/account/avatar-update.service"; import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service"; import { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/abstractions/appId.service"; import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service"; @@ -7,7 +8,8 @@ import { CollectionService as CollectionServiceAbstraction } from "@bitwarden/co import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/abstractions/cryptoFunction.service"; import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service"; -import { EventService as EventServiceAbstraction } from "@bitwarden/common/abstractions/event.service"; +import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service"; import { ExportService as ExportServiceAbstraction } from "@bitwarden/common/abstractions/export.service"; import { Fido2UserInterfaceService as Fido2UserInterfaceServiceAbstraction } from "@bitwarden/common/abstractions/fido2/fido2-user-interface.service.abstraction"; import { Fido2Service as Fido2ServiceAbstraction } from "@bitwarden/common/abstractions/fido2/fido2.service.abstraction"; @@ -19,7 +21,7 @@ import { KeyConnectorService as KeyConnectorServiceAbstraction } from "@bitwarde import { LogService as LogServiceAbstraction } from "@bitwarden/common/abstractions/log.service"; import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/abstractions/messaging.service"; import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service"; -import { OrganizationService as OrganizationServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization.service.abstraction"; +import { InternalOrganizationService as InternalOrganizationServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization.service.abstraction"; import { PasswordGenerationService as PasswordGenerationServiceAbstraction } from "@bitwarden/common/abstractions/passwordGeneration.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/abstractions/policy/policy-api.service.abstraction"; @@ -28,7 +30,10 @@ import { ProviderService as ProviderServiceAbstraction } from "@bitwarden/common import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abstractions/search.service"; import { SendService as SendServiceAbstraction } from "@bitwarden/common/abstractions/send.service"; import { SettingsService as SettingsServiceAbstraction } from "@bitwarden/common/abstractions/settings.service"; -import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service"; +import { + AbstractMemoryStorageService, + AbstractStorageService, +} from "@bitwarden/common/abstractions/storage.service"; import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/abstractions/sync/sync.service.abstraction"; import { SyncNotifierService as SyncNotifierServiceAbstraction } from "@bitwarden/common/abstractions/sync/syncNotifier.service.abstraction"; import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/abstractions/system.service"; @@ -40,12 +45,10 @@ import { UserVerificationService as UserVerificationServiceAbstraction } from "@ import { UsernameGenerationService as UsernameGenerationServiceAbstraction } from "@bitwarden/common/abstractions/usernameGeneration.service"; import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; import { VaultTimeoutSettingsService as VaultTimeoutSettingsServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service"; -import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus"; -import { CipherRepromptType } from "@bitwarden/common/enums/cipherRepromptType"; -import { CipherType } from "@bitwarden/common/enums/cipherType"; import { StateFactory } from "@bitwarden/common/factories/stateFactory"; import { GlobalState } from "@bitwarden/common/models/domain/global-state"; import { CipherView } from "@bitwarden/common/models/view/cipher.view"; +import { AvatarUpdateService } from "@bitwarden/common/services/account/avatar-update.service"; import { ApiService } from "@bitwarden/common/services/api.service"; import { AppIdService } from "@bitwarden/common/services/appId.service"; import { AuditService } from "@bitwarden/common/services/audit.service"; @@ -56,7 +59,8 @@ import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service import { ContainerService } from "@bitwarden/common/services/container.service"; import { EncryptServiceImplementation } from "@bitwarden/common/services/cryptography/encrypt.service.implementation"; import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/services/cryptography/multithread-encrypt.service.implementation"; -import { EventService } from "@bitwarden/common/services/event.service"; +import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service"; +import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; import { ExportService } from "@bitwarden/common/services/export.service"; import { Fido2Service } from "@bitwarden/common/services/fido2/fido2.service"; import { FileUploadService } from "@bitwarden/common/services/fileUpload.service"; @@ -64,14 +68,11 @@ import { FolderApiService } from "@bitwarden/common/services/folder/folder-api.s import { KeyConnectorService } from "@bitwarden/common/services/keyConnector.service"; import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service"; import { NotificationsService } from "@bitwarden/common/services/notifications.service"; -import { OrganizationService } from "@bitwarden/common/services/organization/organization.service"; import { PasswordGenerationService } from "@bitwarden/common/services/passwordGeneration.service"; import { PolicyApiService } from "@bitwarden/common/services/policy/policy-api.service"; -import { PolicyService } from "@bitwarden/common/services/policy/policy.service"; import { ProviderService } from "@bitwarden/common/services/provider.service"; import { SearchService } from "@bitwarden/common/services/search.service"; import { SendService } from "@bitwarden/common/services/send.service"; -import { SettingsService } from "@bitwarden/common/services/settings.service"; import { StateMigrationService } from "@bitwarden/common/services/stateMigration.service"; import { SyncService } from "@bitwarden/common/services/sync/sync.service"; import { SyncNotifierService } from "@bitwarden/common/services/sync/syncNotifier.service"; @@ -86,33 +87,39 @@ import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vaultTim import { WebCryptoFunctionService } from "@bitwarden/common/services/webCryptoFunction.service"; import { BrowserApi } from "../browser/browserApi"; +import { CipherContextMenuHandler } from "../browser/cipher-context-menu-handler"; +import { ContextMenuClickedHandler } from "../browser/context-menu-clicked-handler"; +import { MainContextMenuHandler } from "../browser/main-context-menu-handler"; import { SafariApp } from "../browser/safariApp"; +import { AutofillTabCommand } from "../commands/autofill-tab-command"; import { flagEnabled } from "../flags"; import { UpdateBadge } from "../listeners/update-badge"; import { Account } from "../models/account"; import { PopupUtilsService } from "../popup/services/popup-utils.service"; import { AutofillService as AutofillServiceAbstraction } from "../services/abstractions/autofill.service"; -import { StateService as StateServiceAbstraction } from "../services/abstractions/state.service"; +import { BrowserStateService as StateServiceAbstraction } from "../services/abstractions/browser-state.service"; import AutofillService from "../services/autofill.service"; import { BrowserEnvironmentService } from "../services/browser-environment.service"; +import { BrowserFolderService } from "../services/browser-folder.service"; +import { BrowserOrganizationService } from "../services/browser-organization.service"; +import { BrowserPolicyService } from "../services/browser-policy.service"; +import { BrowserSettingsService } from "../services/browser-settings.service"; +import { BrowserStateService } from "../services/browser-state.service"; import { BrowserCryptoService } from "../services/browserCrypto.service"; import BrowserLocalStorageService from "../services/browserLocalStorage.service"; import BrowserMessagingService from "../services/browserMessaging.service"; import BrowserMessagingPrivateModeBackgroundService from "../services/browserMessagingPrivateModeBackground.service"; import BrowserPlatformUtilsService from "../services/browserPlatformUtils.service"; import { BrowserFido2UserInterfaceService } from "../services/fido2/browser-fido2-user-interface.service"; -import { FolderService } from "../services/folders/folder.service"; import I18nService from "../services/i18n.service"; import { KeyGenerationService } from "../services/keyGeneration.service"; import { LocalBackedSessionStorageService } from "../services/localBackedSessionStorage.service"; -import { StateService } from "../services/state.service"; import { VaultFilterService } from "../services/vaultFilter.service"; import VaultTimeoutService from "../services/vaultTimeout/vaultTimeout.service"; import CommandsBackground from "./commands.background"; import ContextMenusBackground from "./contextMenus.background"; import IdleBackground from "./idle.background"; -import IconDetails from "./models/iconDetails"; import { NativeMessagingBackground } from "./nativeMessaging.background"; import NotificationBackground from "./notification.background"; import RuntimeBackground from "./runtime.background"; @@ -123,7 +130,7 @@ export default class MainBackground { messagingService: MessagingServiceAbstraction; storageService: AbstractStorageService; secureStorageService: AbstractStorageService; - memoryStorageService: AbstractStorageService; + memoryStorageService: AbstractMemoryStorageService; i18nService: I18nServiceAbstraction; platformUtilsService: PlatformUtilsServiceAbstraction; logService: LogServiceAbstraction; @@ -152,12 +159,13 @@ export default class MainBackground { stateService: StateServiceAbstraction; stateMigrationService: StateMigrationService; systemService: SystemServiceAbstraction; - eventService: EventServiceAbstraction; + eventCollectionService: EventCollectionServiceAbstraction; + eventUploadService: EventUploadServiceAbstraction; policyService: InternalPolicyServiceAbstraction; popupUtilsService: PopupUtilsService; sendService: SendServiceAbstraction; fileUploadService: FileUploadServiceAbstraction; - organizationService: OrganizationServiceAbstraction; + organizationService: InternalOrganizationServiceAbstraction; providerService: ProviderServiceAbstraction; keyConnectorService: KeyConnectorServiceAbstraction; userVerificationService: UserVerificationServiceAbstraction; @@ -171,6 +179,9 @@ export default class MainBackground { syncNotifierService: SyncNotifierServiceAbstraction; fido2UserInterfaceService: Fido2UserInterfaceServiceAbstraction; fido2Service: Fido2ServiceAbstraction; + avatarUpdateService: AvatarUpdateServiceAbstraction; + mainContextMenuHandler: MainContextMenuHandler; + cipherContextMenuHandler: CipherContextMenuHandler; // Passed to the popup for Safari to workaround issues with theming, downloading, etc. backgroundWindow = window; @@ -188,8 +199,6 @@ export default class MainBackground { private webRequestBackground: WebRequestBackground; private sidebarAction: any; - private buildingContextMenu: boolean; - private menuOptionsLoaded: any[] = []; private syncTimeout: any; private isSafari: boolean; private nativeMessagingBackground: NativeMessagingBackground; @@ -233,7 +242,7 @@ export default class MainBackground { this.secureStorageService, new StateFactory(GlobalState, Account) ); - this.stateService = new StateService( + this.stateService = new BrowserStateService( this.storageService, this.secureStorageService, this.memoryStorageService, @@ -288,7 +297,7 @@ export default class MainBackground { this.appIdService, (expired: boolean) => this.logout(expired) ); - this.settingsService = new SettingsService(this.stateService); + this.settingsService = new BrowserSettingsService(this.stateService); this.fileUploadService = new FileUploadService(this.logService, this.apiService); this.cipherService = new CipherService( this.cryptoService, @@ -301,7 +310,7 @@ export default class MainBackground { this.stateService, this.encryptService ); - this.folderService = new FolderService( + this.folderService = new BrowserFolderService( this.cryptoService, this.i18nService, this.cipherService, @@ -323,13 +332,12 @@ export default class MainBackground { this.stateService ); this.syncNotifierService = new SyncNotifierService(); - this.organizationService = new OrganizationService(this.stateService, this.syncNotifierService); - this.policyService = new PolicyService(this.stateService, this.organizationService); + this.organizationService = new BrowserOrganizationService(this.stateService); + this.policyService = new BrowserPolicyService(this.stateService, this.organizationService); this.policyApiService = new PolicyApiService( this.policyService, this.apiService, - this.stateService, - this.organizationService + this.stateService ); this.keyConnectorService = new KeyConnectorService( this.stateService, @@ -415,15 +423,19 @@ export default class MainBackground { this.stateService, this.providerService, this.folderApiService, - this.syncNotifierService, + this.organizationService, logoutCallback ); - this.eventService = new EventService( + this.eventUploadService = new EventUploadService( this.apiService, + this.stateService, + this.logService + ); + this.eventCollectionService = new EventCollectionService( this.cipherService, this.stateService, - this.logService, - this.organizationService + this.organizationService, + this.eventUploadService ); this.passwordGenerationService = new PasswordGenerationService( this.cryptoService, @@ -435,7 +447,7 @@ export default class MainBackground { this.cipherService, this.stateService, this.totpService, - this.eventService, + this.eventCollectionService, this.logService ); this.containerService = new ContainerService(this.cryptoService, this.encryptService); @@ -535,15 +547,25 @@ export default class MainBackground { ); this.tabsBackground = new TabsBackground(this, this.notificationBackground); - this.contextMenusBackground = new ContextMenusBackground( - this, - this.cipherService, - this.passwordGenerationService, - this.platformUtilsService, - this.authService, - this.eventService, - this.totpService - ); + if (!this.popupOnlyContext) { + const contextMenuClickedHandler = new ContextMenuClickedHandler( + (options) => this.platformUtilsService.copyToClipboard(options.text, { window: self }), + async (_tab) => { + const options = (await this.passwordGenerationService.getOptions())?.[0] ?? {}; + const password = await this.passwordGenerationService.generatePassword(options); + this.platformUtilsService.copyToClipboard(password, { window: window }); + this.passwordGenerationService.addHistory(password); + }, + this.authService, + this.cipherService, + new AutofillTabCommand(this.autofillService), + this.totpService, + this.eventCollectionService + ); + + this.contextMenusBackground = new ContextMenusBackground(contextMenuClickedHandler); + } + this.idleBackground = new IdleBackground( this.vaultTimeoutService, this.stateService, @@ -560,6 +582,18 @@ export default class MainBackground { this.stateService, this.apiService ); + + this.avatarUpdateService = new AvatarUpdateService(this.apiService, this.stateService); + + if (!this.popupOnlyContext) { + this.mainContextMenuHandler = new MainContextMenuHandler(this.stateService, this.i18nService); + + this.cipherContextMenuHandler = new CipherContextMenuHandler( + this.mainContextMenuHandler, + this.authService, + this.cipherService + ); + } } async bootstrap() { @@ -569,7 +603,7 @@ export default class MainBackground { await (this.vaultTimeoutService as VaultTimeoutService).init(true); await (this.i18nService as I18nService).init(); - await (this.eventService as EventService).init(true); + await (this.eventUploadService as EventUploadService).init(true); await this.runtimeBackground.init(); await this.notificationBackground.init(); await this.commandsBackground.init(); @@ -577,7 +611,9 @@ export default class MainBackground { this.twoFactorService.init(); await this.tabsBackground.init(); - await this.contextMenusBackground.init(); + if (!this.popupOnlyContext) { + this.contextMenusBackground?.init(); + } await this.idleBackground.init(); await this.webRequestBackground.init(); @@ -598,7 +634,9 @@ export default class MainBackground { return new Promise((resolve) => { setTimeout(async () => { await this.environmentService.setUrlsFromStorage(); - await this.refreshBadge(); + if (!this.isPrivateMode) { + await this.refreshBadge(); + } this.fullSync(true); setTimeout(() => this.notificationsService.init(), 2500); resolve(); @@ -615,30 +653,27 @@ export default class MainBackground { return; } - const menuDisabled = await this.stateService.getDisableContextMenuItem(); - if (!menuDisabled) { - await this.buildContextMenu(); - } else { - await this.contextMenusRemoveAll(); - } + await MainContextMenuHandler.removeAll(); if (forLocked) { - await this.loadMenuForNoAccessState(!menuDisabled); + await this.mainContextMenuHandler?.noAccess(); this.onUpdatedRan = this.onReplacedRan = false; return; } + await this.mainContextMenuHandler?.init(); + const tab = await BrowserApi.getTabFromCurrentWindow(); if (tab) { - await this.contextMenuReady(tab, !menuDisabled); + await this.cipherContextMenuHandler?.update(tab.url); + this.onUpdatedRan = this.onReplacedRan = false; } } async logout(expired: boolean, userId?: string) { - await this.eventService.uploadEvents(userId); + await this.eventUploadService.uploadEvents(userId); await Promise.all([ - this.eventService.clearEvents(userId), this.syncService.setLastSync(new Date(0), userId), this.cryptoService.clearKeys(userId), this.settingsService.clear(userId), @@ -663,7 +698,7 @@ export default class MainBackground { BrowserApi.sendMessage("updateBadge"); } await this.refreshBadge(); - await this.refreshMenu(true); + await this.mainContextMenuHandler.noAccess(); await this.reseedStorage(); this.notificationsService.updateConnection(false); await this.systemService.clearPendingClipboard(); @@ -737,204 +772,6 @@ export default class MainBackground { } } - private async buildContextMenu() { - if (!chrome.contextMenus || this.buildingContextMenu) { - return; - } - - this.buildingContextMenu = true; - await this.contextMenusRemoveAll(); - - await this.contextMenusCreate({ - type: "normal", - id: "root", - contexts: ["all"], - title: "Bitwarden", - }); - - await this.contextMenusCreate({ - type: "normal", - id: "autofill", - parentId: "root", - contexts: ["all"], - title: this.i18nService.t("autoFill"), - }); - - await this.contextMenusCreate({ - type: "normal", - id: "copy-username", - parentId: "root", - contexts: ["all"], - title: this.i18nService.t("copyUsername"), - }); - - await this.contextMenusCreate({ - type: "normal", - id: "copy-password", - parentId: "root", - contexts: ["all"], - title: this.i18nService.t("copyPassword"), - }); - - if (await this.stateService.getCanAccessPremium()) { - await this.contextMenusCreate({ - type: "normal", - id: "copy-totp", - parentId: "root", - contexts: ["all"], - title: this.i18nService.t("copyVerificationCode"), - }); - } - - await this.contextMenusCreate({ - type: "separator", - parentId: "root", - }); - - await this.contextMenusCreate({ - type: "normal", - id: "generate-password", - parentId: "root", - contexts: ["all"], - title: this.i18nService.t("generatePasswordCopied"), - }); - - await this.contextMenusCreate({ - type: "normal", - id: "copy-identifier", - parentId: "root", - contexts: ["all"], - title: this.i18nService.t("copyElementIdentifier"), - }); - - this.buildingContextMenu = false; - } - - private async contextMenuReady(tab: any, contextMenuEnabled: boolean) { - await this.loadMenu(tab.url, tab.id, contextMenuEnabled); - this.onUpdatedRan = this.onReplacedRan = false; - } - - private async loadMenu(url: string, tabId: number, contextMenuEnabled: boolean) { - if (!url || (!chrome.browserAction && !this.sidebarAction)) { - return; - } - - this.menuOptionsLoaded = []; - const authStatus = await this.authService.getAuthStatus(); - if (authStatus === AuthenticationStatus.Unlocked) { - try { - const ciphers = await this.cipherService.getAllDecryptedForUrl(url); - ciphers.sort((a, b) => this.cipherService.sortCiphersByLastUsedThenName(a, b)); - - if (contextMenuEnabled) { - ciphers.forEach((cipher) => { - this.loadLoginContextMenuOptions(cipher); - }); - } - - if (contextMenuEnabled && ciphers.length === 0) { - await this.loadNoLoginsContextMenuOptions(this.i18nService.t("noMatchingLogins")); - } - - return; - } catch (e) { - this.logService.error(e); - } - } - - await this.loadMenuForNoAccessState(contextMenuEnabled); - } - - private async loadMenuForNoAccessState(contextMenuEnabled: boolean) { - if (contextMenuEnabled) { - const authed = await this.stateService.getIsAuthenticated(); - await this.loadNoLoginsContextMenuOptions( - this.i18nService.t(authed ? "unlockVaultMenu" : "loginToVaultMenu") - ); - } - } - - private async loadLoginContextMenuOptions(cipher: any) { - if ( - cipher == null || - cipher.type !== CipherType.Login || - cipher.reprompt !== CipherRepromptType.None - ) { - return; - } - - let title = cipher.name; - if (cipher.login.username && cipher.login.username !== "") { - title += " (" + cipher.login.username + ")"; - } - await this.loadContextMenuOptions(title, cipher.id, cipher); - } - - private async loadNoLoginsContextMenuOptions(noLoginsMessage: string) { - await this.loadContextMenuOptions(noLoginsMessage, "noop", null); - } - - private async loadContextMenuOptions(title: string, idSuffix: string, cipher: any) { - if ( - !chrome.contextMenus || - this.menuOptionsLoaded.indexOf(idSuffix) > -1 || - (cipher != null && cipher.type !== CipherType.Login) - ) { - return; - } - - this.menuOptionsLoaded.push(idSuffix); - - if (cipher == null || (cipher.login.password && cipher.login.password !== "")) { - await this.contextMenusCreate({ - type: "normal", - id: "autofill_" + idSuffix, - parentId: "autofill", - contexts: ["all"], - title: this.sanitizeContextMenuTitle(title), - }); - } - - if (cipher == null || (cipher.login.username && cipher.login.username !== "")) { - await this.contextMenusCreate({ - type: "normal", - id: "copy-username_" + idSuffix, - parentId: "copy-username", - contexts: ["all"], - title: this.sanitizeContextMenuTitle(title), - }); - } - - if ( - cipher == null || - (cipher.login.password && cipher.login.password !== "" && cipher.viewPassword) - ) { - await this.contextMenusCreate({ - type: "normal", - id: "copy-password_" + idSuffix, - parentId: "copy-password", - contexts: ["all"], - title: this.sanitizeContextMenuTitle(title), - }); - } - - const canAccessPremium = await this.stateService.getCanAccessPremium(); - if (canAccessPremium && (cipher == null || (cipher.login.totp && cipher.login.totp !== ""))) { - await this.contextMenusCreate({ - type: "normal", - id: "copy-totp_" + idSuffix, - parentId: "copy-totp", - contexts: ["all"], - title: this.sanitizeContextMenuTitle(title), - }); - } - } - - private sanitizeContextMenuTitle(title: string): string { - return title.replace(/&/g, "&&"); - } - private async fullSync(override = false) { const syncInternal = 6 * 60 * 60 * 1000; // 6 hours const lastSync = await this.syncService.getLastSync(); @@ -959,54 +796,4 @@ export default class MainBackground { this.syncTimeout = setTimeout(async () => await this.fullSync(), 5 * 60 * 1000); // check every 5 minutes } - - // Browser API Helpers - - private contextMenusRemoveAll() { - return new Promise((resolve) => { - chrome.contextMenus.removeAll(() => { - resolve(); - if (chrome.runtime.lastError) { - return; - } - }); - }); - } - - private contextMenusCreate(options: any) { - return new Promise((resolve) => { - chrome.contextMenus.create(options, () => { - resolve(); - if (chrome.runtime.lastError) { - return; - } - }); - }); - } - - private async actionSetIcon(theAction: any, suffix: string, windowId?: number): Promise { - if (!theAction || !theAction.setIcon) { - return; - } - - const options: IconDetails = { - path: { - 19: "images/icon19" + suffix + ".png", - 38: "images/icon38" + suffix + ".png", - }, - }; - - if (this.platformUtilsService.isFirefox()) { - options.windowId = windowId; - await theAction.setIcon(options); - } else if (this.platformUtilsService.isSafari()) { - // Workaround since Safari 14.0.3 returns a pending promise - // which doesn't resolve within a reasonable time. - theAction.setIcon(options); - } else { - return new Promise((resolve) => { - theAction.setIcon(options, () => resolve()); - }); - } - } } diff --git a/apps/browser/src/background/notification.background.ts b/apps/browser/src/background/notification.background.ts index 127fc5203d7..2da4791857d 100644 --- a/apps/browser/src/background/notification.background.ts +++ b/apps/browser/src/background/notification.background.ts @@ -15,7 +15,7 @@ import { LoginView } from "@bitwarden/common/models/view/login.view"; import { BrowserApi } from "../browser/browserApi"; import { AutofillService } from "../services/abstractions/autofill.service"; -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; import AddChangePasswordQueueMessage from "./models/addChangePasswordQueueMessage"; import AddLoginQueueMessage from "./models/addLoginQueueMessage"; @@ -33,7 +33,7 @@ export default class NotificationBackground { private authService: AuthService, private policyService: PolicyService, private folderService: FolderService, - private stateService: StateService + private stateService: BrowserStateService ) {} async init() { diff --git a/apps/browser/src/background/runtime.background.ts b/apps/browser/src/background/runtime.background.ts index 98bdd324d9e..1fe1fcd55ee 100644 --- a/apps/browser/src/background/runtime.background.ts +++ b/apps/browser/src/background/runtime.background.ts @@ -110,6 +110,7 @@ export default class RuntimeBackground { await this.main.refreshBadge(); await this.main.refreshMenu(); }, 2000); + this.main.avatarUpdateService.loadColorFromState(); } break; case "openPopup": diff --git a/apps/browser/src/background/service_factories/autofill-service.factory.ts b/apps/browser/src/background/service_factories/autofill-service.factory.ts index a14cd1dd8c3..1df2ec9bbdf 100644 --- a/apps/browser/src/background/service_factories/autofill-service.factory.ts +++ b/apps/browser/src/background/service_factories/autofill-service.factory.ts @@ -2,7 +2,10 @@ import { AutofillService as AbstractAutoFillService } from "../../services/abstr import AutofillService from "../../services/autofill.service"; import { cipherServiceFactory, CipherServiceInitOptions } from "./cipher-service.factory"; -import { EventServiceInitOptions, eventServiceFactory } from "./event-service.factory"; +import { + EventCollectionServiceInitOptions, + eventCollectionServiceFactory, +} from "./event-collection-service.factory"; import { CachedServices, factory, FactoryOptions } from "./factory-options"; import { logServiceFactory, LogServiceInitOptions } from "./log-service.factory"; import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory"; @@ -14,7 +17,7 @@ export type AutoFillServiceInitOptions = AutoFillServiceOptions & CipherServiceInitOptions & StateServiceInitOptions & TotpServiceInitOptions & - EventServiceInitOptions & + EventCollectionServiceInitOptions & LogServiceInitOptions; export function autofillServiceFactory( @@ -30,7 +33,7 @@ export function autofillServiceFactory( await cipherServiceFactory(cache, opts), await stateServiceFactory(cache, opts), await totpServiceFactory(cache, opts), - await eventServiceFactory(cache, opts), + await eventCollectionServiceFactory(cache, opts), await logServiceFactory(cache, opts) ) ); diff --git a/apps/browser/src/background/service_factories/cipher-service.factory.ts b/apps/browser/src/background/service_factories/cipher-service.factory.ts index 6d31ebd76f7..020c19983c2 100644 --- a/apps/browser/src/background/service_factories/cipher-service.factory.ts +++ b/apps/browser/src/background/service_factories/cipher-service.factory.ts @@ -47,7 +47,7 @@ export function cipherServiceFactory( await fileUploadServiceFactory(cache, opts), await i18nServiceFactory(cache, opts), opts.cipherServiceOptions?.searchServiceFactory === undefined - ? () => cache.searchService + ? () => cache.searchService as SearchService : opts.cipherServiceOptions.searchServiceFactory, await logServiceFactory(cache, opts), await stateServiceFactory(cache, opts), diff --git a/apps/browser/src/background/service_factories/encrypt-service.factory.ts b/apps/browser/src/background/service_factories/encrypt-service.factory.ts index 6e4c104b05b..5b2a3766a3f 100644 --- a/apps/browser/src/background/service_factories/encrypt-service.factory.ts +++ b/apps/browser/src/background/service_factories/encrypt-service.factory.ts @@ -1,7 +1,4 @@ import { EncryptServiceImplementation } from "@bitwarden/common/services/cryptography/encrypt.service.implementation"; -import { MultithreadEncryptServiceImplementation } from "@bitwarden/common/services/cryptography/multithread-encrypt.service.implementation"; - -import { flagEnabled } from "../../flags"; import { cryptoFunctionServiceFactory, @@ -24,17 +21,15 @@ export function encryptServiceFactory( cache: { encryptService?: EncryptServiceImplementation } & CachedServices, opts: EncryptServiceInitOptions ): Promise { - return factory(cache, "encryptService", opts, async () => - flagEnabled("multithreadDecryption") - ? new MultithreadEncryptServiceImplementation( - await cryptoFunctionServiceFactory(cache, opts), - await logServiceFactory(cache, opts), - opts.encryptServiceOptions.logMacFailures - ) - : new EncryptServiceImplementation( - await cryptoFunctionServiceFactory(cache, opts), - await logServiceFactory(cache, opts), - opts.encryptServiceOptions.logMacFailures - ) + return factory( + cache, + "encryptService", + opts, + async () => + new EncryptServiceImplementation( + await cryptoFunctionServiceFactory(cache, opts), + await logServiceFactory(cache, opts), + opts.encryptServiceOptions.logMacFailures + ) ); } diff --git a/apps/browser/src/background/service_factories/event-collection-service.factory.ts b/apps/browser/src/background/service_factories/event-collection-service.factory.ts new file mode 100644 index 00000000000..477797d1b42 --- /dev/null +++ b/apps/browser/src/background/service_factories/event-collection-service.factory.ts @@ -0,0 +1,40 @@ +import { EventCollectionService as AbstractEventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service"; + +import { cipherServiceFactory, CipherServiceInitOptions } from "./cipher-service.factory"; +import { + eventUploadServiceFactory, + EventUploadServiceInitOptions, +} from "./event-upload-service.factory"; +import { FactoryOptions, CachedServices, factory } from "./factory-options"; +import { + organizationServiceFactory, + OrganizationServiceInitOptions, +} from "./organization-service.factory"; +import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory"; + +type EventCollectionServiceOptions = FactoryOptions; + +export type EventCollectionServiceInitOptions = EventCollectionServiceOptions & + CipherServiceInitOptions & + StateServiceInitOptions & + OrganizationServiceInitOptions & + EventUploadServiceInitOptions; + +export function eventCollectionServiceFactory( + cache: { eventCollectionService?: AbstractEventCollectionService } & CachedServices, + opts: EventCollectionServiceInitOptions +): Promise { + return factory( + cache, + "eventCollectionService", + opts, + async () => + new EventCollectionService( + await cipherServiceFactory(cache, opts), + await stateServiceFactory(cache, opts), + await organizationServiceFactory(cache, opts), + await eventUploadServiceFactory(cache, opts) + ) + ); +} diff --git a/apps/browser/src/background/service_factories/event-service.factory.ts b/apps/browser/src/background/service_factories/event-service.factory.ts deleted file mode 100644 index 61a82ebeb19..00000000000 --- a/apps/browser/src/background/service_factories/event-service.factory.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { EventService as AbstractEventService } from "@bitwarden/common/abstractions/event.service"; -import { EventService } from "@bitwarden/common/services/event.service"; - -import { apiServiceFactory, ApiServiceInitOptions } from "./api-service.factory"; -import { cipherServiceFactory, CipherServiceInitOptions } from "./cipher-service.factory"; -import { FactoryOptions, CachedServices, factory } from "./factory-options"; -import { logServiceFactory, LogServiceInitOptions } from "./log-service.factory"; -import { - organizationServiceFactory, - OrganizationServiceInitOptions, -} from "./organization-service.factory"; -import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory"; - -type EventServiceOptions = FactoryOptions; - -export type EventServiceInitOptions = EventServiceOptions & - ApiServiceInitOptions & - CipherServiceInitOptions & - StateServiceInitOptions & - LogServiceInitOptions & - OrganizationServiceInitOptions; - -export function eventServiceFactory( - cache: { eventService?: AbstractEventService } & CachedServices, - opts: EventServiceInitOptions -): Promise { - return factory( - cache, - "eventService", - opts, - async () => - new EventService( - await apiServiceFactory(cache, opts), - await cipherServiceFactory(cache, opts), - await stateServiceFactory(cache, opts), - await logServiceFactory(cache, opts), - await organizationServiceFactory(cache, opts) - ) - ); -} diff --git a/apps/browser/src/background/service_factories/event-upload-service.factory.ts b/apps/browser/src/background/service_factories/event-upload-service.factory.ts new file mode 100644 index 00000000000..c0ed0e5426d --- /dev/null +++ b/apps/browser/src/background/service_factories/event-upload-service.factory.ts @@ -0,0 +1,31 @@ +import { EventUploadService as AbstractEventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service"; +import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; + +import { apiServiceFactory, ApiServiceInitOptions } from "./api-service.factory"; +import { FactoryOptions, CachedServices, factory } from "./factory-options"; +import { logServiceFactory, LogServiceInitOptions } from "./log-service.factory"; +import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory"; + +type EventUploadServiceOptions = FactoryOptions; + +export type EventUploadServiceInitOptions = EventUploadServiceOptions & + ApiServiceInitOptions & + StateServiceInitOptions & + LogServiceInitOptions; + +export function eventUploadServiceFactory( + cache: { eventUploadService?: AbstractEventUploadService } & CachedServices, + opts: EventUploadServiceInitOptions +): Promise { + return factory( + cache, + "eventUploadService", + opts, + async () => + new EventUploadService( + await apiServiceFactory(cache, opts), + await stateServiceFactory(cache, opts), + await logServiceFactory(cache, opts) + ) + ); +} diff --git a/apps/browser/src/background/service_factories/factory-options.ts b/apps/browser/src/background/service_factories/factory-options.ts index 12129e4e673..7dde09204ef 100644 --- a/apps/browser/src/background/service_factories/factory-options.ts +++ b/apps/browser/src/background/service_factories/factory-options.ts @@ -1,4 +1,4 @@ -export type CachedServices = Record; +export type CachedServices = Record; export type FactoryOptions = { alwaysInitializeNewService?: boolean; diff --git a/apps/browser/src/background/service_factories/folder-service.factory.ts b/apps/browser/src/background/service_factories/folder-service.factory.ts index a7c90d234b1..bb35970325f 100644 --- a/apps/browser/src/background/service_factories/folder-service.factory.ts +++ b/apps/browser/src/background/service_factories/folder-service.factory.ts @@ -1,6 +1,6 @@ import { FolderService as AbstractFolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction"; -import { FolderService } from "../../services/folders/folder.service"; +import { BrowserFolderService } from "../../services/browser-folder.service"; import { cipherServiceFactory, CipherServiceInitOptions } from "./cipher-service.factory"; import { cryptoServiceFactory, CryptoServiceInitOptions } from "./crypto-service.factory"; @@ -28,7 +28,7 @@ export function folderServiceFactory( "folderService", opts, async () => - new FolderService( + new BrowserFolderService( await cryptoServiceFactory(cache, opts), await i18nServiceFactory(cache, opts), await cipherServiceFactory(cache, opts), diff --git a/apps/browser/src/background/service_factories/organization-service.factory.ts b/apps/browser/src/background/service_factories/organization-service.factory.ts index 2e7c31b596e..4f2eaee8058 100644 --- a/apps/browser/src/background/service_factories/organization-service.factory.ts +++ b/apps/browser/src/background/service_factories/organization-service.factory.ts @@ -1,17 +1,13 @@ import { OrganizationService as AbstractOrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction"; -import { OrganizationService } from "@bitwarden/common/services/organization/organization.service"; + +import { BrowserOrganizationService } from "../../services/browser-organization.service"; import { FactoryOptions, CachedServices, factory } from "./factory-options"; import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory"; -import { - syncNotifierServiceFactory, - SyncNotifierServiceInitOptions, -} from "./sync-notifier-service.factory"; type OrganizationServiceFactoryOptions = FactoryOptions; export type OrganizationServiceInitOptions = OrganizationServiceFactoryOptions & - SyncNotifierServiceInitOptions & StateServiceInitOptions; export function organizationServiceFactory( @@ -22,10 +18,6 @@ export function organizationServiceFactory( cache, "organizationService", opts, - async () => - new OrganizationService( - await stateServiceFactory(cache, opts), - await syncNotifierServiceFactory(cache, opts) - ) + async () => new BrowserOrganizationService(await stateServiceFactory(cache, opts)) ); } diff --git a/apps/browser/src/background/service_factories/policy-service.factory.ts b/apps/browser/src/background/service_factories/policy-service.factory.ts index d4940bef259..d20bca3c62e 100644 --- a/apps/browser/src/background/service_factories/policy-service.factory.ts +++ b/apps/browser/src/background/service_factories/policy-service.factory.ts @@ -1,5 +1,6 @@ import { PolicyService as AbstractPolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction"; -import { PolicyService } from "@bitwarden/common/services/policy/policy.service"; + +import { BrowserPolicyService } from "../../services/browser-policy.service"; import { CachedServices, factory, FactoryOptions } from "./factory-options"; import { @@ -26,7 +27,7 @@ export function policyServiceFactory( "policyService", opts, async () => - new PolicyService( + new BrowserPolicyService( await stateServiceFactory(cache, opts), await organizationServiceFactory(cache, opts) ) diff --git a/apps/browser/src/background/service_factories/settings-service.factory.ts b/apps/browser/src/background/service_factories/settings-service.factory.ts index 745a6d08d60..73e0ae52032 100644 --- a/apps/browser/src/background/service_factories/settings-service.factory.ts +++ b/apps/browser/src/background/service_factories/settings-service.factory.ts @@ -1,5 +1,6 @@ import { SettingsService as AbstractSettingsService } from "@bitwarden/common/abstractions/settings.service"; -import { SettingsService } from "@bitwarden/common/services/settings.service"; + +import { BrowserSettingsService } from "../../services/browser-settings.service"; import { FactoryOptions, CachedServices, factory } from "./factory-options"; import { stateServiceFactory, StateServiceInitOptions } from "./state-service.factory"; @@ -16,6 +17,6 @@ export function settingsServiceFactory( cache, "settingsService", opts, - async () => new SettingsService(await stateServiceFactory(cache, opts)) + async () => new BrowserSettingsService(await stateServiceFactory(cache, opts)) ); } diff --git a/apps/browser/src/background/service_factories/state-service.factory.ts b/apps/browser/src/background/service_factories/state-service.factory.ts index 1b81567ac52..6d2c5cb4fa7 100644 --- a/apps/browser/src/background/service_factories/state-service.factory.ts +++ b/apps/browser/src/background/service_factories/state-service.factory.ts @@ -2,7 +2,7 @@ import { StateFactory } from "@bitwarden/common/factories/stateFactory"; import { GlobalState } from "@bitwarden/common/models/domain/global-state"; import { Account } from "../../models/account"; -import { StateService } from "../../services/state.service"; +import { BrowserStateService } from "../../services/browser-state.service"; import { CachedServices, factory, FactoryOptions } from "./factory-options"; import { logServiceFactory, LogServiceInitOptions } from "./log-service.factory"; @@ -34,15 +34,15 @@ export type StateServiceInitOptions = StateServiceFactoryOptions & StateMigrationServiceInitOptions; export async function stateServiceFactory( - cache: { stateService?: StateService } & CachedServices, + cache: { stateService?: BrowserStateService } & CachedServices, opts: StateServiceInitOptions -): Promise { +): Promise { const service = await factory( cache, "stateService", opts, async () => - await new StateService( + await new BrowserStateService( await diskStorageServiceFactory(cache, opts), await secureStorageServiceFactory(cache, opts), await memoryStorageServiceFactory(cache, opts), diff --git a/apps/browser/src/background/service_factories/storage-service.factory.ts b/apps/browser/src/background/service_factories/storage-service.factory.ts index 51993277c92..c30bda731e6 100644 --- a/apps/browser/src/background/service_factories/storage-service.factory.ts +++ b/apps/browser/src/background/service_factories/storage-service.factory.ts @@ -1,4 +1,7 @@ -import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service"; +import { + AbstractMemoryStorageService, + AbstractStorageService, +} from "@bitwarden/common/abstractions/storage.service"; import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service"; import { BrowserApi } from "../../browser/browserApi"; @@ -35,9 +38,9 @@ export function secureStorageServiceFactory( } export function memoryStorageServiceFactory( - cache: { memoryStorageService?: AbstractStorageService } & CachedServices, + cache: { memoryStorageService?: AbstractMemoryStorageService } & CachedServices, opts: MemoryStorageServiceInitOptions -): Promise { +): Promise { return factory(cache, "memoryStorageService", opts, async () => { if (BrowserApi.manifestVersion === 3) { return new LocalBackedSessionStorageService( diff --git a/apps/browser/src/browser/browserApi.ts b/apps/browser/src/browser/browserApi.ts index ff0737e56ff..3dd9bd624a4 100644 --- a/apps/browser/src/browser/browserApi.ts +++ b/apps/browser/src/browser/browserApi.ts @@ -44,7 +44,7 @@ export class BrowserApi { static async tabsQuery(options: chrome.tabs.QueryInfo): Promise { return new Promise((resolve) => { - chrome.tabs.query(options, (tabs: any[]) => { + chrome.tabs.query(options, (tabs) => { resolve(tabs); }); }); @@ -63,7 +63,7 @@ export class BrowserApi { tab: chrome.tabs.Tab, command: string, data: any = null - ): Promise { + ): Promise { const obj: any = { command: command, }; @@ -75,11 +75,11 @@ export class BrowserApi { return BrowserApi.tabSendMessage(tab, obj); } - static async tabSendMessage( + static async tabSendMessage( tab: chrome.tabs.Tab, - obj: any, + obj: T, options: chrome.tabs.MessageSendOptions = null - ): Promise { + ): Promise { if (!tab || !tab.id) { return; } @@ -94,12 +94,13 @@ export class BrowserApi { }); } - static sendTabsMessage( + static sendTabsMessage( tabId: number, message: TabMessage, + options?: chrome.tabs.MessageSendOptions, responseCallback?: (response: T) => void ) { - chrome.tabs.sendMessage(tabId, message, responseCallback); + chrome.tabs.sendMessage(tabId, message, options, responseCallback); } static async getPrivateModeWindows(): Promise { diff --git a/apps/browser/src/browser/cipher-context-menu-handler.spec.ts b/apps/browser/src/browser/cipher-context-menu-handler.spec.ts new file mode 100644 index 00000000000..39b7f8e3619 --- /dev/null +++ b/apps/browser/src/browser/cipher-context-menu-handler.spec.ts @@ -0,0 +1,109 @@ +import { mock, MockProxy } from "jest-mock-extended"; + +import { AuthService } from "@bitwarden/common/abstractions/auth.service"; +import { CipherService } from "@bitwarden/common/abstractions/cipher.service"; +import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus"; +import { CipherRepromptType } from "@bitwarden/common/enums/cipherRepromptType"; +import { CipherType } from "@bitwarden/common/enums/cipherType"; + +import { CipherContextMenuHandler } from "./cipher-context-menu-handler"; +import { MainContextMenuHandler } from "./main-context-menu-handler"; + +describe("CipherContextMenuHandler", () => { + let mainContextMenuHandler: MockProxy; + let authService: MockProxy; + let cipherService: MockProxy; + + let sut: CipherContextMenuHandler; + + beforeEach(() => { + mainContextMenuHandler = mock(); + authService = mock(); + cipherService = mock(); + + jest.spyOn(MainContextMenuHandler, "removeAll").mockResolvedValue(); + + sut = new CipherContextMenuHandler(mainContextMenuHandler, authService, cipherService); + }); + + afterEach(() => jest.resetAllMocks()); + + describe("update", () => { + it("locked, updates for no access", async () => { + authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Locked); + + await sut.update("https://test.com"); + + expect(mainContextMenuHandler.noAccess).toHaveBeenCalledTimes(1); + }); + + it("logged out, updates for no access", async () => { + authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.LoggedOut); + + await sut.update("https://test.com"); + + expect(mainContextMenuHandler.noAccess).toHaveBeenCalledTimes(1); + }); + + it("has menu disabled, does not load anything", async () => { + authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Unlocked); + + await sut.update("https://test.com"); + + expect(mainContextMenuHandler.loadOptions).not.toHaveBeenCalled(); + + expect(mainContextMenuHandler.noAccess).not.toHaveBeenCalled(); + + expect(mainContextMenuHandler.noLogins).not.toHaveBeenCalled(); + }); + + it("has no ciphers, add no ciphers item", async () => { + authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Unlocked); + + mainContextMenuHandler.init.mockResolvedValue(true); + + cipherService.getAllDecryptedForUrl.mockResolvedValue([]); + + await sut.update("https://test.com"); + + expect(mainContextMenuHandler.noLogins).toHaveBeenCalledTimes(1); + }); + + it("only adds valid ciphers", async () => { + authService.getAuthStatus.mockResolvedValue(AuthenticationStatus.Unlocked); + + mainContextMenuHandler.init.mockResolvedValue(true); + + const realCipher = { + id: "5", + type: CipherType.Login, + reprompt: CipherRepromptType.None, + name: "Test Cipher", + login: { username: "Test Username" }, + }; + + cipherService.getAllDecryptedForUrl.mockResolvedValue([ + null, + undefined, + { type: CipherType.Card }, + { type: CipherType.Login, reprompt: CipherRepromptType.Password }, + realCipher, + ] as any[]); + + await sut.update("https://test.com"); + + expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledTimes(1); + + expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith("https://test.com"); + + expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledTimes(1); + + expect(mainContextMenuHandler.loadOptions).toHaveBeenCalledWith( + "Test Cipher (Test Username)", + "5", + "https://test.com", + realCipher + ); + }); + }); +}); diff --git a/apps/browser/src/browser/cipher-context-menu-handler.ts b/apps/browser/src/browser/cipher-context-menu-handler.ts new file mode 100644 index 00000000000..5942ce3b140 --- /dev/null +++ b/apps/browser/src/browser/cipher-context-menu-handler.ts @@ -0,0 +1,196 @@ +import { AuthService } from "@bitwarden/common/abstractions/auth.service"; +import { CipherService } from "@bitwarden/common/abstractions/cipher.service"; +import { SearchService } from "@bitwarden/common/abstractions/search.service"; +import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus"; +import { CipherRepromptType } from "@bitwarden/common/enums/cipherRepromptType"; +import { CipherType } from "@bitwarden/common/enums/cipherType"; +import { StateFactory } from "@bitwarden/common/factories/stateFactory"; +import { Utils } from "@bitwarden/common/misc/utils"; +import { GlobalState } from "@bitwarden/common/models/domain/global-state"; +import { CipherView } from "@bitwarden/common/models/view/cipher.view"; + +import { + authServiceFactory, + AuthServiceInitOptions, +} from "../background/service_factories/auth-service.factory"; +import { + cipherServiceFactory, + CipherServiceInitOptions, +} from "../background/service_factories/cipher-service.factory"; +import { CachedServices } from "../background/service_factories/factory-options"; +import { searchServiceFactory } from "../background/service_factories/search-service.factory"; +import { Account } from "../models/account"; + +import { BrowserApi } from "./browserApi"; +import { MainContextMenuHandler } from "./main-context-menu-handler"; + +const NOT_IMPLEMENTED = (..._args: unknown[]) => Promise.resolve(); + +const LISTENED_TO_COMMANDS = [ + "loggedIn", + "unlocked", + "syncCompleted", + "bgUpdateContextMenu", + "editedCipher", + "addedCipher", + "deletedCipher", +]; + +export class CipherContextMenuHandler { + constructor( + private mainContextMenuHandler: MainContextMenuHandler, + private authService: AuthService, + private cipherService: CipherService + ) {} + + static async create(cachedServices: CachedServices) { + const stateFactory = new StateFactory(GlobalState, Account); + let searchService: SearchService | null = null; + const serviceOptions: AuthServiceInitOptions & CipherServiceInitOptions = { + apiServiceOptions: { + logoutCallback: NOT_IMPLEMENTED, + }, + cipherServiceOptions: { + searchServiceFactory: () => searchService, + }, + cryptoFunctionServiceOptions: { + win: self, + }, + encryptServiceOptions: { + logMacFailures: false, + }, + i18nServiceOptions: { + systemLanguage: chrome.i18n.getUILanguage(), + }, + keyConnectorServiceOptions: { + logoutCallback: NOT_IMPLEMENTED, + }, + logServiceOptions: { + isDev: false, + }, + platformUtilsServiceOptions: { + biometricCallback: () => Promise.resolve(false), + clipboardWriteCallback: NOT_IMPLEMENTED, + win: self, + }, + stateMigrationServiceOptions: { + stateFactory: stateFactory, + }, + stateServiceOptions: { + stateFactory: stateFactory, + }, + }; + searchService = await searchServiceFactory(cachedServices, serviceOptions); + return new CipherContextMenuHandler( + await MainContextMenuHandler.mv3Create(cachedServices), + await authServiceFactory(cachedServices, serviceOptions), + await cipherServiceFactory(cachedServices, serviceOptions) + ); + } + + static async tabsOnActivatedListener( + activeInfo: chrome.tabs.TabActiveInfo, + serviceCache: CachedServices + ) { + const cipherContextMenuHandler = await CipherContextMenuHandler.create(serviceCache); + const tab = await BrowserApi.getTab(activeInfo.tabId); + await cipherContextMenuHandler.update(tab.url); + } + + static async tabsOnReplacedListener( + addedTabId: number, + removedTabId: number, + serviceCache: CachedServices + ) { + const cipherContextMenuHandler = await CipherContextMenuHandler.create(serviceCache); + const tab = await BrowserApi.getTab(addedTabId); + await cipherContextMenuHandler.update(tab.url); + } + + static async tabsOnUpdatedListener( + tabId: number, + changeInfo: chrome.tabs.TabChangeInfo, + tab: chrome.tabs.Tab, + serviceCache: CachedServices + ) { + if (changeInfo.status !== "complete") { + return; + } + const cipherContextMenuHandler = await CipherContextMenuHandler.create(serviceCache); + await cipherContextMenuHandler.update(tab.url); + } + + static async messageListener( + message: { command: string }, + sender: chrome.runtime.MessageSender, + cachedServices: CachedServices + ) { + if (!CipherContextMenuHandler.shouldListen(message)) { + return; + } + const cipherContextMenuHandler = await CipherContextMenuHandler.create(cachedServices); + await cipherContextMenuHandler.messageListener(message); + } + + private static shouldListen(message: { command: string }) { + return LISTENED_TO_COMMANDS.includes(message.command); + } + + async messageListener(message: { command: string }, sender?: chrome.runtime.MessageSender) { + if (!CipherContextMenuHandler.shouldListen(message)) { + return; + } + + const activeTabs = await BrowserApi.getActiveTabs(); + if (!activeTabs || activeTabs.length === 0) { + return; + } + + await this.update(activeTabs[0].url); + } + + async update(url: string) { + const authStatus = await this.authService.getAuthStatus(); + await MainContextMenuHandler.removeAll(); + if (authStatus !== AuthenticationStatus.Unlocked) { + // Should I pass in the auth status or even have two seperate methods for this + // on MainContextMenuHandler + await this.mainContextMenuHandler.noAccess(); + return; + } + + const menuEnabled = await this.mainContextMenuHandler.init(); + if (!menuEnabled) { + return; + } + + const ciphers = await this.cipherService.getAllDecryptedForUrl(url); + ciphers.sort((a, b) => this.cipherService.sortCiphersByLastUsedThenName(a, b)); + + if (ciphers.length === 0) { + await this.mainContextMenuHandler.noLogins(url); + return; + } + + for (const cipher of ciphers) { + await this.updateForCipher(url, cipher); + } + } + + private async updateForCipher(url: string, cipher: CipherView) { + if ( + cipher == null || + cipher.type !== CipherType.Login || + cipher.reprompt !== CipherRepromptType.None + ) { + return; + } + + let title = cipher.name; + if (!Utils.isNullOrEmpty(title)) { + title += ` (${cipher.login.username})`; + } + + await this.mainContextMenuHandler.loadOptions(title, cipher.id, url, cipher); + } +} diff --git a/apps/browser/src/browser/context-menu-clicked-handler.spec.ts b/apps/browser/src/browser/context-menu-clicked-handler.spec.ts new file mode 100644 index 00000000000..a4d84aad126 --- /dev/null +++ b/apps/browser/src/browser/context-menu-clicked-handler.spec.ts @@ -0,0 +1,193 @@ +import { mock, MockProxy } from "jest-mock-extended"; + +import { AuthService } from "@bitwarden/common/abstractions/auth.service"; +import { CipherService } from "@bitwarden/common/abstractions/cipher.service"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { TotpService } from "@bitwarden/common/abstractions/totp.service"; +import { CipherRepromptType } from "@bitwarden/common/enums/cipherRepromptType"; +import { CipherType } from "@bitwarden/common/enums/cipherType"; +import { Cipher } from "@bitwarden/common/models/domain/cipher"; +import { CipherView } from "@bitwarden/common/models/view/cipher.view"; + +import { AutofillTabCommand } from "../commands/autofill-tab-command"; + +import { + CopyToClipboardAction, + ContextMenuClickedHandler, + CopyToClipboardOptions, + GeneratePasswordToClipboardAction, +} from "./context-menu-clicked-handler"; +import { + AUTOFILL_ID, + COPY_PASSWORD_ID, + COPY_USERNAME_ID, + COPY_VERIFICATIONCODE_ID, + GENERATE_PASSWORD_ID, +} from "./main-context-menu-handler"; + +describe("ContextMenuClickedHandler", () => { + const createData = ( + menuItemId: chrome.contextMenus.OnClickData["menuItemId"], + parentMenuItemId?: chrome.contextMenus.OnClickData["parentMenuItemId"] + ): chrome.contextMenus.OnClickData => { + return { + menuItemId: menuItemId, + parentMenuItemId: parentMenuItemId, + editable: false, + pageUrl: "something", + }; + }; + + const createCipher = (data?: { + id?: CipherView["id"]; + username?: CipherView["login"]["username"]; + password?: CipherView["login"]["password"]; + totp?: CipherView["login"]["totp"]; + }): CipherView => { + const { id, username, password, totp } = data || {}; + const cipherView = new CipherView( + new Cipher({ + id: id ?? "1", + type: CipherType.Login, + } as any) + ); + cipherView.login.username = username ?? "USERNAME"; + cipherView.login.password = password ?? "PASSWORD"; + cipherView.login.totp = totp ?? "TOTP"; + return cipherView; + }; + + let copyToClipboard: CopyToClipboardAction; + let generatePasswordToClipboard: GeneratePasswordToClipboardAction; + let authService: MockProxy; + let cipherService: MockProxy; + let autofillTabCommand: MockProxy; + let totpService: MockProxy; + let eventCollectionService: MockProxy; + + let sut: ContextMenuClickedHandler; + + beforeEach(() => { + copyToClipboard = jest.fn(); + generatePasswordToClipboard = jest.fn, [tab: chrome.tabs.Tab]>(); + authService = mock(); + cipherService = mock(); + autofillTabCommand = mock(); + totpService = mock(); + eventCollectionService = mock(); + + sut = new ContextMenuClickedHandler( + copyToClipboard, + generatePasswordToClipboard, + authService, + cipherService, + autofillTabCommand, + totpService, + eventCollectionService + ); + }); + + afterEach(() => jest.resetAllMocks()); + + describe("run", () => { + it("can generate password", async () => { + await sut.run(createData(GENERATE_PASSWORD_ID), { id: 5 } as any); + + expect(generatePasswordToClipboard).toBeCalledTimes(1); + + expect(generatePasswordToClipboard).toBeCalledWith({ + id: 5, + }); + }); + + it("attempts to autofill the correct cipher", async () => { + const cipher = createCipher(); + cipherService.getAllDecrypted.mockResolvedValue([cipher]); + + await sut.run(createData("T_1", AUTOFILL_ID), { id: 5 } as any); + + expect(autofillTabCommand.doAutofillTabWithCipherCommand).toBeCalledTimes(1); + + expect(autofillTabCommand.doAutofillTabWithCipherCommand).toBeCalledWith({ id: 5 }, cipher); + }); + + it("copies username to clipboard", async () => { + cipherService.getAllDecrypted.mockResolvedValue([ + createCipher({ username: "TEST_USERNAME" }), + ]); + + await sut.run(createData("T_1", COPY_USERNAME_ID)); + + expect(copyToClipboard).toBeCalledTimes(1); + + expect(copyToClipboard).toHaveBeenCalledWith({ text: "TEST_USERNAME", options: undefined }); + }); + + it("copies password to clipboard", async () => { + cipherService.getAllDecrypted.mockResolvedValue([ + createCipher({ password: "TEST_PASSWORD" }), + ]); + + await sut.run(createData("T_1", COPY_PASSWORD_ID)); + + expect(copyToClipboard).toBeCalledTimes(1); + + expect(copyToClipboard).toHaveBeenCalledWith({ text: "TEST_PASSWORD", options: undefined }); + }); + + it("copies totp code to clipboard", async () => { + cipherService.getAllDecrypted.mockResolvedValue([createCipher({ totp: "TEST_TOTP_SEED" })]); + + totpService.getCode.mockImplementation((seed) => { + if (seed === "TEST_TOTP_SEED") { + return Promise.resolve("123456"); + } + + return Promise.resolve("654321"); + }); + + await sut.run(createData("T_1", COPY_VERIFICATIONCODE_ID)); + + expect(totpService.getCode).toHaveBeenCalledTimes(1); + + expect(copyToClipboard).toHaveBeenCalledWith({ text: "123456" }); + }); + + it("attempts to find a cipher when noop but unlocked", async () => { + cipherService.getAllDecryptedForUrl.mockResolvedValue([ + { + ...createCipher({ username: "NOOP_USERNAME" }), + reprompt: CipherRepromptType.None, + } as any, + ]); + + await sut.run(createData("T_noop", COPY_USERNAME_ID), { url: "https://test.com" } as any); + + expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledTimes(1); + + expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith("https://test.com"); + + expect(copyToClipboard).toHaveBeenCalledTimes(1); + + expect(copyToClipboard).toHaveBeenCalledWith({ + text: "NOOP_USERNAME", + tab: { url: "https://test.com" }, + }); + }); + + it("attempts to find a cipher when noop but unlocked", async () => { + cipherService.getAllDecryptedForUrl.mockResolvedValue([ + { + ...createCipher({ username: "NOOP_USERNAME" }), + reprompt: CipherRepromptType.Password, + } as any, + ]); + + await sut.run(createData("T_noop", COPY_USERNAME_ID), { url: "https://test.com" } as any); + + expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledTimes(1); + + expect(cipherService.getAllDecryptedForUrl).toHaveBeenCalledWith("https://test.com"); + }); + }); +}); diff --git a/apps/browser/src/browser/context-menu-clicked-handler.ts b/apps/browser/src/browser/context-menu-clicked-handler.ts new file mode 100644 index 00000000000..f0043805da3 --- /dev/null +++ b/apps/browser/src/browser/context-menu-clicked-handler.ts @@ -0,0 +1,240 @@ +import { AuthService } from "@bitwarden/common/abstractions/auth.service"; +import { CipherService } from "@bitwarden/common/abstractions/cipher.service"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { SearchService } from "@bitwarden/common/abstractions/search.service"; +import { TotpService } from "@bitwarden/common/abstractions/totp.service"; +import { AuthenticationStatus } from "@bitwarden/common/enums/authenticationStatus"; +import { CipherRepromptType } from "@bitwarden/common/enums/cipherRepromptType"; +import { EventType } from "@bitwarden/common/enums/eventType"; +import { StateFactory } from "@bitwarden/common/factories/stateFactory"; +import { GlobalState } from "@bitwarden/common/models/domain/global-state"; +import { CipherView } from "@bitwarden/common/models/view/cipher.view"; + +import LockedVaultPendingNotificationsItem from "../background/models/lockedVaultPendingNotificationsItem"; +import { + authServiceFactory, + AuthServiceInitOptions, +} from "../background/service_factories/auth-service.factory"; +import { autofillServiceFactory } from "../background/service_factories/autofill-service.factory"; +import { + cipherServiceFactory, + CipherServiceInitOptions, +} from "../background/service_factories/cipher-service.factory"; +import { eventCollectionServiceFactory } from "../background/service_factories/event-collection-service.factory"; +import { CachedServices } from "../background/service_factories/factory-options"; +import { passwordGenerationServiceFactory } from "../background/service_factories/password-generation-service.factory"; +import { searchServiceFactory } from "../background/service_factories/search-service.factory"; +import { stateServiceFactory } from "../background/service_factories/state-service.factory"; +import { totpServiceFactory } from "../background/service_factories/totp-service.factory"; +import { BrowserApi } from "../browser/browserApi"; +import { copyToClipboard, GeneratePasswordToClipboardCommand } from "../clipboard"; +import { AutofillTabCommand } from "../commands/autofill-tab-command"; +import { Account } from "../models/account"; + +import { + AUTOFILL_ID, + COPY_IDENTIFIER_ID, + COPY_PASSWORD_ID, + COPY_USERNAME_ID, + COPY_VERIFICATIONCODE_ID, + GENERATE_PASSWORD_ID, + NOOP_COMMAND_SUFFIX, +} from "./main-context-menu-handler"; + +export type CopyToClipboardOptions = { text: string; tab: chrome.tabs.Tab }; +export type CopyToClipboardAction = (options: CopyToClipboardOptions) => void; + +export type GeneratePasswordToClipboardAction = (tab: chrome.tabs.Tab) => Promise; + +const NOT_IMPLEMENTED = (..._args: unknown[]) => + Promise.reject("This action is not implemented inside of a service worker context."); + +export class ContextMenuClickedHandler { + constructor( + private copyToClipboard: CopyToClipboardAction, + private generatePasswordToClipboard: GeneratePasswordToClipboardAction, + private authService: AuthService, + private cipherService: CipherService, + private autofillTabCommand: AutofillTabCommand, + private totpService: TotpService, + private eventCollectionService: EventCollectionService + ) {} + + static async mv3Create(cachedServices: CachedServices) { + const stateFactory = new StateFactory(GlobalState, Account); + let searchService: SearchService | null = null; + const serviceOptions: AuthServiceInitOptions & CipherServiceInitOptions = { + apiServiceOptions: { + logoutCallback: NOT_IMPLEMENTED, + }, + cipherServiceOptions: { + searchServiceFactory: () => searchService, + }, + cryptoFunctionServiceOptions: { + win: self, + }, + encryptServiceOptions: { + logMacFailures: false, + }, + i18nServiceOptions: { + systemLanguage: chrome.i18n.getUILanguage(), + }, + keyConnectorServiceOptions: { + logoutCallback: NOT_IMPLEMENTED, + }, + logServiceOptions: { + isDev: false, + }, + platformUtilsServiceOptions: { + biometricCallback: NOT_IMPLEMENTED, + clipboardWriteCallback: NOT_IMPLEMENTED, + win: self, + }, + stateMigrationServiceOptions: { + stateFactory: stateFactory, + }, + stateServiceOptions: { + stateFactory: stateFactory, + }, + }; + searchService = await searchServiceFactory(cachedServices, serviceOptions); + + const generatePasswordToClipboardCommand = new GeneratePasswordToClipboardCommand( + await passwordGenerationServiceFactory(cachedServices, serviceOptions), + await stateServiceFactory(cachedServices, serviceOptions) + ); + + return new ContextMenuClickedHandler( + (options) => copyToClipboard(options.tab, options.text), + (tab) => generatePasswordToClipboardCommand.generatePasswordToClipboard(tab), + await authServiceFactory(cachedServices, serviceOptions), + await cipherServiceFactory(cachedServices, serviceOptions), + new AutofillTabCommand(await autofillServiceFactory(cachedServices, serviceOptions)), + await totpServiceFactory(cachedServices, serviceOptions), + await eventCollectionServiceFactory(cachedServices, serviceOptions) + ); + } + + static async onClickedListener( + info: chrome.contextMenus.OnClickData, + tab?: chrome.tabs.Tab, + cachedServices: CachedServices = {} + ) { + const contextMenuClickedHandler = await ContextMenuClickedHandler.mv3Create(cachedServices); + await contextMenuClickedHandler.run(info, tab); + } + + static async messageListener( + message: { command: string; data: LockedVaultPendingNotificationsItem }, + sender: chrome.runtime.MessageSender, + cachedServices: CachedServices + ) { + if ( + message.command !== "unlockCompleted" || + message.data.target !== "contextmenus.background" + ) { + return; + } + + const contextMenuClickedHandler = await ContextMenuClickedHandler.mv3Create(cachedServices); + await contextMenuClickedHandler.run( + message.data.commandToRetry.msg.data, + message.data.commandToRetry.sender.tab + ); + } + + async run(info: chrome.contextMenus.OnClickData, tab?: chrome.tabs.Tab) { + switch (info.menuItemId) { + case GENERATE_PASSWORD_ID: + if (!tab) { + return; + } + await this.generatePasswordToClipboard(tab); + break; + case COPY_IDENTIFIER_ID: + if (!tab) { + return; + } + this.copyToClipboard({ text: await this.getIdentifier(tab, info), tab: tab }); + break; + default: + await this.cipherAction(info, tab); + } + } + + async cipherAction(info: chrome.contextMenus.OnClickData, tab?: chrome.tabs.Tab) { + if ((await this.authService.getAuthStatus()) < AuthenticationStatus.Unlocked) { + const retryMessage: LockedVaultPendingNotificationsItem = { + commandToRetry: { + msg: { command: NOOP_COMMAND_SUFFIX, data: info }, + sender: { tab: tab }, + }, + target: "contextmenus.background", + }; + await BrowserApi.tabSendMessageData( + tab, + "addToLockedVaultPendingNotifications", + retryMessage + ); + + await BrowserApi.tabSendMessageData(tab, "promptForLogin"); + return; + } + + // NOTE: We don't actually use the first part of this ID, we further switch based on the parentMenuItemId + // I would really love to not add it but that is a departure from how it currently works. + const id = (info.menuItemId as string).split("_")[1]; // We create all the ids, we can guarantee they are strings + let cipher: CipherView | undefined; + if (id === NOOP_COMMAND_SUFFIX) { + // This NOOP item has come through which is generally only for no access state but since we got here + // we are actually unlocked we will do our best to find a good match of an item to autofill this is useful + // in scenarios like unlock on autofill + const ciphers = await this.cipherService.getAllDecryptedForUrl(tab.url); + cipher = ciphers.find((c) => c.reprompt === CipherRepromptType.None); + } else { + const ciphers = await this.cipherService.getAllDecrypted(); + cipher = ciphers.find((c) => c.id === id); + } + + if (cipher == null) { + return; + } + + switch (info.parentMenuItemId) { + case AUTOFILL_ID: + if (tab == null) { + return; + } + await this.autofillTabCommand.doAutofillTabWithCipherCommand(tab, cipher); + break; + case COPY_USERNAME_ID: + this.copyToClipboard({ text: cipher.login.username, tab: tab }); + break; + case COPY_PASSWORD_ID: + this.copyToClipboard({ text: cipher.login.password, tab: tab }); + this.eventCollectionService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id); + break; + case COPY_VERIFICATIONCODE_ID: + this.copyToClipboard({ text: await this.totpService.getCode(cipher.login.totp), tab: tab }); + break; + } + } + + private async getIdentifier(tab: chrome.tabs.Tab, info: chrome.contextMenus.OnClickData) { + return new Promise((resolve, reject) => { + BrowserApi.sendTabsMessage( + tab.id, + { command: "getClickedElement" }, + { frameId: info.frameId }, + (identifier: string) => { + if (chrome.runtime.lastError) { + reject(chrome.runtime.lastError); + return; + } + + resolve(identifier); + } + ); + }); + } +} diff --git a/apps/browser/src/browser/main-context-menu-handler.spec.ts b/apps/browser/src/browser/main-context-menu-handler.spec.ts new file mode 100644 index 00000000000..2dd41faf043 --- /dev/null +++ b/apps/browser/src/browser/main-context-menu-handler.spec.ts @@ -0,0 +1,137 @@ +import { mock, MockProxy } from "jest-mock-extended"; + +import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; +import { CipherType } from "@bitwarden/common/enums/cipherType"; +import { Cipher } from "@bitwarden/common/models/domain/cipher"; +import { CipherView } from "@bitwarden/common/models/view/cipher.view"; + +import { BrowserStateService } from "../services/abstractions/browser-state.service"; + +import { MainContextMenuHandler } from "./main-context-menu-handler"; + +describe("context-menu", () => { + let stateService: MockProxy; + let i18nService: MockProxy; + + let removeAllSpy: jest.SpyInstance void]>; + let createSpy: jest.SpyInstance< + string | number, + [createProperties: chrome.contextMenus.CreateProperties, callback?: () => void] + >; + + let sut: MainContextMenuHandler; + + beforeEach(() => { + stateService = mock(); + i18nService = mock(); + + removeAllSpy = jest + .spyOn(chrome.contextMenus, "removeAll") + .mockImplementation((callback) => callback()); + + createSpy = jest.spyOn(chrome.contextMenus, "create").mockImplementation((props, callback) => { + if (callback) { + callback(); + } + return props.id; + }); + + sut = new MainContextMenuHandler(stateService, i18nService); + }); + + afterEach(() => jest.resetAllMocks()); + + describe("init", () => { + it("has menu disabled", async () => { + stateService.getDisableContextMenuItem.mockResolvedValue(true); + + const createdMenu = await sut.init(); + expect(createdMenu).toBeFalsy(); + expect(removeAllSpy).toHaveBeenCalledTimes(1); + }); + + it("has menu enabled, but does not have premium", async () => { + stateService.getDisableContextMenuItem.mockResolvedValue(false); + + stateService.getCanAccessPremium.mockResolvedValue(false); + + const createdMenu = await sut.init(); + expect(createdMenu).toBeTruthy(); + expect(createSpy).toHaveBeenCalledTimes(7); + }); + + it("has menu enabled and has premium", async () => { + stateService.getDisableContextMenuItem.mockResolvedValue(false); + + stateService.getCanAccessPremium.mockResolvedValue(true); + + const createdMenu = await sut.init(); + expect(createdMenu).toBeTruthy(); + expect(createSpy).toHaveBeenCalledTimes(8); + }); + }); + + describe("loadOptions", () => { + const createCipher = (data?: { + id?: CipherView["id"]; + username?: CipherView["login"]["username"]; + password?: CipherView["login"]["password"]; + totp?: CipherView["login"]["totp"]; + viewPassword?: CipherView["viewPassword"]; + }): CipherView => { + const { id, username, password, totp, viewPassword } = data || {}; + const cipherView = new CipherView( + new Cipher({ + id: id ?? "1", + type: CipherType.Login, + viewPassword: viewPassword ?? true, + } as any) + ); + cipherView.login.username = username ?? "USERNAME"; + cipherView.login.password = password ?? "PASSWORD"; + cipherView.login.totp = totp ?? "TOTP"; + return cipherView; + }; + + it("is not a login cipher", async () => { + await sut.loadOptions("TEST_TITLE", "1", "", { + ...createCipher(), + type: CipherType.SecureNote, + } as any); + + expect(createSpy).not.toHaveBeenCalled(); + }); + + it("creates item for autofill", async () => { + await sut.loadOptions( + "TEST_TITLE", + "1", + "", + createCipher({ + username: "", + totp: "", + viewPassword: false, + }) + ); + + expect(createSpy).toHaveBeenCalledTimes(1); + }); + + it("create entry for each cipher piece", async () => { + stateService.getCanAccessPremium.mockResolvedValue(true); + + await sut.loadOptions("TEST_TITLE", "1", "", createCipher()); + + // One for autofill, copy username, copy password, and copy totp code + expect(createSpy).toHaveBeenCalledTimes(4); + }); + + it("creates noop item for no cipher", async () => { + stateService.getCanAccessPremium.mockResolvedValue(true); + + await sut.loadOptions("TEST_TITLE", "NOOP", ""); + + expect(createSpy).toHaveBeenCalledTimes(4); + }); + }); +}); diff --git a/apps/browser/src/browser/main-context-menu-handler.ts b/apps/browser/src/browser/main-context-menu-handler.ts new file mode 100644 index 00000000000..e78fb89023d --- /dev/null +++ b/apps/browser/src/browser/main-context-menu-handler.ts @@ -0,0 +1,241 @@ +import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; +import { CipherType } from "@bitwarden/common/enums/cipherType"; +import { StateFactory } from "@bitwarden/common/factories/stateFactory"; +import { Utils } from "@bitwarden/common/misc/utils"; +import { GlobalState } from "@bitwarden/common/models/domain/global-state"; +import { CipherView } from "@bitwarden/common/models/view/cipher.view"; + +import { CachedServices } from "../background/service_factories/factory-options"; +import { + i18nServiceFactory, + I18nServiceInitOptions, +} from "../background/service_factories/i18n-service.factory"; +import { + stateServiceFactory, + StateServiceInitOptions, +} from "../background/service_factories/state-service.factory"; +import { Account } from "../models/account"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; + +export const ROOT_ID = "root"; + +export const AUTOFILL_ID = "autofill"; +export const COPY_USERNAME_ID = "copy-username"; +export const COPY_PASSWORD_ID = "copy-password"; +export const COPY_VERIFICATIONCODE_ID = "copy-totp"; +export const COPY_IDENTIFIER_ID = "copy-identifier"; + +const SEPARATOR_ID = "separator"; +export const GENERATE_PASSWORD_ID = "generate-password"; + +export const NOOP_COMMAND_SUFFIX = "noop"; + +export class MainContextMenuHandler { + // + private initRunning = false; + + create: (options: chrome.contextMenus.CreateProperties) => Promise; + + constructor(private stateService: BrowserStateService, private i18nService: I18nService) { + if (chrome.contextMenus) { + this.create = (options) => { + return new Promise((resolve, reject) => { + chrome.contextMenus.create(options, () => { + if (chrome.runtime.lastError) { + reject(chrome.runtime.lastError); + return; + } + resolve(); + }); + }); + }; + } else { + this.create = (_options) => Promise.resolve(); + } + } + + static async mv3Create(cachedServices: CachedServices) { + const stateFactory = new StateFactory(GlobalState, Account); + const serviceOptions: StateServiceInitOptions & I18nServiceInitOptions = { + cryptoFunctionServiceOptions: { + win: self, + }, + encryptServiceOptions: { + logMacFailures: false, + }, + i18nServiceOptions: { + systemLanguage: chrome.i18n.getUILanguage(), + }, + logServiceOptions: { + isDev: false, + }, + stateMigrationServiceOptions: { + stateFactory: stateFactory, + }, + stateServiceOptions: { + stateFactory: stateFactory, + }, + }; + + return new MainContextMenuHandler( + await stateServiceFactory(cachedServices, serviceOptions), + await i18nServiceFactory(cachedServices, serviceOptions) + ); + } + + /** + * + * @returns a boolean showing whether or not items were created + */ + async init(): Promise { + const menuDisabled = await this.stateService.getDisableContextMenuItem(); + + if (this.initRunning) { + return menuDisabled; + } + + try { + if (menuDisabled) { + await MainContextMenuHandler.removeAll(); + return false; + } + + const create = async (options: Omit) => { + await this.create({ ...options, contexts: ["all"] }); + }; + + await create({ + id: ROOT_ID, + title: "Bitwarden", + }); + + await create({ + id: AUTOFILL_ID, + parentId: ROOT_ID, + title: this.i18nService.t("autoFill"), + }); + + await create({ + id: COPY_USERNAME_ID, + parentId: ROOT_ID, + title: this.i18nService.t("copyUsername"), + }); + + await create({ + id: COPY_PASSWORD_ID, + parentId: ROOT_ID, + title: this.i18nService.t("copyPassword"), + }); + + if (await this.stateService.getCanAccessPremium()) { + await create({ + id: COPY_VERIFICATIONCODE_ID, + parentId: ROOT_ID, + title: this.i18nService.t("copyVerificationCode"), + }); + } + + await create({ + id: SEPARATOR_ID, + type: "separator", + parentId: ROOT_ID, + }); + + await create({ + id: GENERATE_PASSWORD_ID, + parentId: ROOT_ID, + title: this.i18nService.t("generatePasswordCopied"), + }); + + await create({ + id: COPY_IDENTIFIER_ID, + parentId: ROOT_ID, + title: this.i18nService.t("copyElementIdentifier"), + }); + + return true; + } finally { + this.initRunning = false; + } + } + + static async removeAll() { + return new Promise((resolve, reject) => { + chrome.contextMenus.removeAll(() => { + if (chrome.runtime.lastError) { + reject(chrome.runtime.lastError); + return; + } + + resolve(); + }); + }); + } + + static remove(menuItemId: string) { + return new Promise((resolve, reject) => { + chrome.contextMenus.remove(menuItemId, () => { + if (chrome.runtime.lastError) { + reject(chrome.runtime.lastError); + return; + } + + resolve(); + }); + }); + } + + async loadOptions(title: string, id: string, url: string, cipher?: CipherView | undefined) { + if (cipher != null && cipher.type !== CipherType.Login) { + return; + } + + const sanitizedTitle = MainContextMenuHandler.sanitizeContextMenuTitle(title); + + const createChildItem = async (parent: string) => { + const menuItemId = `${parent}_${id}`; + return await this.create({ + type: "normal", + id: menuItemId, + parentId: parent, + title: sanitizedTitle, + contexts: ["all"], + }); + }; + + if (cipher == null || !Utils.isNullOrEmpty(cipher.login.password)) { + await createChildItem(AUTOFILL_ID); + if (cipher?.viewPassword ?? true) { + await createChildItem(COPY_PASSWORD_ID); + } + } + + if (cipher == null || !Utils.isNullOrEmpty(cipher.login.username)) { + await createChildItem(COPY_USERNAME_ID); + } + + const canAccessPremium = await this.stateService.getCanAccessPremium(); + if (canAccessPremium && (cipher == null || !Utils.isNullOrEmpty(cipher.login.totp))) { + await createChildItem(COPY_VERIFICATIONCODE_ID); + } + } + + static sanitizeContextMenuTitle(title: string): string { + return title.replace(/&/g, "&&"); + } + + async noAccess() { + if (await this.init()) { + const authed = await this.stateService.getIsAuthenticated(); + await this.loadOptions( + this.i18nService.t(authed ? "unlockVaultMenu" : "loginToVaultMenu"), + NOOP_COMMAND_SUFFIX, + "" + ); + } + } + + async noLogins(url: string) { + await this.loadOptions(this.i18nService.t("noMatchingLogins"), NOOP_COMMAND_SUFFIX, url); + } +} diff --git a/apps/browser/src/clipboard/clear-clipboard.spec.ts b/apps/browser/src/clipboard/clear-clipboard.spec.ts new file mode 100644 index 00000000000..0166bdb47af --- /dev/null +++ b/apps/browser/src/clipboard/clear-clipboard.spec.ts @@ -0,0 +1,39 @@ +import { BrowserApi } from "../browser/browserApi"; + +import { ClearClipboard } from "./clear-clipboard"; + +describe("clearClipboard", () => { + describe("run", () => { + it("Does not clear clipboard when no active tabs are retrieved", async () => { + jest.spyOn(BrowserApi, "getActiveTabs").mockResolvedValue([] as any); + + jest.spyOn(BrowserApi, "sendTabsMessage").mockReturnValue(); + + await ClearClipboard.run(); + + expect(jest.spyOn(BrowserApi, "sendTabsMessage")).not.toHaveBeenCalled(); + + expect(jest.spyOn(BrowserApi, "sendTabsMessage")).not.toHaveBeenCalledWith(1, { + command: "clearClipboard", + }); + }); + + it("Sends a message to the content script to clear the clipboard", async () => { + jest.spyOn(BrowserApi, "getActiveTabs").mockResolvedValue([ + { + id: 1, + }, + ] as any); + + jest.spyOn(BrowserApi, "sendTabsMessage").mockReturnValue(); + + await ClearClipboard.run(); + + expect(jest.spyOn(BrowserApi, "sendTabsMessage")).toHaveBeenCalledTimes(1); + + expect(jest.spyOn(BrowserApi, "sendTabsMessage")).toHaveBeenCalledWith(1, { + command: "clearClipboard", + }); + }); + }); +}); diff --git a/apps/browser/src/clipboard/clear-clipboard.spec.ts.disabled b/apps/browser/src/clipboard/clear-clipboard.spec.ts.disabled deleted file mode 100644 index 6f7314d91ac..00000000000 --- a/apps/browser/src/clipboard/clear-clipboard.spec.ts.disabled +++ /dev/null @@ -1,79 +0,0 @@ -import { mock, MockProxy } from "jest-mock-extended"; - -import { BrowserApi } from "../browser/browserApi"; -import { StateService } from "../services/abstractions/state.service"; - -import { ClearClipboard } from "./clear-clipboard"; -import { getClearClipboardTime, setClearClipboardTime } from "./clipboard-state"; - -jest.mock("./clipboard-state", () => { - return { - getClearClipboardTime: jest.fn(), - setClearClipboardTime: jest.fn(), - }; -}); - -const getClearClipboardTimeMock = getClearClipboardTime as jest.Mock; -const setClearClipboardTimeMock = setClearClipboardTime as jest.Mock; - -describe("clearClipboard", () => { - describe("run", () => { - let stateService: MockProxy; - let serviceCache: Record; - - beforeEach(() => { - stateService = mock(); - serviceCache = { - stateService: stateService, - }; - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it("has a clear time that is past execution time", async () => { - const executionTime = new Date(2022, 1, 1, 12); - const clearTime = new Date(2022, 1, 1, 12, 1); - - jest.spyOn(BrowserApi, "getActiveTabs").mockResolvedValue([ - { - id: 1, - }, - ] as any); - - jest.spyOn(BrowserApi, "sendTabsMessage").mockReturnValue(); - - getClearClipboardTimeMock.mockResolvedValue(clearTime.getTime()); - - await ClearClipboard.run(executionTime, serviceCache); - - expect(jest.spyOn(BrowserApi, "sendTabsMessage")).toHaveBeenCalledTimes(1); - - expect(jest.spyOn(BrowserApi, "sendTabsMessage")).toHaveBeenCalledWith(1, { - command: "clearClipboard", - }); - }); - - it("has a clear time before execution time", async () => { - const executionTime = new Date(2022, 1, 1, 12); - const clearTime = new Date(2022, 1, 1, 11); - - setClearClipboardTimeMock.mockResolvedValue(clearTime.getTime()); - - await ClearClipboard.run(executionTime, serviceCache); - - expect(jest.spyOn(BrowserApi, "getActiveTabs")).not.toHaveBeenCalled(); - }); - - it("has an undefined clearTime", async () => { - const executionTime = new Date(2022, 1, 1); - - getClearClipboardTimeMock.mockResolvedValue(undefined); - - await ClearClipboard.run(executionTime, serviceCache); - - expect(jest.spyOn(BrowserApi, "getActiveTabs")).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/apps/browser/src/clipboard/clear-clipboard.ts b/apps/browser/src/clipboard/clear-clipboard.ts index 00bf329f8d3..149992555b9 100644 --- a/apps/browser/src/clipboard/clear-clipboard.ts +++ b/apps/browser/src/clipboard/clear-clipboard.ts @@ -1,43 +1,15 @@ -import { StateFactory } from "@bitwarden/common/factories/stateFactory"; -import { GlobalState } from "@bitwarden/common/models/domain/global-state"; - -import { stateServiceFactory } from "../background/service_factories/state-service.factory"; import { BrowserApi } from "../browser/browserApi"; -import { Account } from "../models/account"; -import { getClearClipboardTime } from "./clipboard-state"; +export const clearClipboardAlarmName = "clearClipboard"; export class ClearClipboard { - static async run(executionTime: Date, serviceCache: Record) { - const stateFactory = new StateFactory(GlobalState, Account); - const stateService = await stateServiceFactory(serviceCache, { - cryptoFunctionServiceOptions: { - win: self, - }, - encryptServiceOptions: { - logMacFailures: false, - }, - logServiceOptions: { - isDev: false, - }, - stateMigrationServiceOptions: { - stateFactory: stateFactory, - }, - stateServiceOptions: { - stateFactory: stateFactory, - }, - }); - - const clearClipboardTime = await getClearClipboardTime(stateService); - - if (!clearClipboardTime) { - return; - } - - if (clearClipboardTime < executionTime.getTime()) { - return; - } - + /** + We currently rely on an active tab with an injected content script (`../content/misc-utils.ts`) to clear the clipboard via `window.navigator.clipboard.writeText(text)` + + With https://bugs.chromium.org/p/chromium/issues/detail?id=1160302 it was said that service workers, + would have access to the clipboard api and then we could migrate to a simpler solution + */ + static async run() { const activeTabs = await BrowserApi.getActiveTabs(); if (!activeTabs || activeTabs.length === 0) { return; diff --git a/apps/browser/src/clipboard/clipboard-state.ts b/apps/browser/src/clipboard/clipboard-state.ts deleted file mode 100644 index a1c15addc0a..00000000000 --- a/apps/browser/src/clipboard/clipboard-state.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { StateService } from "../services/abstractions/state.service"; - -const clearClipboardStorageKey = "clearClipboardTime"; -export const getClearClipboardTime = async (stateService: StateService) => { - return await stateService.getFromSessionMemory(clearClipboardStorageKey); -}; - -export const setClearClipboardTime = async (stateService: StateService, time: number) => { - await stateService.setInSessionMemory(clearClipboardStorageKey, time); -}; diff --git a/apps/browser/src/clipboard/generate-password-to-clipboard-command.spec.ts b/apps/browser/src/clipboard/generate-password-to-clipboard-command.spec.ts index e9c2141211f..5d119513f43 100644 --- a/apps/browser/src/clipboard/generate-password-to-clipboard-command.spec.ts +++ b/apps/browser/src/clipboard/generate-password-to-clipboard-command.spec.ts @@ -2,30 +2,30 @@ import { mock, MockProxy } from "jest-mock-extended"; import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; +import { setAlarmTime } from "../alarms/alarm-state"; import { BrowserApi } from "../browser/browserApi"; -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; -import { setClearClipboardTime } from "./clipboard-state"; +import { clearClipboardAlarmName } from "./clear-clipboard"; import { GeneratePasswordToClipboardCommand } from "./generate-password-to-clipboard-command"; -jest.mock("./clipboard-state", () => { +jest.mock("../alarms/alarm-state", () => { return { - getClearClipboardTime: jest.fn(), - setClearClipboardTime: jest.fn(), + setAlarmTime: jest.fn(), }; }); -const setClearClipboardTimeMock = setClearClipboardTime as jest.Mock; +const setAlarmTimeMock = setAlarmTime as jest.Mock; describe("GeneratePasswordToClipboardCommand", () => { let passwordGenerationService: MockProxy; - let stateService: MockProxy; + let stateService: MockProxy; let sut: GeneratePasswordToClipboardCommand; beforeEach(() => { passwordGenerationService = mock(); - stateService = mock(); + stateService = mock(); passwordGenerationService.getOptions.mockResolvedValue([{ length: 8 }, {} as any]); @@ -53,9 +53,9 @@ describe("GeneratePasswordToClipboardCommand", () => { text: "PASSWORD", }); - expect(setClearClipboardTimeMock).toHaveBeenCalledTimes(1); + expect(setAlarmTimeMock).toHaveBeenCalledTimes(1); - expect(setClearClipboardTimeMock).toHaveBeenCalledWith(stateService, expect.any(Number)); + expect(setAlarmTimeMock).toHaveBeenCalledWith(clearClipboardAlarmName, expect.any(Number)); }); it("does not have clear clipboard value", async () => { @@ -70,7 +70,7 @@ describe("GeneratePasswordToClipboardCommand", () => { text: "PASSWORD", }); - expect(setClearClipboardTimeMock).not.toHaveBeenCalled(); + expect(setAlarmTimeMock).not.toHaveBeenCalled(); }); }); }); diff --git a/apps/browser/src/clipboard/generate-password-to-clipboard-command.ts b/apps/browser/src/clipboard/generate-password-to-clipboard-command.ts index ca92d2c686f..0cd8eec2c93 100644 --- a/apps/browser/src/clipboard/generate-password-to-clipboard-command.ts +++ b/apps/browser/src/clipboard/generate-password-to-clipboard-command.ts @@ -1,14 +1,15 @@ import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; -import { StateService } from "../services/abstractions/state.service"; +import { setAlarmTime } from "../alarms/alarm-state"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; -import { setClearClipboardTime } from "./clipboard-state"; +import { clearClipboardAlarmName } from "./clear-clipboard"; import { copyToClipboard } from "./copy-to-clipboard-command"; export class GeneratePasswordToClipboardCommand { constructor( private passwordGenerationService: PasswordGenerationService, - private stateService: StateService + private stateService: BrowserStateService ) {} async generatePasswordToClipboard(tab: chrome.tabs.Tab) { @@ -20,7 +21,7 @@ export class GeneratePasswordToClipboardCommand { const clearClipboard = await this.stateService.getClearClipboard(); if (clearClipboard != null) { - await setClearClipboardTime(this.stateService, Date.now() + clearClipboard * 1000); + await setAlarmTime(clearClipboardAlarmName, clearClipboard * 1000); } } } diff --git a/apps/browser/src/commands/autoFillActiveTabCommand.ts b/apps/browser/src/commands/autofill-tab-command.ts similarity index 57% rename from apps/browser/src/commands/autoFillActiveTabCommand.ts rename to apps/browser/src/commands/autofill-tab-command.ts index 74cdad55d73..cf94dc93ae3 100644 --- a/apps/browser/src/commands/autoFillActiveTabCommand.ts +++ b/apps/browser/src/commands/autofill-tab-command.ts @@ -1,10 +1,12 @@ +import { CipherView } from "@bitwarden/common/models/view/cipher.view"; + import AutofillPageDetails from "../models/autofillPageDetails"; import { AutofillService } from "../services/abstractions/autofill.service"; -export class AutoFillActiveTabCommand { +export class AutofillTabCommand { constructor(private autofillService: AutofillService) {} - async doAutoFillActiveTabCommand(tab: chrome.tabs.Tab) { + async doAutofillTabCommand(tab: chrome.tabs.Tab) { if (!tab.id) { throw new Error("Tab does not have an id, cannot complete autofill."); } @@ -23,6 +25,30 @@ export class AutoFillActiveTabCommand { ); } + async doAutofillTabWithCipherCommand(tab: chrome.tabs.Tab, cipher: CipherView) { + if (!tab.id) { + throw new Error("Tab does not have an id, cannot complete autofill."); + } + + const details = await this.collectPageDetails(tab.id); + await this.autofillService.doAutoFill({ + tab: tab, + cipher: cipher, + pageDetails: [ + { + frameId: 0, + tab: tab, + details: details, + }, + ], + skipLastUsed: false, + skipUsernameOnlyFill: false, + onlyEmptyFields: false, + onlyVisibleFields: false, + fillNewPassword: true, + }); + } + private async collectPageDetails(tabId: number): Promise { return new Promise((resolve, reject) => { chrome.tabs.sendMessage( diff --git a/apps/browser/src/content/autofill.js b/apps/browser/src/content/autofill.js index ee4dfab1b0d..d9ac840c8d8 100644 --- a/apps/browser/src/content/autofill.js +++ b/apps/browser/src/content/autofill.js @@ -42,6 +42,8 @@ 9. Add new handler, for new command that responds with page details in response callback 10. Handle sandbox iframe and sandbox rule in CSP 11. Work on array of saved urls instead of just one to determine if we should autofill non-https sites + 12. Remove setting of attribute com.browser.browser.userEdited on user-inputs + 13. Handle null value URLs in urlNotSecure */ function collect(document, undefined) { @@ -50,11 +52,6 @@ // END MODIFICATION document.elementsByOPID = {}; - document.addEventListener('input', function (inputevent) { - inputevent.a !== false && - inputevent.target.tagName.toLowerCase() === 'input' && - (inputevent.target.dataset['com.bitwarden.browser.userEdited'] = 'yes'); - }, true); function getPageDetails(theDoc, oneShotId) { // start helpers @@ -279,8 +276,6 @@ addProp(field, 'title', getElementAttrValue(el, 'title')); // START MODIFICATION - addProp(field, 'userEdited', !!el.dataset['com.browser.browser.userEdited']); - var elTagName = el.tagName.toLowerCase(); addProp(field, 'tagName', elTagName); @@ -638,7 +633,7 @@ return false; } - return savedURLs.some(url => url.indexOf('https://') === 0) && 'http:' === document.location.protocol && (passwordInputs = document.querySelectorAll('input[type=password]'), + return savedURLs.some(url => url?.indexOf('https://') === 0) && 'http:' === document.location.protocol && (passwordInputs = document.querySelectorAll('input[type=password]'), 0 < passwordInputs.length && (confirmResult = confirm('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.\n\nDo you still wish to fill this login?'), 0 == confirmResult)) ? true : false; } diff --git a/apps/browser/src/content/contextMenuHandler.ts b/apps/browser/src/content/contextMenuHandler.ts index b6d0159df0f..30aa40be4ac 100644 --- a/apps/browser/src/content/contextMenuHandler.ts +++ b/apps/browser/src/content/contextMenuHandler.ts @@ -54,9 +54,12 @@ document.addEventListener("contextmenu", (event) => { }); // Runs when the 'Copy Custom Field Name' context menu item is actually clicked. -chrome.runtime.onMessage.addListener((event) => { +chrome.runtime.onMessage.addListener((event, _sender, sendResponse) => { if (event.command === "getClickedElement") { const identifier = getClickedElementIdentifier(); + if (sendResponse) { + sendResponse(identifier); + } chrome.runtime.sendMessage({ command: "getClickedElementResponse", sender: "contextMenuHandler", diff --git a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts index cc8a5618760..ab29c86e984 100644 --- a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts +++ b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.spec.ts @@ -1,6 +1,9 @@ import { BehaviorSubject } from "rxjs"; -import { StateService } from "../../services/state.service"; +import { AbstractMemoryStorageService } from "@bitwarden/common/abstractions/storage.service"; +import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service"; + +import { BrowserStateService } from "../../services/browser-state.service"; import { browserSession } from "./browser-session.decorator"; import { SessionStorable } from "./session-storable"; @@ -11,36 +14,55 @@ import { sessionSync } from "./session-sync.decorator"; jest.mock("./session-syncer"); describe("browserSession decorator", () => { - it("should throw if StateService is not a constructor argument", () => { + it("should throw if neither StateService nor MemoryStorageService is a constructor argument", () => { @browserSession class TestClass {} expect(() => { new TestClass(); }).toThrowError( - "Cannot decorate TestClass with browserSession, Browser's StateService must be injected" + "Cannot decorate TestClass with browserSession, Browser's AbstractMemoryStorageService must be accessible through the observed classes parameters" ); }); it("should create if StateService is a constructor argument", () => { - const stateService = Object.create(StateService.prototype, {}); + const stateService = Object.create(BrowserStateService.prototype, { + memoryStorageService: { + value: Object.create(MemoryStorageService.prototype, { + type: { value: MemoryStorageService.TYPE }, + }), + }, + }); @browserSession class TestClass { - constructor(private stateService: StateService) {} + constructor(private stateService: BrowserStateService) {} } expect(new TestClass(stateService)).toBeDefined(); }); + it("should create if MemoryStorageService is a constructor argument", () => { + const memoryStorageService = Object.create(MemoryStorageService.prototype, { + type: { value: MemoryStorageService.TYPE }, + }); + + @browserSession + class TestClass { + constructor(private memoryStorageService: AbstractMemoryStorageService) {} + } + + expect(new TestClass(memoryStorageService)).toBeDefined(); + }); + describe("interaction with @sessionSync decorator", () => { - let stateService: StateService; + let memoryStorageService: MemoryStorageService; @browserSession class TestClass { @sessionSync({ initializer: (s: string) => s }) private behaviorSubject = new BehaviorSubject(""); - constructor(private stateService: StateService) {} + constructor(private memoryStorageService: MemoryStorageService) {} fromJSON(json: any) { this.behaviorSubject.next(json); @@ -48,16 +70,18 @@ describe("browserSession decorator", () => { } beforeEach(() => { - stateService = Object.create(StateService.prototype, {}) as StateService; + memoryStorageService = Object.create(MemoryStorageService.prototype, { + type: { value: MemoryStorageService.TYPE }, + }); }); it("should create a session syncer", () => { - const testClass = new TestClass(stateService) as any as SessionStorable; + const testClass = new TestClass(memoryStorageService) as any as SessionStorable; expect(testClass.__sessionSyncers.length).toEqual(1); }); it("should initialize the session syncer", () => { - const testClass = new TestClass(stateService) as any as SessionStorable; + const testClass = new TestClass(memoryStorageService) as any as SessionStorable; expect(testClass.__sessionSyncers[0].init).toHaveBeenCalled(); }); }); diff --git a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts index 73cdf767357..dbb45ddba8e 100644 --- a/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts +++ b/apps/browser/src/decorators/session-sync-observable/browser-session.decorator.ts @@ -1,6 +1,6 @@ import { Constructor } from "type-fest"; -import { StateService } from "../../services/state.service"; +import { AbstractMemoryStorageService } from "@bitwarden/common/abstractions/storage.service"; import { SessionStorable } from "./session-storable"; import { SessionSyncer } from "./session-syncer"; @@ -22,26 +22,51 @@ export function browserSession>(constructor: TCto super(...args); // Require state service to be injected - const stateService = args.find((arg) => arg instanceof StateService); - if (!stateService) { - throw new Error( - `Cannot decorate ${constructor.name} with browserSession, Browser's StateService must be injected` - ); - } + const storageService: AbstractMemoryStorageService = this.findStorageService( + [this as any].concat(args) + ); if (this.__syncedItemMetadata == null || !(this.__syncedItemMetadata instanceof Array)) { return; } this.__sessionSyncers = this.__syncedItemMetadata.map((metadata) => - this.buildSyncer(metadata, stateService) + this.buildSyncer(metadata, storageService) ); } - buildSyncer(metadata: SyncedItemMetadata, stateService: StateService) { - const syncer = new SessionSyncer((this as any)[metadata.propertyKey], stateService, metadata); + buildSyncer(metadata: SyncedItemMetadata, storageSerice: AbstractMemoryStorageService) { + const syncer = new SessionSyncer( + (this as any)[metadata.propertyKey], + storageSerice, + metadata + ); syncer.init(); return syncer; } + + findStorageService(args: any[]): AbstractMemoryStorageService { + const storageService = args.find(this.isMemoryStorageService); + + if (storageService) { + return storageService; + } + + const stateService = args.find( + (arg) => + arg?.memoryStorageService != null && this.isMemoryStorageService(arg.memoryStorageService) + ); + if (stateService) { + return stateService.memoryStorageService; + } + + throw new Error( + `Cannot decorate ${constructor.name} with browserSession, Browser's AbstractMemoryStorageService must be accessible through the observed classes parameters` + ); + } + + isMemoryStorageService(arg: any): arg is AbstractMemoryStorageService { + return arg.type != null && arg.type === AbstractMemoryStorageService.TYPE; + } }; } diff --git a/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.spec.ts b/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.spec.ts index 38ec7aac9a1..c6d46b28fd1 100644 --- a/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.spec.ts +++ b/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.spec.ts @@ -8,9 +8,12 @@ describe("sessionSync decorator", () => { class TestClass { @sessionSync({ ctor: ctor, initializer: initializer }) private testProperty = new BehaviorSubject(""); + @sessionSync({ ctor: ctor, initializer: initializer, initializeAs: "array" }) + private secondTestProperty = new BehaviorSubject(""); complete() { this.testProperty.complete(); + this.secondTestProperty.complete(); } } @@ -19,11 +22,40 @@ describe("sessionSync decorator", () => { expect((testClass as any).__syncedItemMetadata).toEqual([ expect.objectContaining({ propertyKey: "testProperty", - sessionKey: "TestClass_testProperty", + sessionKey: "testProperty_0", ctor: ctor, initializer: initializer, }), - testClass.complete(), + expect.objectContaining({ + propertyKey: "secondTestProperty", + sessionKey: "secondTestProperty_1", + ctor: ctor, + initializer: initializer, + initializeAs: "array", + }), ]); + testClass.complete(); + }); + + class TestClass2 { + @sessionSync({ ctor: ctor, initializer: initializer }) + private testProperty = new BehaviorSubject(""); + + complete() { + this.testProperty.complete(); + } + } + + it("should maintain sessionKey index count for other test classes", () => { + const testClass = new TestClass2(); + expect((testClass as any).__syncedItemMetadata).toEqual([ + expect.objectContaining({ + propertyKey: "testProperty", + sessionKey: "testProperty_2", + ctor: ctor, + initializer: initializer, + }), + ]); + testClass.complete(); }); }); diff --git a/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.ts b/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.ts index df0764528f7..6a6b3d33fa1 100644 --- a/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.ts +++ b/apps/browser/src/decorators/session-sync-observable/session-sync.decorator.ts @@ -1,13 +1,17 @@ import { Jsonify } from "type-fest"; import { SessionStorable } from "./session-storable"; +import { InitializeOptions } from "./sync-item-metadata"; -class BuildOptions { +class BuildOptions> { ctor?: new () => T; - initializer?: (keyValuePair: Jsonify) => T; - initializeAsArray? = false; + initializer?: (keyValuePair: TJson) => T; + initializeAs?: InitializeOptions; } +// Used to ensure uniqueness for each synced observable +let index = 0; + /** * A decorator used to indicate the BehaviorSubject should be synced for this browser session across all contexts. * @@ -20,10 +24,10 @@ class BuildOptions { * @param buildOptions * Builders for the value, requires either a constructor (ctor) for your BehaviorSubject type or an * initializer function that takes a key value pair representation of the BehaviorSubject data - * and returns your instantiated BehaviorSubject value. `initializeAsArray can optionally be used to indicate + * and returns your instantiated BehaviorSubject value. `initializeAs can optionally be used to indicate * the provided initializer function should be used to build an array of values. For example, * ```ts - * \@sessionSync({ initializer: Foo.fromJSON, initializeAsArray: true }) + * \@sessionSync({ initializer: Foo.fromJSON, initializeAs: 'array' }) * ``` * is equivalent to * ``` @@ -43,10 +47,10 @@ export function sessionSync(buildOptions: BuildOptions) { p.__syncedItemMetadata.push({ propertyKey, - sessionKey: `${prototype.constructor.name}_${propertyKey}`, + sessionKey: `${propertyKey}_${index++}`, ctor: buildOptions.ctor, initializer: buildOptions.initializer, - initializeAsArray: buildOptions.initializeAsArray, + initializeAs: buildOptions.initializeAs ?? "object", }); }; } diff --git a/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts b/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts index 5286cece1bb..05d0da7527b 100644 --- a/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts +++ b/apps/browser/src/decorators/session-sync-observable/session-syncer.spec.ts @@ -1,8 +1,10 @@ +import { awaitAsync } from "@bitwarden/angular/../test-utils"; import { mock, MockProxy } from "jest-mock-extended"; -import { BehaviorSubject } from "rxjs"; +import { BehaviorSubject, ReplaySubject } from "rxjs"; + +import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service"; import { BrowserApi } from "../../browser/browserApi"; -import { StateService } from "../../services/abstractions/state.service"; import { SessionSyncer } from "./session-syncer"; import { SyncedItemMetadata } from "./sync-item-metadata"; @@ -10,8 +12,13 @@ import { SyncedItemMetadata } from "./sync-item-metadata"; describe("session syncer", () => { const propertyKey = "behaviorSubject"; const sessionKey = "Test__" + propertyKey; - const metaData = { propertyKey, sessionKey, initializer: (s: string) => s }; - let stateService: MockProxy; + const metaData: SyncedItemMetadata = { + propertyKey, + sessionKey, + initializer: (s: string) => s, + initializeAs: "object", + }; + let storageService: MockProxy; let sut: SessionSyncer; let behaviorSubject: BehaviorSubject; @@ -23,8 +30,9 @@ describe("session syncer", () => { manifest_version: 3, }); - stateService = mock(); - sut = new SessionSyncer(behaviorSubject, stateService, metaData); + storageService = mock(); + storageService.has.mockResolvedValue(false); + sut = new SessionSyncer(behaviorSubject, storageService, metaData); }); afterEach(() => { @@ -34,53 +42,87 @@ describe("session syncer", () => { }); describe("constructor", () => { - it("should throw if behaviorSubject is not an instance of BehaviorSubject", () => { + it("should throw if subject is not an instance of Subject", () => { expect(() => { - new SessionSyncer({} as any, stateService, null); - }).toThrowError("behaviorSubject must be an instance of BehaviorSubject"); + new SessionSyncer({} as any, storageService, null); + }).toThrowError("subject must inherit from Subject"); }); it("should create if either ctor or initializer is provided", () => { expect( - new SessionSyncer(behaviorSubject, stateService, { propertyKey, sessionKey, ctor: String }) + new SessionSyncer(behaviorSubject, storageService, { + propertyKey, + sessionKey, + ctor: String, + initializeAs: "object", + }) ).toBeDefined(); expect( - new SessionSyncer(behaviorSubject, stateService, { + new SessionSyncer(behaviorSubject, storageService, { propertyKey, sessionKey, initializer: (s: any) => s, + initializeAs: "object", }) ).toBeDefined(); }); it("should throw if neither ctor or initializer is provided", () => { expect(() => { - new SessionSyncer(behaviorSubject, stateService, { propertyKey, sessionKey }); + new SessionSyncer(behaviorSubject, storageService, { + propertyKey, + sessionKey, + initializeAs: "object", + }); }).toThrowError("ctor or initializer must be provided"); }); }); - describe("manifest v2 init", () => { - let observeSpy: jest.SpyInstance; - let listenForUpdatesSpy: jest.SpyInstance; - - beforeEach(() => { - observeSpy = jest.spyOn(behaviorSubject, "subscribe").mockReturnThis(); - listenForUpdatesSpy = jest.spyOn(BrowserApi, "messageListener").mockReturnValue(); - jest.spyOn(chrome.runtime, "getManifest").mockReturnValue({ - name: "bitwarden-test", - version: "0.0.0", - manifest_version: 2, - }); + describe("init", () => { + it("should ignore all updates currently in a ReplaySubject's buffer", () => { + const replaySubject = new ReplaySubject(Infinity); + replaySubject.next("1"); + replaySubject.next("2"); + replaySubject.next("3"); + sut = new SessionSyncer(replaySubject, storageService, metaData); + // block observing the subject + jest.spyOn(sut as any, "observe").mockImplementation(); sut.init(); + + expect(sut["ignoreNUpdates"]).toBe(3); }); - it("should not start observing", () => { - expect(observeSpy).not.toHaveBeenCalled(); + it("should ignore BehaviorSubject's initial value", () => { + const behaviorSubject = new BehaviorSubject("initial"); + sut = new SessionSyncer(behaviorSubject, storageService, metaData); + // block observing the subject + jest.spyOn(sut as any, "observe").mockImplementation(); + + sut.init(); + + expect(sut["ignoreNUpdates"]).toBe(1); }); - it("should not start listening", () => { - expect(listenForUpdatesSpy).not.toHaveBeenCalled(); + it("should grab an initial value from storage if it exists", async () => { + storageService.has.mockResolvedValue(true); + //Block a call to update + const updateSpy = jest.spyOn(sut as any, "update").mockImplementation(); + + sut.init(); + await awaitAsync(); + + expect(updateSpy).toHaveBeenCalledWith(); + }); + + it("should not grab an initial value from storage if it does not exist", async () => { + storageService.has.mockResolvedValue(false); + //Block a call to update + const updateSpy = jest.spyOn(sut as any, "update").mockImplementation(); + + sut.init(); + await awaitAsync(); + + expect(updateSpy).not.toHaveBeenCalled(); }); }); @@ -98,8 +140,8 @@ describe("session syncer", () => { it("should update the session memory", async () => { // await finishing of fire-and-forget operation await new Promise((resolve) => setTimeout(resolve, 100)); - expect(stateService.setInSessionMemory).toHaveBeenCalledTimes(1); - expect(stateService.setInSessionMemory).toHaveBeenCalledWith(sessionKey, "test"); + expect(storageService.save).toHaveBeenCalledTimes(1); + expect(storageService.save).toHaveBeenCalledWith(sessionKey, "test"); }); it("should update sessionSyncers in other contexts", async () => { @@ -129,26 +171,29 @@ describe("session syncer", () => { it("should ignore messages with the wrong command", async () => { await sut.updateFromMessage({ command: "wrong_command", id: sut.id }); - expect(stateService.getFromSessionMemory).not.toHaveBeenCalled(); + expect(storageService.getBypassCache).not.toHaveBeenCalled(); expect(nextSpy).not.toHaveBeenCalled(); }); it("should ignore messages from itself", async () => { await sut.updateFromMessage({ command: `${sessionKey}_update`, id: sut.id }); - expect(stateService.getFromSessionMemory).not.toHaveBeenCalled(); + expect(storageService.getBypassCache).not.toHaveBeenCalled(); expect(nextSpy).not.toHaveBeenCalled(); }); it("should update from message on emit from another instance", async () => { const builder = jest.fn(); jest.spyOn(SyncedItemMetadata, "builder").mockReturnValue(builder); - stateService.getFromSessionMemory.mockResolvedValue("test"); + storageService.getBypassCache.mockResolvedValue("test"); await sut.updateFromMessage({ command: `${sessionKey}_update`, id: "different_id" }); + await awaitAsync(); - expect(stateService.getFromSessionMemory).toHaveBeenCalledTimes(1); - expect(stateService.getFromSessionMemory).toHaveBeenCalledWith(sessionKey, builder); + expect(storageService.getBypassCache).toHaveBeenCalledTimes(1); + expect(storageService.getBypassCache).toHaveBeenCalledWith(sessionKey, { + deserializer: builder, + }); expect(nextSpy).toHaveBeenCalledTimes(1); expect(nextSpy).toHaveBeenCalledWith("test"); diff --git a/apps/browser/src/decorators/session-sync-observable/session-syncer.ts b/apps/browser/src/decorators/session-sync-observable/session-syncer.ts index 2acfed2954f..8c24ffb1852 100644 --- a/apps/browser/src/decorators/session-sync-observable/session-syncer.ts +++ b/apps/browser/src/decorators/session-sync-observable/session-syncer.ts @@ -1,9 +1,9 @@ -import { BehaviorSubject, concatMap, Subscription } from "rxjs"; +import { BehaviorSubject, concatMap, ReplaySubject, Subject, Subscription } from "rxjs"; +import { AbstractMemoryStorageService } from "@bitwarden/common/abstractions/storage.service"; import { Utils } from "@bitwarden/common/misc/utils"; import { BrowserApi } from "../../browser/browserApi"; -import { StateService } from "../../services/abstractions/state.service"; import { SyncedItemMetadata } from "./sync-item-metadata"; @@ -11,16 +11,16 @@ export class SessionSyncer { subscription: Subscription; id = Utils.newGuid(); - // everyone gets the same initial values - private ignoreNextUpdate = true; + // ignore initial values + private ignoreNUpdates = 0; constructor( - private behaviorSubject: BehaviorSubject, - private stateService: StateService, + private subject: Subject, + private memoryStorageService: AbstractMemoryStorageService, private metaData: SyncedItemMetadata ) { - if (!(behaviorSubject instanceof BehaviorSubject)) { - throw new Error("behaviorSubject must be an instance of BehaviorSubject"); + if (!(subject instanceof Subject)) { + throw new Error("subject must inherit from Subject"); } if (metaData.ctor == null && metaData.initializer == null) { @@ -29,11 +29,26 @@ export class SessionSyncer { } init() { - if (BrowserApi.manifestVersion !== 3) { - return; + switch (this.subject.constructor) { + case ReplaySubject: + // ignore all updates currently in the buffer + this.ignoreNUpdates = (this.subject as any)._buffer.length; + break; + case BehaviorSubject: + this.ignoreNUpdates = 1; + break; + default: + break; } this.observe(); + // must be synchronous + this.memoryStorageService.has(this.metaData.sessionKey).then((hasInSessionMemory) => { + if (hasInSessionMemory) { + this.update(); + } + }); + this.listenForUpdates(); } @@ -41,11 +56,11 @@ export class SessionSyncer { // This may be a memory leak. // There is no good time to unsubscribe from this observable. Hopefully Manifest V3 clears memory from temporary // contexts. If so, this is handled by destruction of the context. - this.subscription = this.behaviorSubject + this.subscription = this.subject .pipe( concatMap(async (next) => { - if (this.ignoreNextUpdate) { - this.ignoreNextUpdate = false; + if (this.ignoreNUpdates > 0) { + this.ignoreNUpdates -= 1; return; } await this.updateSession(next); @@ -66,14 +81,20 @@ export class SessionSyncer { if (message.command != this.updateMessageCommand || message.id === this.id) { return; } + this.update(); + } + + async update() { const builder = SyncedItemMetadata.builder(this.metaData); - const value = await this.stateService.getFromSessionMemory(this.metaData.sessionKey, builder); - this.ignoreNextUpdate = true; - this.behaviorSubject.next(value); + const value = await this.memoryStorageService.getBypassCache(this.metaData.sessionKey, { + deserializer: builder, + }); + this.ignoreNUpdates = 1; + this.subject.next(value); } private async updateSession(value: any) { - await this.stateService.setInSessionMemory(this.metaData.sessionKey, value); + await this.memoryStorageService.save(this.metaData.sessionKey, value); await BrowserApi.sendMessage(this.updateMessageCommand, { id: this.id }); } diff --git a/apps/browser/src/decorators/session-sync-observable/sync-item-metadata.ts b/apps/browser/src/decorators/session-sync-observable/sync-item-metadata.ts index 2b3f4715d46..facfda32fd8 100644 --- a/apps/browser/src/decorators/session-sync-observable/sync-item-metadata.ts +++ b/apps/browser/src/decorators/session-sync-observable/sync-item-metadata.ts @@ -1,17 +1,27 @@ +export type InitializeOptions = "array" | "record" | "object"; + export class SyncedItemMetadata { propertyKey: string; sessionKey: string; ctor?: new () => any; initializer?: (keyValuePair: any) => any; - initializeAsArray?: boolean; + initializeAs: InitializeOptions; static builder(metadata: SyncedItemMetadata): (o: any) => any { const itemBuilder = metadata.initializer != null ? metadata.initializer : (o: any) => Object.assign(new metadata.ctor(), o); - if (metadata.initializeAsArray) { + if (metadata.initializeAs === "array") { return (keyValuePair: any) => keyValuePair.map((o: any) => itemBuilder(o)); + } else if (metadata.initializeAs === "record") { + return (keyValuePair: any) => { + const record: Record = {}; + for (const key in keyValuePair) { + record[key] = itemBuilder(keyValuePair[key]); + } + return record; + }; } else { return (keyValuePair: any) => itemBuilder(keyValuePair); } diff --git a/apps/browser/src/decorators/session-sync-observable/synced-item-metadata.spec.ts b/apps/browser/src/decorators/session-sync-observable/synced-item-metadata.spec.ts index 5cd869a5b67..12b0b57d523 100644 --- a/apps/browser/src/decorators/session-sync-observable/synced-item-metadata.spec.ts +++ b/apps/browser/src/decorators/session-sync-observable/synced-item-metadata.spec.ts @@ -8,32 +8,60 @@ describe("builder", () => { const ctor = TestClass; it("should use initializer if provided", () => { - const metadata = { propertyKey, sessionKey: key, initializer }; + const metadata: SyncedItemMetadata = { + propertyKey, + sessionKey: key, + initializer, + initializeAs: "object", + }; const builder = SyncedItemMetadata.builder(metadata); expect(builder({})).toBe("used initializer"); }); it("should use ctor if initializer is not provided", () => { - const metadata = { propertyKey, sessionKey: key, ctor }; + const metadata: SyncedItemMetadata = { + propertyKey, + sessionKey: key, + ctor, + initializeAs: "object", + }; const builder = SyncedItemMetadata.builder(metadata); expect(builder({})).toBeInstanceOf(TestClass); }); it("should prefer initializer over ctor", () => { - const metadata = { propertyKey, sessionKey: key, ctor, initializer }; + const metadata: SyncedItemMetadata = { + propertyKey, + sessionKey: key, + ctor, + initializer, + initializeAs: "object", + }; const builder = SyncedItemMetadata.builder(metadata); expect(builder({})).toBe("used initializer"); }); it("should honor initialize as array", () => { - const metadata = { + const metadata: SyncedItemMetadata = { propertyKey, sessionKey: key, initializer: initializer, - initializeAsArray: true, + initializeAs: "array", }; const builder = SyncedItemMetadata.builder(metadata); expect(builder([{}])).toBeInstanceOf(Array); expect(builder([{}])[0]).toBe("used initializer"); }); + + it("should honor initialize as record", () => { + const metadata: SyncedItemMetadata = { + propertyKey, + sessionKey: key, + initializer: initializer, + initializeAs: "record", + }; + const builder = SyncedItemMetadata.builder(metadata); + expect(builder({ key: "" })).toBeInstanceOf(Object); + expect(builder({ key: "" })).toStrictEqual({ key: "used initializer" }); + }); }); diff --git a/apps/browser/src/listeners/combine.spec.ts b/apps/browser/src/listeners/combine.spec.ts new file mode 100644 index 00000000000..10a0fd30d1d --- /dev/null +++ b/apps/browser/src/listeners/combine.spec.ts @@ -0,0 +1,27 @@ +import { combine } from "./combine"; + +describe("combine", () => { + it("runs", async () => { + const combined = combine([ + (arg: Record, serviceCache: Record) => { + arg["one"] = true; + serviceCache["one"] = true; + return Promise.resolve(); + }, + (arg: Record, serviceCache: Record) => { + if (serviceCache["one"] !== true) { + throw new Error("One should have ran."); + } + arg["two"] = true; + return Promise.resolve(); + }, + ]); + + const arg: Record = {}; + await combined(arg); + + expect(arg["one"]).toBeTruthy(); + + expect(arg["two"]).toBeTruthy(); + }); +}); diff --git a/apps/browser/src/listeners/combine.ts b/apps/browser/src/listeners/combine.ts new file mode 100644 index 00000000000..b87631de977 --- /dev/null +++ b/apps/browser/src/listeners/combine.ts @@ -0,0 +1,15 @@ +import { CachedServices } from "../background/service_factories/factory-options"; + +type Listener = (...args: [...T, CachedServices]) => Promise; + +export const combine = ( + listeners: Listener[], + startingServices: CachedServices = {} +) => { + return async (...args: T) => { + const cachedServices = { ...startingServices }; + for (const listener of listeners) { + await listener(...[...args, cachedServices]); + } + }; +}; diff --git a/apps/browser/src/listeners/index.ts b/apps/browser/src/listeners/index.ts new file mode 100644 index 00000000000..0c00dea784f --- /dev/null +++ b/apps/browser/src/listeners/index.ts @@ -0,0 +1,43 @@ +import { CipherContextMenuHandler } from "../browser/cipher-context-menu-handler"; +import { ContextMenuClickedHandler } from "../browser/context-menu-clicked-handler"; + +import { combine } from "./combine"; +import { onCommandListener } from "./onCommandListener"; +import { onInstallListener } from "./onInstallListener"; +import { UpdateBadge } from "./update-badge"; + +const tabsOnActivatedListener = combine([ + UpdateBadge.tabsOnActivatedListener, + CipherContextMenuHandler.tabsOnActivatedListener, +]); + +const tabsOnReplacedListener = combine([ + UpdateBadge.tabsOnReplacedListener, + CipherContextMenuHandler.tabsOnReplacedListener, +]); + +const tabsOnUpdatedListener = combine([ + UpdateBadge.tabsOnUpdatedListener, + CipherContextMenuHandler.tabsOnUpdatedListener, +]); + +const contextMenusClickedListener = ContextMenuClickedHandler.onClickedListener; + +// TODO: All message listeners should be RuntimeMessage in Notifications follow up then this type annotation can be inferred +const runtimeMessageListener = combine< + [message: { command: string }, sender: chrome.runtime.MessageSender] +>([ + UpdateBadge.messageListener, + CipherContextMenuHandler.messageListener, + ContextMenuClickedHandler.messageListener, +]); + +export { + tabsOnActivatedListener, + tabsOnReplacedListener, + tabsOnUpdatedListener, + contextMenusClickedListener, + runtimeMessageListener, + onCommandListener, + onInstallListener, +}; diff --git a/apps/browser/src/listeners/onCommandListener.ts b/apps/browser/src/listeners/onCommandListener.ts index c52e5cb61ac..395285d40e6 100644 --- a/apps/browser/src/listeners/onCommandListener.ts +++ b/apps/browser/src/listeners/onCommandListener.ts @@ -14,7 +14,7 @@ import { import { stateServiceFactory } from "../background/service_factories/state-service.factory"; import { BrowserApi } from "../browser/browserApi"; import { GeneratePasswordToClipboardCommand } from "../clipboard"; -import { AutoFillActiveTabCommand } from "../commands/autoFillActiveTabCommand"; +import { AutofillTabCommand } from "../commands/autofill-tab-command"; import { Account } from "../models/account"; export const onCommandListener = async (command: string, tab: chrome.tabs.Tab) => { @@ -75,8 +75,8 @@ const doAutoFillLogin = async (tab: chrome.tabs.Tab): Promise => { return; } - const command = new AutoFillActiveTabCommand(autofillService); - await command.doAutoFillActiveTabCommand(tab); + const command = new AutofillTabCommand(autofillService); + await command.doAutofillTabCommand(tab); }; const doGeneratePasswordToClipboard = async (tab: chrome.tabs.Tab): Promise => { diff --git a/apps/browser/src/listeners/onInstallListener.ts b/apps/browser/src/listeners/onInstallListener.ts index cc2d60594c9..92cd2fd6b78 100644 --- a/apps/browser/src/listeners/onInstallListener.ts +++ b/apps/browser/src/listeners/onInstallListener.ts @@ -1,13 +1,16 @@ import { StateFactory } from "@bitwarden/common/factories/stateFactory"; import { GlobalState } from "@bitwarden/common/models/domain/global-state"; -import { environmentServiceFactory } from "../background/service_factories/environment-service.factory"; +import { + environmentServiceFactory, + EnvironmentServiceInitOptions, +} from "../background/service_factories/environment-service.factory"; import { BrowserApi } from "../browser/browserApi"; import { Account } from "../models/account"; export async function onInstallListener(details: chrome.runtime.InstalledDetails) { const cache = {}; - const opts = { + const opts: EnvironmentServiceInitOptions = { encryptServiceOptions: { logMacFailures: false, }, @@ -27,7 +30,7 @@ export async function onInstallListener(details: chrome.runtime.InstalledDetails const environmentService = await environmentServiceFactory(cache, opts); setTimeout(async () => { - if (details.reason != null && details.reason === "install") { + if (details.reason != null && details.reason === chrome.runtime.OnInstalledReason.INSTALL) { BrowserApi.createNewTab("https://bitwarden.com/browser-start/"); if (await environmentService.hasManagedEnvironment()) { diff --git a/apps/browser/src/listeners/update-badge.ts b/apps/browser/src/listeners/update-badge.ts index 9c7c122a45a..ac1ce880389 100644 --- a/apps/browser/src/listeners/update-badge.ts +++ b/apps/browser/src/listeners/update-badge.ts @@ -15,7 +15,7 @@ import { searchServiceFactory } from "../background/service_factories/search-ser import { stateServiceFactory } from "../background/service_factories/state-service.factory"; import { BrowserApi } from "../browser/browserApi"; import { Account } from "../models/account"; -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; import BrowserPlatformUtilsService from "../services/browserPlatformUtils.service"; export type BadgeOptions = { @@ -25,7 +25,7 @@ export type BadgeOptions = { export class UpdateBadge { private authService: AuthService; - private stateService: StateService; + private stateService: BrowserStateService; private cipherService: CipherService; private badgeAction: typeof chrome.action; private sidebarAction: OperaSidebarAction | FirefoxSidebarAction; @@ -43,27 +43,47 @@ export class UpdateBadge { "deletedCipher", ]; - static async tabsOnActivatedListener(activeInfo: chrome.tabs.TabActiveInfo) { - await new UpdateBadge(self).run({ tabId: activeInfo.tabId }); + static async tabsOnActivatedListener( + activeInfo: chrome.tabs.TabActiveInfo, + serviceCache: Record + ) { + await new UpdateBadge(self).run({ + tabId: activeInfo.tabId, + existingServices: serviceCache, + windowId: activeInfo.windowId, + }); } - static async tabsOnReplacedListener(addedTabId: number, removedTabId: number) { - await new UpdateBadge(self).run({ tabId: addedTabId }); + static async tabsOnReplacedListener( + addedTabId: number, + removedTabId: number, + serviceCache: Record + ) { + await new UpdateBadge(self).run({ tabId: addedTabId, existingServices: serviceCache }); } - static async tabsOnUpdatedListener(tabId: number) { - await new UpdateBadge(self).run({ tabId }); + static async tabsOnUpdatedListener( + tabId: number, + changeInfo: chrome.tabs.TabChangeInfo, + tab: chrome.tabs.Tab, + serviceCache: Record + ) { + await new UpdateBadge(self).run({ + tabId, + existingServices: serviceCache, + windowId: tab.windowId, + }); } static async messageListener( - serviceCache: Record, - message: { command: string; tabId: number } + message: { command: string; tabId: number }, + serviceCache: Record ) { if (!UpdateBadge.listenedToCommands.includes(message.command)) { return; } - await new UpdateBadge(self).run(); + await new UpdateBadge(self).run({ existingServices: serviceCache }); } constructor(win: Window & typeof globalThis) { @@ -81,41 +101,50 @@ export class UpdateBadge { const authStatus = await this.authService.getAuthStatus(); - const tab = await this.getTab(opts?.tabId, opts?.windowId); - const windowId = tab?.windowId; - await this.setBadgeBackgroundColor(); switch (authStatus) { case AuthenticationStatus.LoggedOut: { - await this.setLoggedOut({ tab, windowId }); + await this.setLoggedOut(); break; } case AuthenticationStatus.Locked: { - await this.setLocked({ tab, windowId }); + await this.setLocked(); break; } case AuthenticationStatus.Unlocked: { - await this.setUnlocked({ tab, windowId }); + const tab = await this.getTab(opts?.tabId, opts?.windowId); + await this.setUnlocked({ tab, windowId: tab?.windowId }); break; } } } - async setLoggedOut(opts: BadgeOptions): Promise { - await this.setBadgeIcon("_gray", opts?.windowId); - await this.setBadgeText("", opts?.tab?.id); + async setLoggedOut(): Promise { + await this.setBadgeIcon("_gray"); + await this.clearBadgeText(); } - async setLocked(opts: BadgeOptions) { - await this.setBadgeIcon("_locked", opts?.windowId); - await this.setBadgeText("", opts?.tab?.id); + async setLocked() { + await this.setBadgeIcon("_locked"); + await this.clearBadgeText(); + } + + private async clearBadgeText() { + const tabs = await BrowserApi.getActiveTabs(); + if (tabs != null) { + tabs.forEach(async (tab) => { + if (tab.id != null) { + await this.setBadgeText("", tab.id); + } + }); + } } async setUnlocked(opts: BadgeOptions) { await this.initServices(); - await this.setBadgeIcon("", opts?.windowId); + await this.setBadgeIcon(""); const disableBadgeCounter = await this.stateService.getDisableBadgeCounter(); if (disableBadgeCounter) { @@ -151,7 +180,7 @@ export class UpdateBadge { 38: "/images/icon38" + iconSuffix + ".png", }, }; - if (BrowserPlatformUtilsService.isFirefox()) { + if (windowId && BrowserPlatformUtilsService.isFirefox()) { options.windowId = windowId; } @@ -202,7 +231,9 @@ export class UpdateBadge { private async getTab(tabId?: number, windowId?: number) { return ( (await BrowserApi.getTab(tabId)) ?? - (await BrowserApi.tabsQueryFirst({ active: true, windowId })) ?? + (windowId + ? await BrowserApi.tabsQueryFirst({ active: true, windowId }) + : await BrowserApi.tabsQueryFirst({ active: true, currentWindow: true })) ?? (await BrowserApi.tabsQueryFirst({ active: true, lastFocusedWindow: true })) ?? (await BrowserApi.tabsQueryFirst({ active: true })) ); diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index f993c82fb63..d7f79a2b88f 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": "2022.10.2", + "version": "2023.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 8558b7f32fc..9afcdd5a885 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": "2022.10.2", + "version": "2023.1.0", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/models/account.ts b/apps/browser/src/models/account.ts index f49c55d2909..cfbcbecf979 100644 --- a/apps/browser/src/models/account.ts +++ b/apps/browser/src/models/account.ts @@ -1,3 +1,5 @@ +import { Jsonify } from "type-fest"; + import { Account as BaseAccount, AccountSettings as BaseAccountSettings, @@ -9,6 +11,14 @@ import { BrowserSendComponentState } from "./browserSendComponentState"; export class AccountSettings extends BaseAccountSettings { vaultTimeout = -1; // On Restart + + static fromJSON(json: Jsonify): AccountSettings { + if (json == null) { + return null; + } + + return Object.assign(new AccountSettings(), json, super.fromJSON(json)); + } } export class Account extends BaseAccount { @@ -29,4 +39,18 @@ export class Account extends BaseAccount { this.ciphers = init?.ciphers ?? new BrowserComponentState(); this.sendType = init?.sendType ?? new BrowserComponentState(); } + + static fromJSON(json: Jsonify): Account { + if (json == null) { + return null; + } + + return Object.assign(new Account({}), json, super.fromJSON(json), { + settings: AccountSettings.fromJSON(json.settings), + groupings: BrowserGroupingsComponentState.fromJSON(json.groupings), + send: BrowserSendComponentState.fromJSON(json.send), + ciphers: BrowserComponentState.fromJSON(json.ciphers), + sendType: BrowserComponentState.fromJSON(json.sendType), + }); + } } diff --git a/apps/browser/src/models/browserComponentState.ts b/apps/browser/src/models/browserComponentState.ts index d968726c413..c5540d088ff 100644 --- a/apps/browser/src/models/browserComponentState.ts +++ b/apps/browser/src/models/browserComponentState.ts @@ -1,4 +1,14 @@ +import { Jsonify } from "type-fest"; + export class BrowserComponentState { scrollY: number; searchText: string; + + static fromJSON(json: Jsonify) { + if (json == null) { + return null; + } + + return Object.assign(new BrowserComponentState(), json); + } } diff --git a/apps/browser/src/models/browserGroupingsComponentState.ts b/apps/browser/src/models/browserGroupingsComponentState.ts index 63eb4aaa88f..f406e3d8271 100644 --- a/apps/browser/src/models/browserGroupingsComponentState.ts +++ b/apps/browser/src/models/browserGroupingsComponentState.ts @@ -1,7 +1,9 @@ import { CipherType } from "@bitwarden/common/enums/cipherType"; +import { Utils } from "@bitwarden/common/misc/utils"; import { CipherView } from "@bitwarden/common/models/view/cipher.view"; import { CollectionView } from "@bitwarden/common/models/view/collection.view"; import { FolderView } from "@bitwarden/common/models/view/folder.view"; +import { DeepJsonify } from "@bitwarden/common/types/deep-jsonify"; import { BrowserComponentState } from "./browserComponentState"; @@ -15,4 +17,28 @@ export class BrowserGroupingsComponentState extends BrowserComponentState { folders: FolderView[]; collections: CollectionView[]; deletedCount: number; + + toJSON() { + return Utils.merge(this, { + collectionCounts: Utils.mapToRecord(this.collectionCounts), + folderCounts: Utils.mapToRecord(this.folderCounts), + typeCounts: Utils.mapToRecord(this.typeCounts), + }); + } + + static fromJSON(json: DeepJsonify) { + if (json == null) { + return null; + } + + return Object.assign(new BrowserGroupingsComponentState(), json, { + favoriteCiphers: json.favoriteCiphers?.map((c) => CipherView.fromJSON(c)), + noFolderCiphers: json.noFolderCiphers?.map((c) => CipherView.fromJSON(c)), + ciphers: json.ciphers?.map((c) => CipherView.fromJSON(c)), + collectionCounts: Utils.recordToMap(json.collectionCounts), + folderCounts: Utils.recordToMap(json.folderCounts), + typeCounts: Utils.recordToMap(json.typeCounts), + folders: json.folders?.map((f) => FolderView.fromJSON(f)), + }); + } } diff --git a/apps/browser/src/models/browserSendComponentState.ts b/apps/browser/src/models/browserSendComponentState.ts index e2bf4eaa5d6..99508737ab1 100644 --- a/apps/browser/src/models/browserSendComponentState.ts +++ b/apps/browser/src/models/browserSendComponentState.ts @@ -1,9 +1,28 @@ import { SendType } from "@bitwarden/common/enums/sendType"; +import { Utils } from "@bitwarden/common/misc/utils"; import { SendView } from "@bitwarden/common/models/view/send.view"; +import { DeepJsonify } from "@bitwarden/common/types/deep-jsonify"; import { BrowserComponentState } from "./browserComponentState"; export class BrowserSendComponentState extends BrowserComponentState { sends: SendView[]; typeCounts: Map; + + toJSON() { + return Utils.merge(this, { + typeCounts: Utils.mapToRecord(this.typeCounts), + }); + } + + static fromJSON(json: DeepJsonify) { + if (json == null) { + return null; + } + + return Object.assign(new BrowserSendComponentState(), json, { + sends: json.sends?.map((s) => SendView.fromJSON(s)), + typeCounts: Utils.recordToMap(json.typeCounts), + }); + } } diff --git a/apps/browser/src/popup/accounts/environment.component.html b/apps/browser/src/popup/accounts/environment.component.html index 3b998d1703d..ff19739548a 100644 --- a/apps/browser/src/popup/accounts/environment.component.html +++ b/apps/browser/src/popup/accounts/environment.component.html @@ -36,13 +36,14 @@ id="baseUrl" type="text" name="BaseUrl" + aria-describedby="baseUrlHelp" [(ngModel)]="baseUrl" placeholder="ex. https://bitwarden.company.com" appInputVerbatim /> - @@ -50,7 +51,13 @@

{{ "customEnvironment" | i18n }}

-
+
- diff --git a/apps/browser/src/popup/accounts/hint.component.html b/apps/browser/src/popup/accounts/hint.component.html index 4f5f975ccf9..c3d5ef3d8b7 100644 --- a/apps/browser/src/popup/accounts/hint.component.html +++ b/apps/browser/src/popup/accounts/hint.component.html @@ -24,6 +24,7 @@ id="email" type="text" name="Email" + aria-describedby="emailHelp" [(ngModel)]="email" required appAutofocus @@ -32,7 +33,7 @@ />
- diff --git a/apps/browser/src/popup/accounts/home.component.html b/apps/browser/src/popup/accounts/home.component.html index 7fc8163a7f0..24feb9c83e5 100644 --- a/apps/browser/src/popup/accounts/home.component.html +++ b/apps/browser/src/popup/accounts/home.component.html @@ -9,9 +9,18 @@ - -
@@ -22,10 +31,10 @@
- diff --git a/apps/browser/src/popup/accounts/home.component.ts b/apps/browser/src/popup/accounts/home.component.ts index 28d42dc482a..e6a31bea9a6 100644 --- a/apps/browser/src/popup/accounts/home.component.ts +++ b/apps/browser/src/popup/accounts/home.component.ts @@ -4,6 +4,7 @@ import { ActivatedRoute, Router } from "@angular/router"; import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; +import { LoginService } from "@bitwarden/common/abstractions/login.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { StateService } from "@bitwarden/common/abstractions/state.service"; @@ -16,6 +17,7 @@ export class HomeComponent implements OnInit { formGroup = this.formBuilder.group({ email: ["", [Validators.required, Validators.email]], + rememberEmail: [false], }); constructor( @@ -25,12 +27,26 @@ export class HomeComponent implements OnInit { private router: Router, private i18nService: I18nService, private environmentService: EnvironmentService, - private route: ActivatedRoute + private route: ActivatedRoute, + private loginService: LoginService ) {} async ngOnInit(): Promise { - const rememberedEmail = await this.stateService.getRememberedEmail(); - if (rememberedEmail != null) { - this.formGroup.patchValue({ email: await this.stateService.getRememberedEmail() }); + let savedEmail = this.loginService.getEmail(); + const rememberEmail = this.loginService.getRememberEmail(); + + if (savedEmail != null) { + this.formGroup.patchValue({ + email: savedEmail, + rememberEmail: rememberEmail, + }); + } else { + savedEmail = await this.stateService.getRememberedEmail(); + if (savedEmail != null) { + this.formGroup.patchValue({ + email: savedEmail, + rememberEmail: true, + }); + } } } @@ -45,12 +61,17 @@ export class HomeComponent implements OnInit { return; } - this.stateService.setRememberedEmail(this.formGroup.value.email); - + this.loginService.setEmail(this.formGroup.value.email); + this.loginService.setRememberEmail(this.formGroup.value.rememberEmail); this.router.navigate(["login"], { queryParams: { email: this.formGroup.value.email } }); } get selfHostedDomain() { return this.environmentService.hasBaseUrl() ? this.environmentService.getWebVaultUrl() : null; } + + setFormValues() { + this.loginService.setEmail(this.formGroup.value.email); + this.loginService.setRememberEmail(this.formGroup.value.rememberEmail); + } } diff --git a/apps/browser/src/popup/accounts/lock.component.html b/apps/browser/src/popup/accounts/lock.component.html index 3c96458766c..c5f4bfdf404 100644 --- a/apps/browser/src/popup/accounts/lock.component.html +++ b/apps/browser/src/popup/accounts/lock.component.html @@ -30,6 +30,7 @@ id="masterPassword" type="{{ showPassword ? 'text' : 'password' }}" name="MasterPassword" + aria-describedby="masterPasswordHelp" class="monospaced" [(ngModel)]="masterPassword" required @@ -54,7 +55,7 @@ - -
@@ -100,12 +102,23 @@
- +
-
diff --git a/apps/browser/src/popup/accounts/register.component.ts b/apps/browser/src/popup/accounts/register.component.ts index 6ee4719457a..d199b484853 100644 --- a/apps/browser/src/popup/accounts/register.component.ts +++ b/apps/browser/src/popup/accounts/register.component.ts @@ -4,6 +4,7 @@ import { Router } from "@angular/router"; import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; @@ -34,7 +35,8 @@ export class RegisterComponent extends BaseRegisterComponent { platformUtilsService: PlatformUtilsService, passwordGenerationService: PasswordGenerationService, environmentService: EnvironmentService, - logService: LogService + logService: LogService, + auditService: AuditService ) { super( formValidationErrorService, @@ -48,7 +50,8 @@ export class RegisterComponent extends BaseRegisterComponent { platformUtilsService, passwordGenerationService, environmentService, - logService + logService, + auditService ); } } diff --git a/apps/browser/src/popup/accounts/set-password.component.html b/apps/browser/src/popup/accounts/set-password.component.html index 517374578c2..656664facbf 100644 --- a/apps/browser/src/popup/accounts/set-password.component.html +++ b/apps/browser/src/popup/accounts/set-password.component.html @@ -39,19 +39,17 @@
-
- @@ -130,10 +128,16 @@
- +
- diff --git a/apps/browser/src/popup/accounts/set-password.component.ts b/apps/browser/src/popup/accounts/set-password.component.ts index a9d16d7305e..25732b07148 100644 --- a/apps/browser/src/popup/accounts/set-password.component.ts +++ b/apps/browser/src/popup/accounts/set-password.component.ts @@ -6,6 +6,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; +import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; @@ -32,7 +33,8 @@ export class SetPasswordComponent extends BaseSetPasswordComponent { router: Router, syncService: SyncService, route: ActivatedRoute, - organizationApiService: OrganizationApiServiceAbstraction + organizationApiService: OrganizationApiServiceAbstraction, + organizationUserService: OrganizationUserService ) { super( i18nService, @@ -47,7 +49,8 @@ export class SetPasswordComponent extends BaseSetPasswordComponent { syncService, route, stateService, - organizationApiService + organizationApiService, + organizationUserService ); } } diff --git a/apps/browser/src/popup/accounts/two-factor.component.html b/apps/browser/src/popup/accounts/two-factor.component.html index b2d017a9f97..5263bf0c401 100644 --- a/apps/browser/src/popup/accounts/two-factor.component.html +++ b/apps/browser/src/popup/accounts/two-factor.component.html @@ -62,7 +62,7 @@

{{ "insertYubiKey" | i18n }}

- +
@@ -86,7 +86,9 @@
-
+
+ +
diff --git a/apps/browser/src/popup/accounts/update-temp-password.component.html b/apps/browser/src/popup/accounts/update-temp-password.component.html index 6fcbb76c3c2..3dea8039cff 100644 --- a/apps/browser/src/popup/accounts/update-temp-password.component.html +++ b/apps/browser/src/popup/accounts/update-temp-password.component.html @@ -109,10 +109,10 @@
- +
- diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 1f12bb7062a..2d467a76d90 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -35,12 +35,12 @@ import { SyncComponent } from "./settings/sync.component"; import { TabsComponent } from "./tabs.component"; import { AddEditComponent } from "./vault/add-edit.component"; import { AttachmentsComponent } from "./vault/attachments.component"; -import { CiphersComponent } from "./vault/ciphers.component"; import { CollectionsComponent } from "./vault/collections.component"; import { CurrentTabComponent } from "./vault/current-tab.component"; import { PasswordHistoryComponent } from "./vault/password-history.component"; import { ShareComponent } from "./vault/share.component"; import { VaultFilterComponent } from "./vault/vault-filter.component"; +import { VaultItemsComponent } from "./vault/vault-items.component"; import { ViewComponent } from "./vault/view.component"; const routes: Routes = [ @@ -125,7 +125,7 @@ const routes: Routes = [ }, { path: "ciphers", - component: CiphersComponent, + component: VaultItemsComponent, canActivate: [AuthGuard], data: { state: "ciphers" }, }, diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index f1a69a6d6f3..da7b0062b8b 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -19,7 +19,7 @@ import { MessagingService } from "@bitwarden/common/abstractions/messaging.servi import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { BrowserApi } from "../browser/browserApi"; -import { StateService } from "../services/abstractions/state.service"; +import { BrowserStateService } from "../services/abstractions/browser-state.service"; import { routerTransition } from "./app-routing.animations"; @@ -43,7 +43,7 @@ export class AppComponent implements OnInit, OnDestroy { private authService: AuthService, private i18nService: I18nService, private router: Router, - private stateService: StateService, + private stateService: BrowserStateService, private messagingService: MessagingService, private changeDetectorRef: ChangeDetectorRef, private ngZone: NgZone, @@ -276,7 +276,7 @@ export class AppComponent implements OnInit, OnDestroy { await Promise.all([ this.stateService.setBrowserGroupingComponentState(null), - this.stateService.setBrowserCipherComponentState(null), + this.stateService.setBrowserVaultItemsComponentState(null), this.stateService.setBrowserSendComponentState(null), this.stateService.setBrowserSendTypeComponentState(null), ]); diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index 750f9555c7a..b6f76f8bdaf 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -64,6 +64,8 @@ import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { BitwardenToastModule } from "@bitwarden/angular/components/toastr.component"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { ColorPasswordCountPipe } from "@bitwarden/angular/pipes/color-password-count.pipe"; +import { ColorPasswordPipe } from "@bitwarden/angular/pipes/color-password.pipe"; import { EnvironmentComponent } from "./accounts/environment.component"; import { HintComponent } from "./accounts/hint.component"; @@ -109,12 +111,12 @@ import { TabsComponent } from "./tabs.component"; import { AddEditCustomFieldsComponent } from "./vault/add-edit-custom-fields.component"; import { AddEditComponent } from "./vault/add-edit.component"; import { AttachmentsComponent } from "./vault/attachments.component"; -import { CiphersComponent } from "./vault/ciphers.component"; import { CollectionsComponent } from "./vault/collections.component"; import { CurrentTabComponent } from "./vault/current-tab.component"; import { PasswordHistoryComponent } from "./vault/password-history.component"; import { ShareComponent } from "./vault/share.component"; import { VaultFilterComponent } from "./vault/vault-filter.component"; +import { VaultItemsComponent } from "./vault/vault-items.component"; import { VaultSelectComponent } from "./vault/vault-select.component"; import { ViewCustomFieldsComponent } from "./vault/view-custom-fields.component"; import { ViewComponent } from "./vault/view.component"; @@ -202,8 +204,10 @@ registerLocaleData(localeZhTw, "zh-TW"); AppComponent, AttachmentsComponent, CipherRowComponent, - CiphersComponent, + VaultItemsComponent, CollectionsComponent, + ColorPasswordPipe, + ColorPasswordCountPipe, CurrentTabComponent, EnvironmentComponent, ExcludedDomainsComponent, diff --git a/apps/browser/src/popup/components/action-buttons.component.ts b/apps/browser/src/popup/components/action-buttons.component.ts index 3df8e0d4cd5..60bc0051fbb 100644 --- a/apps/browser/src/popup/components/action-buttons.component.ts +++ b/apps/browser/src/popup/components/action-buttons.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; -import { EventService } from "@bitwarden/common/abstractions/event.service"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { PasswordRepromptService } from "@bitwarden/common/abstractions/passwordReprompt.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; @@ -27,7 +27,7 @@ export class ActionButtonsComponent { constructor( private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, - private eventService: EventService, + private eventCollectionService: EventCollectionService, private totpService: TotpService, private stateService: StateService, private passwordRepromptService: PasswordRepromptService @@ -68,9 +68,12 @@ export class ActionButtonsComponent { ); if (typeI18nKey === "password" || typeI18nKey === "verificationCodeTotp") { - this.eventService.collect(EventType.Cipher_ClientToggledHiddenFieldVisible, cipher.id); + this.eventCollectionService.collect( + EventType.Cipher_ClientToggledHiddenFieldVisible, + cipher.id + ); } else if (typeI18nKey === "securityCode") { - this.eventService.collect(EventType.Cipher_ClientCopiedCardCode, cipher.id); + this.eventCollectionService.collect(EventType.Cipher_ClientCopiedCardCode, cipher.id); } } diff --git a/apps/browser/src/popup/components/cipher-row.component.html b/apps/browser/src/popup/components/cipher-row.component.html index ebb18bebe83..8ac9147cb92 100644 --- a/apps/browser/src/popup/components/cipher-row.component.html +++ b/apps/browser/src/popup/components/cipher-row.component.html @@ -1,49 +1,51 @@
- - - + {{ cipher.subTitle }} +
+ + + +
diff --git a/apps/browser/src/popup/components/password-reprompt.component.html b/apps/browser/src/popup/components/password-reprompt.component.html index ed698097356..730e96fab96 100644 --- a/apps/browser/src/popup/components/password-reprompt.component.html +++ b/apps/browser/src/popup/components/password-reprompt.component.html @@ -12,6 +12,7 @@ id="masterPassword" type="{{ showPassword ? 'text' : 'password' }}" name="MasterPassword" + aria-describedby="masterPasswordHelp" class="monospaced" [(ngModel)]="masterPassword" required @@ -36,7 +37,7 @@
- diff --git a/apps/browser/src/popup/components/user-verification.component.html b/apps/browser/src/popup/components/user-verification.component.html index 2fd78bb907d..8d7f1ed8706 100644 --- a/apps/browser/src/popup/components/user-verification.component.html +++ b/apps/browser/src/popup/components/user-verification.component.html @@ -5,6 +5,7 @@ id="masterPassword" type="password" name="MasterPasswordHash" + aria-describedby="confirmIdentityHelp" class="form-control" [formControl]="secret" required diff --git a/apps/browser/src/popup/generator/generator.component.html b/apps/browser/src/popup/generator/generator.component.html index c3ae6c6ab53..3b07c3b2cea 100644 --- a/apps/browser/src/popup/generator/generator.component.html +++ b/apps/browser/src/popup/generator/generator.component.html @@ -56,12 +56,12 @@ type="button" appStopClick appA11yTitle="{{ 'regenerateUsername' | i18n }}" - (click)="regenerate()" - [disabled]="form.loading" + (click)="$any(form).loading ? false : regenerate()" + [attr.aria-disabled]="$any(form).loading ? 'true' : null" > @@ -312,7 +312,9 @@ />
@@ -320,22 +322,18 @@
-
- -
- -
- @@ -104,13 +112,14 @@
-
- @@ -215,6 +225,7 @@ id="password" type="{{ showPassword ? 'text' : 'password' }}" name="Password" + aria-describedby="passwordHelp" class="monospaced" [(ngModel)]="password" appInputVerbatim @@ -239,7 +250,7 @@ - @@ -251,13 +262,14 @@ - @@ -305,8 +317,11 @@ >
{{ "deleteSend" | i18n }}
diff --git a/apps/browser/src/popup/send/send-add-edit.component.ts b/apps/browser/src/popup/send/send-add-edit.component.ts index 2fb45996ad1..0355b764c30 100644 --- a/apps/browser/src/popup/send/send-add-edit.component.ts +++ b/apps/browser/src/popup/send/send-add-edit.component.ts @@ -12,7 +12,7 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction"; import { SendService } from "@bitwarden/common/abstractions/send.service"; -import { StateService } from "../../services/abstractions/state.service"; +import { BrowserStateService } from "../../services/abstractions/browser-state.service"; import { PopupUtilsService } from "../services/popup-utils.service"; @Component({ @@ -33,7 +33,7 @@ export class SendAddEditComponent extends BaseAddEditComponent { constructor( i18nService: I18nService, platformUtilsService: PlatformUtilsService, - stateService: StateService, + stateService: BrowserStateService, messagingService: MessagingService, policyService: PolicyService, environmentService: EnvironmentService, diff --git a/apps/browser/src/popup/send/send-groupings.component.ts b/apps/browser/src/popup/send/send-groupings.component.ts index 1af715ada0f..a5d63eb9d58 100644 --- a/apps/browser/src/popup/send/send-groupings.component.ts +++ b/apps/browser/src/popup/send/send-groupings.component.ts @@ -15,7 +15,7 @@ import { SendType } from "@bitwarden/common/enums/sendType"; import { SendView } from "@bitwarden/common/models/view/send.view"; import { BrowserSendComponentState } from "../../models/browserSendComponentState"; -import { StateService } from "../../services/abstractions/state.service"; +import { BrowserStateService } from "../../services/abstractions/browser-state.service"; import { PopupUtilsService } from "../services/popup-utils.service"; const ComponentId = "SendComponent"; @@ -42,7 +42,7 @@ export class SendGroupingsComponent extends BaseSendComponent { policyService: PolicyService, searchService: SearchService, private popupUtils: PopupUtilsService, - private stateService: StateService, + private stateService: BrowserStateService, private router: Router, private syncService: SyncService, private changeDetectorRef: ChangeDetectorRef, @@ -165,12 +165,12 @@ export class SendGroupingsComponent extends BaseSendComponent { } private async saveState() { - this.state = { + this.state = Object.assign(new BrowserSendComponentState(), { scrollY: this.popupUtils.getContentScrollY(window), searchText: this.searchText, sends: this.sends, typeCounts: this.typeCounts, - }; + }); await this.stateService.setBrowserSendComponentState(this.state); } diff --git a/apps/browser/src/popup/send/send-type.component.ts b/apps/browser/src/popup/send/send-type.component.ts index afd3daeeda5..e899ab9f00f 100644 --- a/apps/browser/src/popup/send/send-type.component.ts +++ b/apps/browser/src/popup/send/send-type.component.ts @@ -16,7 +16,7 @@ import { SendType } from "@bitwarden/common/enums/sendType"; import { SendView } from "@bitwarden/common/models/view/send.view"; import { BrowserComponentState } from "../../models/browserComponentState"; -import { StateService } from "../../services/abstractions/state.service"; +import { BrowserStateService } from "../../services/abstractions/browser-state.service"; import { PopupUtilsService } from "../services/popup-utils.service"; const ComponentId = "SendTypeComponent"; @@ -41,7 +41,7 @@ export class SendTypeComponent extends BaseSendComponent { policyService: PolicyService, searchService: SearchService, private popupUtils: PopupUtilsService, - private stateService: StateService, + private stateService: BrowserStateService, private route: ActivatedRoute, private location: Location, private changeDetectorRef: ChangeDetectorRef, diff --git a/apps/browser/src/popup/services/init.service.ts b/apps/browser/src/popup/services/init.service.ts index a73792cc10f..8008f6c88ce 100644 --- a/apps/browser/src/popup/services/init.service.ts +++ b/apps/browser/src/popup/services/init.service.ts @@ -5,7 +5,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService as LogServiceAbstraction } from "@bitwarden/common/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; -import { StateService as StateServiceAbstraction } from "../../services/abstractions/state.service"; +import { BrowserStateService as StateServiceAbstraction } from "../../services/abstractions/browser-state.service"; import { PopupUtilsService } from "./popup-utils.service"; diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 236ff4e5b18..677e8648bc8 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -15,7 +15,8 @@ import { CollectionService } from "@bitwarden/common/abstractions/collection.ser import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { CryptoFunctionService } from "@bitwarden/common/abstractions/cryptoFunction.service"; import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; -import { EventService } from "@bitwarden/common/abstractions/event.service"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service"; import { ExportService } from "@bitwarden/common/abstractions/export.service"; import { FileDownloadService } from "@bitwarden/common/abstractions/fileDownload/fileDownload.service"; import { FileUploadService } from "@bitwarden/common/abstractions/fileUpload.service"; @@ -38,7 +39,11 @@ import { SearchService as SearchServiceAbstraction } from "@bitwarden/common/abs import { SendService } from "@bitwarden/common/abstractions/send.service"; import { SettingsService } from "@bitwarden/common/abstractions/settings.service"; import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/abstractions/state.service"; -import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service"; +import { StateMigrationService } from "@bitwarden/common/abstractions/stateMigration.service"; +import { + AbstractMemoryStorageService, + AbstractStorageService, +} from "@bitwarden/common/abstractions/storage.service"; import { SyncService } from "@bitwarden/common/abstractions/sync/sync.service.abstraction"; import { TokenService } from "@bitwarden/common/abstractions/token.service"; import { TotpService } from "@bitwarden/common/abstractions/totp.service"; @@ -47,6 +52,8 @@ import { UserVerificationService } from "@bitwarden/common/abstractions/userVeri import { UsernameGenerationService } from "@bitwarden/common/abstractions/usernameGeneration.service"; import { VaultTimeoutService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeoutSettings.service"; +import { StateFactory } from "@bitwarden/common/factories/stateFactory"; +import { GlobalState } from "@bitwarden/common/models/domain/global-state"; import { AuthService } from "@bitwarden/common/services/auth.service"; import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service"; import { LoginService } from "@bitwarden/common/services/login.service"; @@ -54,9 +61,14 @@ import { SearchService } from "@bitwarden/common/services/search.service"; import MainBackground from "../../background/main.background"; import { BrowserApi } from "../../browser/browserApi"; +import { Account } from "../../models/account"; import { AutofillService } from "../../services/abstractions/autofill.service"; -import { StateService as StateServiceAbstraction } from "../../services/abstractions/state.service"; +import { BrowserStateService as StateServiceAbstraction } from "../../services/abstractions/browser-state.service"; import { BrowserEnvironmentService } from "../../services/browser-environment.service"; +import { BrowserOrganizationService } from "../../services/browser-organization.service"; +import { BrowserPolicyService } from "../../services/browser-policy.service"; +import { BrowserSettingsService } from "../../services/browser-settings.service"; +import { BrowserStateService } from "../../services/browser-state.service"; import { BrowserFileDownloadService } from "../../services/browserFileDownloadService"; import BrowserMessagingService from "../../services/browserMessaging.service"; import BrowserMessagingPrivateModePopupService from "../../services/browserMessagingPrivateModePopup.service"; @@ -187,11 +199,25 @@ function getBgService(service: keyof MainBackground) { { provide: TokenService, useFactory: getBgService("tokenService"), deps: [] }, { provide: I18nService, useFactory: getBgService("i18nService"), deps: [] }, { provide: CryptoService, useFactory: getBgService("cryptoService"), deps: [] }, - { provide: EventService, useFactory: getBgService("eventService"), deps: [] }, + { + provide: EventUploadService, + useFactory: getBgService("eventUploadService"), + deps: [], + }, + { + provide: EventCollectionService, + useFactory: getBgService("eventCollectionService"), + deps: [], + }, { provide: PolicyService, - useFactory: getBgService("policyService"), - deps: [], + useFactory: ( + stateService: StateServiceAbstraction, + organizationService: OrganizationService + ) => { + return new BrowserPolicyService(stateService, organizationService); + }, + deps: [StateServiceAbstraction, OrganizationService], }, { provide: PolicyApiServiceAbstraction, @@ -212,8 +238,10 @@ function getBgService(service: keyof MainBackground) { { provide: SyncService, useFactory: getBgService("syncService"), deps: [] }, { provide: SettingsService, - useFactory: getBgService("settingsService"), - deps: [], + useFactory: (stateService: StateServiceAbstraction) => { + return new BrowserSettingsService(stateService); + }, + deps: [StateServiceAbstraction], }, { provide: AbstractStorageService, @@ -261,8 +289,10 @@ function getBgService(service: keyof MainBackground) { { provide: PasswordRepromptServiceAbstraction, useClass: PasswordRepromptService }, { provide: OrganizationService, - useFactory: getBgService("organizationService"), - deps: [], + useFactory: (stateService: StateServiceAbstraction) => { + return new BrowserOrganizationService(stateService); + }, + deps: [StateServiceAbstraction], }, { provide: VaultFilterService, @@ -293,10 +323,36 @@ function getBgService(service: keyof MainBackground) { useFactory: getBgService("memoryStorageService"), }, { - provide: StateServiceAbstraction, - useFactory: getBgService("stateService"), + provide: StateMigrationService, + useFactory: getBgService("stateMigrationService"), deps: [], }, + { + provide: StateServiceAbstraction, + useFactory: ( + storageService: AbstractStorageService, + secureStorageService: AbstractStorageService, + memoryStorageService: AbstractMemoryStorageService, + logService: LogServiceAbstraction, + stateMigrationService: StateMigrationService + ) => { + return new BrowserStateService( + storageService, + secureStorageService, + memoryStorageService, + logService, + stateMigrationService, + new StateFactory(GlobalState, Account) + ); + }, + deps: [ + AbstractStorageService, + SECURE_STORAGE, + MEMORY_STORAGE, + LogServiceAbstraction, + StateMigrationService, + ], + }, { provide: UsernameGenerationService, useFactory: getBgService("usernameGenerationService"), @@ -314,20 +370,23 @@ function getBgService(service: keyof MainBackground) { { provide: LoginServiceAbstraction, useClass: LoginService, + deps: [StateServiceAbstraction], }, { provide: AbstractThemingService, - useFactory: () => { + useFactory: ( + stateService: StateServiceAbstraction, + platformUtilsService: PlatformUtilsService + ) => { return new ThemingService( - getBgService("stateService")(), + stateService, // Safari doesn't properly handle the (prefers-color-scheme) media query in the popup window, it always returns light. // In Safari we have to use the background page instead, which comes with limitations like not dynamically changing the extension theme when the system theme is changed. - getBgService("platformUtilsService")().isSafari() - ? getBgService("backgroundWindow")() - : window, + platformUtilsService.isSafari() ? getBgService("backgroundWindow")() : window, document ); }, + deps: [StateServiceAbstraction, PlatformUtilsService], }, ], }) diff --git a/apps/browser/src/popup/settings/excluded-domains.component.html b/apps/browser/src/popup/settings/excluded-domains.component.html index 96ec3ec2fc5..89981648722 100644 --- a/apps/browser/src/popup/settings/excluded-domains.component.html +++ b/apps/browser/src/popup/settings/excluded-domains.component.html @@ -74,7 +74,7 @@ type="button" appStopClick (click)="addUri()" - class="box-content-row box-content-row-newmulti" + class="box-content-row box-content-row-newmulti single-line" > {{ "newUri" | i18n }} diff --git a/apps/browser/src/popup/settings/export.component.html b/apps/browser/src/popup/settings/export.component.html index ebd0e03d6f6..db072d6b504 100644 --- a/apps/browser/src/popup/settings/export.component.html +++ b/apps/browser/src/popup/settings/export.component.html @@ -32,7 +32,7 @@ - diff --git a/apps/browser/src/popup/settings/export.component.ts b/apps/browser/src/popup/settings/export.component.ts index 47301b5fdee..c13a495bc5a 100644 --- a/apps/browser/src/popup/settings/export.component.ts +++ b/apps/browser/src/popup/settings/export.component.ts @@ -4,7 +4,7 @@ import { Router } from "@angular/router"; import { ExportComponent as BaseExportComponent } from "@bitwarden/angular/components/export.component"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; -import { EventService } from "@bitwarden/common/abstractions/event.service"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { ExportService } from "@bitwarden/common/abstractions/export.service"; import { FileDownloadService } from "@bitwarden/common/abstractions/fileDownload/fileDownload.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; @@ -23,7 +23,7 @@ export class ExportComponent extends BaseExportComponent { i18nService: I18nService, platformUtilsService: PlatformUtilsService, exportService: ExportService, - eventService: EventService, + eventCollectionService: EventCollectionService, policyService: PolicyService, private router: Router, logService: LogService, @@ -36,7 +36,7 @@ export class ExportComponent extends BaseExportComponent { i18nService, platformUtilsService, exportService, - eventService, + eventCollectionService, policyService, window, logService, diff --git a/apps/browser/src/popup/settings/folder-add-edit.component.html b/apps/browser/src/popup/settings/folder-add-edit.component.html index b8aa391f0fa..3568a971b80 100644 --- a/apps/browser/src/popup/settings/folder-add-edit.component.html +++ b/apps/browser/src/popup/settings/folder-add-edit.component.html @@ -40,8 +40,11 @@ >
{{ "deleteFolder" | i18n }}
diff --git a/apps/browser/src/popup/settings/options.component.html b/apps/browser/src/popup/settings/options.component.html index 8f9874faa71..cdf8d93c0c4 100644 --- a/apps/browser/src/popup/settings/options.component.html +++ b/apps/browser/src/popup/settings/options.component.html @@ -19,9 +19,9 @@ (click)="showGeneral = !showGeneral" [attr.aria-expanded]="showGeneral" > + + General - - @@ -33,6 +33,7 @@ - +
@@ -49,6 +52,7 @@
- +
@@ -67,12 +71,13 @@
- +
@@ -81,12 +86,15 @@
- +
@@ -97,12 +105,15 @@
- +
@@ -111,12 +122,13 @@
- +
@@ -127,9 +139,9 @@ (click)="showDisplay = !showDisplay" [attr.aria-expanded]="showDisplay" > + + Display - -
@@ -141,12 +153,15 @@ - +
@@ -155,12 +170,15 @@
- +
@@ -169,12 +187,13 @@
- +
@@ -183,23 +202,30 @@
- +
-
- +
@@ -210,9 +236,9 @@ (click)="showAutofill = !showAutofill" [attr.aria-expanded]="showAutofill" > + + Autofill - -
@@ -224,12 +250,13 @@ - - diff --git a/apps/browser/src/popup/vault/collections.component.html b/apps/browser/src/popup/vault/collections.component.html index d8bcf9a0292..36c1336c5b4 100644 --- a/apps/browser/src/popup/vault/collections.component.html +++ b/apps/browser/src/popup/vault/collections.component.html @@ -33,7 +33,7 @@ diff --git a/apps/browser/src/popup/vault/current-tab.component.ts b/apps/browser/src/popup/vault/current-tab.component.ts index 48b22a518f7..fd0e356476a 100644 --- a/apps/browser/src/popup/vault/current-tab.component.ts +++ b/apps/browser/src/popup/vault/current-tab.component.ts @@ -200,7 +200,7 @@ export class CurrentTabComponent implements OnInit, OnDestroy { } } - private async load() { + protected async load() { this.isLoading = false; this.tab = await BrowserApi.getTabFromCurrentWindow(); if (this.tab != null) { diff --git a/apps/browser/src/popup/vault/password-history.component.html b/apps/browser/src/popup/vault/password-history.component.html index 73a28bb2520..6286aa1022d 100644 --- a/apps/browser/src/popup/vault/password-history.component.html +++ b/apps/browser/src/popup/vault/password-history.component.html @@ -13,9 +13,10 @@
- - {{ h.password }} - + {{ h.lastUsedDate | date: "medium" }}
diff --git a/apps/browser/src/popup/vault/share.component.html b/apps/browser/src/popup/vault/share.component.html index dcec42415c0..46aaecd06b8 100644 --- a/apps/browser/src/popup/vault/share.component.html +++ b/apps/browser/src/popup/vault/share.component.html @@ -35,6 +35,7 @@
- diff --git a/apps/browser/src/popup/vault/vault-filter.component.html b/apps/browser/src/popup/vault/vault-filter.component.html index 8ddc6c01dc1..2008001aabc 100644 --- a/apps/browser/src/popup/vault/vault-filter.component.html +++ b/apps/browser/src/popup/vault/vault-filter.component.html @@ -213,7 +213,7 @@

{{ "noItemsInList" | i18n }}

{ this.ngZone.run(async () => { @@ -373,7 +373,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy { } private async saveState() { - this.state = { + this.state = Object.assign(new BrowserGroupingsComponentState(), { scrollY: this.popupUtils.getContentScrollY(window), searchText: this.searchText, favoriteCiphers: this.favoriteCiphers, @@ -385,7 +385,7 @@ export class VaultFilterComponent implements OnInit, OnDestroy { folders: this.folders, collections: this.collections, deletedCount: this.deletedCount, - }; + }); await this.browserStateService.setBrowserGroupingComponentState(this.state); } diff --git a/apps/browser/src/popup/vault/ciphers.component.html b/apps/browser/src/popup/vault/vault-items.component.html similarity index 99% rename from apps/browser/src/popup/vault/ciphers.component.html rename to apps/browser/src/popup/vault/vault-items.component.html index d4a535885e7..bf53884721c 100644 --- a/apps/browser/src/popup/vault/ciphers.component.html +++ b/apps/browser/src/popup/vault/vault-items.component.html @@ -93,7 +93,7 @@ { if (this.applySavedState) { - this.state = await this.stateService.getBrowserCipherComponentState(); + this.state = await this.stateService.getBrowserVaultItemsComponentState(); if (this.state?.searchText) { this.searchText = this.state.searchText; } @@ -161,7 +161,7 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On 0 ); } - await this.stateService.setBrowserCipherComponentState(null); + await this.stateService.setBrowserVaultItemsComponentState(null); }); this.broadcasterService.subscribe(ComponentId, (message: any) => { @@ -291,6 +291,6 @@ export class CiphersComponent extends BaseCiphersComponent implements OnInit, On scrollY: this.popupUtils.getContentScrollY(window, this.scrollingContainer), searchText: this.searchText, }; - await this.stateService.setBrowserCipherComponentState(this.state); + await this.stateService.setBrowserVaultItemsComponentState(this.state); } } diff --git a/apps/browser/src/popup/vault/vault-select.component.html b/apps/browser/src/popup/vault/vault-select.component.html index f57056feb59..a25f49ebf2d 100644 --- a/apps/browser/src/popup/vault/vault-select.component.html +++ b/apps/browser/src/popup/vault/vault-select.component.html @@ -10,12 +10,16 @@ [attr.aria-expanded]="isOpen" [attr.aria-label]="vaultFilterDisplay" > - {{ vaultFilterDisplay | ellipsis: 45 }}  - + + {{ vaultFilterDisplay }}  + @@ -27,42 +31,43 @@ role="dialog" aria-modal="true" > - diff --git a/apps/browser/src/popup/vault/vault-select.component.ts b/apps/browser/src/popup/vault/vault-select.component.ts index 5193f520edc..957e181ed96 100644 --- a/apps/browser/src/popup/vault/vault-select.component.ts +++ b/apps/browser/src/popup/vault/vault-select.component.ts @@ -16,7 +16,10 @@ import { import { BehaviorSubject, concatMap, map, merge, Observable, Subject, takeUntil } from "rxjs"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; -import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction"; +import { + isNotProviderUser, + OrganizationService, +} from "@bitwarden/common/abstractions/organization/organization.service.abstraction"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { Organization } from "@bitwarden/common/models/domain/organization"; @@ -100,7 +103,9 @@ export class VaultSelectComponent implements OnInit, OnDestroy { async ngOnInit() { this.organizations$ = this.organizationService.organizations$ .pipe(takeUntil(this._destroy)) - .pipe(map((orgs) => orgs.sort((a, b) => a.name.localeCompare(b.name)))); + .pipe( + map((orgs) => orgs.filter(isNotProviderUser).sort((a, b) => a.name.localeCompare(b.name))) + ); this.organizations$ .pipe( diff --git a/apps/browser/src/popup/vault/view-custom-fields.component.ts b/apps/browser/src/popup/vault/view-custom-fields.component.ts index 7d9b338f0fa..3ea41db713c 100644 --- a/apps/browser/src/popup/vault/view-custom-fields.component.ts +++ b/apps/browser/src/popup/vault/view-custom-fields.component.ts @@ -1,14 +1,14 @@ import { Component } from "@angular/core"; import { ViewCustomFieldsComponent as BaseViewCustomFieldsComponent } from "@bitwarden/angular/components/view-custom-fields.component"; -import { EventService } from "@bitwarden/common/abstractions/event.service"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; @Component({ selector: "app-vault-view-custom-fields", templateUrl: "view-custom-fields.component.html", }) export class ViewCustomFieldsComponent extends BaseViewCustomFieldsComponent { - constructor(eventService: EventService) { - super(eventService); + constructor(eventCollectionService: EventCollectionService) { + super(eventCollectionService); } } diff --git a/apps/browser/src/popup/vault/view.component.html b/apps/browser/src/popup/vault/view.component.html index e844541f8fc..bf3be9ae835 100644 --- a/apps/browser/src/popup/vault/view.component.html +++ b/apps/browser/src/popup/vault/view.component.html @@ -88,17 +88,17 @@ appA11yTitle="{{ 'checkPassword' | i18n }}" (click)="checkPassword()" [appApiAction]="checkPasswordPromise" - [disabled]="checkPasswordBtn.loading" + [disabled]="$any(checkPasswordBtn).loading" *ngIf="cipher.viewPassword" > @@ -552,7 +552,7 @@

{{ "attachments" | i18n }}

-
+
@@ -601,12 +601,7 @@ class="box-content-row" appStopClick (click)="fillCipherAndSave()" - *ngIf=" - cipher.type === cipherType.Login && - !cipher.isDeleted && - !inPopout && - cipher.type != cipherType.Fido2Key - " + *ngIf="cipher.type === cipherType.Login && !cipher.isDeleted && !inPopout" >
- +
+
-

-
+
- diff --git a/apps/desktop/src/app/accounts/hint.component.html b/apps/desktop/src/app/accounts/hint.component.html index 3ab6088c947..a46479efee0 100644 --- a/apps/desktop/src/app/accounts/hint.component.html +++ b/apps/desktop/src/app/accounts/hint.component.html @@ -9,6 +9,7 @@ id="email" type="text" name="Email" + aria-describedby="emailHelp" [(ngModel)]="email" required appAutofocus @@ -16,7 +17,7 @@ />
- diff --git a/apps/desktop/src/app/accounts/lock.component.html b/apps/desktop/src/app/accounts/lock.component.html index f5d8b369355..23adbff18cd 100644 --- a/apps/desktop/src/app/accounts/lock.component.html +++ b/apps/desktop/src/app/accounts/lock.component.html @@ -23,6 +23,7 @@ id="masterPassword" type="{{ showPassword ? 'text' : 'password' }}" name="MasterPassword" + aria-describedby="masterPasswordHelp" class="monospaced" [(ngModel)]="masterPassword" required @@ -47,7 +48,7 @@ - diff --git a/apps/desktop/src/app/accounts/premium.component.html b/apps/desktop/src/app/accounts/premium.component.html index f47f377cff2..4ac3cb422cb 100644 --- a/apps/desktop/src/app/accounts/premium.component.html +++ b/apps/desktop/src/app/accounts/premium.component.html @@ -57,7 +57,7 @@ class="primary" (click)="purchase()" *ngIf="!isPremium" - [disabled]="purchaseBtn.loading" + [disabled]="$any(purchaseBtn).loading" > {{ "premiumPurchase" | i18n }} @@ -67,18 +67,18 @@ #refreshBtn type="button" (click)="refresh()" - [disabled]="refreshBtn.loading" + [disabled]="$any(refreshBtn).loading" appA11yTitle="{{ 'premiumRefresh' | i18n }}" [appApiAction]="refreshPromise" > diff --git a/apps/desktop/src/app/accounts/register.component.html b/apps/desktop/src/app/accounts/register.component.html index f270ceec1ff..388c8751312 100644 --- a/apps/desktop/src/app/accounts/register.component.html +++ b/apps/desktop/src/app/accounts/register.component.html @@ -26,6 +26,7 @@ id="masterPassword" type="{{ showPassword ? 'text' : 'password' }}" class="monospaced" + aria-describedby="masterPasswordHelp" formControlName="masterPassword" appInputVerbatim /> @@ -57,7 +58,7 @@ - @@ -93,7 +94,7 @@
- +
@@ -107,7 +108,7 @@
- diff --git a/apps/desktop/src/app/accounts/register.component.ts b/apps/desktop/src/app/accounts/register.component.ts index 2cfc1cd8ad5..94706064d23 100644 --- a/apps/desktop/src/app/accounts/register.component.ts +++ b/apps/desktop/src/app/accounts/register.component.ts @@ -4,6 +4,7 @@ import { Router } from "@angular/router"; import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; @@ -36,7 +37,8 @@ export class RegisterComponent extends BaseRegisterComponent implements OnInit, environmentService: EnvironmentService, private broadcasterService: BroadcasterService, private ngZone: NgZone, - logService: LogService + logService: LogService, + auditService: AuditService ) { super( formValidationErrorService, @@ -50,7 +52,8 @@ export class RegisterComponent extends BaseRegisterComponent implements OnInit, platformUtilsService, passwordGenerationService, environmentService, - logService + logService, + auditService ); } diff --git a/apps/desktop/src/app/accounts/set-password.component.html b/apps/desktop/src/app/accounts/set-password.component.html index 4730e39e384..0a1522c45d1 100644 --- a/apps/desktop/src/app/accounts/set-password.component.html +++ b/apps/desktop/src/app/accounts/set-password.component.html @@ -46,6 +46,7 @@ type="{{ showPassword ? 'text' : 'password' }}" name="MasterPassword" class="monospaced" + aria-describedby="masterPasswordHelp" [(ngModel)]="masterPassword" required appInputVerbatim @@ -77,7 +78,7 @@ - @@ -122,10 +123,16 @@
- +
- diff --git a/apps/desktop/src/app/accounts/set-password.component.ts b/apps/desktop/src/app/accounts/set-password.component.ts index ca82aaed5e6..fd9d41a6acd 100644 --- a/apps/desktop/src/app/accounts/set-password.component.ts +++ b/apps/desktop/src/app/accounts/set-password.component.ts @@ -7,6 +7,7 @@ import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.s import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; +import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; @@ -37,7 +38,8 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On private broadcasterService: BroadcasterService, private ngZone: NgZone, stateService: StateService, - organizationApiService: OrganizationApiServiceAbstraction + organizationApiService: OrganizationApiServiceAbstraction, + organizationUserService: OrganizationUserService ) { super( i18nService, @@ -52,7 +54,8 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On syncService, route, stateService, - organizationApiService + organizationApiService, + organizationUserService ); } diff --git a/apps/desktop/src/app/accounts/settings.component.html b/apps/desktop/src/app/accounts/settings.component.html index 06cbb7fe8c6..0a1ed59d3ba 100644 --- a/apps/desktop/src/app/accounts/settings.component.html +++ b/apps/desktop/src/app/accounts/settings.component.html @@ -16,17 +16,17 @@ [attr.aria-expanded]="showSecurity" appAutofocus > - {{ "security" | i18n }} + {{ "security" | i18n }} @@ -44,13 +44,16 @@ name="VaultTimeoutAction" id="vaultTimeoutActionLock" value="lock" + aria-describedby="vaultTimeoutActionLockHelp" [(ngModel)]="vaultTimeoutAction" (change)="saveVaultTimeoutOptions()" /> {{ "lock" | i18n }} - {{ "vaultTimeoutActionLockDesc" | i18n }} + {{ + "vaultTimeoutActionLockDesc" | i18n + }}
- {{ "vaultTimeoutActionLogOutDesc" | i18n }} + {{ + "vaultTimeoutActionLogOutDesc" | i18n + }}
@@ -120,17 +126,17 @@ (click)="showAccountPreferences = !showAccountPreferences" [attr.aria-expanded]="showAccountPreferences" > - {{ "accountPreferences" | i18n }} + {{ "accountPreferences" | i18n }} @@ -139,6 +145,7 @@ - {{ "clearClipboardDesc" | i18n }} + {{ + "clearClipboardDesc" | i18n + }}
@@ -155,13 +164,16 @@ id="minimizeOnCopyToClipboard" type="checkbox" name="MinimizeOnCopyToClipboard" + aria-describedby="minimizeOnCopyToClipboardHelp" [(ngModel)]="minimizeOnCopyToClipboard" (change)="saveMinOnCopyToClipboard()" /> {{ "minimizeOnCopyToClipboard" | i18n }}
- {{ "minimizeOnCopyToClipboardDesc" | i18n }} + {{ + "minimizeOnCopyToClipboardDesc" | i18n + }}
@@ -170,13 +182,14 @@ id="enableFavicons" type="checkbox" name="enableFavicons" + aria-describedby="enableFaviconsHelp" [(ngModel)]="enableFavicons" (change)="saveFavicons()" /> {{ "enableFavicon" | i18n }}
- {{ "faviconDesc" | i18n }} + {{ "faviconDesc" | i18n }}
@@ -190,17 +203,17 @@ (click)="showAppPreferences = !showAppPreferences" [attr.aria-expanded]="showAppPreferences" > - {{ "appPreferences" | i18n }} + {{ "appPreferences" | i18n }} @@ -211,13 +224,14 @@ id="enableTray" type="checkbox" name="EnableTray" + aria-describedby="enableTrayHelp" [(ngModel)]="enableTray" (change)="saveTray()" /> {{ enableTrayText }} - {{ enableTrayDescText }} + {{ enableTrayDescText }}
@@ -226,13 +240,16 @@ id="enableMinToTray" type="checkbox" name="EnableMinToTray" + aria-describedby="enableMinToTrayHelp" [(ngModel)]="enableMinToTray" (change)="saveMinToTray()" /> {{ enableMinToTrayText }}
- {{ enableMinToTrayDescText }} + {{ + enableMinToTrayDescText + }}
@@ -241,13 +258,16 @@ id="enableCloseToTray" type="checkbox" name="EnableCloseToTray" + aria-describedby="enableCloseToTrayHelp" [(ngModel)]="enableCloseToTray" (change)="saveCloseToTray()" /> {{ enableCloseToTrayText }}
- {{ enableCloseToTrayDescText }} + {{ + enableCloseToTrayDescText + }}
@@ -256,13 +276,14 @@ id="startToTray" type="checkbox" name="StartToTray" + aria-describedby="startToTrayHelp" [(ngModel)]="startToTray" (change)="saveStartToTray()" /> {{ startToTrayText }}
- {{ startToTrayDescText }} + {{ startToTrayDescText }}
@@ -271,13 +292,16 @@ id="openAtLogin" type="checkbox" name="OpenAtLogin" + aria-describedby="openAtLoginHelp" [(ngModel)]="openAtLogin" (change)="saveOpenAtLogin()" /> {{ "openAtLogin" | i18n }}
- {{ "openAtLoginDesc" | i18n }} + {{ + "openAtLoginDesc" | i18n + }}
@@ -286,13 +310,16 @@ id="alwaysShowDock" type="checkbox" name="AlwaysShowDock" + aria-describedby="alwaysShowDockHelp" [(ngModel)]="alwaysShowDock" (change)="saveAlwaysShowDock()" /> {{ "alwaysShowDock" | i18n }}
- {{ "alwaysShowDockDesc" | i18n }} + {{ + "alwaysShowDockDesc" | i18n + }}
@@ -301,13 +328,16 @@ id="enableBrowserIntegration" type="checkbox" name="EnableBrowserIntegration" + aria-describedby="enableBrowserIntegrationHelp" [(ngModel)]="enableBrowserIntegration" (change)="saveBrowserIntegration()" /> {{ "enableBrowserIntegration" | i18n }}
- {{ "enableBrowserIntegrationDesc" | i18n }} + {{ + "enableBrowserIntegrationDesc" | i18n + }}
@@ -316,6 +346,7 @@ id="enableBrowserIntegrationFingerprint" type="checkbox" name="EnableBrowserIntegrationFingerprint" + aria-describedby="enableBrowserIntegrationFingerprintHelp" [(ngModel)]="enableBrowserIntegrationFingerprint" (change)="saveBrowserIntegrationFingerprint()" [disabled]="!enableBrowserIntegration" @@ -323,7 +354,7 @@ {{ "enableBrowserIntegrationFingerprint" | i18n }}
- {{ + {{ "enableBrowserIntegrationFingerprintDesc" | i18n }}
@@ -346,17 +377,29 @@
- - {{ "themeDesc" | i18n }} + {{ "themeDesc" | i18n }}
- - {{ "languageDesc" | i18n }} + {{ "languageDesc" | i18n }}
diff --git a/apps/desktop/src/app/accounts/settings.component.ts b/apps/desktop/src/app/accounts/settings.component.ts index c0be20dc770..b07a049f18f 100644 --- a/apps/desktop/src/app/accounts/settings.component.ts +++ b/apps/desktop/src/app/accounts/settings.component.ts @@ -14,9 +14,9 @@ import { DeviceType } from "@bitwarden/common/enums/deviceType"; import { StorageLocation } from "@bitwarden/common/enums/storageLocation"; import { ThemeType } from "@bitwarden/common/enums/themeType"; import { Utils } from "@bitwarden/common/misc/utils"; -import { isWindowsStore } from "@bitwarden/electron/utils"; import { flagEnabled } from "../../flags"; +import { isWindowsStore } from "../../utils"; import { SetPinComponent } from "../components/set-pin.component"; @Component({ diff --git a/apps/desktop/src/app/accounts/two-factor.component.html b/apps/desktop/src/app/accounts/two-factor.component.html index dc67e4e0b40..8809ff2465e 100644 --- a/apps/desktop/src/app/accounts/two-factor.component.html +++ b/apps/desktop/src/app/accounts/two-factor.component.html @@ -65,7 +65,7 @@
- +
diff --git a/apps/desktop/src/app/accounts/update-temp-password.component.html b/apps/desktop/src/app/accounts/update-temp-password.component.html index 1bba7fcdc4f..893257077a8 100644 --- a/apps/desktop/src/app/accounts/update-temp-password.component.html +++ b/apps/desktop/src/app/accounts/update-temp-password.component.html @@ -96,10 +96,10 @@
- +
- diff --git a/apps/desktop/src/app/accounts/vault-timeout-input.component.html b/apps/desktop/src/app/accounts/vault-timeout-input.component.html index 62e5a104c19..21b10d4f736 100644 --- a/apps/desktop/src/app/accounts/vault-timeout-input.component.html +++ b/apps/desktop/src/app/accounts/vault-timeout-input.component.html @@ -8,12 +8,13 @@ - {{ "vaultTimeoutDesc" | i18n }} + {{ "vaultTimeoutDesc" | i18n }}
diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index 82753a1776a..3d6d864f671 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -20,7 +20,7 @@ import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.s import { CipherService } from "@bitwarden/common/abstractions/cipher.service"; import { CollectionService } from "@bitwarden/common/abstractions/collection.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; -import { EventService } from "@bitwarden/common/abstractions/event.service"; +import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service"; import { InternalFolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service"; @@ -125,7 +125,7 @@ export class AppComponent implements OnInit, OnDestroy { private platformUtilsService: PlatformUtilsService, private systemService: SystemService, private stateService: StateService, - private eventService: EventService, + private eventUploadService: EventUploadService, private policyService: InternalPolicyService, private modalService: ModalService, private keyConnectorService: KeyConnectorService @@ -465,7 +465,7 @@ export class AppComponent implements OnInit, OnDestroy { private async logOut(expired: boolean, userId?: string) { const userBeingLoggedOut = await this.stateService.getUserId({ userId: userId }); await Promise.all([ - this.eventService.uploadEvents(userBeingLoggedOut), + this.eventUploadService.uploadEvents(userBeingLoggedOut), this.syncService.setLastSync(new Date(0), userBeingLoggedOut), this.cryptoService.clearKeys(userBeingLoggedOut), this.settingsService.clear(userBeingLoggedOut), diff --git a/apps/desktop/src/app/app.module.ts b/apps/desktop/src/app/app.module.ts index c1575e40392..d40c9833f65 100644 --- a/apps/desktop/src/app/app.module.ts +++ b/apps/desktop/src/app/app.module.ts @@ -58,6 +58,9 @@ import localeZhCn from "@angular/common/locales/zh-Hans"; import localeZhTw from "@angular/common/locales/zh-Hant"; import { NgModule } from "@angular/core"; +import { ColorPasswordCountPipe } from "@bitwarden/angular/pipes/color-password-count.pipe"; +import { ColorPasswordPipe } from "@bitwarden/angular/pipes/color-password.pipe"; + import { AccessibilityCookieComponent } from "./accounts/accessibility-cookie.component"; import { DeleteAccountComponent } from "./accounts/delete-account.component"; import { EnvironmentComponent } from "./accounts/environment.component"; @@ -90,7 +93,6 @@ import { SharedModule } from "./shared/shared.module"; import { AddEditCustomFieldsComponent } from "./vault/add-edit-custom-fields.component"; import { AddEditComponent } from "./vault/add-edit.component"; import { AttachmentsComponent } from "./vault/attachments.component"; -import { CiphersComponent } from "./vault/ciphers.component"; import { CollectionsComponent } from "./vault/collections.component"; import { ExportComponent } from "./vault/export.component"; import { FolderAddEditComponent } from "./vault/folder-add-edit.component"; @@ -99,6 +101,7 @@ import { PasswordGeneratorHistoryComponent } from "./vault/password-generator-hi import { PasswordHistoryComponent } from "./vault/password-history.component"; import { ShareComponent } from "./vault/share.component"; import { VaultFilterModule } from "./vault/vault-filter/vault-filter.module"; +import { VaultItemsComponent } from "./vault/vault-items.component"; import { VaultComponent } from "./vault/vault.component"; import { ViewCustomFieldsComponent } from "./vault/view-custom-fields.component"; import { ViewComponent } from "./vault/view.component"; @@ -168,8 +171,10 @@ registerLocaleData(localeZhTw, "zh-TW"); AddEditCustomFieldsComponent, AppComponent, AttachmentsComponent, - CiphersComponent, + VaultItemsComponent, CollectionsComponent, + ColorPasswordPipe, + ColorPasswordCountPipe, DeleteAccountComponent, EnvironmentComponent, ExportComponent, diff --git a/apps/desktop/src/app/components/avatar.component.ts b/apps/desktop/src/app/components/avatar.component.ts index b837c541eb1..8caba7f4593 100644 --- a/apps/desktop/src/app/components/avatar.component.ts +++ b/apps/desktop/src/app/components/avatar.component.ts @@ -5,12 +5,7 @@ import { Utils } from "@bitwarden/common/misc/utils"; @Component({ selector: "app-avatar", - template: ``, + template: ``, }) export class AvatarComponent implements OnChanges, OnInit { @Input() size = 45; @@ -20,7 +15,7 @@ export class AvatarComponent implements OnChanges, OnInit { @Input() circle = false; @Input() color?: string; - @Input() id?: number; + @Input() id?: string; @Input() text?: string; private svgCharCount = 2; diff --git a/apps/desktop/src/app/components/password-reprompt.component.html b/apps/desktop/src/app/components/password-reprompt.component.html index 1ec4ef702c2..1ff853c278c 100644 --- a/apps/desktop/src/app/components/password-reprompt.component.html +++ b/apps/desktop/src/app/components/password-reprompt.component.html @@ -14,6 +14,7 @@ id="masterPassword" type="{{ showPassword ? 'text' : 'password' }}" name="MasterPassword" + aria-describedby="masterPasswordHelp" class="monospaced" [(ngModel)]="masterPassword" required @@ -38,7 +39,7 @@
- diff --git a/apps/desktop/src/app/components/user-verification.component.html b/apps/desktop/src/app/components/user-verification.component.html index 2fd78bb907d..10b16a04087 100644 --- a/apps/desktop/src/app/components/user-verification.component.html +++ b/apps/desktop/src/app/components/user-verification.component.html @@ -5,6 +5,7 @@ id="masterPassword" type="password" name="MasterPasswordHash" + aria-describedby="confirmIdentityHelp" class="form-control" [formControl]="secret" required @@ -36,6 +37,7 @@ id="verificationCode" type="input" name="verificationCode" + aria-describedby="confirmIdentityHelp" class="form-control" [formControl]="secret" required diff --git a/apps/desktop/src/app/layout/account-switcher.component.html b/apps/desktop/src/app/layout/account-switcher.component.html index 4080b2e0314..991e0dfb716 100644 --- a/apps/desktop/src/app/layout/account-switcher.component.html +++ b/apps/desktop/src/app/layout/account-switcher.component.html @@ -12,7 +12,8 @@ diff --git a/apps/desktop/src/app/layout/account-switcher.component.ts b/apps/desktop/src/app/layout/account-switcher.component.ts index 495c461a420..09222cbced0 100644 --- a/apps/desktop/src/app/layout/account-switcher.component.ts +++ b/apps/desktop/src/app/layout/account-switcher.component.ts @@ -16,6 +16,7 @@ type ActiveAccount = { id: string; name: string; email: string; + avatarColor: string; }; export class SwitcherAccount extends Account { @@ -27,6 +28,8 @@ export class SwitcherAccount extends Account { ); } + avatarColor: string; + private removeWebProtocolFromString(urlString: string) { const regex = /http(s)?(:)?(\/\/)?|(\/\/)?(www\.)?/g; return urlString.replace(regex, ""); @@ -112,6 +115,7 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy { id: await this.tokenService.getUserId(), name: (await this.tokenService.getName()) ?? (await this.tokenService.getEmail()), email: await this.tokenService.getEmail(), + avatarColor: await this.stateService.getAvatarColor(), }; } catch { this.activeAccount = undefined; @@ -162,6 +166,9 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy { userId: userId, }); switcherAccounts[userId] = new SwitcherAccount(baseAccounts[userId]); + switcherAccounts[userId].avatarColor = await this.stateService.getAvatarColor({ + userId: userId, + }); } return switcherAccounts; } diff --git a/apps/desktop/src/app/main.ts b/apps/desktop/src/app/main.ts index 4db6a9419b7..9385f859b19 100644 --- a/apps/desktop/src/app/main.ts +++ b/apps/desktop/src/app/main.ts @@ -1,7 +1,7 @@ import { enableProdMode } from "@angular/core"; import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"; -import { isDev } from "@bitwarden/electron/utils"; +import { isDev } from "../utils"; // tslint:disable-next-line require("../scss/styles.scss"); diff --git a/apps/desktop/src/app/send/add-edit.component.html b/apps/desktop/src/app/send/add-edit.component.html index d61cbf99652..e9e1b319adb 100644 --- a/apps/desktop/src/app/send/add-edit.component.html +++ b/apps/desktop/src/app/send/add-edit.component.html @@ -35,7 +35,7 @@ name="Type_{{ o.value }}" id="type_{{ o.value }}" [value]="o.value" - (change)="typeChanged(o)" + (change)="typeChanged()" [checked]="send.type === o.value" [disabled]="disableSend" /> @@ -51,6 +51,7 @@ id="file" class="form-control-file" name="file" + aria-describedby="fileHelp" required [disabled]="disableSend" /> @@ -64,16 +65,17 @@ - @@ -126,15 +128,16 @@ id="maxAccessCount" type="number" name="maxAccessCount" + aria-describedby="maxAccessCountHelp" [(ngModel)]="send.maxAccessCount" [readOnly]="disableSend" /> - -

- {{ "notes" | i18n }} +

- @@ -280,10 +285,14 @@ appA11yTitle="{{ 'delete' | i18n }}" *ngIf="editMode" > - + diff --git a/apps/desktop/src/app/send/efflux-dates.component.html b/apps/desktop/src/app/send/efflux-dates.component.html index e0fc766b124..156dfae9ddd 100644 --- a/apps/desktop/src/app/send/efflux-dates.component.html +++ b/apps/desktop/src/app/send/efflux-dates.component.html @@ -6,12 +6,13 @@ - {{ "deletionDateDesc" | i18n }} + {{ "deletionDateDesc" | i18n }}
@@ -19,23 +20,27 @@ id="deletionDateCustom" type="datetime-local" name="deletionDate" + aria-describedby="deletionDateCustomHelp" formControlName="defaultDeletionDateTime" required placeholder="MM/DD/YYYY HH:MM AM/PM" /> - {{ "deletionDateDesc" | i18n }} + {{ + "deletionDateDesc" | i18n + }}
- {{ "expirationDateDesc" | i18n }} + {{ "expirationDateDesc" | i18n }}
@@ -43,12 +48,14 @@ id="expirationDateCustom" type="datetime-local" name="expirationDate" + aria-describedby="expirationDateCustomHelp" formControlName="defaultExpirationDateTime" required placeholder="MM/DD/YYYY HH:MM AM/PM" - [readOnly]="disableSend" /> - {{ "expirationDateDesc" | i18n }} + {{ + "expirationDateDesc" | i18n + }}
diff --git a/apps/desktop/src/app/send/send.component.ts b/apps/desktop/src/app/send/send.component.ts index 95dfb83f367..2ba29cc4ad1 100644 --- a/apps/desktop/src/app/send/send.component.ts +++ b/apps/desktop/src/app/send/send.component.ts @@ -10,8 +10,8 @@ import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.serv import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { SendService } from "@bitwarden/common/abstractions/send.service"; import { SendView } from "@bitwarden/common/models/view/send.view"; -import { invokeMenu, RendererMenuItem } from "@bitwarden/electron/utils"; +import { invokeMenu, RendererMenuItem } from "../../utils"; import { SearchBarService } from "../layout/search/search-bar.service"; import { AddEditComponent } from "./add-edit.component"; @@ -132,6 +132,18 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro label: this.i18nService.t("copyLink"), click: () => this.copy(send), }); + if (send.password && !send.disabled) { + menu.push({ + label: this.i18nService.t("removePassword"), + click: async () => { + await this.removePassword(send); + if (this.sendId === send.id) { + this.sendId = null; + this.selectSend(send.id); + } + }, + }); + } menu.push({ label: this.i18nService.t("delete"), click: async () => { diff --git a/apps/desktop/src/app/services/desktopFileDownloadService.ts b/apps/desktop/src/app/services/desktop-file-download.service.ts similarity index 100% rename from apps/desktop/src/app/services/desktopFileDownloadService.ts rename to apps/desktop/src/app/services/desktop-file-download.service.ts diff --git a/apps/desktop/src/app/services/init.service.ts b/apps/desktop/src/app/services/init.service.ts index 525d593911d..fd29631ab4b 100644 --- a/apps/desktop/src/app/services/init.service.ts +++ b/apps/desktop/src/app/services/init.service.ts @@ -5,7 +5,7 @@ import { AbstractThemingService } from "@bitwarden/angular/services/theming/them import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service"; import { EncryptService } from "@bitwarden/common/abstractions/encrypt.service"; import { EnvironmentService as EnvironmentServiceAbstraction } from "@bitwarden/common/abstractions/environment.service"; -import { EventService as EventServiceAbstraction } from "@bitwarden/common/abstractions/event.service"; +import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service"; import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/abstractions/platformUtils.service"; @@ -14,11 +14,11 @@ import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/abstrac import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/abstractions/twoFactor.service"; import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; import { ContainerService } from "@bitwarden/common/services/container.service"; -import { EventService } from "@bitwarden/common/services/event.service"; +import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; import { VaultTimeoutService } from "@bitwarden/common/services/vaultTimeout/vaultTimeout.service"; import { I18nService } from "../../services/i18n.service"; -import { NativeMessagingService } from "../../services/nativeMessaging.service"; +import { NativeMessagingService } from "../../services/native-messaging.service"; @Injectable() export class InitService { @@ -28,7 +28,7 @@ export class InitService { private syncService: SyncServiceAbstraction, private vaultTimeoutService: VaultTimeoutServiceAbstraction, private i18nService: I18nServiceAbstraction, - private eventService: EventServiceAbstraction, + private eventUploadService: EventUploadServiceAbstraction, private twoFactorService: TwoFactorServiceAbstraction, private notificationsService: NotificationsServiceAbstraction, private platformUtilsService: PlatformUtilsServiceAbstraction, @@ -48,7 +48,7 @@ export class InitService { (this.vaultTimeoutService as VaultTimeoutService).init(true); const locale = await this.stateService.getLocale(); await (this.i18nService as I18nService).init(locale); - (this.eventService as EventService).init(true); + (this.eventUploadService as EventUploadService).init(true); this.twoFactorService.init(); setTimeout(() => this.notificationsService.init(), 3000); const htmlEl = this.win.document.documentElement; diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 6dc9b8b0dbb..28d27a9ce22 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -39,25 +39,25 @@ import { GlobalState } from "@bitwarden/common/models/domain/global-state"; import { LoginService } from "@bitwarden/common/services/login.service"; import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service"; import { SystemService } from "@bitwarden/common/services/system.service"; -import { ElectronCryptoService } from "@bitwarden/electron/services/electronCrypto.service"; -import { ElectronLogService } from "@bitwarden/electron/services/electronLog.service"; -import { ElectronPlatformUtilsService } from "@bitwarden/electron/services/electronPlatformUtils.service"; -import { ElectronRendererMessagingService } from "@bitwarden/electron/services/electronRendererMessaging.service"; -import { ElectronRendererSecureStorageService } from "@bitwarden/electron/services/electronRendererSecureStorage.service"; -import { ElectronRendererStorageService } from "@bitwarden/electron/services/electronRendererStorage.service"; import { Account } from "../../models/account"; -import { EncryptedMessageHandlerService } from "../../services/encryptedMessageHandlerService"; +import { ElectronCryptoService } from "../../services/electron-crypto.service"; +import { ElectronLogService } from "../../services/electron-log.service"; +import { ElectronPlatformUtilsService } from "../../services/electron-platform-utils.service"; +import { ElectronRendererMessagingService } from "../../services/electron-renderer-messaging.service"; +import { ElectronRendererSecureStorageService } from "../../services/electron-renderer-secure-storage.service"; +import { ElectronRendererStorageService } from "../../services/electron-renderer-storage.service"; +import { EncryptedMessageHandlerService } from "../../services/encrypted-message-handler.service"; import { I18nService } from "../../services/i18n.service"; -import { NativeMessageHandlerService } from "../../services/nativeMessageHandler.service"; -import { NativeMessagingService } from "../../services/nativeMessaging.service"; -import { PasswordRepromptService } from "../../services/passwordReprompt.service"; +import { NativeMessageHandlerService } from "../../services/native-message-handler.service"; +import { NativeMessagingService } from "../../services/native-messaging.service"; +import { PasswordRepromptService } from "../../services/password-reprompt.service"; import { StateService } from "../../services/state.service"; import { LoginGuard } from "../guards/login.guard"; import { SearchBarService } from "../layout/search/search-bar.service"; +import { DesktopFileDownloadService } from "./desktop-file-download.service"; import { DesktopThemingService } from "./desktop-theming.service"; -import { DesktopFileDownloadService } from "./desktopFileDownloadService"; import { InitService } from "./init.service"; const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK"); @@ -180,6 +180,7 @@ const RELOAD_CALLBACK = new InjectionToken<() => any>("RELOAD_CALLBACK"); { provide: LoginServiceAbstraction, useClass: LoginService, + deps: [StateServiceAbstraction], }, ], }) diff --git a/apps/desktop/src/app/vault/add-edit-custom-fields.component.ts b/apps/desktop/src/app/vault/add-edit-custom-fields.component.ts index 7e6e91686f8..44f9e9539ad 100644 --- a/apps/desktop/src/app/vault/add-edit-custom-fields.component.ts +++ b/apps/desktop/src/app/vault/add-edit-custom-fields.component.ts @@ -1,7 +1,7 @@ import { Component } from "@angular/core"; import { AddEditCustomFieldsComponent as BaseAddEditCustomFieldsComponent } from "@bitwarden/angular/components/add-edit-custom-fields.component"; -import { EventService } from "@bitwarden/common/abstractions/event.service"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; @Component({ @@ -9,7 +9,7 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; templateUrl: "add-edit-custom-fields.component.html", }) export class AddEditCustomFieldsComponent extends BaseAddEditCustomFieldsComponent { - constructor(i18nService: I18nService, eventService: EventService) { - super(i18nService, eventService); + constructor(i18nService: I18nService, eventCollectionService: EventCollectionService) { + super(i18nService, eventCollectionService); } } diff --git a/apps/desktop/src/app/vault/add-edit.component.html b/apps/desktop/src/app/vault/add-edit.component.html index 25de9f6f7b8..2bd1364448e 100644 --- a/apps/desktop/src/app/vault/add-edit.component.html +++ b/apps/desktop/src/app/vault/add-edit.component.html @@ -75,16 +75,16 @@ appA11yTitle="{{ 'checkPassword' | i18n }}" (click)="checkPassword()" [appApiAction]="checkPasswordPromise" - [disabled]="checkPasswordBtn.loading" + [disabled]="$any(checkPasswordBtn).loading" > @@ -485,7 +485,10 @@ id="loginUriMatch{{ i }}" name="Login.Uris[{{ i }}].Match" [(ngModel)]="u.match" - [hidden]="u.showOptions === false || (u.showOptions == null && u.match == null)" + [hidden]=" + $any(u).showOptions === false || + ($any(u).showOptions == null && u.match == null) + " (change)="loginUriMatchChanged(u)" >
-
- -
- -
@@ -388,7 +380,7 @@ diff --git a/apps/desktop/src/app/vault/password-generator-history.component.html b/apps/desktop/src/app/vault/password-generator-history.component.html index 6a8dfd909ef..7799dcaade6 100644 --- a/apps/desktop/src/app/vault/password-generator-history.component.html +++ b/apps/desktop/src/app/vault/password-generator-history.component.html @@ -10,7 +10,7 @@
diff --git a/apps/desktop/src/app/vault/password-history.component.html b/apps/desktop/src/app/vault/password-history.component.html index 49493273c16..362061b250d 100644 --- a/apps/desktop/src/app/vault/password-history.component.html +++ b/apps/desktop/src/app/vault/password-history.component.html @@ -9,9 +9,7 @@
- - {{ h.password }} - + {{ h.lastUsedDate | date: "medium" }}
diff --git a/apps/desktop/src/app/vault/share.component.html b/apps/desktop/src/app/vault/share.component.html index 9232d08df3b..a83cbb66298 100644 --- a/apps/desktop/src/app/vault/share.component.html +++ b/apps/desktop/src/app/vault/share.component.html @@ -18,6 +18,7 @@
- diff --git a/apps/desktop/src/app/vault/vault-filter/filters/folder-filter.component.html b/apps/desktop/src/app/vault/vault-filter/filters/folder-filter.component.html index 91a5b9ee772..eaa874d28e1 100644 --- a/apps/desktop/src/app/vault/vault-filter/filters/folder-filter.component.html +++ b/apps/desktop/src/app/vault/vault-filter/filters/folder-filter.component.html @@ -63,7 +63,7 @@ class="edit-button" *ngIf="f.node.id" (click)="editFolder(f.node)" - appA11yTitle="{{ 'editFolder' | i18n }}" + appA11yTitle="{{ 'editFolder' | i18n }}: {{ f.node.name }}" > diff --git a/apps/desktop/src/app/vault/vault-filter/filters/organization-filter.component.html b/apps/desktop/src/app/vault/vault-filter/filters/organization-filter.component.html index 3663b69fc46..68aebbf41dd 100644 --- a/apps/desktop/src/app/vault/vault-filter/filters/organization-filter.component.html +++ b/apps/desktop/src/app/vault/vault-filter/filters/organization-filter.component.html @@ -24,7 +24,7 @@ class="filter-button" (click)="clearFilter()" [attr.aria-pressed]="!hasActiveFilter" - appA11yTitle="{{ 'vault' | i18n }}: {{ organizationGrouping.name }}" + appA11yTitle="{{ 'vault' | i18n }}: {{ organizationGrouping.name | i18n }}" > {{ organizationGrouping.name | i18n }} @@ -46,6 +46,13 @@  {{ organization.name }} + + + @@ -73,7 +80,7 @@ class="filter-button" (click)="clearFilter()" [attr.aria-pressed]="!hasActiveFilter" - appA11yTitle="{{ 'vault' | i18n }}: {{ organizationGrouping.name }}" + appA11yTitle="{{ 'vault' | i18n }}: {{ organizationGrouping.name | i18n }}" > {{ organizationGrouping.name | i18n }} @@ -102,17 +109,18 @@ - + diff --git a/apps/desktop/src/app/vault/vault-filter/vault-filter.module.ts b/apps/desktop/src/app/vault/vault-filter/vault-filter.module.ts index 6442a2b7b8b..996bdf807ab 100644 --- a/apps/desktop/src/app/vault/vault-filter/vault-filter.module.ts +++ b/apps/desktop/src/app/vault/vault-filter/vault-filter.module.ts @@ -1,6 +1,7 @@ import { NgModule } from "@angular/core"; import { BrowserModule } from "@angular/platform-browser"; +import { DeprecatedVaultFilterService as DeprecatedVaultFilterServiceAbstraction } from "@bitwarden/angular/abstractions/deprecated-vault-filter.service"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { VaultFilterService } from "@bitwarden/angular/vault/vault-filter/services/vault-filter.service"; @@ -22,6 +23,11 @@ import { VaultFilterComponent } from "./vault-filter.component"; TypeFilterComponent, ], exports: [VaultFilterComponent], - providers: [VaultFilterService], + providers: [ + { + provide: DeprecatedVaultFilterServiceAbstraction, + useClass: VaultFilterService, + }, + ], }) export class VaultFilterModule {} diff --git a/apps/desktop/src/app/vault/ciphers.component.html b/apps/desktop/src/app/vault/vault-items.component.html similarity index 100% rename from apps/desktop/src/app/vault/ciphers.component.html rename to apps/desktop/src/app/vault/vault-items.component.html diff --git a/apps/desktop/src/app/vault/ciphers.component.ts b/apps/desktop/src/app/vault/vault-items.component.ts similarity index 73% rename from apps/desktop/src/app/vault/ciphers.component.ts rename to apps/desktop/src/app/vault/vault-items.component.ts index 4e0c6016ca9..1bd039d6994 100644 --- a/apps/desktop/src/app/vault/ciphers.component.ts +++ b/apps/desktop/src/app/vault/vault-items.component.ts @@ -1,17 +1,17 @@ import { Component } from "@angular/core"; -import { CiphersComponent as BaseCiphersComponent } from "@bitwarden/angular/components/ciphers.component"; +import { VaultItemsComponent as BaseVaultItemsComponent } from "@bitwarden/angular/components/vault-items.component"; import { SearchService } from "@bitwarden/common/abstractions/search.service"; import { CipherView } from "@bitwarden/common/models/view/cipher.view"; import { SearchBarService } from "../layout/search/search-bar.service"; @Component({ - selector: "app-vault-ciphers", - templateUrl: "ciphers.component.html", + selector: "app-vault-items", + templateUrl: "vault-items.component.html", }) // eslint-disable-next-line rxjs-angular/prefer-takeuntil -export class CiphersComponent extends BaseCiphersComponent { +export class VaultItemsComponent extends BaseVaultItemsComponent { constructor(searchService: SearchService, searchBarService: SearchBarService) { super(searchService); diff --git a/apps/desktop/src/app/vault/vault.component.html b/apps/desktop/src/app/vault/vault.component.html index bf6773e4689..9c8d9ecfddf 100644 --- a/apps/desktop/src/app/vault/vault.component.html +++ b/apps/desktop/src/app/vault/vault.component.html @@ -1,5 +1,5 @@
- - + { this.copyValue(cipher, cipher.login.password, "password", "Password"); - this.eventService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id); + this.eventCollectionService.collect(EventType.Cipher_ClientCopiedPassword, cipher.id); }, }); } @@ -338,7 +338,7 @@ export class VaultComponent implements OnInit, OnDestroy { label: this.i18nService.t("copySecurityCode"), click: () => { this.copyValue(cipher, cipher.card.code, "securityCode", "Security Code"); - this.eventService.collect(EventType.Cipher_ClientCopiedCardCode, cipher.id); + this.eventCollectionService.collect(EventType.Cipher_ClientCopiedCardCode, cipher.id); }, }); } @@ -429,21 +429,21 @@ export class VaultComponent implements OnInit, OnDestroy { this.cipherId = cipher.id; this.action = "view"; this.go(); - await this.ciphersComponent.refresh(); + await this.vaultItemsComponent.refresh(); } async deletedCipher(cipher: CipherView) { this.cipherId = null; this.action = null; this.go(); - await this.ciphersComponent.refresh(); + await this.vaultItemsComponent.refresh(); } async restoredCipher(cipher: CipherView) { this.cipherId = null; this.action = null; this.go(); - await this.ciphersComponent.refresh(); + await this.vaultItemsComponent.refresh(); } async editCipherAttachments(cipher: CipherView) { @@ -468,7 +468,7 @@ export class VaultComponent implements OnInit, OnDestroy { this.modal.onClosed.subscribe(async () => { this.modal = null; if (madeAttachmentChanges) { - await this.ciphersComponent.refresh(); + await this.vaultItemsComponent.refresh(); } madeAttachmentChanges = false; }); @@ -490,7 +490,7 @@ export class VaultComponent implements OnInit, OnDestroy { childComponent.onSharedCipher.subscribe(async () => { this.modal.close(); this.viewCipher(cipher); - await this.ciphersComponent.refresh(); + await this.vaultItemsComponent.refresh(); }); // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.modal.onClosed.subscribe(async () => { @@ -549,7 +549,7 @@ export class VaultComponent implements OnInit, OnDestroy { this.i18nService.t(this.calculateSearchBarLocalizationString(vaultFilter)) ); this.activeFilter = vaultFilter; - await this.ciphersComponent.reload( + await this.vaultItemsComponent.reload( this.activeFilter.buildFilter(), vaultFilter.status === "trash" ); diff --git a/apps/desktop/src/app/vault/view-custom-fields.component.html b/apps/desktop/src/app/vault/view-custom-fields.component.html index 5f2f110fbaa..492c7fbb23e 100644 --- a/apps/desktop/src/app/vault/view-custom-fields.component.html +++ b/apps/desktop/src/app/vault/view-custom-fields.component.html @@ -17,12 +17,16 @@ {{ field.value || " " }}
+ {{ field.maskedValue }} - {{ field.maskedValue }} +
@@ -41,7 +45,18 @@ {{ cipher.linkedFieldI18nKey(field.linkedId) | i18n }}
-
+
+
+ diff --git a/apps/desktop/src/app/vault/view.component.ts b/apps/desktop/src/app/vault/view.component.ts index 52db3e22bc7..70f55d400a4 100644 --- a/apps/desktop/src/app/vault/view.component.ts +++ b/apps/desktop/src/app/vault/view.component.ts @@ -13,7 +13,7 @@ import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.service"; import { CipherService } from "@bitwarden/common/abstractions/cipher.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; -import { EventService } from "@bitwarden/common/abstractions/event.service"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { FileDownloadService } from "@bitwarden/common/abstractions/fileDownload/fileDownload.service"; import { FolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; @@ -47,7 +47,7 @@ export class ViewComponent extends BaseViewComponent implements OnChanges { broadcasterService: BroadcasterService, ngZone: NgZone, changeDetectorRef: ChangeDetectorRef, - eventService: EventService, + eventCollectionService: EventCollectionService, apiService: ApiService, private messagingService: MessagingService, passwordRepromptService: PasswordRepromptService, @@ -68,7 +68,7 @@ export class ViewComponent extends BaseViewComponent implements OnChanges { broadcasterService, ngZone, changeDetectorRef, - eventService, + eventCollectionService, apiService, passwordRepromptService, logService, diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index 0d4c31cde12..4f5c29b5a55 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Gedeaktiveer" }, + "removePassword": { + "message": "Verwyder wagwoord" + }, + "removedPassword": { + "message": "Wagwoord geverwydered" + }, + "removePasswordConfirmation": { + "message": "Is jy seker jy wil die wagwoord verwyder?" + }, "maxAccessCountReached": { "message": "Maks toegangsaantal bereik" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Meld aan met 'n ander toestel" + }, + "toggleCharacterCount": { + "message": "Tokkel karakter telling", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index f53447c68e1..7b755b0ed79 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "معطّل" }, + "removePassword": { + "message": "إزالة كلمة المرور" + }, + "removedPassword": { + "message": "تمت إزالة كلمة المرور" + }, + "removePasswordConfirmation": { + "message": "هل أنت متأكد من أنك تريد إزالة كلمة المرور؟" + }, "maxAccessCountReached": { "message": "تم بلوغ الحد الأقصى لعدد الدخول" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "تسجيل الدخول باستخدام جهاز آخر" + }, + "toggleCharacterCount": { + "message": "تبديل عدد الأحرف", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index 3d3585997b2..353c1cd832e 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Sıradan çıxarıldı" }, + "removePassword": { + "message": "Parolu çıxart" + }, + "removedPassword": { + "message": "Parol çıxarıldı" + }, + "removePasswordConfirmation": { + "message": "Parolu çıxartmaq istədiyinizə əminsiniz?" + }, "maxAccessCountReached": { "message": "Maksimal müraciət sayına çatıldı" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Başqa cihazla giriş et" + }, + "toggleCharacterCount": { + "message": "Simvol sayını dəyişdir", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index 247d15f2d47..2fd22515dc7 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Адключана" }, + "removePassword": { + "message": "Выдаліць пароль" + }, + "removedPassword": { + "message": "Пароль выдалены" + }, + "removePasswordConfirmation": { + "message": "Вы сапраўды хочаце выдаліць пароль?" + }, "maxAccessCountReached": { "message": "Дасягнута максімальная колькасць доступаў" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Увайсці з іншай прылады" + }, + "toggleCharacterCount": { + "message": "Пераключыць лічыльнік сімвалаў", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index a6c64d38a77..1c9a4d62251 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Изключено" }, + "removePassword": { + "message": "Премахване на паролата" + }, + "removedPassword": { + "message": "Паролата е премахната" + }, + "removePasswordConfirmation": { + "message": "Наистина ли искате да премахнете паролата?" + }, "maxAccessCountReached": { "message": "Достигнат е максималният брой достъпвания" }, @@ -1955,14 +1964,14 @@ "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": "Използвайте възможностите за под-адресиране на е-поща на своя доставчик." }, "catchallEmail": { - "message": "Всички имейли" + "message": "Хващаща всичко е-поща" }, "catchallEmailDesc": { "message": "Използвайте конфигурираната входяща кутия за събиране на всичко." @@ -1989,10 +1998,10 @@ "message": "Търсене в моя трезор" }, "forwardedEmail": { - "message": "Псевдоним на препратен имейл" + "message": "Псевдоним на препратена е-поща" }, "forwardedEmailDesc": { - "message": "Генерирайте имейл псевдоним с външна услуга за препращане." + "message": "Създайте псевдоним на е-поща с външна услуга за препращане." }, "hostname": { "message": "Име на сървъра", @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Вписване с друго устройство" + }, + "toggleCharacterCount": { + "message": "Превключване на броя знаци", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index 0a8416dd2fe..d54c7263b9d 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index a3b4053cc91..17faab218e3 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index 3848160332e..4322f934aa6 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -1459,7 +1459,7 @@ "message": "Una o més polítiques d’organització afecten la configuració del generador." }, "vaultTimeoutAction": { - "message": "Acció del temps d'espera de la caixa forta" + "message": "Acció quan acabe el temps d'espera de la caixa forta" }, "vaultTimeoutActionLockDesc": { "message": "Una caixa forta bloquejada requereix que torneu a introduir la contrasenya principal per accedir-ne de nou." @@ -1779,6 +1779,15 @@ "disabled": { "message": "Deshabilitat" }, + "removePassword": { + "message": "Suprimeix la contrasenya" + }, + "removedPassword": { + "message": "Contrasenya suprimida" + }, + "removePasswordConfirmation": { + "message": "Esteu segur que voleu suprimir la contrasenya?" + }, "maxAccessCountReached": { "message": "S'ha assolit el recompte màxim d'accesos" }, @@ -2023,7 +2032,7 @@ "message": "Caixa forta" }, "loginWithMasterPassword": { - "message": "Inicia sessió amb la contrasenya mestra" + "message": "Inici de sessió amb contrasenya mestra" }, "loggingInAs": { "message": "Has iniciat sessió com" @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Inicia sessió amb un altre dispositiu" + }, + "toggleCharacterCount": { + "message": "Commuta el recompte de caràcters", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index cca2d86a35a..99124120ce2 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Zakázáno" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Dosažen maximální počet přístupů" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Přihlásit se pomocí jiného zařízení" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index c7d1aca6626..046f667cd57 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -33,7 +33,7 @@ "message": "Samlinger" }, "searchVault": { - "message": "Søg i Boks" + "message": "Søg i boks" }, "addItem": { "message": "Tilføj emne" @@ -61,7 +61,7 @@ } }, "moveToOrgDesc": { - "message": "Vælg den organisation, som du vil flytte dette emne til. Flytning overfører ejerskab af emnet til organisationen, og du vil efter flytningen ikke længere være den direkte ejer af emnet." + "message": "Vælg en organisation, hvortil dette emne skal flyttes. Flytning overfører ejerskab af emnet til organisationen, og efter flytningen vil man ikke længere være den direkte ejer af emnet." }, "attachments": { "message": "Vedhæftninger" @@ -92,7 +92,7 @@ "message": "Brugernavn" }, "password": { - "message": "Kodeord" + "message": "Adgangskode" }, "passphrase": { "message": "Adgangssætning" @@ -123,20 +123,20 @@ "description": "Copy value to clipboard" }, "minimizeOnCopyToClipboard": { - "message": "Minimér, når du kopierer til udklipsholder" + "message": "Minimér under kopiering til udklipsholder" }, "minimizeOnCopyToClipboardDesc": { - "message": "Minimér programmet, når du kopierer et elements data til udklipsholderen." + "message": "Minimér applikation under kopiering af emnes data til udklipsholder." }, "toggleVisibility": { "message": "Slå synlighed til/fra" }, "toggleCollapse": { - "message": "Fold sammen/fold ud", + "message": "Fold sammen/ud", "description": "Toggling an expand/collapse state." }, "cardholderName": { - "message": "Kortindehaverens navn" + "message": "Kortholdernavn" }, "number": { "message": "Nummer" @@ -148,7 +148,7 @@ "message": "Udløb" }, "securityCode": { - "message": "Sikkerhedskode" + "message": "Bekræftelseskode" }, "identityName": { "message": "Identitetsnavn" @@ -163,7 +163,7 @@ "message": "Pasnummer" }, "licenseNumber": { - "message": "Licensnummer" + "message": "Kørekortnummer" }, "email": { "message": "E-mail" @@ -178,7 +178,7 @@ "message": "Premium kræves" }, "premiumRequiredDesc": { - "message": "Premium-medlemskab kræves for at anvende denne funktion." + "message": "Premium-medlemskab kræves for brug af denne funktion." }, "errorOccurred": { "message": "En fejl er opstået." @@ -230,16 +230,16 @@ "message": "Titel" }, "mr": { - "message": "Hr" + "message": "Hr." }, "mrs": { "message": "Fru" }, "ms": { - "message": "Frøken" + "message": "Frk." }, "dr": { - "message": "Dr" + "message": "Dr." }, "expirationMonth": { "message": "Udløbsmåned" @@ -251,7 +251,7 @@ "message": "Vælg" }, "other": { - "message": "Andet" + "message": "Andre" }, "generatePassword": { "message": "Generér adgangskode" @@ -308,13 +308,13 @@ "message": "Redigér" }, "authenticatorKeyTotp": { - "message": "Autentificeringsnøgle (TOTP)" + "message": "Godkendelsesnøgle (TOTP)" }, "folder": { "message": "Mappe" }, "newCustomField": { - "message": "Nyt brugerdefineret felt" + "message": "Nyt tilpasset felt" }, "value": { "message": "Værdi" @@ -343,16 +343,16 @@ "message": "Fjern" }, "nameRequired": { - "message": "Navn er påkrævet." + "message": "Navn er obligatorisk." }, "addedItem": { - "message": "Tilføjede element" + "message": "Emne tilføjet" }, "editedItem": { - "message": "Regiderede element" + "message": "Emne gemt" }, "deleteItem": { - "message": "Slet element" + "message": "Slet emne" }, "deleteFolder": { "message": "Slet mappe" @@ -361,19 +361,19 @@ "message": "Slet vedhæftning" }, "deleteItemConfirmation": { - "message": "Er du sikker på, at du sende til papirkurven?" + "message": "Send til papirkurven, sikker?" }, "deletedItem": { - "message": "Element sendt til papirkurven" + "message": "Emne sendt til papirkurven" }, "overwritePasswordConfirmation": { - "message": "Er du sikker på, at du vil overskrive den aktuelle adgangskode?" + "message": "Sikker på, at den aktuelle adgangskode skal overskrives?" }, "overwriteUsername": { "message": "Overskriv brugernavn" }, "overwriteUsernameConfirmation": { - "message": "Er du sikker på, at du vil overskrive det aktuelle brugernavn?" + "message": "Sikker på, at det aktuelle brugernavn skal overskrives?" }, "noneFolder": { "message": "Ingen mappe", @@ -389,7 +389,7 @@ "message": "Regenerér adgangskode" }, "copyPassword": { - "message": "Kopiér kodeord" + "message": "Kopiér adgangskode" }, "copyUri": { "message": "Kopiér URI" @@ -401,10 +401,10 @@ "message": "Længde" }, "uppercase": { - "message": "Store bogstaver (A-Z)" + "message": "Majuskler (A-Z)" }, "lowercase": { - "message": "Små bogstaver (a-z)" + "message": "Minuskler (a-z)" }, "numbers": { "message": "Cifre (0-9)" @@ -455,13 +455,13 @@ "message": "Tilføj ny vedhæftning" }, "deletedAttachment": { - "message": "Slettet vedhæftning" + "message": "Vedhæftning slettet" }, "deleteAttachmentConfirmation": { - "message": "Er du sikker på du vil slette denne vedhæftning?" + "message": "Sikker på, at denne vedhæftning skal slettes?" }, "attachmentSaved": { - "message": "Den vedhæftede fil er blevet gemt." + "message": "Vedhæftning gemt" }, "file": { "message": "Fil" @@ -473,22 +473,22 @@ "message": "Maksimum filstørrelse er 500 MB." }, "updateKey": { - "message": "Du kan ikke bruge denne funktion, før du opdaterer din krypteringsnøgle." + "message": "Denne funktion kan ikke bruges, før din krypteringsnøgle opdateres." }, "editedFolder": { - "message": "Redigerede mappe" + "message": "Mappe gemt" }, "addedFolder": { - "message": "Tilføjede mappe" + "message": "Mappe tilføjet" }, "deleteFolderConfirmation": { - "message": "Er du sikker på du vil slette denne mappe?" + "message": "Sikker på, at denne mappe skal slettes?" }, "deletedFolder": { - "message": "Slettede mappe" + "message": "Mappe slettet" }, "loginOrCreateNewAccount": { - "message": "Log ind eller opret en ny konto for at få adgang til din sikre boks." + "message": "Log ind, eller opret en ny konto, for at få tilgå din sikre boks." }, "createAccount": { "message": "Opret konto" @@ -503,16 +503,16 @@ "message": "Hovedadgangskode" }, "masterPassDesc": { - "message": "Hovedadgangskoden er den adgangskode, du bruger til at få adgang til din boks. Det er meget vigtigt, at du ikke glemmer din hovedadgangskode. Der er ingen måde hvorpå koden kan genoprettes, i tilfælde af at du glemmer den." + "message": "Hovedadgangskoden er den adgangskode, man bruger for at kunne tilgå sin boks. Det er uhyre vigtigt, at hovedadgangskoden ikke glemmes, da den ikke vil kunne genoprettes, såfremt man glemmer den." }, "masterPassHintDesc": { - "message": "Et tip til hovedadgangskoden kan hjælpe dig med at huske din adgangskode, hvis du glemmer den." + "message": "Et tip til hovedadgangskoden kan hjælpe hukommelsen på vej, såfremt man glemmer koden." }, "reTypeMasterPass": { - "message": "Gentast hovedadgangskode" + "message": "Angiv hovedadgangskode igen" }, "masterPassHint": { - "message": "Hovedadgangskodetip (valgfri)" + "message": "Hovedadgangskodetip (valgfrit)" }, "settings": { "message": "Indstillinger" @@ -527,16 +527,16 @@ "message": "Få hovedadgangskodetip" }, "emailRequired": { - "message": "E-mailadresse er påkrævet." + "message": "E-mailadresse er obligatorisk." }, "invalidEmail": { "message": "Ugyldig e-mailadresse." }, "masterPasswordRequired": { - "message": "Hovedadgangskode er påkrævet." + "message": "Hovedadgangskode er obligatorisk." }, "confirmMasterPasswordRequired": { - "message": "Hovedadgangskode kræves angivet igen." + "message": "Angivelse af hovedadgangskode igen er obligatorisk." }, "masterPasswordMinlength": { "message": "Hovedadgangskode skal være mindst 8 tegn." @@ -545,19 +545,19 @@ "message": "De to adgangskoder matcher ikke." }, "newAccountCreated": { - "message": "Din nye konto er oprettet! Du kan nu logge ind." + "message": "Den nye konto er oprettet! Der kan nu logges ind." }, "masterPassSent": { - "message": "Vi har sendt dig en e-mail med dit hovedadgangskodetip." + "message": "Der er sendt en e-mail til dig med dit hovedadgangskodetip." }, "unexpectedError": { - "message": "Der opstod en uventet fejl." + "message": "En uventet fejl opstod." }, "itemInformation": { - "message": "Elementinformation" + "message": "Emneinformation" }, "noItemsInList": { - "message": "Der er ingen elementer at vise." + "message": "Ingen emner at vise." }, "sendVerificationCode": { "message": "Send en bekræftelseskode til din e-mail" @@ -575,7 +575,7 @@ "message": "Bekræft din identitet for at fortsætte." }, "verificationCodeRequired": { - "message": "Bekræftelseskode er påkrævet." + "message": "Bekræftelseskode er obligatorisk." }, "invalidVerificationCode": { "message": "Ugyldig bekræftelseskode" @@ -584,10 +584,10 @@ "message": "Fortsæt" }, "enterVerificationCodeApp": { - "message": "Indtast den 6-cifrede verifikationskode fra din autentificeringsapp." + "message": "Angiv den 6-cifrede bekræftelseskode fra godkendelses-appen." }, "enterVerificationCodeEmail": { - "message": "Indtast den 6-cifrede verifikationskode, der blev sendt til $EMAIL$.", + "message": "Angiv den 6-cifrede bekræftelseskode, der blev sendt til $EMAIL$.", "placeholders": { "email": { "content": "$1", @@ -596,7 +596,7 @@ } }, "verificationCodeEmailSent": { - "message": "Bekræftelses-email sendt til $EMAIL$.", + "message": "Bekræftelsese-mail sendt til $EMAIL$.", "placeholders": { "email": { "content": "$1", @@ -608,10 +608,10 @@ "message": "Husk mig" }, "sendVerificationCodeEmailAgain": { - "message": "Send verifikationskode-email igen" + "message": "Send bekræftelseskodee-mail igen" }, "useAnotherTwoStepMethod": { - "message": "Brug en anden to-trins-loginmetode" + "message": "Brug en anden totrins-loginmetode" }, "insertYubiKey": { "message": "Indsæt din YubiKey i din computers USB-port og tryk derefter på dens knap." @@ -620,37 +620,37 @@ "message": "Indsæt din sikkerhedsnøgle i din computers USB-port. Hvis den har en knap, tryk på den." }, "recoveryCodeDesc": { - "message": "Mistet adgang til alle dine to-faktor-udbydere? Brug din genoprettelseskode til at deaktivere alle to-faktor udbydere på din konto." + "message": "Mistet adgang til alle dine tofaktorudbydere? Brug din genoprettelseskode til at deaktivere alle tofaktorudbydere på din konto." }, "recoveryCodeTitle": { "message": "Gendannelseskode" }, "authenticatorAppTitle": { - "message": "Autentificeringsapp" + "message": "Godkendelses-app" }, "authenticatorAppDesc": { - "message": "Brug en autentificeringsapp (f.eks. Authy eller Google Autentificering) til at generere tidsbaserede bekræftelseskoder.", + "message": "Brug en godkendelses-app (såsom Authy eller Google Autenticator) til at generere tidsbaserede bekræftelseskoder.", "description": "'Authy' and 'Google Authenticator' are product names and should not be translated." }, "yubiKeyTitle": { - "message": "YubiKey OTP sikkerhedsnøgle" + "message": "YubiKey OTP-sikkerhedsnøgle" }, "yubiKeyDesc": { - "message": "Brug en YubiKey til at få adgang til din konto. Virker med YubiKey 4, 4 Nano, 4C og NEO enheder." + "message": "Brug en YubiKey for at tilgå din konto. Fungerer med YubiKey 4-, 4 Nano-, 4C- samt NEO-enheder." }, "duoDesc": { - "message": "Bekræft med Duo sikkerhed ved hjælp af Duo Mobile app, SMS, telefonopkald eller U2F sikkerhedsnøgle.", + "message": "Bekræft med Duo Security vha. Duo Mobile-app, SMS, telefonopkald eller U2F-sikkerhedsnøgle.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { - "message": "Bekræft med Duo Security for din organisation ved hjælp af Duo Mobile app, SMS, telefonopkald eller U2F-sikkerhedsnøgle.", + "message": "Bekræft med Duo Security for din organisation vha. Duo Mobile-app, SMS, telefonopkald eller U2F-sikkerhedsnøgle.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "webAuthnTitle": { "message": "FIDO2 WebAuthn" }, "webAuthnDesc": { - "message": "Brug en hvilken som helst WebAuthn-aktiveret sikkerhedsnøgle til at få adgang til din konto." + "message": "Brug enhver type WebAuthn-kompatibel sikkerhedsnøgle for at tilgå din konto." }, "emailTitle": { "message": "E-mail" @@ -662,49 +662,49 @@ "message": "Login utilgængelig" }, "noTwoStepProviders": { - "message": "Denne konto har to-trins login aktiveret, men ingen af de konfigurerede to-trinsudbydere understøttes af denne enhed." + "message": "Denne konto har totrins-login aktiveret, men ingen af de opsatte totrinsudbydere understøttes af denne enhed." }, "noTwoStepProviders2": { - "message": "Tilføj venligst yderligere udbydere, der understøttes bedre på tværs af enheder (såsom en autentificeringsapp)." + "message": "Tilføj yderligere udbydere, som er bedre understøttet på tværs af enheder (såsom en godkendelses-app)." }, "twoStepOptions": { - "message": "To-trins-login indstillinger" + "message": "Totrins-login indstillinger" }, "selfHostedEnvironment": { "message": "Selv-hostet miljø" }, "selfHostedEnvironmentFooter": { - "message": "Angiv grund-URL'en i din lokal-hostede Bitwarden-installation." + "message": "Angiv grund-URL'en til den lokal-hostede Bitwarden-installation." }, "customEnvironment": { - "message": "Brugerdefineret miljø" + "message": "Tilpasset miljø" }, "customEnvironmentFooter": { - "message": "Til avancerede brugere. Du kan angive grund URL'en for hver tjeneste uafhængigt." + "message": "Til avancerede brugere. Hver tjenestes basis-URL kan angives uafhængigt." }, "baseUrl": { - "message": "Server URL" + "message": "Server-URL" }, "apiUrl": { - "message": "API server URL" + "message": "API-server URL" }, "webVaultUrl": { - "message": "Web-boks server URL" + "message": "Web-boks server-URL" }, "identityUrl": { - "message": "Identitetsserver URL" + "message": "Identitetsserver-URL" }, "notificationsUrl": { - "message": "Meddelelsesserver URL" + "message": "Notifikationsserver-URL" }, "iconsUrl": { - "message": "Ikonserver URL" + "message": "Ikonserver-URL" }, "environmentSaved": { - "message": "Miljøets URLs er blevet gemt." + "message": "Miljø-URL'er gemt" }, "ok": { - "message": "Ok" + "message": "OK" }, "yes": { "message": "Ja" @@ -716,7 +716,7 @@ "message": "Overskriv adgangskode" }, "learnMore": { - "message": "Lær mere" + "message": "Læs mere" }, "featureUnavailable": { "message": "Funktion utilgængelig" @@ -725,22 +725,22 @@ "message": "Logget ud" }, "loginExpired": { - "message": "Din login-session er udløbet." + "message": "Loginsessionen er udløbet." }, "logOutConfirmation": { - "message": "Er du sikker på, at du vil logge ud?" + "message": "Sikker på, at du vil logge ud?" }, "logOut": { "message": "Log ud" }, "addNewLogin": { - "message": "Tilføj nyt login" + "message": "Nyt login" }, "addNewItem": { - "message": "Tilføj nyt element" + "message": "Nyt emne" }, "addNewFolder": { - "message": "Tilføj ny mappe" + "message": "Ny mappe" }, "view": { "message": "Vis" @@ -773,13 +773,13 @@ "message": "Følg os" }, "syncVault": { - "message": "Synkronisér boks" + "message": "Synk boks" }, "changeMasterPass": { "message": "Skift hovedadgangskode" }, "changeMasterPasswordConfirmation": { - "message": "Du kan ændre din hovedadgangskode i bitwarden.com web-boksen. Vil du besøge hjemmesiden nu?" + "message": "Man kan ændre sin hovedadgangskode via bitwarden.com web-boksen. Besøg webstedet nu?" }, "fingerprintPhrase": { "message": "Fingeraftrykssætning", @@ -793,22 +793,22 @@ "message": "Gå til web-boks" }, "getMobileApp": { - "message": "Hent mobilapp" + "message": "Hent mobil-app" }, "getBrowserExtension": { "message": "Hent browserudvidelse" }, "syncingComplete": { - "message": "Synkronisering fuldført" + "message": "Synkning fuldført" }, "syncingFailed": { - "message": "Synkronisering mislykkedes" + "message": "Synkning mislykkedes" }, "yourVaultIsLocked": { "message": "Din boks er låst. Bekræft din identitet for at fortsætte." }, "unlock": { - "message": "Lås op" + "message": "Oplås" }, "loggedInAsOn": { "message": "Logget ind som $EMAIL$ på $HOSTNAME$.", @@ -827,10 +827,10 @@ "message": "Ugyldig hovedadgangskode" }, "twoStepLoginConfirmation": { - "message": "To-trins login gør din konto mere sikker ved at kræve, at du verificerer dit login med en anden enhed, med en sikkerhedsnøgle, autentificerings app, SMS, telefonopkald eller email. To-trins login kan aktiveres i bitwarden.com web-boksen. Vil du besøge hjemmesiden nu?" + "message": "Totrins-login gør kontoen mere sikker ved at kræve, at man bekræfter sit login med en anden enhed, såsom en sikkerhedsnøgle, godkendelses-app, SMS, telefonopkald eller e-mail. Totrins-login kan aktiveres via bitwarden.com web-boksen. Besøg webstedet nu?" }, "twoStepLogin": { - "message": "To-trins login" + "message": "Totrins-login" }, "vaultTimeout": { "message": "Boks timeout" @@ -894,7 +894,7 @@ "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "clearClipboardDesc": { - "message": "Fjern automatisk kopierede data fra din udklipsholder.", + "message": "Ryd automatisk kopierede data fra udklipsholderen.", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "enableFavicon": { @@ -904,64 +904,64 @@ "message": "Vis et genkendeligt billede ud for hvert login." }, "enableMinToTray": { - "message": "Minimer til bakkeikon" + "message": "Minimér som bakkeikon" }, "enableMinToTrayDesc": { - "message": "Når vinduet minimeres, vis i stedet et ikon i meddelelsesområdet." + "message": "Når vinduet minimeres, vis i stedet et ikon i systembakken." }, "enableMinToMenuBar": { - "message": "Minimér til menulinjen" + "message": "Minimér til menubjælke" }, "enableMinToMenuBarDesc": { - "message": "Når vinduet minimeres, vis i stedet et ikon på menulinjen." + "message": "Når vinduet minimeres, vis i stedet et ikon på menubjælken." }, "enableCloseToTray": { - "message": "Luk ned til ikon i meddelelsesområdet" + "message": "Luk ned til bakkeikon" }, "enableCloseToTrayDesc": { - "message": "Når vinduet lukkes, vis i stedet et ikon i meddelelsesområdet." + "message": "Når vinduet lukkes, vis i stedet et ikon i systembakken." }, "enableCloseToMenuBar": { - "message": "Luk til menulinjen" + "message": "Luk til menubjælke" }, "enableCloseToMenuBarDesc": { - "message": "Når vinduet lukkes, vis i stedet et ikon på menulinjen." + "message": "Når vinduet lukkes, vis i stedet et ikon på menubjælken." }, "enableTray": { - "message": "Aktivér bakkeikon" + "message": "Vis bakkeikon" }, "enableTrayDesc": { - "message": "Vis altid et ikon i meddelelsesområdet." + "message": "Vis altid et ikon i systembakken." }, "startToTray": { - "message": "Start som ikon i meddelelsesområdet" + "message": "Start som bakkeikon" }, "startToTrayDesc": { - "message": "Når applikationen startes første gang, skal der kun vises et ikon i meddelelsesområdet." + "message": "Når applikationen startes første gang, vis kun et ikon i systembakken." }, "startToMenuBar": { - "message": "Start på menulinjen" + "message": "Start på menubjælke" }, "startToMenuBarDesc": { - "message": "Når applikationen startes første gang, skal der kun vises et ikon på menulinjen." + "message": "Når applikationen startes første gang, vis kun et ikon på menubjælken." }, "openAtLogin": { "message": "Start automatisk ved login" }, "openAtLoginDesc": { - "message": "Start Bitwarden skrivebordsapplikationen automatisk ved login." + "message": "Start automatisk Bitwarden-computerapplikationen ved login." }, "alwaysShowDock": { "message": "Vis altid i Dock" }, "alwaysShowDockDesc": { - "message": "Vis Bitwarden-ikonet i Dock, selv når det er minimeret til menulinjen." + "message": "Vis Bitwarden-ikonet i Dock, selv når minimeret til menubjælken." }, "confirmTrayTitle": { - "message": "Bekræft deaktivering af bakke" + "message": "Bekræft skjulning af bakke" }, "confirmTrayDesc": { - "message": "Deaktivering af denne indstilling vil også deaktivere alle andre indstillinger relateret til bakken." + "message": "Deaktivering af denne indstilling deaktiverer alle øvrige indstillinger relateret til bakken." }, "language": { "message": "Sprog" @@ -976,11 +976,11 @@ "message": "Skift applikationens farvetema." }, "dark": { - "message": "Mørk", + "message": "Mørkt", "description": "Dark color" }, "light": { - "message": "Lys", + "message": "Lyst", "description": "Light color" }, "copy": { @@ -1003,7 +1003,7 @@ "message": "Genstart for at opdatere" }, "restartToUpdateDesc": { - "message": "Version $VERSION_NUM$ er klar til at installere. Du skal genstarte applikationen for at færdiggøre installationen. Vil du genstarte og opdatere nu?", + "message": "Version $VERSION_NUM$ er klar til installation. Applikationen skal genstartes for at færdiggøre installationen. Genstart og opdatér nu?", "placeholders": { "version_num": { "content": "$1", @@ -1015,7 +1015,7 @@ "message": "Opdatering tilgængelig" }, "updateAvailableDesc": { - "message": "En opdatering blev fundet. Vil du hente den nu?" + "message": "En opdatering er fundet. Download den nu?" }, "restart": { "message": "Genstart" @@ -1024,7 +1024,7 @@ "message": "Senere" }, "noUpdatesAvailable": { - "message": "Ingen opdateringer er tilgængelige i øjeblikket. Du anvender den nyeste version." + "message": "Ingen opdateringer tilgængelige pt. Seneste version anvendes." }, "updateError": { "message": "Opdateringsfejl" @@ -1040,7 +1040,7 @@ "description": "Copy credit card number" }, "copySecurityCode": { - "message": "Kopiér kortverifikationskode", + "message": "Kopiér bekræftelseskode", "description": "Copy credit card security code (CVV)" }, "premiumMembership": { @@ -1050,49 +1050,49 @@ "message": "Håndtér medlemsskab" }, "premiumManageAlert": { - "message": "Du kan håndtere dit medlemskab i bitwarden.com web-boksen. Vil du besøge hjemmesiden nu?" + "message": "Medlemskab kan håndteres via bitwarden.com web-boksen. Besøg webstedet nu?" }, "premiumRefresh": { "message": "Opdatér medlemskab" }, "premiumNotCurrentMember": { - "message": "Du er i øjeblikket ikke premium-medlem." + "message": "Du er ikke pt. Premium-medlem." }, "premiumSignUpAndGet": { - "message": "Tilmeld dig et premium medlemskab og få:" + "message": "Tilmeld dig et Premium-medlemskab og få:" }, "premiumSignUpStorage": { - "message": "1 GB krypteret lager til vedhæftede filer." + "message": "1 GB krypteret lagerplads til filvedhæftninger." }, "premiumSignUpTwoStep": { - "message": "Yderligere to-trins-loginmuligheder såsom YubiKey, FIDO U2F og Duo." + "message": "Yderligere totrins-loginmuligheder, såsom YubiKey, FIDO U2F og Duo." }, "premiumSignUpReports": { "message": "Adgangskodehygiejne, kontosundhed og rapporter om datalæk til at holde din boks sikker." }, "premiumSignUpTotp": { - "message": "TOTP verifikationskode (2FA) generator til logins i din boks." + "message": "TOTP bekræftelseskode (2FA) generator til logins i din boks." }, "premiumSignUpSupport": { "message": "Prioriteret kundeservice." }, "premiumSignUpFuture": { - "message": "Alle fremtidige premium-funktioner. Mere kommer snart!" + "message": "Alle fremtidige Premium-funktioner. Flere kommer snart!" }, "premiumPurchase": { - "message": "Køb premium" + "message": "Køb Premium" }, "premiumPurchaseAlert": { - "message": "Du kan købe premium-medlemskab i bitwarden.com web-boksen. Vil du besøge hjemmesiden nu?" + "message": "Premium-medlemskab kan købes via bitwarden.com web-boksen. Besøg webstedet nu?" }, "premiumCurrentMember": { - "message": "Du er premium-medlem!" + "message": "Du er Premium-medlem!" }, "premiumCurrentMemberThanks": { - "message": "Tak fordi du støtter Bitwarden." + "message": "Tak for støtten til Bitwarden." }, "premiumPrice": { - "message": "Alt dette for kun $PRICE$ /år!", + "message": "Alt dette for kun $PRICE$/år!", "placeholders": { "price": { "content": "$1", @@ -1140,7 +1140,7 @@ "message": "Nulstil zoom" }, "toggleFullScreen": { - "message": "Aktivér/deaktivér fuld skærm" + "message": "Fuldskærm til/fra" }, "reload": { "message": "Genindlæs" @@ -1194,10 +1194,10 @@ "message": "Vindue" }, "checkPassword": { - "message": "Undersøg om adgangskoden er blevet afsløret." + "message": "Tjek om adgangskode er kompromitteret." }, "passwordExposed": { - "message": "Denne adgangskode er blevet afsløret $VALUE$ gang(e) i datalæk. Du burde skifte den.", + "message": "Denne adgangskode er kompromitteret $VALUE$ gang(e) i datalæk og bør straks skiftes.", "placeholders": { "value": { "content": "$1", @@ -1206,10 +1206,10 @@ } }, "passwordSafe": { - "message": "Denne adgangskode er ikke fundet i nogen kendte datalæk. Den burde være sikker at bruge." + "message": "Denne adgangskode er ikke fundet i nogen kendte datalæk og bør derfor være sikker at bruge." }, "baseDomain": { - "message": "Grund-domæne", + "message": "Basisdomæne", "description": "Domain name. Ex. website.com" }, "domainName": { @@ -1239,7 +1239,7 @@ "description": "Default URI match detection for auto-fill." }, "toggleOptions": { - "message": "Skift indstillinger" + "message": "Slå indstillinger til/fra" }, "organization": { "message": "Organisation", @@ -1252,7 +1252,7 @@ "message": "Afslut" }, "showHide": { - "message": "Vis / skjul", + "message": "Vis/skjul", "description": "Text for a button that toggles the visibility of the window. Shows the window when it is hidden or hides the window if it is currently open." }, "hideToTray": { @@ -1281,7 +1281,7 @@ "message": "Filformat" }, "hCaptchaUrl": { - "message": "hCaptcha URL", + "message": "hCaptcha-URL", "description": "hCaptcha is the name of a website, should not be translated" }, "loadAccessibilityCookie": { @@ -1292,14 +1292,14 @@ "description": "ex. Register as an accessibility user at hcaptcha.com" }, "copyPasteLink": { - "message": "Kopiér og indsæt linket, der er sendt til din e-mail nedenfor" + "message": "Kopiér og indsæt linket, der er sendt til din e-mail, nedenfor" }, "enterhCaptchaUrl": { "message": "Indtast URL for at indlæse tilgængelighedscookie til hCaptcha", "description": "hCaptcha is the name of a website, should not be translated" }, "hCaptchaUrlRequired": { - "message": "hCaptcha-URL er påkrævet", + "message": "hCaptcha-URL er obligatorisk", "description": "hCaptcha is the name of a website, should not be translated" }, "invalidUrl": { @@ -1322,16 +1322,16 @@ "message": "Bekræft eksport af boks" }, "exportWarningDesc": { - "message": "Denne eksport indeholder dine boksdata i ukrypteret form. Du bør ikke gemme eller sende den eksporterede fil over usikre kanaler (f.eks. e-mail). Slet den straks efter at du er færdig med at bruge den." + "message": "Denne eksport indeholder dine boksdata i ukrypteret form. Den eksporterede fil bør ikke gemmes eller sendes via usikre kanaler (såsom e-mail). Slet den straks efter du er færdig med at bruge den." }, "encExportKeyWarningDesc": { - "message": "Denne eksport krypterer dine data vha. din kontos krypteringsnøgle. Roterer du på et tidspunkt denne kontokrypteringsnøgle, skal du eksportere igen, da du ikke vil kunne dekryptere denne eksportfil." + "message": "Denne eksport krypterer dine data vha. din kontos krypteringsnøgle. Roteres kontokrypteringsnøglen på et tidspunkt, skal der eksportes igen, da du ikke vil kunne dekryptere denne eksportfil." }, "encExportAccountWarningDesc": { - "message": "Kontokrypteringsnøgler er unikke for hver Bitwarden-brugerkonto, så du kan ikke importere en krypteret eksport til en anden konto." + "message": "Kontokrypteringsnøgler er unikke for hver Bitwarden-brugerkonto, så en krypteret eksport kan ikke importeres til en anden konto." }, "noOrganizationsList": { - "message": "Du tilhører ikke nogen organisationer. Organisationer giver dig mulighed for at dele elementer med andre brugere på en sikker måde." + "message": "Du tilhører ikke nogen organisationer. Organisationer muliggør deling af emner med andre brugere på sikker vis." }, "noCollectionsInList": { "message": "Der er ingen samlinger at vise." @@ -1340,7 +1340,7 @@ "message": "Ejerskab" }, "whoOwnsThisItem": { - "message": "Hvem ejer dette element?" + "message": "Hvem ejer dette emne?" }, "strong": { "message": "Stærk", @@ -1358,35 +1358,35 @@ "message": "Svag hovedadgangskode" }, "weakMasterPasswordDesc": { - "message": "Hovedadgangskoden du har valgt er svag. Du skal bruge en stærk hovedadgangskode (eller en adgangssætning) for at beskytte din Bitwarden-konto korrekt. Er du sikker på, at du vil bruge denne hovedadgangskode?" + "message": "Den valgte hovedadgangskode er svag. Der skal bruges en stærk hovedadgangskode (eller adgangssætning) for at beskytte din Bitwarden-konto korrekt. Sikker på, at du vil bruge denne hovedadgangskode?" }, "pin": { - "message": "Pinkode", + "message": "PIN-kode", "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." }, "unlockWithPin": { - "message": "Lås op med pinkode" + "message": "Oplås med PIN-kode" }, "setYourPinCode": { - "message": "Indstil din pinkode til at låse Bitwarden op. Dine pin-indstillinger nulstilles, hvis du nogensinde logger helt ud af programmet." + "message": "Opsæt PIN-kode til oplåsning af Bitwarden. PIN-indstillinger nulstilles, hvis du nogensinde logger helt ud af applikationen." }, "pinRequired": { - "message": "Pinkode er påkrævet." + "message": "PIN-kode kræves." }, "invalidPin": { - "message": "Ugyldig pinkode." + "message": "Ugyldig PIN-kode." }, "unlockWithWindowsHello": { - "message": "Lås op med Windows Hello" + "message": "Oplås med Windows Hello" }, "windowsHelloConsentMessage": { "message": "Bekræft for Bitwarden." }, "unlockWithTouchId": { - "message": "Lås op med Touch ID" + "message": "Oplås med Touch ID" }, "touchIdConsentMessage": { - "message": "lås din boks op" + "message": "oplås din boks" }, "autoPromptWindowsHello": { "message": "Bed om Windows Hello ved start" @@ -1404,7 +1404,7 @@ "message": "Fortsæt nedenfor for at slette din konto og alle boks-data." }, "deleteAccountWarning": { - "message": "Sletning af din konto er permanent. Det kan ikke fortrydes." + "message": "Sletning af din konto er permanent og irreversibel." }, "accountDeleted": { "message": "Konto slettet" @@ -1413,41 +1413,41 @@ "message": "Din konto er blevet lukket, og alle tilknyttede data er blevet slettet." }, "preferences": { - "message": "Indstillinger" + "message": "Præferencer" }, "enableMenuBar": { - "message": "Aktivér menulinjeikon" + "message": "Vis menubjælkeikon" }, "enableMenuBarDesc": { - "message": "Vis altid et ikon på menulinjen." + "message": "Vis altid et ikon på menubjælken." }, "hideToMenuBar": { - "message": "Skjul på menulinjen" + "message": "Skjul til menubjælke" }, "selectOneCollection": { - "message": "Du skal vælge minimum én samling." + "message": "Minimum én samling skal vælges." }, "premiumUpdated": { - "message": "Du har opgraderet til premium." + "message": "Du har opgraderet til Premium." }, "restore": { "message": "Gendan" }, "premiumManageAlertAppStore": { - "message": "Du kan administrere dit abonnement fra app butikken. Vil du besøge app butikken nu?" + "message": "Du kan håndtere abonnementet fra App Store. Besøge App Store nu?" }, "legal": { "message": "Juridisk", "description": "Noun. As in 'legal documents', like our terms of service and privacy policy." }, "termsOfService": { - "message": "Servicevilkår" + "message": "Tjenestevilkår" }, "privacyPolicy": { "message": "Fortrolighedspolitik" }, "unsavedChangesConfirmation": { - "message": "Er du sikker på, at du vil lukke? Hvis du lukker nu, gemmes dine nuværende oplysninger ikke." + "message": "Sikker på, at du vil lukke? Lukkes nu, gemmes dine nuværende oplysninger ikke." }, "unsavedChangesTitle": { "message": "Ikke-gemte ændringer" @@ -1479,34 +1479,34 @@ "message": "Søg i papirkurven" }, "permanentlyDeleteItem": { - "message": "Slet element permanent" + "message": "Slet emne permanent" }, "permanentlyDeleteItemConfirmation": { - "message": "Er du sikker på, at du vil slette dette element permanent?" + "message": "Sikker på, at du vil slette dette emne permanent?" }, "permanentlyDeletedItem": { - "message": "Element slettet permanent" + "message": "Emne slettet permanent" }, "restoreItem": { - "message": "Gendan element" + "message": "Gendan emne" }, "restoreItemConfirmation": { - "message": "Er du sikker på, at du vil gendanne dette element?" + "message": "Sikker på, at du vil gendanne dette emne?" }, "restoredItem": { - "message": "Element gendannet" + "message": "Emne gendannet" }, "permanentlyDelete": { "message": "Slette permanent" }, "vaultTimeoutLogOutConfirmation": { - "message": "Ved at logge ud fjernes al adgang til din boks og kræver online-godkendelse efter timeout-perioden. Er du sikker på, at du vil bruge denne indstilling?" + "message": "Udlogning fjerner al adgang til din boks og vil kræve online-godkendelse efter timeout-perioden. Sikker på, at du vil bruge denne indstilling?" }, "vaultTimeoutLogOutConfirmationTitle": { "message": "Bekræft timeout-handling" }, "enterpriseSingleSignOn": { - "message": "Virksomheds Single Sign On" + "message": "Virksomheds Single Sign-On" }, "setMasterPassword": { "message": "Indstil hovedadgangskode" @@ -1542,16 +1542,16 @@ } }, "policyInEffectUppercase": { - "message": "Indeholder ét eller flere store bogstaver" + "message": "Indeholder én eller flere majuskler" }, "policyInEffectLowercase": { - "message": "Indeholder ét eller flere små bogstaver" + "message": "Indeholder én eller flere minuskler" }, "policyInEffectNumbers": { "message": "Indeholder ét eller flere cifre" }, "policyInEffectSpecial": { - "message": "Indeholder ét eller flere af følgende specialtegn $CHARS$", + "message": "Indeholder ét eller flere af flg. specialtegn $CHARS$", "placeholders": { "chars": { "content": "$1", @@ -1563,10 +1563,10 @@ "message": "Din nye hovedadgangskode opfylder ikke politikkravene." }, "acceptPolicies": { - "message": "Ved at markere dette felt accepterer du følgende:" + "message": "Ved at afkrydse dette felt accepterer du flg.:" }, "acceptPoliciesRequired": { - "message": "Tjenestevilkår og fortrolighedspolitik er ikke blevet accepteret." + "message": "Tjenestevilkår og Fortrolighedspolitik er ikke blevet accepteret." }, "enableBrowserIntegration": { "message": "Tillad browserintegration" @@ -1575,37 +1575,37 @@ "message": "Bruges til biometri i browser." }, "enableDuckDuckGoBrowserIntegration": { - "message": "Tillad DuckDuckGo browserintegration" + "message": "Tillad DuckDuckGo-browserintegration" }, "enableDuckDuckGoBrowserIntegrationDesc": { - "message": "Brug din Bitwarden-boks, når du browser med DuckDuckGo." + "message": "Brug din Bitwarden-boks, når der surfes med DuckDuckGo." }, "browserIntegrationUnsupportedTitle": { "message": "Browserintegration understøttes ikke" }, "browserIntegrationMasOnlyDesc": { - "message": "Desværre understøttes browserintegration kun i Mac App Store-versionen indtil videre." + "message": "Desværre understøttes browserintegration indtil videre kun i Mac App Store-versionen." }, "browserIntegrationWindowsStoreDesc": { - "message": "Desværre understøttes browserintegration i øjeblikket ikke i Windows Store-versionen." + "message": "Desværre understøttes browserintegration pt. ikke i Microsoft Store-versionen." }, "browserIntegrationLinuxDesc": { - "message": "Desværre understøttes browserintegration i øjeblikket ikke i linux-versionen." + "message": "Desværre understøttes browserintegration pt. ikke i Linux-versionen." }, "enableBrowserIntegrationFingerprint": { - "message": "Kræv verifikation for browserintegration" + "message": "Kræv bekræftelse for browserintegration" }, "enableBrowserIntegrationFingerprintDesc": { - "message": "Tilføj et ekstra sikkerhedslag ved at kræve bekræftelse af fingeraftrykssætning, når du opretter forbindelse mellem dit skrivebord og din browser. Dette kræver brugerhandling og verifikation, hver gang en forbindelse oprettes." + "message": "Tilføj et ekstra sikkerhedslag ved at kræve bekræftelse af fingeraftrykssætning, når der oprettes forbindelse mellem din computer og din browser. Dette kræver brugerhandling og bekræftelse, hver gang der forbindelse oprettes." }, "approve": { "message": "Godkend" }, "verifyBrowserTitle": { - "message": "Verificér browserforbindelse" + "message": "Bekræft browserforbindelse" }, "verifyBrowserDesc": { - "message": "Sørg for, at det viste fingeraftryk er identisk med det fingeraftryk, der vises i browserudvidelsen." + "message": "Sørg for, at det viste fingeraftryk er identisk med det i browserudvidelsen viste fingeraftryk." }, "verifyNativeMessagingConnectionTitle": { "message": "$APPID$ ønsker at oprette forbindelse til Bitwarden", @@ -1617,22 +1617,22 @@ } }, "verifyNativeMessagingConnectionDesc": { - "message": "Vil du godkende denne anmodning?" + "message": "Godkend denne anmodning?" }, "verifyNativeMessagingConnectionWarning": { - "message": "Hvis du ikke indledte denne anmodning, skal du ikke godkende den." + "message": "Har du ikke indledt denne anmodning, så godkend den ikke." }, "biometricsNotEnabledTitle": { - "message": "Biometri ikke aktiveret" + "message": "Biometri ikke opsat" }, "biometricsNotEnabledDesc": { - "message": "Browserbiometri kræver, at desktop-biometri er aktiveret i indstillingerne først." + "message": "Browserbiometri kræver, at computerbiometri er opsat i indstillingerne først." }, "personalOwnershipSubmitError": { - "message": "På grund af en virksomhedspolitik er du begrænset til at gemme elementer i din personlige boks. Skift ejerskabsindstillingen til en organisation, og vælg blandt de tilgængelige samlinger." + "message": "Grundet en virksomhedspolitik forhindres du i at gemme emner i din personlige boks. Skift ejerskabsindstillingen til en organisation, og vælg blandt de tilgængelige samlinger." }, "hintEqualsPassword": { - "message": "Dit adgangskodetip kan ikke være det samme som din adgangskode." + "message": "Adgangskodetip og adgangskoden må ikke være identiske." }, "personalOwnershipPolicyInEffect": { "message": "En organisationspolitik påvirker dine ejerskabsmuligheder." @@ -1684,7 +1684,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "currentAccessCount": { - "message": "Aktuelt antal tilgange" + "message": "Aktuelt tilgangsantal" }, "disableSend": { "message": "Deaktivér denne Send, så ingen kan tilgå den.", @@ -1699,11 +1699,11 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { - "message": "Send link", + "message": "Send-link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinkLabel": { - "message": "Send link", + "message": "Send-link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "textHiddenByDefault": { @@ -1711,11 +1711,11 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSend": { - "message": "Send oprettet", + "message": "Send tilføjet", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editedSend": { - "message": "Send opdateret", + "message": "Send gemt", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deletedSend": { @@ -1723,14 +1723,14 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "newPassword": { - "message": "Nyt kodeord" + "message": "Ny adgangskode" }, "whatTypeOfSend": { "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." }, "createSend": { - "message": "Opret Send", + "message": "Ny Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTextDesc": { @@ -1755,22 +1755,22 @@ "message": "Tilpasset" }, "deleteSendConfirmation": { - "message": "Er du sikker på, at du vil slette denne Send?", + "message": "Sikker på, at du vil slette denne Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLinkToClipboard": { - "message": "Kopiér Send link til udklipsholder", + "message": "Kopiér Send-link til udklipsholder", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLinkOnSave": { - "message": "Kopier linket for at dele denne Send til min udklipsholder ved gem." + "message": "Kopiér linket for at dele denne Send til udklipsholderen ved gem." }, "sendDisabled": { - "message": "Send deaktiveret", + "message": "Send fjernet", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { - "message": "Du kan grundet en virksomhedspolitik kun slette en eksisterende Send.", + "message": "Grundet en virksomhedspolitik kan kun en eksisterende Send slettes.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copyLink": { @@ -1779,6 +1779,15 @@ "disabled": { "message": "Deaktiveret" }, + "removePassword": { + "message": "Fjern adgangskode" + }, + "removedPassword": { + "message": "Adgangskode fjernet" + }, + "removePasswordConfirmation": { + "message": "Sikker på, at adgangskoden skal fjernes?" + }, "maxAccessCountReached": { "message": "Maksimalt adgangsantal nået" }, @@ -1810,7 +1819,7 @@ "message": "Bekræftelse af hovedadgangskode" }, "passwordConfirmationDesc": { - "message": "Denne handling er beskyttet. For at fortsætte, indtast venligst din hovedadgangskode igen for at bekræfte din identitet." + "message": "Denne handling er beskyttet. For at fortsætte, så bekræft din identitet ved at angive din hovedadgangskode igen." }, "updatedMasterPassword": { "message": "Hovedadgangskode opdateret" @@ -1819,7 +1828,7 @@ "message": "Opdatér hovedadgangskode" }, "updateMasterPasswordWarning": { - "message": "Dit hovedadgangskode blev for nylig ændret af en administrator i din organisation. For at få adgang til boksen skal du opdatere din hovedadgangskode nu. Hvis du fortsætter, logges du ud af din nuværende session, hvilket kræver, at du logger ind igen. Aktive sessioner på andre enheder kan fortsætte med at være aktive i op til én time." + "message": "Din hovedadgangskode blev for nylig ændret af en organisations-admin. For at tilgå boksen, så opdatér din hovedadgangskode nu. Fortsættes, logges du ud af den nuværende session og vil skulle logger ind igen. Aktive sessioner på andre enheder kan forblive aktive i op til én time." }, "hours": { "message": "Timer" @@ -1828,7 +1837,7 @@ "message": "Minutter" }, "vaultTimeoutPolicyInEffect": { - "message": "Din organisations politikker påvirker din boks-timeout. Maksimalt tilladt boks-timeout er $HOURS$ time(r) og $MINUTES$ minut(ter)", + "message": "Organisationspolitikker påvirker din boks-timeout. Maksimalt tilladt boks-timeout er $HOURS$ time(r) og $MINUTES$ minut(ter)", "placeholders": { "hours": { "content": "$1", @@ -1841,7 +1850,7 @@ } }, "vaultTimeoutTooLarge": { - "message": "Din boks-timeout overskrider de begrænsninger, der er fastsat af din organisation." + "message": "Din boks-timeout overskrider de organisationsbestemte restriktioner." }, "resetPasswordPolicyAutoEnroll": { "message": "Auto-indrullering" @@ -1850,7 +1859,7 @@ "message": "Denne organisation har en virksomhedspolitik, der automatisk tilmelder dig til nulstilling af adgangskode. Tilmelding giver organisationsadministratorer mulighed for at skifte din hovedadgangskode." }, "vaultExportDisabled": { - "message": "Bokseksport deaktiveret" + "message": "Bokseksport fjernet" }, "personalVaultExportPolicyInEffect": { "message": "En eller flere organisationspolitikker forhindrer dig i at eksportere din personlige boks." @@ -1865,7 +1874,7 @@ "message": "Hovedadgangskode fjernet." }, "convertOrganizationEncryptionDesc": { - "message": "$ORGANIZATION$ bruger SSO med en selv-hostet nøgleserver. En hovedadgangskode er ikke længere påkrævet for at logge ind for medlemmer af denne organisation.", + "message": "$ORGANIZATION$ bruger SSO med en selv-hostet nøgleserver. Organisationsmedlemmer behøver ikke længere hovedadgangskode for at logge ind.", "placeholders": { "organization": { "content": "$1", @@ -1877,7 +1886,7 @@ "message": "Forlad organisation" }, "leaveOrganizationConfirmation": { - "message": "Er du sikker på, at du vil forlade denne organisation?" + "message": "Sikker på, at du vil forlade denne organisation?" }, "leftOrganization": { "message": "Du har forladt organisationen." @@ -1889,19 +1898,19 @@ "message": "Lås alle bokse" }, "accountLimitReached": { - "message": "Der må ikke være logget på mere end 5 konti på samme tid." + "message": "Maksimalt 5 konti kan være logget ind samtidigt." }, "accountPreferences": { - "message": "Indstillinger" + "message": "Præferencer" }, "appPreferences": { - "message": "Appindstillinger (alle konti)" + "message": "App-indstillinger (alle konti)" }, "accountSwitcherLimitReached": { - "message": "Kontogrænsen er nået. Log ud af en konto for at tilføje en anden." + "message": "Kontokvote nået. Log ud af en konto for at tilføje en anden." }, "settingsTitle": { - "message": "Appindstillinger for $EMAIL$", + "message": "App-indstillinger for $EMAIL$", "placeholders": { "email": { "content": "$1", @@ -1916,13 +1925,13 @@ "message": "Indstillinger" }, "sessionTimeout": { - "message": "Din session er udløbet. Gå tilbage og prøv at logge ind igen." + "message": "Session udløbet. Gå tilbage og prøv at logge ind igen." }, "exportingPersonalVaultTitle": { - "message": "Eksporterer personlig boks" + "message": "Eksporterer individuel boks" }, "exportingPersonalVaultDescription": { - "message": "Kun de personlige bokselementer tilknyttet $EMAIL$ eksporteres. Organisationsbokseelementer medtages ikke.", + "message": "Kun de individuelle boksemner tilknyttet $EMAIL$ eksporteres. Organisationsboksemner medtages ikke.", "placeholders": { "email": { "content": "$1", @@ -1934,13 +1943,13 @@ "message": "Låst" }, "unlocked": { - "message": "Låst op" + "message": "Oplåst" }, "generator": { "message": "Generator" }, "whatWouldYouLikeToGenerate": { - "message": "Hvad vil du gerne generere?" + "message": "Hvad ønskes genereret?" }, "passwordType": { "message": "Adgangskodetype" @@ -1965,7 +1974,7 @@ "message": "Fang-alle e-mail" }, "catchallEmailDesc": { - "message": "Brug dit domænes konfigurerede fang-alle-indbakke." + "message": "Brug domænets opsatte fang-alle indbakke." }, "random": { "message": "Tilfældig" @@ -1974,7 +1983,7 @@ "message": "Tilfældigt ord" }, "websiteName": { - "message": "Hjemmeside navn" + "message": "Webstedsnavn" }, "service": { "message": "Tjeneste" @@ -2008,13 +2017,13 @@ "message": "Premium-abonnement kræves" }, "organizationIsDisabled": { - "message": "Organisationen er deaktiveret." + "message": "Organisationen suspenderet" }, "disabledOrganizationFilterError": { - "message": "Elementer i deaktiverede organisationer kan ikke tilgås. Kontakt din organisationsejer for at få hjælp." + "message": "Emner i suspenderede organisationer kan ikke tilgås. Kontakt organisationsejeren for hjælp." }, "neverLockWarning": { - "message": "Er du sikker på, at du vil bruge indstillingen \"Aldrig\"? Hvis du sætter dine låseindstillinger til \"Aldrig\" gemmes din boks krypteringsnøgle på din enhed. Hvis du bruger denne indstilling, skal du sikre dig, at du holder din enhed ordentligt beskyttet." + "message": "Sikker på, at indstillingen \"Aldrig\" skal bruges? Sættes låseindstillinger til \"Aldrig\", gemmes din bokskrypteringsnøgle på enheden. Bruges denne indstilling, så sørg for at holde din enhed ordentligt beskyttet." }, "cardBrandMir": { "message": "Mir" @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log ind med en anden enhed" + }, + "toggleCharacterCount": { + "message": "Vis/skjul tegnantal", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index 3b8cee65475..7d3c2e26ca3 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -611,7 +611,7 @@ "message": "E-Mail mit Bestätigungscode erneut versenden" }, "useAnotherTwoStepMethod": { - "message": "Eine andere Zwei-Faktor-Login-Methode verwenden" + "message": "Eine andere Zwei-Faktor-Anmeldemethode verwenden" }, "insertYubiKey": { "message": "Stecke deinen YubiKey in einen USB-Anschluss deines Computers, dann berühre den Button." @@ -805,7 +805,7 @@ "message": "Synchronisierung fehlgeschlagen" }, "yourVaultIsLocked": { - "message": "Ihr Tresor ist gesperrt. Überprüfen Sie Ihr Master-Passwort, um fortzufahren." + "message": "Dein Tresor ist gesperrt. Überprüfe deine Identität, um fortzufahren." }, "unlock": { "message": "Entsperren" @@ -1033,7 +1033,7 @@ "message": "Unbekannt" }, "copyUsername": { - "message": "Nutzernamen kopieren" + "message": "Benutzerrname kopieren" }, "copyNumber": { "message": "Nummer kopieren", @@ -1779,6 +1779,15 @@ "disabled": { "message": "Deaktiviert" }, + "removePassword": { + "message": "Passwort entfernen" + }, + "removedPassword": { + "message": "Passwort entfernt" + }, + "removePasswordConfirmation": { + "message": "Bist du sicher, dass du das Passwort entfernen möchtest?" + }, "maxAccessCountReached": { "message": "Maximale Zugriffsanzahl erreicht" }, @@ -1940,7 +1949,7 @@ "message": "Generator" }, "whatWouldYouLikeToGenerate": { - "message": "Was möchtest du generieren?" + "message": "Was möchtest du erzeugen?" }, "passwordType": { "message": "Passworttyp" @@ -1949,7 +1958,7 @@ "message": "Benutzername neu generieren" }, "generateUsername": { - "message": "Benutzernamen generieren" + "message": "Benutzernamen erzeugen" }, "usernameType": { "message": "Benutzernamentyp" @@ -1962,7 +1971,7 @@ "message": "Verwende die Unteradressierungsmöglichkeiten deines E-Mail-Providers." }, "catchallEmail": { - "message": "Catch-All E-Mail-Adresse" + "message": "Catch-All E-Mail Adresse" }, "catchallEmailDesc": { "message": "Verwende den konfigurierten Catch-All-Posteingang deiner Domain." @@ -1992,7 +2001,7 @@ "message": "Weitergeleitetes E-Mail-Alias" }, "forwardedEmailDesc": { - "message": "Generiere ein E-Mail-Alias mit einem externen Weiterleitungsdienst." + "message": "Erzeuge ein E-Mail-Alias mit einem externen Weiterleitungsdienst." }, "hostname": { "message": "Hostname", @@ -2008,7 +2017,7 @@ "message": "Premium-Abonnement erforderlich" }, "organizationIsDisabled": { - "message": "Organisation ist deaktiviert." + "message": "Organisation ist deaktiviert" }, "disabledOrganizationFilterError": { "message": "Auf Einträge in deaktivierten Organisationen kann nicht zugegriffen werden. Kontaktiere deinen Organisationseigentümer für Unterstützung." @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Mit einem anderen Gerät anmelden" + }, + "toggleCharacterCount": { + "message": "Zeichenzählung ein-/ausschalten", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index 449f9871685..58fb18bbe9e 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Απενεργοποιημένο" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Φτάσατε στον μέγιστο αριθμό πρόσβασης" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Συνδεθείτε με άλλη συσκευή" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index fd168510b38..2398c77b005 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -238,6 +238,9 @@ "ms": { "message": "Ms" }, + "mx": { + "message": "Mx" + }, "dr": { "message": "Dr" }, @@ -1779,6 +1782,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2060,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index 12a4f27de17..cb2d96daf81 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -45,7 +45,7 @@ "message": "Share" }, "moveToOrganization": { - "message": "Move to Organisation" + "message": "Move to organisation" }, "movedItemToOrg": { "message": "$ITEMNAME$ moved to $ORGNAME$", @@ -178,7 +178,7 @@ "message": "Premium required" }, "premiumRequiredDesc": { - "message": "A premium membership is required to use this feature." + "message": "A Premium membership is required to use this feature." }, "errorOccurred": { "message": "An error has occurred." @@ -269,7 +269,7 @@ "message": "Last name" }, "fullName": { - "message": "Full Name" + "message": "Full name" }, "address1": { "message": "Address 1" @@ -346,10 +346,10 @@ "message": "Name is required." }, "addedItem": { - "message": "Added item" + "message": "Item added" }, "editedItem": { - "message": "Edited item" + "message": "Item saved" }, "deleteItem": { "message": "Delete item" @@ -370,7 +370,7 @@ "message": "Are you sure you want to overwrite the current password?" }, "overwriteUsername": { - "message": "Overwrite Username" + "message": "Overwrite username" }, "overwriteUsernameConfirmation": { "message": "Are you sure you want to overwrite the current username?" @@ -395,7 +395,7 @@ "message": "Copy URI" }, "copyVerificationCodeTotp": { - "message": "Copy Verification Code (TOTP)" + "message": "Copy verification code (TOTP)" }, "length": { "message": "Length" @@ -410,7 +410,7 @@ "message": "Numbers (0-9)" }, "specialCharacters": { - "message": "Special Characters (!@#$%^&*)" + "message": "Special characters (!@#$%^&*)" }, "numWords": { "message": "Number of words" @@ -455,19 +455,19 @@ "message": "Add new attachment" }, "deletedAttachment": { - "message": "Deleted attachment" + "message": "Attachment deleted" }, "deleteAttachmentConfirmation": { "message": "Are you sure you want to delete this attachment?" }, "attachmentSaved": { - "message": "The attachment has been saved." + "message": "Attachment saved" }, "file": { "message": "File" }, "selectFile": { - "message": "Select a file." + "message": "Select a file" }, "maxFileSize": { "message": "Maximum file size is 500 MB." @@ -476,16 +476,16 @@ "message": "You cannot use this feature until you update your encryption key." }, "editedFolder": { - "message": "Edited folder" + "message": "Folder saved" }, "addedFolder": { - "message": "Added folder" + "message": "Folder added" }, "deleteFolderConfirmation": { "message": "Are you sure you want to delete this folder?" }, "deletedFolder": { - "message": "Deleted folder" + "message": "Folder deleted" }, "loginOrCreateNewAccount": { "message": "Log in or create a new account to access your secure vault." @@ -563,10 +563,10 @@ "message": "Send a verification code to your email" }, "sendCode": { - "message": "Send Code" + "message": "Send code" }, "codeSent": { - "message": "Code Sent" + "message": "Code sent" }, "verificationCode": { "message": "Verification code" @@ -620,7 +620,7 @@ "message": "Insert your security key into your computer's USB port. If it has a button, touch it." }, "recoveryCodeDesc": { - "message": "Lost access to all of your two-factor providers? Use your recovery code to disable all two-factor providers from your account." + "message": "Lost access to all of your two-factor providers? Use your recovery code to turn off all two-factor providers on your account." }, "recoveryCodeTitle": { "message": "Recovery code" @@ -650,7 +650,7 @@ "message": "FIDO2 WebAuthn" }, "webAuthnDesc": { - "message": "Use any WebAuthn enabled security key to access your account." + "message": "Use any WebAuthn compatible security key to access your account." }, "emailTitle": { "message": "Email" @@ -662,7 +662,7 @@ "message": "Login unavailable" }, "noTwoStepProviders": { - "message": "This account has two-step login enabled. However, none of the configured two-step providers are supported by this device." + "message": "This account has two-step login set up, however, none of the configured two-step providers are supported by this device." }, "noTwoStepProviders2": { "message": "Please add additional providers that are better supported across devices (such as an authenticator app)." @@ -701,7 +701,7 @@ "message": "Icons server URL" }, "environmentSaved": { - "message": "The environment URLs have been saved." + "message": "Environment URLs saved" }, "ok": { "message": "OK" @@ -734,13 +734,13 @@ "message": "Log out" }, "addNewLogin": { - "message": "Add new login" + "message": "New login" }, "addNewItem": { - "message": "Add new item" + "message": "New item" }, "addNewFolder": { - "message": "Add new folder" + "message": "New folder" }, "view": { "message": "View" @@ -752,16 +752,16 @@ "message": "Loading..." }, "lockVault": { - "message": "Lock Vault" + "message": "Lock vault" }, "passwordGenerator": { "message": "Password generator" }, "contactUs": { - "message": "Contact Us" + "message": "Contact us" }, "getHelp": { - "message": "Get Help" + "message": "Get help" }, "fileBugReport": { "message": "File a bug report" @@ -790,7 +790,7 @@ "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." }, "goToWebVault": { - "message": "Go to Web Vault" + "message": "Go to web vault" }, "getMobileApp": { "message": "Get mobile app" @@ -827,7 +827,7 @@ "message": "Invalid master password" }, "twoStepLoginConfirmation": { - "message": "Two-step login makes your account more secure by requiring you to verify your login with another device such as a security key, authenticator app, SMS, phone call, or email. Two-step login can be enabled on the bitwarden.com web vault. Do you want to visit the website now?" + "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?" }, "twoStepLogin": { "message": "Two-step login" @@ -928,7 +928,7 @@ "message": "When closing the window, show an icon in the menu bar instead." }, "enableTray": { - "message": "Enable tray icon" + "message": "Show tray icon" }, "enableTrayDesc": { "message": "Always show an icon in the system tray." @@ -958,10 +958,10 @@ "message": "Show the Bitwarden icon in the Dock even when minimised to the menu bar." }, "confirmTrayTitle": { - "message": "Confirm disable tray" + "message": "Confirm hiding tray" }, "confirmTrayDesc": { - "message": "Disabling this setting will also disable all other tray related settings." + "message": "Turning off this setting will also turn off all other tray related settings." }, "language": { "message": "Language" @@ -988,7 +988,7 @@ "description": "Copy to clipboard" }, "checkForUpdates": { - "message": "Check for Updates…" + "message": "Check for updates…" }, "version": { "message": "Version $VERSION_NUM$", @@ -1000,7 +1000,7 @@ } }, "restartToUpdate": { - "message": "Restart to Update" + "message": "Restart to update" }, "restartToUpdateDesc": { "message": "Version $VERSION_NUM$ is ready to install. You must restart the application to complete the installation. Do you want to restart and update now?", @@ -1056,10 +1056,10 @@ "message": "Refresh membership" }, "premiumNotCurrentMember": { - "message": "You are not currently a premium member." + "message": "You are not currently a Premium member." }, "premiumSignUpAndGet": { - "message": "Sign up for a premium membership and get:" + "message": "Sign up for a Premium membership and get:" }, "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." @@ -1213,7 +1213,7 @@ "description": "Domain name. Ex. website.com" }, "domainName": { - "message": "Domain Name", + "message": "Domain name", "description": "Domain name. Ex. website.com" }, "host": { @@ -1285,7 +1285,7 @@ "description": "hCaptcha is the name of a website, should not be translated" }, "loadAccessibilityCookie": { - "message": "Load Accessibility Cookie" + "message": "Load accessibility cookie" }, "registerAccessibilityUser": { "message": "Register as an accessibility user at", @@ -1319,7 +1319,7 @@ "description": "WARNING (should stay in capitalized letters if the language permits)" }, "confirmVaultExport": { - "message": "Confirm Vault Export" + "message": "Confirm vault export" }, "exportWarningDesc": { "message": "This export contains your vault data in an unencrypted format. You should not store or send the exported file over insecure channels (such as email). Delete it immediately after you are done using it." @@ -1428,7 +1428,7 @@ "message": "You must select at least one collection." }, "premiumUpdated": { - "message": "You've upgraded to premium." + "message": "You've upgraded to Premium." }, "restore": { "message": "Restore" @@ -1476,7 +1476,7 @@ "description": "Noun: a special folder to hold deleted items" }, "searchTrash": { - "message": "Search Bin" + "message": "Search bin" }, "permanentlyDeleteItem": { "message": "Permanently delete item" @@ -1485,7 +1485,7 @@ "message": "Are you sure you want to permanently delete this item?" }, "permanentlyDeletedItem": { - "message": "Permanently deleted item" + "message": "Item permanently deleted" }, "restoreItem": { "message": "Restore item" @@ -1494,7 +1494,7 @@ "message": "Are you sure you want to restore this item?" }, "restoredItem": { - "message": "Restored item" + "message": "Item restored" }, "permanentlyDelete": { "message": "Permanently delete" @@ -1626,10 +1626,10 @@ "message": "Biometrics not enabled" }, "biometricsNotEnabledDesc": { - "message": "Browser biometrics requires desktop biometrics to be enabled in the settings first." + "message": "Browser biometrics requires desktop biometrics to be set up in the settings first." }, "personalOwnershipSubmitError": { - "message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organisation and choose from available Collections." + "message": "Due to an enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organisation and choose from available collections." }, "hintEqualsPassword": { "message": "Your password hint cannot be the same as your password." @@ -1656,27 +1656,27 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "myVault": { - "message": "My Vault" + "message": "My vault" }, "text": { "message": "Text" }, "deletionDate": { - "message": "Deletion Date" + "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." }, "expirationDate": { - "message": "Expiration Date" + "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." }, "maxAccessCount": { - "message": "Maximum Access Count", + "message": "Maximum access count", "description": "This text will be displayed after a Send has been accessed the maximum amount of times." }, "maxAccessCountDesc": { @@ -1684,10 +1684,10 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "currentAccessCount": { - "message": "Current Access Count" + "message": "Current access count" }, "disableSend": { - "message": "Disable this Send so that no one can access it.", + "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." }, "sendPasswordDesc": { @@ -1703,7 +1703,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinkLabel": { - "message": "Send Link", + "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "textHiddenByDefault": { @@ -1711,26 +1711,26 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSend": { - "message": "Created Send", + "message": "Send added", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editedSend": { - "message": "Edited Send", + "message": "Send saved", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deletedSend": { - "message": "Deleted Send", + "message": "Send deleted", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "newPassword": { - "message": "New Password" + "message": "New password" }, "whatTypeOfSend": { "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." }, "createSend": { - "message": "Create Send", + "message": "New Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTextDesc": { @@ -1766,7 +1766,7 @@ "message": "Copy the link to share this Send to my clipboard upon save." }, "sendDisabled": { - "message": "Send disabled", + "message": "Send removed", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -1798,7 +1807,7 @@ "message": "One or more organisation policies are affecting your Send options." }, "emailVerificationRequired": { - "message": "Email Verification Required" + "message": "Email verification required" }, "emailVerificationRequiredDesc": { "message": "You must verify your email to use this feature." @@ -1813,13 +1822,13 @@ "message": "This action is protected. To continue, please re-enter your master password to verify your identity." }, "updatedMasterPassword": { - "message": "Updated Master Password" + "message": "Updated master password" }, "updateMasterPassword": { - "message": "Update Master Password" + "message": "Update master password" }, "updateMasterPasswordWarning": { - "message": "Your Master Password was recently changed by an administrator in your organisation. 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": "Your master password was recently changed by an administrator in your organisation. 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." }, "hours": { "message": "Hours" @@ -1828,7 +1837,7 @@ "message": "Minutes" }, "vaultTimeoutPolicyInEffect": { - "message": "Your organisation policies are affecting your vault timeout. Maximum allowed Vault Timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)", + "message": "Your organisation policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)", "placeholders": { "hours": { "content": "$1", @@ -1844,25 +1853,25 @@ "message": "Your vault timeout exceeds the restrictions set by your organisation." }, "resetPasswordPolicyAutoEnroll": { - "message": "Automatic Enrollment" + "message": "Automatic enrollment" }, "resetPasswordAutoEnrollInviteWarning": { "message": "This organisation has an enterprise policy that will automatically enroll you in password reset. Enrollment will allow organisation administrators to change your master password." }, "vaultExportDisabled": { - "message": "Vault Export Disabled" + "message": "Vault export removed" }, "personalVaultExportPolicyInEffect": { "message": "One or more organisation policies prevents you from exporting your personal vault." }, "addAccount": { - "message": "Add Account" + "message": "Add account" }, "removeMasterPassword": { - "message": "Remove Master Password" + "message": "Remove master password" }, "removedMasterPassword": { - "message": "Master password removed." + "message": "Master password removed" }, "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 organisation.", @@ -1874,7 +1883,7 @@ } }, "leaveOrganization": { - "message": "Leave Organisation" + "message": "Leave organisation" }, "leaveOrganizationConfirmation": { "message": "Are you sure you want to leave this organisation?" @@ -1883,10 +1892,10 @@ "message": "You have left the organisation." }, "ssoKeyConnectorError": { - "message": "Key Connector error: make sure Key Connector is available and working correctly." + "message": "Key connector error: make sure key connector is available and working correctly." }, "lockAllVaults": { - "message": "Lock All Vaults" + "message": "Lock all vaults" }, "accountLimitReached": { "message": "No more than 5 accounts may be logged in at the same time." @@ -1895,7 +1904,7 @@ "message": "Preferences" }, "appPreferences": { - "message": "App Settings (All Accounts)" + "message": "App settings (all accounts)" }, "accountSwitcherLimitReached": { "message": "Account limit reached. Log out of an account to add another." @@ -1910,7 +1919,7 @@ } }, "switchAccount": { - "message": "Switch Account" + "message": "Switch account" }, "options": { "message": "Options" @@ -1919,10 +1928,10 @@ "message": "Your session has timed out. Please go back and try logging in again." }, "exportingPersonalVaultTitle": { - "message": "Exporting Personal Vault" + "message": "Exporting individual vault" }, "exportingPersonalVaultDescription": { - "message": "Only the personal vault items associated with $EMAIL$ will be exported. Organisation vault items will not be included.", + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organisation vault items will not be included.", "placeholders": { "email": { "content": "$1", @@ -1943,26 +1952,26 @@ "message": "What would you like to generate?" }, "passwordType": { - "message": "Password Type" + "message": "Password type" }, "regenerateUsername": { - "message": "Regenerate Username" + "message": "Regenerate username" }, "generateUsername": { - "message": "Generate Username" + "message": "Generate username" }, "usernameType": { - "message": "Username Type" + "message": "Username type" }, "plusAddressedEmail": { - "message": "Plus Addressed Email", + "message": "Plus addressed email", "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." }, "catchallEmail": { - "message": "Catch-all Email" + "message": "Catch-all email" }, "catchallEmailDesc": { "message": "Use your domain's configured catch-all inbox." @@ -1971,25 +1980,25 @@ "message": "Random" }, "randomWord": { - "message": "Random Word" + "message": "Random word" }, "websiteName": { - "message": "Website Name" + "message": "Website name" }, "service": { "message": "Service" }, "allVaults": { - "message": "All Vaults" + "message": "All vaults" }, "searchOrganization": { - "message": "Search Organisation" + "message": "Search organisation" }, "searchMyVault": { - "message": "Search My Vault" + "message": "Search my vault" }, "forwardedEmail": { - "message": "Forwarded Email Alias" + "message": "Forwarded email alias" }, "forwardedEmailDesc": { "message": "Generate an email alias with an external forwarding service." @@ -2002,16 +2011,16 @@ "message": "API Access Token" }, "apiKey": { - "message": "API Key" + "message": "API key" }, "premiumSubcriptionRequired": { "message": "Premium subscription required" }, "organizationIsDisabled": { - "message": "Organisation is disabled." + "message": "Organisation suspended" }, "disabledOrganizationFilterError": { - "message": "Items in disabled Organisations cannot be accessed. Contact your Organisation owner for assistance." + "message": "Items in suspended organisations cannot be accessed. Contact your organisation owner for assistance." }, "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." @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index 20a1fc3930e..acf68d0d6ec 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index 8c665ad12d9..428908952e1 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index fa0ef7a3866..892005ce5d9 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Deshabilitado" }, + "removePassword": { + "message": "Eliminar contraseña" + }, + "removedPassword": { + "message": "Contraseña eliminada" + }, + "removePasswordConfirmation": { + "message": "¿Está seguro que desea eliminar la contraseña?" + }, "maxAccessCountReached": { "message": "Se ha alcanzado el número máximo de accesos" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Iniciar sesión con otro dispositivo" + }, + "toggleCharacterCount": { + "message": "Activar recuento de caracteres", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index 1568209bf9f..48230bcfbc6 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Keelatud" }, + "removePassword": { + "message": "Eemalda parool" + }, + "removedPassword": { + "message": "Parool on eemaldatud" + }, + "removePasswordConfirmation": { + "message": "Oled kindel, et soovid selle parooli eemaldada?" + }, "maxAccessCountReached": { "message": "Maksimaalne ligipääsude arv on saavutatud" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Logi sisse läbi teise seadme" + }, + "toggleCharacterCount": { + "message": "Loenda kirjatähtede hulka", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index 8d6452a5015..5632fb31313 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Desgaitua" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Sarbide kopuru maximoa gaindituta" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Hasi saioa beste gailu batekin" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index bbae26fb906..4d20d8e9f41 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -21,7 +21,7 @@ "message": "کارت" }, "typeIdentity": { - "message": "مشخصات" + "message": "هویت" }, "typeSecureNote": { "message": "یادداشت امن" @@ -42,7 +42,7 @@ "message": "اشتراک گذاری شد" }, "share": { - "message": "اشتراک‌گذاری" + "message": "اشتراک گذاری" }, "moveToOrganization": { "message": "انتقال به سازمان" @@ -61,7 +61,7 @@ } }, "moveToOrgDesc": { - "message": "سازمانی را انتخاب کنید که می خواهید این مورد را به آن منتقل کنید. انتقال به یک سازمان، مالکیت مورد را به آن سازمان منتقل می کند. پس از انتقال این مورد، دیگر مالک مستقیم آن نخواهید بود." + "message": "سازمانی را انتخاب کنید که می‌خواهید این مورد را به آن منتقل کنید. انتقال به یک سازمان، مالکیت مورد را به آن سازمان منتقل می‌کند. پس از انتقال این مورد، دیگر مالک مستقیم آن نخواهید بود." }, "attachments": { "message": "پیوست‌ها" @@ -73,10 +73,10 @@ "message": "نام" }, "uri": { - "message": "URI" + "message": "نشانی اینترنتی" }, "uriPosition": { - "message": "URI $POSITION$", + "message": "نشانی اینترنتی $POSITION$", "description": "A listing of URIs. Ex: URI 1, URI 2, URI 3, etc.", "placeholders": { "position": { @@ -86,7 +86,7 @@ } }, "newUri": { - "message": "URI جدید" + "message": "نشانی اینترنتی جدید" }, "username": { "message": "نام کاربری" @@ -101,13 +101,13 @@ "message": "ویرایش مورد" }, "emailAddress": { - "message": "نشانی رایانامه" + "message": "نشانی ایمیل" }, "verificationCodeTotp": { - "message": "کد تایید (TOTP)" + "message": "کد تأیید (TOTP)" }, "website": { - "message": "وب سایت" + "message": "وب‌سایت" }, "notes": { "message": "یادداشت‌ها" @@ -119,7 +119,7 @@ "message": "راه اندازی" }, "copyValue": { - "message": "رونوشت مقدار", + "message": "کپی مقدار", "description": "Copy value to clipboard" }, "minimizeOnCopyToClipboard": { @@ -129,7 +129,7 @@ "message": "پایین کشیدن پنجره موقع کپی کردن اطلاعات یک مورد در کلیپ بورد." }, "toggleVisibility": { - "message": "نمایش" + "message": "قابلیت مشاهده را تغییر دهید" }, "toggleCollapse": { "message": "باز و بسته کردن", @@ -151,22 +151,22 @@ "message": "کد امنیتی" }, "identityName": { - "message": "نام شناسایی" + "message": "نام هویت" }, "company": { "message": "شرکت" }, "ssn": { - "message": "شماره امنیتی اجتماعی" + "message": "کد ملی" }, "passportNumber": { - "message": "شماره پاسپورت" + "message": "شماره گذرنامه" }, "licenseNumber": { - "message": "شماره مجوز" + "message": "شماره گواهی‌نامه" }, "email": { - "message": "رایانامه" + "message": "ایمیل" }, "phone": { "message": "تلفن" @@ -233,10 +233,10 @@ "message": "آقا" }, "mrs": { - "message": "بانو" + "message": "خانم" }, "ms": { - "message": "خانم" + "message": "بانو" }, "dr": { "message": "دکتر" @@ -308,7 +308,7 @@ "message": "ویرایش" }, "authenticatorKeyTotp": { - "message": "کلید تاییدکننده (TOTP)" + "message": "کلید احراز هویت (TOTP)" }, "folder": { "message": "پوشه" @@ -329,14 +329,14 @@ "message": "مخفی" }, "cfTypeBoolean": { - "message": "بولی" + "message": "منطقی" }, "cfTypeLinked": { - "message": "لینک شده", + "message": "پیوند شده", "description": "This describes a field that is 'linked' (related) to another field." }, "linkedValue": { - "message": "ارزش لینک شده", + "message": "مقدار پیوند شده", "description": "This describes a value that is 'linked' (related) to another value." }, "remove": { @@ -346,10 +346,10 @@ "message": "نام ضروری است." }, "addedItem": { - "message": "مورد افزوده شده" + "message": "مورد اضافه شد" }, "editedItem": { - "message": "مورد ویرایش شده" + "message": "مورد ذخیره شد" }, "deleteItem": { "message": "حذف مورد" @@ -361,10 +361,10 @@ "message": "حذف پیوست" }, "deleteItemConfirmation": { - "message": "آیا مطمئن هستید که می‌خواهید این مورد را حذف کنید؟" + "message": "واقعاً می‌خواهید این آیتم را به سطل زباله ارسال کنید؟" }, "deletedItem": { - "message": "مورد حذف شد" + "message": "مورد به زباله‌ها فرستاده شد" }, "overwritePasswordConfirmation": { "message": "آیا از بازنویسی بر روی پسورد فعلی مطمئن هستید؟" @@ -386,13 +386,13 @@ "message": "ويرايش پوشه" }, "regeneratePassword": { - "message": "تولید دوباره کلمه عبور" + "message": "تولید مجدد کلمه عبور" }, "copyPassword": { - "message": "رونوشت کلمه عبور" + "message": "کپی کلمه عبور" }, "copyUri": { - "message": "رونوشت URI" + "message": "کپی نشانی اینترنتی" }, "copyVerificationCodeTotp": { "message": "کپی کد تأیید (TOTP)" @@ -436,7 +436,7 @@ "description": "Minimum Special Characters" }, "ambiguous": { - "message": "از کاراکترهای مبهم اجتناب شود" + "message": "از کاراکترهای مبهم اجتناب کن" }, "searchCollection": { "message": "جستجوی مجموعه" @@ -461,25 +461,25 @@ "message": "آیا از پاک کردن این پیوست مطمئن هستید؟" }, "attachmentSaved": { - "message": "پیوست ذخیره شد." + "message": "پیوست ذخیره شد" }, "file": { "message": "پرونده" }, "selectFile": { - "message": "ﺍﻧﺘﺨﺎﺏ یک ﭘﺮﻭﻧﺪﻩ." + "message": "ﺍﻧﺘﺨﺎﺏ یک ﭘﺮﻭﻧﺪﻩ" }, "maxFileSize": { - "message": "بیشترین حجم فایل ۱۰۰ مگابایت است." + "message": "بیشترین حجم پرونده ۵۰۰ مگابایت است." }, "updateKey": { - "message": "تا زمانی که کد رمزنگاری را بروز نکنید نمی‌توانید از این قابلیت استفاده کنید." + "message": "تا زمانی که کد رمزنگاری را به‌روز نکنید نمی‌توانید از این قابلیت استفاده کنید." }, "editedFolder": { - "message": "پوشه ویرایش شده" + "message": "پوشه ذخیره شد" }, "addedFolder": { - "message": "پوشه اضافه شده" + "message": "پوشه اضافه شد" }, "deleteFolderConfirmation": { "message": "آیا از حذف این پوشه اطمینان دارید؟" @@ -503,40 +503,40 @@ "message": "کلمه عبور اصلی" }, "masterPassDesc": { - "message": "کلمه عبور اصلی کلمه عبوری است که شما برای دسترسی به گاوصندوق خود استفاده می‌کنید. بیاد داشتن کلمه عبور اصلی بسیار اهمیت دارد. اگر آن را فراموش کنید هیچ راهی برای بازگردانی آن وجود ندارد." + "message": "کلمه عبور اصلی، کلمه عبوری است که شما برای دسترسی به گاوصندوق خود استفاده می‌کنید. به یاد داشتن کلمه عبور اصلی بسیار اهمیت دارد. اگر فراموشش کنید هیچ راهی برای بازگردانی آن وجود ندارد." }, "masterPassHintDesc": { - "message": "راهنمای کلمه عبور اصلی می تواند کمک کند تا در صورت فراموشی آن را بیاد بیاورید." + "message": "یادآور کلمه عبور اصلی کمک می‌کند در صورت فراموشی آن را به یاد بیارید." }, "reTypeMasterPass": { "message": "نوشتن دوباره کلمه عبور اصلی" }, "masterPassHint": { - "message": "راهنمای کلمه عبور اصلی (اختیاری)" + "message": "یادآور کلمه عبور اصلی (اختیاری)" }, "settings": { "message": "تنظیمات" }, "passwordHint": { - "message": "راهنمای کلمه عبور" + "message": "یادآور کلمه عبور" }, "enterEmailToGetHint": { - "message": "برای دریافت راهنمای کلمه عبور اصلی خود آدرس رایانامه‌تان را وارد کنید." + "message": "برای دریافت یادآور کلمه عبور اصلی خود نشانی ایمیل‌تان را وارد کنید." }, "getMasterPasswordHint": { - "message": "دریافت راهنمای کلمه عبور اصلی" + "message": "دریافت یادآور کلمه عبور اصلی" }, "emailRequired": { - "message": "نشانی رایانامه ضروری است." + "message": "نشانی ایمیل ضروری است." }, "invalidEmail": { - "message": "نشانی رایانامه نامعتبر است." + "message": "نشانی ایمیل نامعتبر است." }, "masterPasswordRequired": { - "message": "گذرواژه اصلی ضروری است." + "message": "کلمه عبور اصلی ضروری است." }, "confirmMasterPasswordRequired": { - "message": "تایپ مجدد گذرواژه اصلی نیاز است." + "message": "نوشتن مجدد کلمه عبور اصلی ضروری است." }, "masterPasswordMinlength": { "message": "طول کلمه عبور اصلی باید حداقل ۸ کاراکتر باشد." @@ -545,10 +545,10 @@ "message": "کلمه عبور اصلی با تکرار آن مطابقت ندارد." }, "newAccountCreated": { - "message": "حساب جدید شما ساخته شد! حالا می‌توانید وارد شوید." + "message": "حساب کاربری جدید شما ساخته شد! حالا می‌توانید وارد شوید." }, "masterPassSent": { - "message": "ما یک رایانامه همراه با راهنمای کلمه عبور اصلی برایتان ارسال کردیم." + "message": "ما یک ایمیل همراه با یادآور کلمه عبور اصلی برایتان ارسال کردیم." }, "unexpectedError": { "message": "یک خطای غیر منتظره رخ داده است." @@ -560,7 +560,7 @@ "message": "هیچ موردی برای نمایش وجود ندارد." }, "sendVerificationCode": { - "message": "ارسال یک کد تأیید به ایمیل شما" + "message": "یک کد تأیید به ایمیل خود ارسال کنید" }, "sendCode": { "message": "ارسال کد" @@ -569,25 +569,25 @@ "message": "کد ارسال شد" }, "verificationCode": { - "message": "کد تایید" + "message": "کد تأیید" }, "confirmIdentity": { - "message": "برای ادامه هویت خود را تأیید کنید." + "message": "برای ادامه، هویت خود را تأیید کنید." }, "verificationCodeRequired": { - "message": "کد تایید مورد نیاز است." + "message": "کد تأیید مورد نیاز است." }, "invalidVerificationCode": { - "message": "کد تایید نامعتبر" + "message": "کد تأیید نامعتبر است" }, "continue": { "message": "ادامه" }, "enterVerificationCodeApp": { - "message": "کد تایید ۶ رقمی را از برنامه تایید کننده‌تان وارد کنید." + "message": "کد ۶ رقمی تأیید را از برنامه احراز هویت وارد کنید." }, "enterVerificationCodeEmail": { - "message": "کد تایید ۶ رقمی که به $EMAIL$ ارسال شد را وارد کنید.", + "message": "کد ۶ رقمی تأیید را که به $EMAIL$ ایمیل شده را وارد کنید.", "placeholders": { "email": { "content": "$1", @@ -596,7 +596,7 @@ } }, "verificationCodeEmailSent": { - "message": "رایانامه تایید به $EMAIL$ ارسال شد.", + "message": "ایمیل تأیید به $EMAIL$ ارسال شد.", "placeholders": { "email": { "content": "$1", @@ -608,7 +608,7 @@ "message": "مرا به خاطر بسپار" }, "sendVerificationCodeEmailAgain": { - "message": "ارسال دوباره رایانامه کد تایید" + "message": "ارسال دوباره ایمیل کد تأیید" }, "useAnotherTwoStepMethod": { "message": "استفاده از روش ورود دو مرحله‌ای دیگر" @@ -626,24 +626,24 @@ "message": "کد بازیابی" }, "authenticatorAppTitle": { - "message": "برنامه تاییدکننده" + "message": "برنامه احراز هویت" }, "authenticatorAppDesc": { - "message": "از یک برنامه تاییدکننده (همانند Authy یا Google Authenticator) استفاده کنید تا کدهای تایید بر پایه زمان تولید کنید.", + "message": "از یک برنامه احراز هویت (مانند Authy یا Google Authenticator) استفاده کنید تا کدهای تأیید بر پایه زمان تولید کنید.", "description": "'Authy' and 'Google Authenticator' are product names and should not be translated." }, "yubiKeyTitle": { "message": "کلید امنیتی YubiKey OTP" }, "yubiKeyDesc": { - "message": "از یک YubiKey برای دسترسی به حسابتان استفاده کنید. همراه با دستگاه‌های YubiKey 4،4 Nano، NEO کار میکند." + "message": "از یک YubiKey برای دسترسی به حسابتان استفاده کنید. همراه با دستگاه‌های YubiKey 4 ،4 Nano ،NEO کار می‌کند." }, "duoDesc": { - "message": "با Duo Security با استفاده از برنامه تلفن همراه، پیامک، تماس تلفنی، یا کلید امنیتی U2F تایید کنید.", + "message": "با Duo Security با استفاده از برنامه تلفن همراه، پیامک، تماس تلفنی، یا کلید امنیتی U2F تأیید کنید.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { - "message": "از Duo Security با استفاده از برنامه تلفن همراه، پیامک، تماس تلفنی یا کلید امنیتی U2F برای تایید سازمان خود استفاده کنید.", + "message": "از Duo Security با استفاده از برنامه تلفن همراه، پیامک، تماس تلفنی یا کلید امنیتی U2F برای تأیید سازمان خود استفاده کنید.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "webAuthnTitle": { @@ -653,19 +653,19 @@ "message": "برای دسترسی به حساب خود از هر کلید امنیتی فعال شده WebAuthn استفاده کنید." }, "emailTitle": { - "message": "رایانامه" + "message": "ایمیل" }, "emailDesc": { - "message": "کد تایید برایتان ارسال می‌شود." + "message": "کد تأیید برایتان ارسال می‌شود." }, "loginUnavailable": { - "message": "ورود به سیستم موجود نیست" + "message": "ورود به سیستم در دسترس نیست" }, "noTwoStepProviders": { - "message": "ورود دو مرحله‌ای برای این حساب فعال است، با این حال، هیچ یک از ارائه‌دهندگان دو مرحله‌ای پیکربندی شده توسط این دستگاه پشتیبانی‌نمی شوند." + "message": "ورود دو مرحله‌ای برای این حساب فعال است، با این حال، هیچ یک از ارائه‌دهندگان دو مرحله‌ای پیکربندی شده توسط این دستگاه پشتیبانی نمی‌شوند." }, "noTwoStepProviders2": { - "message": "لطفا ارائه‌دهندگان دیگری را که بهتر در سایر دستگاه‌ها پشتیبانی می‌شوند اضافه کنید (همانند یک برنامه تاییدکننده)." + "message": "لطفاً ارائه‌دهندگان دیگری را که بهتر در سایر دستگاه‌ها پشتیبانی می‌شوند اضافه کنید (مانند یک برنامه احراز هویت)." }, "twoStepOptions": { "message": "گزینه‌های ورود دو مرحله‌ای" @@ -674,22 +674,22 @@ "message": "محیط خود میزبان" }, "selfHostedEnvironmentFooter": { - "message": "آدرس اینترنتی پایه نصب Bitwarden میزبانی شده را مشخص کنید." + "message": "نشانی اینترنتی پایه فرضی نصب Bitwarden میزبانی شده را مشخص کنید." }, "customEnvironment": { "message": "محیط سفارشی" }, "customEnvironmentFooter": { - "message": "برای کاربران پیشرفته. شما می‌توانید آدرس پایه هر سرویس را به صورت مستقل تعیین کنید." + "message": "برای کاربران پیشرفته. شما می‌توانید نشانی پایه هر سرویس را مستقلاً تعیین کنید." }, "baseUrl": { - "message": "نشانی سرور" + "message": "نشانی اینترنتی سرور" }, "apiUrl": { - "message": "نشانی API سرور" + "message": "نشانی سرور API" }, "webVaultUrl": { - "message": "نشانی سرور گاوصندوق وب" + "message": "نشانی اینترنتی سرور گاوصندوق وب" }, "identityUrl": { "message": "نشانی سرور شناسایی" @@ -698,13 +698,13 @@ "message": "نشانی سرور اعلان‌ها" }, "iconsUrl": { - "message": "نشانی سرور آیکون‌ها" + "message": "آدرس سرور آیکون ها" }, "environmentSaved": { - "message": "نشانی‌های اینترنتی محیط ذخیره شدند." + "message": "نشانی‌های اینترنتی محیط ذخیره شد" }, "ok": { - "message": "تایید" + "message": "تأیید" }, "yes": { "message": "بله" @@ -734,13 +734,13 @@ "message": "خروج" }, "addNewLogin": { - "message": "افزودن ورود جدید" + "message": "ورود جدید" }, "addNewItem": { - "message": "افزودن مورد جدید" + "message": "مورد جدید" }, "addNewFolder": { - "message": "افزودن پوشه جدید" + "message": "پوشه جدید" }, "view": { "message": "مشاهده" @@ -773,7 +773,7 @@ "message": "ﻣﺎ ﺭﺍ ﺩﻧﺒﺎﻝ ﮐﻨﻴﺪ" }, "syncVault": { - "message": "همگام سازی گاوصندوق" + "message": "همگام‌سازی گاوصندوق" }, "changeMasterPass": { "message": "تغییر کلمه عبور اصلی" @@ -793,19 +793,19 @@ "message": "برو به گاوصندق وب" }, "getMobileApp": { - "message": "دریافت برنامه تلفن همراه" + "message": "دریافت برنامه‌ی تلفن همراه" }, "getBrowserExtension": { "message": "دریافت افزونه مرورگر" }, "syncingComplete": { - "message": "همگام سازی کامل شد" + "message": "همگام‌سازی کامل شد" }, "syncingFailed": { - "message": "همگام سازی ناموفق بود" + "message": "همگام‌سازی شکست خورد" }, "yourVaultIsLocked": { - "message": "گاوصندوق شما فقل شد. برای ادامه کلمه عبور اصلی را وارد کنید." + "message": "گاوصندوق شما قفل شده است. برای ادامه هویت خود را تأیید کنید." }, "unlock": { "message": "باز کردن قفل" @@ -827,16 +827,16 @@ "message": "کلمه عبور اصلی نامعتبر است" }, "twoStepLoginConfirmation": { - "message": "ورودی دو مرحله‌ای باعث می‌شود که حساب کاربری شما با استفاده از یک دستگاه دیگر مانند کلید امنیتی، برنامه تایید هویت، پیامک، تماس تلفنی و یا رایانامه، اعتبار خود را با ایمنی بیشتر ثابت کند. ورود دو مرحله‌ای می‌تواند در bitwarden.com فعال شود. آیا می‌خواهید از سایت بازدید کنید؟" + "message": "ورود دو مرحله ای باعث می‌شود که حساب کاربری شما با استفاده از یک دستگاه دیگر مانند کلید امنیتی، برنامه احراز هویت، پیامک، تماس تلفنی و یا ایمیل، اعتبار خود را با ایمنی بیشتر اثبات کند. ورود دو مرحله ای می تواند در bitwarden.com فعال شود. آیا می‌خواهید از سایت بازدید کنید؟" }, "twoStepLogin": { - "message": "ورود دو مرحله‌ای" + "message": "ورود دو مرحله ای" }, "vaultTimeout": { "message": "متوقف شدن گاو‌صندوق" }, "vaultTimeoutDesc": { - "message": "انتخاب کنید که گاو‌صندوق شما چه موقع متوقف شود و عملکرد انتخاب شده را انجام دهد." + "message": "انتخاب کنید که گاو‌صندوق شما چه زمانی عمل توقف زمانی گاوصندوق را انجام دهد." }, "immediately": { "message": "بلافاصله" @@ -872,16 +872,16 @@ "message": "۴ ساعت" }, "onIdle": { - "message": "هنگام بیکاری سیستم" + "message": "در سیستم بیکار" }, "onSleep": { - "message": "هنگام خواب سیستم" + "message": "در خواب سیستم" }, "onLocked": { - "message": "هنگام قفل سیستم" + "message": "در قفل سیستم" }, "onRestart": { - "message": "هنگام راه اندازی مجدد" + "message": "در راه اندازی مجدد" }, "never": { "message": "هرگز" @@ -890,54 +890,54 @@ "message": "امنیت" }, "clearClipboard": { - "message": "پاک‌سازی کلیپ‌برد", + "message": "پاکسازی کلیپ بورد", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "clearClipboardDesc": { - "message": "به صورت خودکار، مقادیر رونوشت شده را از کلیپ‌برد پاک کن.", + "message": "به صورت خودکار، مقادیر کپی شده را از کلیپ بورد پاک کن.", "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "enableFavicon": { - "message": "نمایش نمادهای وب سایت" + "message": "نمایش نمادهای وب‌سایت" }, "faviconDesc": { "message": "یک تصویر قابل تشخیص در کنار هر ورود نشان دهید." }, "enableMinToTray": { - "message": "کوچک کردن به نماد Tray" + "message": "کوچک کردن به نماد سینی" }, "enableMinToTrayDesc": { - "message": "هنگام کوچک‌کردن پنچره، یک نماد در قسمت نمادهای سیستم بجای آن نشان بده." + "message": "هنگام کوچک کردن پنچره، یک نماد در قسمت سینی سیستم به‌جای آن نشان بده." }, "enableMinToMenuBar": { - "message": "به نوار منو کوچک کنید" + "message": "به نوار منو کوچک کن" }, "enableMinToMenuBarDesc": { - "message": "هنگام کوچک کردن پنجره، به جای آن یک نماد در نوار منو نشان بده." + "message": "هنگام کوچک کردن پنجره، به‌جای آن یک نماد در نوار منو نشان بده." }, "enableCloseToTray": { - "message": "بستن به نماد Tray" + "message": "بستن به نماد سینی" }, "enableCloseToTrayDesc": { - "message": "هنگام بستن پنچره، یک نماد در قسمت نمادهای سیستم بجای آن نشان بده." + "message": "هنگام بستن پنچره، یک آیکون در قسمت tray به‌جای آن نشان بده." }, "enableCloseToMenuBar": { "message": "بستن به نوار منو" }, "enableCloseToMenuBarDesc": { - "message": "هنگام بستن پنجره، به جای آن یک نماد در نوار منو نشان دهید." + "message": "هنگام بستن پنجره، به جای آن یک نماد در نوار منو نشان بده." }, "enableTray": { - "message": "فعال کردن نماد Tray" + "message": "فعال کردن نماد سینی" }, "enableTrayDesc": { - "message": "همیشه یک نماد در قسمت نمادهای سیستم نشان بده." + "message": "همیشه یک نماد در قسمت سینی سیستم نشان بده." }, "startToTray": { - "message": "در زمان شروع، به نماد Tray برو" + "message": "در زمان شروع، به نماد سینی برو" }, "startToTrayDesc": { - "message": "زمانی که برنامه برای بار اول شروع می‌شود، فقط یک نماد در نمادهای سیستم نشان بده." + "message": "زمانی که برنامه برای بار اول شروع می‌شود، فقط یک نماد در سینی سیستم نشان بده." }, "startToMenuBar": { "message": "شروع به نوار منو" @@ -946,10 +946,10 @@ "message": "زمانی که برنامه برای بار اول شروع می‌شود، فقط یک نماد در نوار منو نشان بده." }, "openAtLogin": { - "message": "هنگام ورود به سیستم به طور خودکار شروع کنید" + "message": "هنگام ورود به سیستم به طور خودکار شروع کن" }, "openAtLoginDesc": { - "message": "برنامه دستکتاپ Bitwarden را به طور خودکار هنگام ورود به سیستم شروع کنید." + "message": "برنامه دسکتاپ Bitwarden را به طور خودکار هنگام ورود به سیستم شروع کن." }, "alwaysShowDock": { "message": "همیشه در داک نشان بده" @@ -958,10 +958,10 @@ "message": "نماد Bitwarden را در داک نمایش بده حتی زمانی که به نوار منو کوچک شود." }, "confirmTrayTitle": { - "message": "غیرفعال کردن tray را تأیید کنید" + "message": "غیرفعال کردن سینی را تأیید کنید" }, "confirmTrayDesc": { - "message": "غیرفعال کردن این تنظیم تمام تنظیمات مربوط به tray را غیرفعال می کند." + "message": "غیرفعال کردن این تنظیم تمام تنظیمات مربوط به سینی را غیرفعال می‌کند." }, "language": { "message": "زبان" @@ -984,11 +984,11 @@ "description": "Light color" }, "copy": { - "message": "رونوشت", + "message": "کپی", "description": "Copy to clipboard" }, "checkForUpdates": { - "message": "بررسی برای بروزرسانی‌ها" + "message": "بررسی برای به‌روزرسانی‌ها…" }, "version": { "message": "نسخه $VERSION_NUM$", @@ -1000,10 +1000,10 @@ } }, "restartToUpdate": { - "message": "برای بروزرسانی راه اندازی مجدد کنید" + "message": "برای به‌روزرسانی، مجدداً راه اندازی کن" }, "restartToUpdateDesc": { - "message": "نسخه $VERSION_NUM$ آماده نصب است. برای تکمیل نصب باید Bitwarden را مجددا راه اندازی کنید. آیا تمایل به راه اندازی مجدد و بروزرسانی دارید؟", + "message": "نسخه $VERSION_NUM$ آماده نصب است. برای تکمیل نصب باید Bitwarden را مجددا راه اندازی کنید. آیا تمایل به راه اندازی مجدد و به‌روزرسانی دارید؟", "placeholders": { "version_num": { "content": "$1", @@ -1012,54 +1012,54 @@ } }, "updateAvailable": { - "message": "بروزرسانی موجود است" + "message": "به‌روزرسانی در دسترس است" }, "updateAvailableDesc": { - "message": "یک بروزرسانی یافت شد. مایل به دانلود و نصب آن هستید؟" + "message": "یک به‌روزرسانی یافت شد. مایل به دانلود و نصب آن هستید؟" }, "restart": { "message": "راه اندازی مجدد" }, "later": { - "message": "بعدا" + "message": "بعداً" }, "noUpdatesAvailable": { - "message": "در حال حاظر هیچ بروزرسانی در دسترس نمی‌باشد. شما در در حال استفاده از آخرین نسخه هستید." + "message": "در حال حاضر هیچ به‌روزرسانی در دسترس نمی‌باشد. شما در حال استفاده از آخرین نسخه هستید." }, "updateError": { - "message": "خطا در بروزرسانی" + "message": "خطا در به‌روز رسانی" }, "unknown": { "message": "ناشناخته" }, "copyUsername": { - "message": "رونوشت نام کاربری" + "message": "کپی نام کاربری" }, "copyNumber": { - "message": "رونوشت شماره", + "message": "کپی شماره", "description": "Copy credit card number" }, "copySecurityCode": { - "message": "رونوشت کد امنیتی", + "message": "کپی کد امنیتی", "description": "Copy credit card security code (CVV)" }, "premiumMembership": { - "message": "عضویت پریمیوم" + "message": "عضویت پرمیوم" }, "premiumManage": { "message": "مدیریت عضویت" }, "premiumManageAlert": { - "message": "شما می‌توانید عضویت خود را در نسخه وب گاوصندوق در bitwarden.com مدیریت کنید. آیا مایل به دیدن وبسایت هستید؟" + "message": "شما می‌توانید عضویت خود را در نسخه وب گاوصندوق در bitwarden.com مدیریت کنید. آیا مایل به دیدن وب‌سایت هستید؟" }, "premiumRefresh": { "message": "نوسازی عضویت" }, "premiumNotCurrentMember": { - "message": "شما در حال حاظر کاربر پریمیوم نیستید." + "message": "شما در حال حاظر کاربر پرمیوم نیستید." }, "premiumSignUpAndGet": { - "message": "ثبت نام برای عضویت پرمیوم و گرفتن:" + "message": "برای عضویت پرمیوم ثبت نام کنید و دریافت کنید:" }, "premiumSignUpStorage": { "message": "۱ گیگابایت فضای ذخیره‌سازی رمزنگاری شده برای پرونده‌های پیوست." @@ -1068,25 +1068,25 @@ "message": "گزینه‌های ورود دو مرحله‌ای اضافی مانند YubiKey, FIDO U2F و Duo." }, "premiumSignUpReports": { - "message": "دانش کلمه عبور، سلامت حساب کاربری و گزارش‌های نقص اطلاعات، برای حفظ امنیت گاوصندوق شما." + "message": "گزارش‌های بهداشت رمز عبور، سلامت حساب و نقض داده‌ها برای ایمن نگهداشتن گاوصندوق شما." }, "premiumSignUpTotp": { - "message": "تولید کننده کد تایید (2FA) از نوع TOTP برای ورودهای موجود در گاوصندوقتان." + "message": "تولید کننده کد تأیید (2FA) از نوع TOTP برای ورودهای موجود در گاوصندوقتان." }, "premiumSignUpSupport": { - "message": "پشتیبانی از مشتری با اولویت." + "message": "اولویت پشتیبانی از مشتری." }, "premiumSignUpFuture": { - "message": "تمام ویژگی‌های پریمیوم آینده. به زودی بیشتر!" + "message": "تمام ویژگی‌های پرمیوم آینده. به زودی بیشتر!" }, "premiumPurchase": { - "message": "خرید پریمیوم" + "message": "خرید پرمیوم" }, "premiumPurchaseAlert": { - "message": "شما می‌توانید عضویت پریمیوم را از گاوصندوق وب bitwarden.com خریداری کنید. آیا مایلید اکنون از وبسایت بازید کنید؟" + "message": "شما می‌توانید عضویت پرمیوم را از گاوصندوق وب bitwarden.com خریداری کنید. مایلید اکنون از وب‌سایت بازید کنید؟" }, "premiumCurrentMember": { - "message": "شما یک عضو پریمیوم هستید!" + "message": "شما یک عضو پرمیوم هستید!" }, "premiumCurrentMemberThanks": { "message": "برای حمایتتان از Bitwarden سپاسگزاریم." @@ -1111,7 +1111,7 @@ "description": "To clear something out. example: To clear browser history." }, "noPasswordsInList": { - "message": "هیچ کلمه عبوری در لیست وجود ندارد." + "message": "هیچ کلمه عبوری برای فهرست کردن وجود ندارد." }, "undo": { "message": "بازگرداندن" @@ -1163,7 +1163,7 @@ "message": "درباره Bitwarden" }, "services": { - "message": "خدمات" + "message": "سرویس‌ها" }, "hideBitwarden": { "message": "پنهان‌سازی Bitwarden" @@ -1178,7 +1178,7 @@ "message": "خروج از Bitwarden" }, "valueCopied": { - "message": "$VALUE$ رونوشت شد", + "message": "$VALUE$ کپی شد", "description": "Value has been copied to the clipboard.", "placeholders": { "value": { @@ -1194,7 +1194,7 @@ "message": "پنجره" }, "checkPassword": { - "message": "بررسی اینکه که آیا کلمه عبور افشا شده است." + "message": "بررسی کنید که آیا کلمه عبور افشا شده است." }, "passwordExposed": { "message": "این کلمه عبور $VALUE$ بار در رخنه داده‌ها افشا شده است. باید آن را تغییر دهید.", @@ -1206,7 +1206,7 @@ } }, "passwordSafe": { - "message": "این کلمه عبور در هیچ رخنه داده‌ای شناخته نشده است. باید برای استفاده امن باشد." + "message": "این کلمه عبور در هیچ رخنه داده ای شناخته نشده است. استفاده از آن باید ایمن باشد." }, "baseDomain": { "message": "دامنه پایه", @@ -1231,11 +1231,11 @@ "description": "A programming term, also known as 'RegEx'." }, "matchDetection": { - "message": "تشخیص تطابق", + "message": "تشخیص مطابقت", "description": "URI match detection for auto-fill." }, "defaultMatchDetection": { - "message": "تشخیص تطابق پیشفرض", + "message": "بررسی مطابقت پیش‌فرض", "description": "Default URI match detection for auto-fill." }, "toggleOptions": { @@ -1246,7 +1246,7 @@ "description": "An entity of multiple related people (ex. a team or business organization)." }, "default": { - "message": "پیش فرض" + "message": "پیش‌فرض" }, "exit": { "message": "خروج" @@ -1256,36 +1256,36 @@ "description": "Text for a button that toggles the visibility of the window. Shows the window when it is hidden or hides the window if it is currently open." }, "hideToTray": { - "message": "پنهان‌سازی در قسمت نمادهای سیستم" + "message": "پنهان‌سازی در سینی" }, "alwaysOnTop": { "message": "همشه در بالا", "description": "Application window should always stay on top of other windows" }, "dateUpdated": { - "message": "بروزرسانی شد", + "message": "به‌روزرسانی شد", "description": "ex. Date this item was updated" }, "dateCreated": { - "message": "ایجاد شده", + "message": "ایجاد شد", "description": "ex. Date this item was created" }, "datePasswordUpdated": { - "message": "کلمه عبور بروزرسانی شد", + "message": "کلمه عبور به‌روزرسانی شد", "description": "ex. Date this password was updated" }, "exportVault": { - "message": "خروجی گرفتن از گاوصندوق" + "message": "برون ریزی گاوصندوق" }, "fileFormat": { "message": "فرمت پرونده" }, "hCaptchaUrl": { - "message": "آدرس اینترنتی hCaptcha", + "message": "نشانی اینترنتی hCaptcha", "description": "hCaptcha is the name of a website, should not be translated" }, "loadAccessibilityCookie": { - "message": "بارگیری کوکی دسترس‌پذیری" + "message": "بارگیری کوکی دسترسی" }, "registerAccessibilityUser": { "message": "ثبت نام به عنوان کاربر قابلیت دسترسی در", @@ -1295,21 +1295,21 @@ "message": "لینک ارسال شده به ایمیل خود را در زیر کپی و پیست کنید" }, "enterhCaptchaUrl": { - "message": "برای بارگیری کوکی دسترسی برای hCaptcha، URL را وارد کنید", + "message": "برای بارگیری کوکی دسترسی کپچا نشانی اینترنتی را وارد کنید", "description": "hCaptcha is the name of a website, should not be translated" }, "hCaptchaUrlRequired": { - "message": "آدرس اینترنتی hCaptcha مورد نیاز است", + "message": "نشانی اینترنتی کپچا مورد نیاز است", "description": "hCaptcha is the name of a website, should not be translated" }, "invalidUrl": { - "message": "آدرس اینترنتی نامعتبر" + "message": "نشانی اینترنتی نامعتبر" }, "done": { - "message": "انجام شده" + "message": "انجام شد" }, "accessibilityCookieSaved": { - "message": "کوکی دسترس‌پذیری ذخیره شد!" + "message": "کوکی دسترسی ذخیره شد!" }, "noAccessibilityCookieSaved": { "message": "هیچ کوکی دسترسی ذخیره نشد" @@ -1319,22 +1319,22 @@ "description": "WARNING (should stay in capitalized letters if the language permits)" }, "confirmVaultExport": { - "message": "صادرات گاوصندوق را تأیید کنید" + "message": "برون ریزی گاوصندوق را تأیید کنید" }, "exportWarningDesc": { - "message": "این خروجی شامل داده‌های گاوصندوق در یک قالب رمزنگاری نشده است. شما نباید آن را از طریق یک راه ارتباطی ناامن (مثل ایمیل) ذخیره یا ارسال کنید. به محض اینکه استفاده‌تان از آن تمام شد، آن را حذف کنید." + "message": "این برون ریزی شامل داده‌های گاوصندوق در یک قالب رمزنگاری نشده است. شما نباید آن را از طریق یک راه ارتباطی نا امن (مثل ایمیل) ذخیره یا ارسال کنید. به محض اینکه کارتان با آن تمام شد، آن را حذف کنید." }, "encExportKeyWarningDesc": { - "message": "این صادرات با استفاده از کلید رمزگذاری حساب شما ، اطلاعات شما را رمزگذاری می کند. اگر حتی کلید رمزگذاری حساب خود را بچرخانید ، باید دوباره صادر کنید چون قادر به رمزگشایی این پرونده صادراتی نخواهید بود." + "message": "این برون ریزی با استفاده از کلید رمزگذاری حساب شما، اطلاعاتتان را رمزگذاری می کند. اگر زمانی کلید رمزگذاری حساب خود را بچرخانید، باید دوباره خروجی بگیرید، چون قادر به رمزگشایی این پرونده برون ریزی نخواهید بود." }, "encExportAccountWarningDesc": { - "message": "کلیدهای رمزگذاری حساب برای هر حساب کاربری Bitwarden منحصر به فرد است ، بنابراین نمی توانید صادرات رمزگذاری شده را به حساب دیگری وارد کنید." + "message": "کلیدهای رمزگذاری حساب برای هر حساب کاربری Bitwarden منحصر به فرد است، بنابراین نمی‌توانید برون ریزی رمزگذاری شده را به حساب دیگری وارد کنید." }, "noOrganizationsList": { - "message": "شما به هیچ سازمانی تعلق ندارید. سازمان‌ها به شما اجازه می‌دهند تا داده‌های خود را را با کاربران دیگر به صورت امن به اشتراک بگذارید." + "message": "شما به هیچ سازمانی تعلق ندارید. سازمان‌ها به شما اجازه می‌دهند تا داده‌های خود را با کاربران دیگر به صورت امن به اشتراک بگذارید." }, "noCollectionsInList": { - "message": "هیچ مجموعه‌ای برای نمایش وجود ندارد." + "message": "هیچ مجموعه ای برای لیست کردن وجود ندارد." }, "ownership": { "message": "مالکیت" @@ -1365,10 +1365,10 @@ "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." }, "unlockWithPin": { - "message": "بازکردن با پین" + "message": "باز کردن با پین" }, "setYourPinCode": { - "message": "کد پین خود را برای باز کردن Bitwarden تنظیم کنید. اگر به طور کامل از برنامه خارج شوید (Log out)، تنظیمات پین شما از بین می‌رود." + "message": "کد پین خود را برای باز کردن Bitwarden تنظیم کنید. اگر به طور کامل از برنامه خارج شوید، تنظیمات پین شما از بین می‌رود." }, "pinRequired": { "message": "کد پین الزامیست." @@ -1380,19 +1380,19 @@ "message": "باز کردن با Windows Hello" }, "windowsHelloConsentMessage": { - "message": "تایید برای Bitwarden." + "message": "تأیید برای Bitwarden." }, "unlockWithTouchId": { - "message": "باز کردن با اثر انگشت" + "message": "باز کردن با Touch ID" }, "touchIdConsentMessage": { - "message": "تایید برای Bitwarden." + "message": "قفل گاوصندوق خود را باز کنید" }, "autoPromptWindowsHello": { "message": "درخواست Windows Hello در هنگام راه اندازی" }, "autoPromptTouchId": { - "message": "درخواست تاچ آیدی در هنگام راه اندازی" + "message": "درخواست Touch ID در هنگام راه اندازی" }, "lockWithMasterPassOnRestart": { "message": "در زمان شروع مجدد، با کلمه عبور اصلی قفل کن" @@ -1404,7 +1404,7 @@ "message": "برای حذف حساب کاربری خود و تمام داده‌های گاوصندوق، به زیر ادامه دهید." }, "deleteAccountWarning": { - "message": "حذف حساب شما دائمی است. نمی توان آن را برگرداند." + "message": "حذف حساب شما دائمی است. نمی‌توان آن را برگرداند." }, "accountDeleted": { "message": "حساب حذف شد" @@ -1447,25 +1447,25 @@ "message": "سیاست حفظ حریم خصوصی" }, "unsavedChangesConfirmation": { - "message": "آیا مطمئن هستید که می‌خواهید خارج شوید؟ اگر الان خارج شوید اطلاعات فعلی ذخیره نخواهند شد" + "message": "آیا مطمئن هستید که می‌خواهید خارج شوید؟ اگر الان خارج شوید اطلاعات فعلی ذخیره نخواهند شد." }, "unsavedChangesTitle": { - "message": "تغییرات ذخیره نشده وجود دارند" + "message": "تغییرات ذخیره نشده" }, "clone": { "message": "شبیه سازی" }, "passwordGeneratorPolicyInEffect": { - "message": "یک یا چند خط مشی سازمان بر تنظیمات تولیدکننده شما تأثیر می گذارد." + "message": "یک یا چند سیاست سازمان بر تنظیمات تولید کننده شما تأثیر می‌گذارد." }, "vaultTimeoutAction": { "message": "عمل متوقف شدن گاو‌صندوق" }, "vaultTimeoutActionLockDesc": { - "message": "یک گاوصندوق قفل شده درخواست وارد کردن مجدد کلمه عبور اصلی را برای دسترسی میدهد." + "message": "یک گاوصندوق قفل شده درخواست وارد کردن مجدد کلمه عبور اصلی را برای دسترسی می‌دهد." }, "vaultTimeoutActionLogOutDesc": { - "message": "یک گاوصندوق خارج شده درخواست احراز هویت مجدد را برای دسترسی آن میدهد." + "message": "یک گاوصندوق خارج شده درخواست احراز هویت مجدد را برای دسترسی آن می‌دهد." }, "lock": { "message": "قفل", @@ -1482,7 +1482,7 @@ "message": "حذف دائمی مورد" }, "permanentlyDeleteItemConfirmation": { - "message": "آیا مطمئن هستید که می خواهید این مورد را برای همیشه حذف کنید؟" + "message": "مطمئن هستید که می‌خواهید این مورد را برای همیشه پاک کنید؟" }, "permanentlyDeletedItem": { "message": "مورد برای همیشه حذف شد" @@ -1491,7 +1491,7 @@ "message": "بازیابی مورد" }, "restoreItemConfirmation": { - "message": "آیا مطمئن هستید می خواهید این مورد را بازیابی کنید؟" + "message": "آیا مطمئن هستید که می‌خواهید این مورد را بازیابی کنید؟" }, "restoredItem": { "message": "مورد بازیابی شد" @@ -1500,28 +1500,28 @@ "message": "حذف دائمی" }, "vaultTimeoutLogOutConfirmation": { - "message": "خروج از سیستم تمام دسترسی ها به گاو‌صندوق شما را از بین می برد و نیاز به احراز هویت آنلاین پس از مدت زمان توقف دارد. آیا مطمئن هستید که می خواهید از این تنظیمات استفاده کنید؟" + "message": "خروج از سیستم، تمام دسترسی ها به گاو‌صندوق شما را از بین می‌برد و نیاز به احراز هویت آنلاین پس از مدت زمان توقف دارد. آیا مطمئن هستید که می‌خواهید از این تنظیمات استفاده کنید؟" }, "vaultTimeoutLogOutConfirmationTitle": { - "message": "تایید عمل توقف" + "message": "تأیید عمل توقف" }, "enterpriseSingleSignOn": { - "message": "ورود به سیستم پروژه" + "message": "ورود به سیستم واحد سازمانی" }, "setMasterPassword": { "message": "تنظیم کلمه عبور اصلی" }, "ssoCompleteRegistration": { - "message": "برای تکمیل ورود به سیستم با SSO ، لطفاً یک کلمه عبور اصلی برای دسترسی و محافظت از گاوصندوق خود تنظیم کنید." + "message": "برای پر کردن ورود به سیستم با SSO، لطفاً یک کلمه عبور اصلی برای دسترسی و محافظت از گاوصندوق خود تنظیم کنید." }, "newMasterPass": { "message": "کلمه عبور اصلی جدید" }, "confirmNewMasterPass": { - "message": "تایید کلمه عبور اصلی جدید" + "message": "تأیید کلمه عبور اصلی جدید" }, "masterPasswordPolicyInEffect": { - "message": "یک یا چند سیاست سازمانی برای تأمین شرایط زیر به گذرواژه اصلی شما احتیاج دارد:" + "message": "یک یا چند سیاست سازمانی برای تأمین شرایط زیر به کلمه عبور اصلی شما احتیاج دارد:" }, "policyInEffectMinComplexity": { "message": "حداقل نمره پیچیدگی $SCORE$", @@ -1560,10 +1560,10 @@ } }, "masterPasswordPolicyRequirementsNotMet": { - "message": "کلمه عبور اصلی جدید شما از شرایط سیاست پپیروی نمی کند." + "message": "کلمه عبور اصلی جدید شما از شرایط سیاست پیروی نمی‌کند." }, "acceptPolicies": { - "message": "با علامت زدن این کادر با موارد زیر موافقت می کنید:" + "message": "با علامت زدن این کادر با موارد زیر موافقت می‌کنید:" }, "acceptPoliciesRequired": { "message": "شرایط خدمات و سیاست حفظ حریم خصوصی تأیید نشده است." @@ -1572,7 +1572,7 @@ "message": "فعال کردن ادغام مرورگر" }, "enableBrowserIntegrationDesc": { - "message": "یکپارچه سازی مرورگر برای بیومتریک در مرورگر استفاده می شود." + "message": "یکپارچه سازی مرورگر برای بیومتریک در مرورگر استفاده می‌شود." }, "enableDuckDuckGoBrowserIntegration": { "message": "اجازه ادغام مرورگر DuckDuckGo را بدهید" @@ -1581,16 +1581,16 @@ "message": "هنگام مرور با DuckDuckGo از گاوصندوق Bitwarden خود استفاده کنید." }, "browserIntegrationUnsupportedTitle": { - "message": "ادغام مرورگر پشتیبانی نمی شود" + "message": "ادغام مرورگر پشتیبانی نمی‌شود" }, "browserIntegrationMasOnlyDesc": { - "message": "متأسفانه در حال حاضر ادغام مرورگر فقط در نسخه Mac App Store پشتیبانی می شود." + "message": "متأسفانه در حال حاضر ادغام مرورگر فقط در نسخه Mac App Store پشتیبانی می‌شود." }, "browserIntegrationWindowsStoreDesc": { - "message": "متأسفانه در حال حاضر ادغام مرورگر در نسخه فروشگاه ویندوز پشتیبانی نمی شود." + "message": "متأسفانه در حال حاضر ادغام مرورگر در نسخه فروشگاه ویندوز پشتیبانی نمی‌شود." }, "browserIntegrationLinuxDesc": { - "message": "متأسفانه در حال حاضر ادغام مرورگر در نسخه لینوکس پشتیبانی نمی شود." + "message": "متأسفانه در حال حاضر ادغام مرورگر در نسخه لینوکس پشتیبانی نمی‌شود." }, "enableBrowserIntegrationFingerprint": { "message": "برای ادغام مرورگر نیاز به تأیید است" @@ -1599,7 +1599,7 @@ "message": "هنگام ایجاد پیوند بین دسکتاپ و مرورگر خود، با تأیید اعتبار اثر انگشت، یک لایه امنیتی دیگر را فعال کنید. درصورت فعال بودن، این امر مستلزم مداخله و تأیید کاربر در هر بار برقراری ارتباط است." }, "approve": { - "message": "تایید" + "message": "تأیید" }, "verifyBrowserTitle": { "message": "اتصال مرورگر را تأیید کنید" @@ -1629,26 +1629,26 @@ "message": "بیومتریک مرورگر ابتدا نیاز به فعالسازی بیومتریک دسکتاپ در تنظیمات دارد." }, "personalOwnershipSubmitError": { - "message": "به دلیل خط مشی Enterprise ، برای ذخیره موارد در گاوصندوق شخصی خود محدود شده اید. گزینه مالکیت را به یک سازمان تغییر دهید و مجموعه های موجود را انتخاب کنید." + "message": "به دلیل سیاست پرمیوم، برای ذخیره موارد در گاوصندوق شخصی خود محدود شده اید. گزینه مالکیت را به یک سازمان تغییر دهید و مجموعه های موجود را انتخاب کنید." }, "hintEqualsPassword": { - "message": "نکته کلمه عبور شما نمی تواند همان کلمه عبور شما باشد." + "message": "اشاره به کلمه عبور شما نمی‌تواند همان کلمه عبور شما باشد." }, "personalOwnershipPolicyInEffect": { - "message": "خط مشی سازمانی بر تنظیمات مالکیت شما تأثیر می گذارد." + "message": "سیاست سازمانی بر تنظیمات مالکیت شما تأثیر می‌گذارد." }, "allSends": { "message": "همه ارسال ها", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTypeFile": { - "message": "فایل" + "message": "پرونده" }, "sendTypeText": { "message": "متن" }, "searchSends": { - "message": "ارسال ها را جستجو کن", + "message": "جستجوی ارسال‌ها", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editSend": { @@ -1672,7 +1672,7 @@ "message": "تاريخ انقضاء" }, "expirationDateDesc": { - "message": "در صورت تنظیم، دسترسی به این ارسال در تاریخ و ساعت مشخص شده منقضی می شود.", + "message": "در صورت تنظیم، دسترسی به این ارسال در تاریخ و ساعت مشخص شده منقضی می‌شود.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "maxAccessCount": { @@ -1680,7 +1680,7 @@ "description": "This text will be displayed after a Send has been accessed the maximum amount of times." }, "maxAccessCountDesc": { - "message": "در صورت تنظیم، با رسیدن به حداکثر تعداد دسترسی، کاربران دیگر نمی توانند به این ارسال دسترسی پیدا کنند.", + "message": "در صورت تنظیم، با رسیدن به حداکثر تعداد دسترسی، کاربران دیگر نمی‌توانند به این ارسال دسترسی پیدا کنند.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "currentAccessCount": { @@ -1711,15 +1711,15 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSend": { - "message": "ارسال جدید ساخته شد", + "message": "ارسال اضافه شد", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editedSend": { - "message": "ارسال جدید ویرایش شد", + "message": "ارسال ذخیره شد", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deletedSend": { - "message": "ارسال پاک شد", + "message": "ارسال حذف شد", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "newPassword": { @@ -1730,14 +1730,14 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createSend": { - "message": "ساختن ارسال", + "message": "ارسال جدید", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTextDesc": { - "message": "متنی که می خواهید ارسال کنید." + "message": "متنی که می‌خواهید ارسال کنید." }, "sendFileDesc": { - "message": "فایلی که می خواهید ارسال کنید." + "message": "پرونده ای که می‌خواهید ارسال کنید." }, "days": { "message": "$DAYS$ روز", @@ -1755,22 +1755,22 @@ "message": "سفارشی" }, "deleteSendConfirmation": { - "message": "آیا مطمئن هستید می خواهید این ارسال را حذف کنید؟", + "message": "آیا مطمئن هستید که می‌خواهید این ارسال را حذف کنید؟", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLinkToClipboard": { - "message": "کپی لینک ارسال به حافظه موقت", + "message": "کپی پیوند ارسال به کلیپ بورد", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLinkOnSave": { "message": "این پیوند را برای به اشتراک گذاری ارسال بعد از ارسال کپی کن." }, "sendDisabled": { - "message": "ارسال غیرفعال شد", + "message": "ارسال حذف شد", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { - "message": "به دلیل خط مشی سازمانی، شما فقط می توانید ارسال موجود را حذف کنید.", + "message": "به دلیل سیاست سازمانی، شما فقط می‌توانید ارسال موجود را حذف کنید.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copyLink": { @@ -1779,6 +1779,15 @@ "disabled": { "message": "غیرفعال شد" }, + "removePassword": { + "message": "حذف کلمه عبور" + }, + "removedPassword": { + "message": "کلمه عبور حذف شد" + }, + "removePasswordConfirmation": { + "message": "مطمئنید که می‌خواهید کلمه عبور حذف شود؟" + }, "maxAccessCountReached": { "message": "به حداکثر تعداد دسترسی رسیده است" }, @@ -1792,34 +1801,34 @@ "message": "تأیید اعتبار در WebAuthn" }, "hideEmail": { - "message": "آدرس ایمیلم را از گیرندگان مخفی کن." + "message": "نشانی ایمیلم را از گیرندگان مخفی کن." }, "sendOptionsPolicyInEffect": { - "message": "یک یا چند سیاست سازمان بر گزینه های ارسال شما تأثیر می گذارد." + "message": "یک یا چند سیاست سازمان بر گزینه های ارسال شما تأثیر می‌گذارد." }, "emailVerificationRequired": { - "message": "تایید ایمیل لازم است" + "message": "تأیید ایمیل لازم است" }, "emailVerificationRequiredDesc": { "message": "برای استفاده از این ویژگی باید ایمیل خود را تأیید کنید." }, "passwordPrompt": { - "message": "کلمه عبور اصلی دوباره تولید می شود" + "message": "درخواست مجدد کلمه عبور اصلی" }, "passwordConfirmation": { "message": "تأیید کلمه عبور اصلی" }, "passwordConfirmationDesc": { - "message": "این عمل محافظت می شود. برای ادامه، لطفاً کلمه ورود اصلی خود را دوباره وارد کنید تا هویتان را تأیید کنید." + "message": "این عمل محافظت می‌شود. برای ادامه، لطفاً کلمه عبور اصلی خود را دوباره وارد کنید تا هویت‌تان را تأیید کنید." }, "updatedMasterPassword": { - "message": "کلمه عبور اصلی بروز شد" + "message": "کلمه عبور اصلی به‌روز شد" }, "updateMasterPassword": { - "message": "بروزرسانی کلمه عبور اصلی" + "message": "به‌روزرسانی کلمه عبور اصلی" }, "updateMasterPasswordWarning": { - "message": "کلمه عبور اصلی شما اخیراً توسط سرپرست سازمانتان تغییر کرده است. برای دسترسی به گاوصندوق، باید همین حالا کلمه عبور اصلی خود را به روز کنید. در صورت ادامه، شما از نشست فعلی خود خارج می شوید و باید دوباره وارد سیستم شوید. نشست فعال در دستگاه های دیگر ممکن است تا یک ساعت همچنان فعال باقی بمانند." + "message": "کلمه عبور اصلی شما اخیراً توسط سرپرست سازمان‌تان تغییر کرده است. برای دسترسی به گاوصندوق، باید همین حالا کلمه عبور اصلی خود را به‌روز کنید. در صورت ادامه، شما از نشست فعلی خود خارج می‌شوید و باید دوباره وارد سیستم شوید. نشست فعال در دستگاه های دیگر ممکن است تا یک ساعت همچنان فعال باقی بمانند." }, "hours": { "message": "ساعت" @@ -1828,7 +1837,7 @@ "message": "دقیقه" }, "vaultTimeoutPolicyInEffect": { - "message": "خط مشی های سازمانتان بر مهلت زمانی گاوصندوق شما تأثیر می گذارد. حداکثر زمان مجاز گاوصندوق $HOURS$ ساعت و $MINUTES$ دقیقه است", + "message": "سیاست‌های سازمانتان بر مهلت زمانی گاوصندوق شما تأثیر می‌گذارد. حداکثر زمان مجاز گاوصندوق $HOURS$ ساعت و $MINUTES$ دقیقه است", "placeholders": { "hours": { "content": "$1", @@ -1847,22 +1856,22 @@ "message": "ثبت نام خودکار" }, "resetPasswordAutoEnrollInviteWarning": { - "message": "این سازمان دارای خط مشی سازمانی ای است که به طور خودکار شما را در بازنشانی کلمه عبور ثبت نام می کند. این ثبت نام به مدیران سازمان اجازه می دهد تا کلمه عبور اصلی شما را تغییر دهند." + "message": "این سازمان دارای سیاست سازمانی ای است که به طور خودکار شما را در بازنشانی کلمه عبور ثبت نام می‌کند. این ثبت نام به مدیران سازمان اجازه می‌دهد تا کلمه عبور اصلی شما را تغییر دهند." }, "vaultExportDisabled": { - "message": "صادرات گاوصندوق غیرفعال شده است" + "message": "برون ریزی گاوصندوق غیرفعال شده است" }, "personalVaultExportPolicyInEffect": { - "message": "یک یا چند خط مشی سازمان از صادرات گاوصندوق شخصی شما جلوگیری می کند." + "message": "یک یا چند سیاست سازمان از برون ریزی گاوصندوق شخصی شما جلوگیری می‌کند." }, "addAccount": { "message": "افزودن حساب کاربری" }, "removeMasterPassword": { - "message": "پاک کردن کلمه عبور اصلی" + "message": "حذف کلمه عبور اصلی" }, "removedMasterPassword": { - "message": "کلمه عبور اصلی پاک شد." + "message": "کلمه عبور اصلی حذف شد" }, "convertOrganizationEncryptionDesc": { "message": "$ORGANIZATION$ در حال استفاده از SSO با یک سرور کلید خود میزبان است. برای ورود اعضای این سازمان دیگر نیازی به کلمه عبور اصلی نیست.", @@ -1877,31 +1886,31 @@ "message": "ترک سازمان" }, "leaveOrganizationConfirmation": { - "message": "آيا مطمئن هستيد که می خواهيد سازمان های انتخاب شده را ترک کنيد؟" + "message": "آيا مطمئنید که می‌خواهيد سازمان انتخاب شده را ترک کنيد؟" }, "leftOrganization": { - "message": "شما از سازمان ها خارج شده اید." + "message": "شما از سازمان خارج شده اید." }, "ssoKeyConnectorError": { - "message": "خطای Key Connector: مطمئن شوید که Key Connector در دسترس است و به درستی کار می کند." + "message": "خطای رابط کلید: مطمئن شوید که رابط کلید در دسترس است و به درستی کار می‌کند." }, "lockAllVaults": { "message": "قفل کردن تمام گاوصندوق ها" }, "accountLimitReached": { - "message": "بیش از 5 حساب را نمی توان همزمان وارد کرد." + "message": "بیش از 5 حساب را نمی‌توان همزمان وارد کرد." }, "accountPreferences": { "message": "تنظیمات" }, "appPreferences": { - "message": "تنظیمات اپ (تمام حسابها)" + "message": "تنظیمات برنامه (تمام حساب‌ها)" }, "accountSwitcherLimitReached": { "message": "محدودیت حساب تکمیل شد. برای افزودن حساب دیگر، از یک حساب خارج شوید." }, "settingsTitle": { - "message": "تنظیمات اپ برای $EMAIL$", + "message": "تنظیمات برنامه برای $EMAIL$", "placeholders": { "email": { "content": "$1", @@ -1919,10 +1928,10 @@ "message": "زمان نشست شما به پایان رسید. لطفاً برگردید و دوباره وارد سیستم شوید." }, "exportingPersonalVaultTitle": { - "message": "صادرات گاو‌صندوق شخصی" + "message": "برون ریزی گاو‌صندوق شخصی" }, "exportingPersonalVaultDescription": { - "message": "فقط موارد گاو‌صندوق شخصی مرتبط با $EMAIL$ صادر خواهد شد. موارد گاو‌صندوق سازمان شامل نخواهد شد.", + "message": "فقط موارد گاو‌صندوق شخصی مرتبط با $EMAIL$ برون ریزی خواهد شد. موارد گاو‌صندوق سازمان شامل نخواهد شد.", "placeholders": { "email": { "content": "$1", @@ -1943,7 +1952,7 @@ "message": "چه چیزی دوست دارید تولید کنید؟" }, "passwordType": { - "message": "نوع گذرواژه" + "message": "نوع کلمه عبور" }, "regenerateUsername": { "message": "ایجاد مجدد نام کاربری" @@ -1955,14 +1964,14 @@ "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": "از قابلیت های آدرس دهی فرعی ارائه دهنده ایمیل خود استفاده کنید." }, "catchallEmail": { - "message": "رایانامه همه‌گیر" + "message": "دریافت همه ایمیل‌ها" }, "catchallEmailDesc": { "message": "از صندوق ورودی پیکربندی شده دامنه خود استفاده کنید." @@ -1974,16 +1983,16 @@ "message": "کلمه تصادفی" }, "websiteName": { - "message": "نام وب سایت" + "message": "نام وب‌سایت" }, "service": { - "message": "خدمت" + "message": "سرویس" }, "allVaults": { "message": "تمام گاو‌صندوق‌ها" }, "searchOrganization": { - "message": "سازماندهی جستجو" + "message": "جستجوی سازمان" }, "searchMyVault": { "message": "جستجوی گاوصندوق من" @@ -1992,7 +2001,7 @@ "message": "نام مستعار ایمیل فوروارد شده" }, "forwardedEmailDesc": { - "message": "یک نام مستعار ایمیل با یک سرویس فروارد خارجی ایجاد کنید." + "message": "یک نام مستعار ایمیل با یک سرویس ارسال خارجی ایجاد کنید." }, "hostname": { "message": "نام میزبان", @@ -2014,7 +2023,7 @@ "message": "موارد موجود در سازمان‌های غیرفعال، قابل دسترسی نیستند. برای دریافت کمک با مالک سازمان خود تماس بگیرید." }, "neverLockWarning": { - "message": "آیا جدا میخواهید از گزینه \"هرگز\" استفاده کنید؟ تنظیم کردن کردن گزینه قفل به \"هرگز\" کلیدهای رمزنگاری گاوصندوقتان را بر روی دستگاه شما ذخیره خواهد کرد. اگر از این گزینه استفاده میکنید باید اطمینان داشته باشید که دستگاه شما کاملا محافظت شده است." + "message": "آیا جداً می‌خواهید از گزینه \"هرگز\" استفاده کنید؟ تنظیم کردن گزینه قفل به \"هرگز\" کلیدهای رمزنگاری گاوصندوقتان را بر روی دستگاه شما ذخیره خواهد کرد. اگر از این گزینه استفاده می‌کنید باید اطمینان داشته باشید که دستگاه شما کاملا محافظت شده است." }, "cardBrandMir": { "message": "میر" @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "با دستگاه دیگری وارد شوید" + }, + "toggleCharacterCount": { + "message": "تغییر تعداد کاراکترها", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index d0a0bd56cf2..2df0f3d3c37 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Poistettu käytöstä" }, + "removePassword": { + "message": "Poista salasana" + }, + "removedPassword": { + "message": "Salasana poistettiin" + }, + "removePasswordConfirmation": { + "message": "Haluatko varmasti poistaa salasanan?" + }, "maxAccessCountReached": { "message": "Käyttökertojen enimmäismäärä on saavutettu" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Kirjaudu toisella laitteella" + }, + "toggleCharacterCount": { + "message": "Näytä merkkikohtainen numerointi", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index 1e3ed274ccc..57ea12a961d 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index 8601d607135..701621414d0 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -175,10 +175,10 @@ "message": "Adresse" }, "premiumRequired": { - "message": "Version Premium requise" + "message": "Premium requis" }, "premiumRequiredDesc": { - "message": "Une adhésion premium est requise pour utiliser cette fonctionnalité." + "message": "Une adhésion Premium est requise pour utiliser cette fonctionnalité." }, "errorOccurred": { "message": "Une erreur est survenue." @@ -269,7 +269,7 @@ "message": "Nom" }, "fullName": { - "message": "Nom et prénom" + "message": "Nom complet" }, "address1": { "message": "Adresse 1" @@ -500,19 +500,19 @@ "message": "Soumettre" }, "masterPass": { - "message": "Mot de passe maître" + "message": "Mot de passe principal" }, "masterPassDesc": { - "message": "Le mot de passe maître est le mot de passe que vous utilisez pour accéder à votre coffre. Il est très important de ne pas l'oublier. Il n'existe aucun moyen de le récupérer si vous le perdez." + "message": "Le mot de passe principal est le mot de passe que vous utilisez pour accéder à votre coffre. Il est très important de ne pas oublier votre mot de passe principal. Il n'existe aucun moyen de récupérer le mot de passe si vous l'oubliez." }, "masterPassHintDesc": { - "message": "Un indice de mot de passe maître peut vous aider à vous rappeler de votre mot de passe en cas d'oubli." + "message": "Un indice de mot de passe principal peut vous aider à vous souvenir de votre mot de passe si vous l'oubliez." }, "reTypeMasterPass": { - "message": "Saisir à nouveau le mot de passe maître" + "message": "Ressaisir le mot de passe principal" }, "masterPassHint": { - "message": "Indice du mot de passe maître (facultatif)" + "message": "Indice du mot de passe principal (facultatif)" }, "settings": { "message": "Paramètres" @@ -521,10 +521,10 @@ "message": "Indice mot de passe" }, "enterEmailToGetHint": { - "message": "Saisissez l'adresse e-mail de votre compte pour recevoir l'indice de votre mot de passe maître." + "message": "Saisissez l'adresse électronique de votre compte pour recevoir l'indice de votre mot de passe principal." }, "getMasterPasswordHint": { - "message": "Obtenir l'indice du mot de passe maître" + "message": "Obtenir l'indice du mot de passe principal" }, "emailRequired": { "message": "L'adresse e-mail est requise." @@ -533,22 +533,22 @@ "message": "Adresse e-mail invalide." }, "masterPasswordRequired": { - "message": "Le mot de passe maître est requis." + "message": "Le mot de passe principal est requis." }, "confirmMasterPasswordRequired": { - "message": "Le mot de passe maître doit être entré de nouveau." + "message": "Une nouvelle saisie du mot de passe principal est nécessaire." }, "masterPasswordMinlength": { - "message": "Le mot de passe maître doit comporter au moins 8 caractères." + "message": "Le mot de passe principal doit comporter au moins 8 caractères." }, "masterPassDoesntMatch": { - "message": "La confirmation du mot de passe maître ne correspond pas." + "message": "La confirmation du mot de passe principal ne correspond pas." }, "newAccountCreated": { "message": "Votre nouveau compte a été créé ! Vous pouvez maintenant vous authentifier." }, "masterPassSent": { - "message": "Nous vous avons envoyé un e-mail contenant votre indice de mot de passe maître." + "message": "Nous vous avons envoyé un courriel avec votre indice de mot de passe principal." }, "unexpectedError": { "message": "Une erreur inattendue est survenue." @@ -662,13 +662,13 @@ "message": "Identifiant non disponible" }, "noTwoStepProviders": { - "message": "Ce compte dispose d'une authentification à deux étapes, cependant, aucun service d'authentification à deux étapes n'est supporté par ce navigateur web." + "message": "Ce compte a une authentification à deux facteurs configurée, mais aucun des fournisseurs d'authentification à deux facteurs configurés ne sont pris en charge par cet appareil." }, "noTwoStepProviders2": { "message": "Veuillez ajouter des services additionnels qui supportent une utilisation sur plusieurs appareils (comme une application d'authentification)." }, "twoStepOptions": { - "message": "Options d'identification à deux étapes" + "message": "Options d'authentification à deux facteurs" }, "selfHostedEnvironment": { "message": "Environnement auto-hébergé" @@ -731,7 +731,7 @@ "message": "Êtes-vous sûr de vouloir vous déconnecter ?" }, "logOut": { - "message": "Déconnexion" + "message": "Se déconnecter" }, "addNewLogin": { "message": "Ajouter un site" @@ -776,10 +776,10 @@ "message": "Synchroniser le coffre maintenant" }, "changeMasterPass": { - "message": "Modifier le mot de passe maître" + "message": "Changer le mot de passe principal" }, "changeMasterPasswordConfirmation": { - "message": "Vous pouvez modifier votre mot de passe maître depuis le coffre web sur bitwarden.com. Souhaitez-vous visiter le site maintenant ?" + "message": "Vous pouvez changer votre mot de passe principal depuis le coffre web de bitwarden.com. Voulez-vous visiter le site web maintenant ?" }, "fingerprintPhrase": { "message": "Phrase d'empreinte", @@ -824,13 +824,13 @@ } }, "invalidMasterPassword": { - "message": "Mot de passe maître invalide" + "message": "Mot de passe principal invalide" }, "twoStepLoginConfirmation": { - "message": "L'identification en deux étapes sécurise plus encore votre compte en vous demandant à chaque identification la saisie d'un code de sécurité obtenu depuis un autre appareil, comme une clef de sécurité, une application d'authentification, un SMS, un appel téléphonique ou encore un courriel. L'identification en deux étapes peut être activée dans le coffre web sur bitwarden.com. Voulez-vous vous rendre sur le site web maintenant ?" + "message": "L'authentification à deux facteurs rend votre compte plus sûr en vous demandant de vérifier votre connexion avec un autre dispositif tel qu'une clé de sécurité, une application d'authentification, un SMS, un appel téléphonique ou un courriel. L'authentification à deux facteurs peut être configurée sur le coffre web de bitwarden.com. Voulez-vous visiter le site web maintenant ?" }, "twoStepLogin": { - "message": "Identification à deux facteurs" + "message": "Authentification à deux facteurs" }, "vaultTimeout": { "message": "Délai d'expiration du coffre" @@ -1050,49 +1050,49 @@ "message": "Gérer l'adhésion" }, "premiumManageAlert": { - "message": "Vous pouvez gérer votre adhésion depuis le coffre web sur bitwarden.com. Souhaitez-vous visiter le site web maintenant ?" + "message": "Vous pouvez gérer votre adhésion sur le coffre web de bitwarden.com. Voulez-vous visiter le site web maintenant ?" }, "premiumRefresh": { "message": "Actualiser l'adhésion" }, "premiumNotCurrentMember": { - "message": "Vous n'êtes actuellement pas un membre premium." + "message": "Vous n'êtes pas actuellement un membre Premium." }, "premiumSignUpAndGet": { - "message": "Devenez un membre premium et obtenez :" + "message": "Inscrivez-vous pour une adhésion Premium et obtenez :" }, "premiumSignUpStorage": { - "message": "1 Go de stockage de fichiers chiffrés." + "message": "1 Go de stockage chiffré pour les fichiers joints." }, "premiumSignUpTwoStep": { - "message": "Options d'identification en deux étapes additionnelles comme YubiKey, FIDO U2F et Duo." + "message": "Options additionnelles d'identification à deux étapes telles que YubiKey, FIDO U2F et Duo." }, "premiumSignUpReports": { - "message": "Rapports sur l'hygiène des mots de passe, la santé des comptes et les fuites de données pour assurer la sécurité de votre coffre." + "message": "Hygiène du mot de passe, santé du compte et rapports sur les brèches de données pour assurer la sécurité de votre coffre." }, "premiumSignUpTotp": { - "message": "Génération d'un code de vérification TOTP (2FA) pour les identifiants de votre coffre." + "message": "Générateur de code de vérification TOTP (2FA) pour les identifiants dans votre coffre." }, "premiumSignUpSupport": { - "message": "Support client prioritaire." + "message": "Assistance client prioritaire." }, "premiumSignUpFuture": { - "message": "Toutes les futures options premium. D'autres suivront prochainement !" + "message": "Toutes les futures fonctionnalités Premium. Plus à venir prochainement !" }, "premiumPurchase": { "message": "Acheter Premium" }, "premiumPurchaseAlert": { - "message": "Vous pouvez opter pour une adhésion premium depuis le coffre web sur bitwarden.com. Souhaitez-vous visiter le site web maintenant ?" + "message": "Vous pouvez acheter une adhésion Premium sur le coffre web de bitwarden.com. Voulez-vous visiter le site web maintenant ?" }, "premiumCurrentMember": { - "message": "Vous êtes un adhérent premium !" + "message": "Vous êtes un membre Premium !" }, "premiumCurrentMemberThanks": { - "message": "Merci de supporter Bitwarden." + "message": "Merci de soutenir Bitwarden." }, "premiumPrice": { - "message": "Tout pour seulement $PRICE$ /an !", + "message": "Tout pour seulement $PRICE$/an !", "placeholders": { "price": { "content": "$1", @@ -1355,10 +1355,10 @@ "description": "ex. A weak password. Scale: Weak -> Good -> Strong" }, "weakMasterPassword": { - "message": "Mot de passe maître faible" + "message": "Mot de passe principal faible" }, "weakMasterPasswordDesc": { - "message": "Le mot de passe maître que vous avez choisi est faible. Vous devriez utiliser un mot de passe (ou une phrase secrète) fort(e) pour protéger correctement votre compte Bitwarden. Êtes-vous sûr de vouloir utiliser ce mot de passe maître ?" + "message": "Le mot de passe principal que vous avez choisi est faible. Vous devriez utiliser un mot de passe principal fort (ou une phrase de passe) pour protéger correctement votre compte Bitwarden. Êtes-vous sûr de vouloir utiliser ce mot de passe principal ?" }, "pin": { "message": "Code PIN", @@ -1395,7 +1395,7 @@ "message": "Demander à Touch ID au démarrage" }, "lockWithMasterPassOnRestart": { - "message": "Verrouiller avec le mot de passe maître lors du redémarrage" + "message": "Verrouiller avec le mot de passe principal au redémarrage" }, "deleteAccount": { "message": "Supprimer le compte" @@ -1428,7 +1428,7 @@ "message": "Vous devez sélectionner au moins une collection." }, "premiumUpdated": { - "message": "Vous venez de passer à un compte Premium." + "message": "Vous avez mis à niveau vers Premium." }, "restore": { "message": "Restaurer" @@ -1462,7 +1462,7 @@ "message": "Action lors de l'expiration du délai du coffre" }, "vaultTimeoutActionLockDesc": { - "message": "Votre mot de passe maître ou une autre méthode de déverrouillage est nécessaire pour accéder de nouveau à votre coffre." + "message": "Un mot de passe principal ou une autre méthode de déverrouillage est nécessaire pour accéder à nouveau à votre coffre." }, "vaultTimeoutActionLogOutDesc": { "message": "Un coffre déconnecté nécessite que vous vous ré-authentifiez pour y accéder de nouveau." @@ -1500,7 +1500,7 @@ "message": "Supprimer définitivement" }, "vaultTimeoutLogOutConfirmation": { - "message": "La déconnexion supprimera tous les accès à votre coffre et nécessite une authentification en ligne après la période d'expiration. Êtes-vous sûr de vouloir utiliser ce paramètre?" + "message": "La déconnexion supprimera tout accès à votre coffre et nécessitera une authentification en ligne après la période d'expiration. Êtes-vous sûr de vouloir utiliser ce paramètre ?" }, "vaultTimeoutLogOutConfirmationTitle": { "message": "Confirmation de l'action lors de l'expiration du délai" @@ -1509,19 +1509,19 @@ "message": "Portail de connexion unique d'entreprise" }, "setMasterPassword": { - "message": "Définir le mot de passe maître" + "message": "Définir le mot de passe principal" }, "ssoCompleteRegistration": { - "message": "Afin de terminer la connexion avec SSO, veuillez définir un mot de passe maître pour accéder à votre coffre et le protéger." + "message": "Afin de finaliser la connexion avec SSO, veuillez définir un mot de passe principal pour accéder et protéger votre coffre." }, "newMasterPass": { - "message": "Nouveau mot de passe maître" + "message": "Nouveau mot de passe principal" }, "confirmNewMasterPass": { - "message": "Confirmer le nouveau mot de passe maître" + "message": "Confirmer le nouveau mot de passe principal" }, "masterPasswordPolicyInEffect": { - "message": "Une ou plusieurs politiques de l'organisation exigent que votre mot de passe maître réponde aux exigences suivantes :" + "message": "Une ou plusieurs politiques de l'organisation exigent que votre mot de passe principal réponde aux exigences suivantes :" }, "policyInEffectMinComplexity": { "message": "Score de complexité minimum de $SCORE$", @@ -1560,7 +1560,7 @@ } }, "masterPasswordPolicyRequirementsNotMet": { - "message": "Votre nouveau mot de passe maître ne répond pas aux exigences de la politique." + "message": "Votre nouveau mot de passe principal ne répond pas aux exigences en matière de politique de sécurité." }, "acceptPolicies": { "message": "En cochant cette case, vous acceptez les éléments suivants :" @@ -1608,7 +1608,7 @@ "message": "Veuillez vous assurer que la phrase d'empreinte affichée est identique à celle affichée dans l'extension de navigateur." }, "verifyNativeMessagingConnectionTitle": { - "message": "$APPID$ souhaite se connecter à Bitwarden", + "message": "$APPID$ veut se connecter à Bitwarden", "placeholders": { "appid": { "content": "$1", @@ -1766,7 +1766,7 @@ "message": "Copier le lien de ce Send dans mon presse-papiers lors de l'enregistrement." }, "sendDisabled": { - "message": "Send désactivé", + "message": "Send supprimé", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { @@ -1779,6 +1779,15 @@ "disabled": { "message": "Désactivé" }, + "removePassword": { + "message": "Supprimer le mot de passe" + }, + "removedPassword": { + "message": "Mot de passe supprimé" + }, + "removePasswordConfirmation": { + "message": "Êtes-vous sûr de vouloir supprimer le mot de passe ?" + }, "maxAccessCountReached": { "message": "Nombre maximum d'accès atteint" }, @@ -1804,22 +1813,22 @@ "message": "Vous devez vérifier votre adresse e-mail pour utiliser cette fonctionnalité." }, "passwordPrompt": { - "message": "Ressaisie du mot de passe maître" + "message": "Ressaisir le mot de passe principal" }, "passwordConfirmation": { - "message": "Confirmation du mot de passe maître" + "message": "Confirmation du mot de passe principal" }, "passwordConfirmationDesc": { - "message": "Cette action est protégée. Pour continuer, veuillez ressaisir votre mot de passe maître pour vérifier votre identité." + "message": "Cette action est protégée. Pour continuer, veuillez saisir à nouveau votre mot de passe principal pour vérifier votre identité." }, "updatedMasterPassword": { - "message": "Mot de passe maître mis à jour" + "message": "Mot de passe principal mis à jour" }, "updateMasterPassword": { - "message": "Mettre à jour le mot de passe maître" + "message": "Mettre à jour le mot de passe principal" }, "updateMasterPasswordWarning": { - "message": "Votre mot de passe maître a récemment été modifié par un administrateur de votre organisation. Pour pouvoir accéder au coffre, vous devez mettre à jour votre mot de passe maître maintenant. Poursuivre vous déconnectera de votre session actuelle, vous obligeant à vous reconnecter. Les sessions actives sur d'autres appareils peuvent rester actives jusqu'à une heure." + "message": "Votre mot de passe principal a été récemment changé par un administrateur de votre organisation. Pour pouvoir accéder au coffre, vous devez le mettre à jour maintenant. En poursuivant, vous serez déconnecté de votre session actuelle et vous devrez vous reconnecter. Les sessions actives sur d'autres appareils peuvent rester actives pendant encore une heure." }, "hours": { "message": "Heures" @@ -1847,10 +1856,10 @@ "message": "Inscription automatique" }, "resetPasswordAutoEnrollInviteWarning": { - "message": "Cette organisation a une politique d'entreprise qui vous inscrira automatiquement à la réinitialisation du mot de passe. L'inscription permettra aux administrateurs de l'organisation de changer votre mot de passe maître." + "message": "Cette organisation dispose d'une politique d'entreprise qui vous inscrira automatiquement à la réinitialisation du mot de passe. L'inscription permettra aux administrateurs de l'organisation de changer votre mot de passe principal." }, "vaultExportDisabled": { - "message": "Export du coffre désactivé" + "message": "Export du coffre supprimé" }, "personalVaultExportPolicyInEffect": { "message": "Une ou plusieurs politiques d'organisation vous empêchent d'exporter votre coffre personnel." @@ -1859,13 +1868,13 @@ "message": "Ajouter un compte" }, "removeMasterPassword": { - "message": "Supprimer le mot de passe maître" + "message": "Supprimer le mot de passe principal" }, "removedMasterPassword": { - "message": "Mot de passe maître supprimé." + "message": "Mot de passe principal supprimé" }, "convertOrganizationEncryptionDesc": { - "message": "$ORGANIZATION$ utilise SSO avec un serveur de clés auto-hébergé. Un mot de passe maître n'est plus nécessaire aux membres de cette organisation pour se connecter.", + "message": "$ORGANIZATION$ utilise le SSO avec un serveur de clés auto-hébergé. Un mot de passe principal n'est plus nécessaire aux membres de cette organisation pour se connecter.", "placeholders": { "organization": { "content": "$1", @@ -2023,7 +2032,7 @@ "message": "Coffre" }, "loginWithMasterPassword": { - "message": "Connectez-vous avec le mot de passe maître" + "message": "Se connecter avec le mot de passe principal" }, "loggingInAs": { "message": "Connexion en tant que" @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Connectez-vous avec un autre appareil" + }, + "toggleCharacterCount": { + "message": "Activer/Désactiver le compte de caractères", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index 6db87d185bf..89134d7dfb7 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "מושבת" }, + "removePassword": { + "message": "הסרת סיסמה" + }, + "removedPassword": { + "message": "הסיסמה הוסרה" + }, + "removePasswordConfirmation": { + "message": "להסיר את הסיסמה?" + }, "maxAccessCountReached": { "message": "מספר הגישות המרבי הושג" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "התחבר עם מכשיר אחר" + }, + "toggleCharacterCount": { + "message": "החלפת מצב ספירת תווים", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index 266fb589e54..65e6dcda725 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index 99ae141e59f..abded651d28 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Onemogućeno" }, + "removePassword": { + "message": "Ukloni lozinku" + }, + "removedPassword": { + "message": "Lozinka uklonjena" + }, + "removePasswordConfirmation": { + "message": "Sigurno želiš ukloniti lozinku?" + }, "maxAccessCountReached": { "message": "Dostignut najveći broj pristupanja" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Prijava drugim uređajem" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index e71cb0a4524..f728b3bbbb6 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -104,7 +104,7 @@ "message": "Email cím" }, "verificationCodeTotp": { - "message": "Ellenőrző kód (egyszeri-idő alapú)" + "message": "Ellenőrző kód (TOTP)" }, "website": { "message": "Webhely" @@ -126,7 +126,7 @@ "message": "Minimalizálás vágólapra másoláskor" }, "minimizeOnCopyToClipboardDesc": { - "message": "Minimalizálás egy elem adatainak vágólapra való másolásakor." + "message": "Minimalizálás asz összes elemadat vágólapra másolásakor." }, "toggleVisibility": { "message": "Láthatóság váltása" @@ -467,7 +467,7 @@ "message": "Fájl" }, "selectFile": { - "message": "Válassz ki egy fájlt." + "message": "Válasszunk egy fájlt." }, "maxFileSize": { "message": "Maximális fájl méret 500 MB." @@ -476,7 +476,7 @@ "message": "Ez a funkció nem használható a titkosítási kulcs frissítéséig." }, "editedFolder": { - "message": "A mappa szerkesztésre került." + "message": "A mappa mentésre került." }, "addedFolder": { "message": "A mappa hozzáadásra került." @@ -734,13 +734,13 @@ "message": "Kijelentkezés" }, "addNewLogin": { - "message": "Új fiók hozzáadása" + "message": "Új bejelentkezés" }, "addNewItem": { - "message": "Új elem hozzáadása" + "message": "Új elem" }, "addNewFolder": { - "message": "Új mappa hozzáadása" + "message": "Új mappa" }, "view": { "message": "Megtekintés" @@ -770,7 +770,7 @@ "message": "Blog" }, "followUs": { - "message": "Kövess minket" + "message": "Követés" }, "syncVault": { "message": "Széf szinkronizálása" @@ -793,7 +793,7 @@ "message": "Ugrás a webes széfhez" }, "getMobileApp": { - "message": "Mobil app letöltése" + "message": "Mobil alkalmazás beszerzése" }, "getBrowserExtension": { "message": "Böngésző bővítmény beszerzése" @@ -833,10 +833,10 @@ "message": "Kétlépcsős bejelentkezés" }, "vaultTimeout": { - "message": "Széf időkorlát" + "message": "Széf időkifutás" }, "vaultTimeoutDesc": { - "message": "Állítsd be a széfedhez egy időkorlátot és egy végrehajtandó műveletet." + "message": "Válasszuk ki, hogy a széfnél mikor legyen időkifutás és a kiválasztott művelet végrehajtása." }, "immediately": { "message": "Azonnal" @@ -872,13 +872,13 @@ "message": "4 óra" }, "onIdle": { - "message": "Tétlenség esetén" + "message": "Rendszer üresjárat esetén" }, "onSleep": { - "message": "Alvó módba lépéskor" + "message": "Rendszer alvó mód esetén" }, "onLocked": { - "message": "A rendszer zárolásakor" + "message": "Rendszer zárolás esetén" }, "onRestart": { "message": "Újraindításkor" @@ -904,7 +904,7 @@ "message": "Felismerhető kép megjelenítése minden bejelentkezés mellett." }, "enableMinToTray": { - "message": "Kicsinyítés tálcára" + "message": "Kicsinyítés tálcaikonná" }, "enableMinToTrayDesc": { "message": "Az ablak minimalizálásakor helyette egy ikon jelenik meg a rendszertálcán." @@ -1056,10 +1056,10 @@ "message": "Előfizetés ellenőrzése" }, "premiumNotCurrentMember": { - "message": "Jelenleg nem vagy prémium tag." + "message": "Jelenleg nincs prémium tagság." }, "premiumSignUpAndGet": { - "message": "Fizess elő prémiumra, hogy különféle előnyöket élvezhess:" + "message": "Regisztráció a prémium tagságra az alábbi funkciókért:" }, "premiumSignUpStorage": { "message": "1 GB titkosított fájlmelléklet tárhely." @@ -1231,7 +1231,7 @@ "description": "A programming term, also known as 'RegEx'." }, "matchDetection": { - "message": "Találatfelismerés", + "message": "Találat érzékelés", "description": "URI match detection for auto-fill." }, "defaultMatchDetection": { @@ -1503,7 +1503,7 @@ "message": "Kijelentkezve az összes széf elérés eltávolításra kerül és webes hitelesítésre van szükség az időkifutás után. Biztosan szeretnénk használni ezt a beállítást?" }, "vaultTimeoutLogOutConfirmationTitle": { - "message": "Időkifutáskori művelet megerősítése" + "message": "Időkifutás művelet megerősítés" }, "enterpriseSingleSignOn": { "message": "Vállalati önálló bejelentkezés" @@ -1587,7 +1587,7 @@ "message": "Sajnos a böngésző integrációt egyelőre csak a Mac App Store verzió támogatja." }, "browserIntegrationWindowsStoreDesc": { - "message": "A böngésző integrációt egyelőre csak a Microsoft Store verzió támogatja." + "message": "A böngésző integrációt egyelőre csak a Windows Store verzió támogatja." }, "browserIntegrationLinuxDesc": { "message": "Sajnos a böngésző integrációját a linux verzió jelenleg nem támogatja." @@ -1703,7 +1703,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinkLabel": { - "message": "Hivatkozás küldése", + "message": "Send küldése", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "textHiddenByDefault": { @@ -1711,15 +1711,15 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSend": { - "message": "A küldés létrejött", + "message": "A Send létrejött.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editedSend": { - "message": "A küldés szerkesztésre került", + "message": "A Send mentésre került.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deletedSend": { - "message": "A küldés törlésre került", + "message": "A Send törlésre került.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "newPassword": { @@ -1730,7 +1730,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createSend": { - "message": "A küldés létrejött.", + "message": "Új Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTextDesc": { @@ -1766,7 +1766,7 @@ "message": "A hivatkozás másolása a Küldés megosztásához a vágólapra mentéskor." }, "sendDisabled": { - "message": "A küldés kikapcsolásra került", + "message": "A Send eltávolításra került.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { @@ -1779,6 +1779,15 @@ "disabled": { "message": "Letiltva" }, + "removePassword": { + "message": "Jelszó eltávolítása" + }, + "removedPassword": { + "message": "A jelszó eltávolításra került." + }, + "removePasswordConfirmation": { + "message": "Biztosan eltávolításra kerüljön ez a jelszó?" + }, "maxAccessCountReached": { "message": "A maximális hozzáférések száma elérésre került." }, @@ -1798,7 +1807,7 @@ "message": "Egy vagy több szervezeti szabály érinti a Send opciókat." }, "emailVerificationRequired": { - "message": "E-mail hitelesítés szükséges" + "message": "Email hitelesítés szükséges" }, "emailVerificationRequiredDesc": { "message": "A funkció használatához ellenőrizni kell az email címet." @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Bejelentkezés más eszközzel" + }, + "toggleCharacterCount": { + "message": "Karakterszámláló váltás", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index 52ea55bf761..f5e42e40a4e 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -269,7 +269,7 @@ "message": "Nama Belakang" }, "fullName": { - "message": "Full name" + "message": "Nama lengkap" }, "address1": { "message": "Alamat 1" @@ -332,11 +332,11 @@ "message": "Boolean" }, "cfTypeLinked": { - "message": "Linked", + "message": "Terhubung", "description": "This describes a field that is 'linked' (related) to another field." }, "linkedValue": { - "message": "Linked value", + "message": "Nilai terkait", "description": "This describes a value that is 'linked' (related) to another value." }, "remove": { @@ -370,10 +370,10 @@ "message": "Anda yakin ingin menimpa sandi saat ini?" }, "overwriteUsername": { - "message": "Overwrite username" + "message": "Timpa username" }, "overwriteUsernameConfirmation": { - "message": "Are you sure you want to overwrite the current username?" + "message": "Anda yakin ingin menimpa nama pengguna saat ini?" }, "noneFolder": { "message": "Tidak Ada Folder", @@ -401,16 +401,16 @@ "message": "Panjang" }, "uppercase": { - "message": "Uppercase (A-Z)" + "message": "Huruf Kapital (A-Z)" }, "lowercase": { - "message": "Lowercase (a-z)" + "message": "Huruf Kecil (a-z)" }, "numbers": { - "message": "Numbers (0-9)" + "message": "Angka (0-9)" }, "specialCharacters": { - "message": "Special characters (!@#$%^&*)" + "message": "Karakter khusus (!@#$%^&*)" }, "numWords": { "message": "Jumlah kata" @@ -533,13 +533,13 @@ "message": "Alamat email tidak valid." }, "masterPasswordRequired": { - "message": "Master password is required." + "message": "Kata sandi utama diperlukan." }, "confirmMasterPasswordRequired": { - "message": "Master password retype is required." + "message": "Diperlukan pengetikan ulang kata sandi utama." }, "masterPasswordMinlength": { - "message": "Master password must be at least 8 characters long." + "message": "Kata sandi utama harus memiliki panjang setidaknya 8 karakter." }, "masterPassDoesntMatch": { "message": "Konfirmasi sandi utama tidak cocok." @@ -560,25 +560,25 @@ "message": "Tidak ada item yang dapat dicantumkan." }, "sendVerificationCode": { - "message": "Send a verification code to your email" + "message": "Kirim kode verifikasi ke email Anda" }, "sendCode": { - "message": "Send code" + "message": "Kirim kode" }, "codeSent": { - "message": "Code sent" + "message": "Kode terkirim" }, "verificationCode": { "message": "Kode Verifikasi" }, "confirmIdentity": { - "message": "Confirm your identity to continue." + "message": "Konfirmasi identitas Anda untuk melanjutkan." }, "verificationCodeRequired": { "message": "Kode verifikasi diperlukan." }, "invalidVerificationCode": { - "message": "Invalid verification code" + "message": "Kode verifikasi tidak valid" }, "continue": { "message": "Lanjutkan" @@ -752,16 +752,16 @@ "message": "Memuat..." }, "lockVault": { - "message": "Lock vault" + "message": "Kunci brankas" }, "passwordGenerator": { "message": "Pembuat Kata Sandi" }, "contactUs": { - "message": "Contact us" + "message": "Hubungi kami" }, "getHelp": { - "message": "Get help" + "message": "Dapatkan bantuan" }, "fileBugReport": { "message": "Buat Laporan Masalah" @@ -898,10 +898,10 @@ "description": "Clipboard is the operating system thing where you copy/paste data to on your device." }, "enableFavicon": { - "message": "Show website icons" + "message": "Tampilkan ikon website" }, "faviconDesc": { - "message": "Show a recognizable image next to each login." + "message": "Tampilkan gambar pengenal di samping setiap login." }, "enableMinToTray": { "message": "Kecilkan ke Ikon Baki" @@ -1213,7 +1213,7 @@ "description": "Domain name. Ex. website.com" }, "domainName": { - "message": "Domain name", + "message": "Nama Domain", "description": "Domain name. Ex. website.com" }, "host": { @@ -1267,7 +1267,7 @@ "description": "ex. Date this item was updated" }, "dateCreated": { - "message": "Created", + "message": "Dibuat", "description": "ex. Date this item was created" }, "datePasswordUpdated": { @@ -1285,34 +1285,34 @@ "description": "hCaptcha is the name of a website, should not be translated" }, "loadAccessibilityCookie": { - "message": "Load accessibility cookie" + "message": "Muat cookie aksesibilitas" }, "registerAccessibilityUser": { - "message": "Register as an accessibility user at", + "message": "Daftar sebagai pengguna aksesibilitas di", "description": "ex. Register as an accessibility user at hcaptcha.com" }, "copyPasteLink": { - "message": "Copy and paste the link sent to your email below" + "message": "Salin dan tempel tautan yang dikirimkan ke email Anda di bawah" }, "enterhCaptchaUrl": { - "message": "Enter URL to load accessibility cookie for hCaptcha", + "message": "Masukkan URL untuk memuat cookie aksesibilitas untuk hCaptcha", "description": "hCaptcha is the name of a website, should not be translated" }, "hCaptchaUrlRequired": { - "message": "hCaptcha Url is required", + "message": "url hCaptcha diperlukan", "description": "hCaptcha is the name of a website, should not be translated" }, "invalidUrl": { - "message": "Invalid Url" + "message": "Url tidak valid" }, "done": { - "message": "Done" + "message": "Selesai" }, "accessibilityCookieSaved": { - "message": "Accessibility cookie saved!" + "message": "Cookie aksesibilitas disimpan!" }, "noAccessibilityCookieSaved": { - "message": "No accessibility cookie saved" + "message": "Tidak ada cookie aksesibilitas yang disimpan" }, "warning": { "message": "PERINGATAN", @@ -1389,28 +1389,28 @@ "message": "Verifikasi untuk Bitwarden." }, "autoPromptWindowsHello": { - "message": "Ask for Windows Hello on launch" + "message": "Minta Windows Hello saat diluncurkan" }, "autoPromptTouchId": { - "message": "Ask for Touch ID on launch" + "message": "Minta Touch ID saat diluncurkan" }, "lockWithMasterPassOnRestart": { "message": "Kunci dengan kata sandi utama saat komputer dihidupkan ulang" }, "deleteAccount": { - "message": "Delete account" + "message": "Hapus akun" }, "deleteAccountDesc": { - "message": "Proceed below to delete your account and all vault data." + "message": "Lanjutkan di bawah untuk menghapus akun Anda dan semua data brankas." }, "deleteAccountWarning": { - "message": "Deleting your account is permanent. It cannot be undone." + "message": "Menghapus akun Anda bersifat permanen. Tindakan ini tidak dapat dibatalkan." }, "accountDeleted": { - "message": "Account deleted" + "message": "Akun Dihapus" }, "accountDeletedDesc": { - "message": "Your account has been closed and all associated data has been deleted." + "message": "Akun Anda telah ditutup dan semua data terkait telah dihapus." }, "preferences": { "message": "Preferensi" @@ -1566,7 +1566,7 @@ "message": "Dengan mencentang kotak ini, anda menyetujui yang berikut:" }, "acceptPoliciesRequired": { - "message": "Terms of Service and Privacy Policy have not been acknowledged." + "message": "Persyaratan Layanan dan Kebijakan Privasi belum disetujui." }, "enableBrowserIntegration": { "message": "Aktifkan integrasi browser" @@ -1575,13 +1575,13 @@ "message": "Integrasi browser digunakan untuk biometrik di browser." }, "enableDuckDuckGoBrowserIntegration": { - "message": "Allow DuckDuckGo browser integration" + "message": "Izinkan integrasi browser DuckDuckGo" }, "enableDuckDuckGoBrowserIntegrationDesc": { - "message": "Use your Bitwarden vault when browsing with DuckDuckGo." + "message": "Gunakan brankas Bitwarden Anda saat menjelajah dengan DuckDuckGo." }, "browserIntegrationUnsupportedTitle": { - "message": "Browser integration not supported" + "message": "Integrasi browser tidak didukung" }, "browserIntegrationMasOnlyDesc": { "message": "Sayangnya integrasi browser hanya didukung di versi Mac App Store untuk saat ini." @@ -1590,7 +1590,7 @@ "message": "Sayangnya integrasi browser saat ini tidak didukung di versi Windows Store." }, "browserIntegrationLinuxDesc": { - "message": "Unfortunately browser integration is currently not supported in the linux version." + "message": "Sayangnya, integrasi browser saat ini tidak didukung di versi linux." }, "enableBrowserIntegrationFingerprint": { "message": "Mewajibkan verifikasi untuk integrasi browser" @@ -1608,7 +1608,7 @@ "message": "Harap pastikan sidik jari yang ditampilkan identik dengan sidik jari yang ditunjukkan di ekstensi browser." }, "verifyNativeMessagingConnectionTitle": { - "message": "$APPID$ wants to connect to Bitwarden", + "message": "$APPID$ ingin menyambungkan ke Bitwarden", "placeholders": { "appid": { "content": "$1", @@ -1617,10 +1617,10 @@ } }, "verifyNativeMessagingConnectionDesc": { - "message": "Would you like to approve this request?" + "message": "Apakah Anda ingin menyetujui permintaan ini?" }, "verifyNativeMessagingConnectionWarning": { - "message": "If you did not initiate this request, do not approve it." + "message": "Jika Anda bukan yang memulai permintaan ini, jangan setujui." }, "biometricsNotEnabledTitle": { "message": "Biometrik tidak diaktifkan" @@ -1779,6 +1779,15 @@ "disabled": { "message": "Dinonaktifkan" }, + "removePassword": { + "message": "Hapus kata sandi" + }, + "removedPassword": { + "message": "Kata sandi dihapus" + }, + "removePasswordConfirmation": { + "message": "Anda yakin ingin menghapus kata sandi?" + }, "maxAccessCountReached": { "message": "Jumlah akses maksimum tercapai" }, @@ -1813,22 +1822,22 @@ "message": "Aksi ini terproteksi. Untuk melanjutkan, masukkan kembali sandi utama Anda untuk verifikasi identitas." }, "updatedMasterPassword": { - "message": "Updated master password" + "message": "Kata sandi utama diperbarui" }, "updateMasterPassword": { - "message": "Update master password" + "message": "Perbarui kata sandi utama" }, "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": "Kata sandi utama Anda baru-baru ini diubah oleh administrator di organisasi Anda. Untuk mengakses brankas, Anda harus memperbaruinya sekarang. Melanjutkan akan mengeluarkan Anda dari sesi Anda saat ini, mengharuskan Anda untuk masuk kembali. Sesi aktif di perangkat lain dapat terus aktif hingga satu jam." }, "hours": { - "message": "Hours" + "message": "Jam" }, "minutes": { - "message": "Minutes" + "message": "Menit" }, "vaultTimeoutPolicyInEffect": { - "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)", + "message": "Kebijakan organisasi Anda memengaruhi waktu tunggu brankas. Waktu tunggu brankas maksimum yang diizinkan adalah $HOURS$ jam dan $MINUTES$ menit", "placeholders": { "hours": { "content": "$1", @@ -1841,31 +1850,31 @@ } }, "vaultTimeoutTooLarge": { - "message": "Your vault timeout exceeds the restrictions set by your organization." + "message": "Waktu tunggu brankas Anda melebihi batasan yang diatur organisasi Anda." }, "resetPasswordPolicyAutoEnroll": { - "message": "Automatic enrollment" + "message": "Pendaftaran otomatis" }, "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": "Organisasi ini memiliki kebijakan perusahaan yang secara otomatis mendaftarkan Anda dalam pengaturan ulang kata sandi. Dengan mendaftar, akan memungkinkan administrator organisasi untuk mengubah kata sandi utama Anda." }, "vaultExportDisabled": { - "message": "Vault export removed" + "message": "Ekspor brankas dihapus" }, "personalVaultExportPolicyInEffect": { - "message": "One or more organization policies prevents you from exporting your personal vault." + "message": "Satu atau beberapa kebijakan organisasi mencegah Anda mengekspor brankas pribadi Anda." }, "addAccount": { - "message": "Add account" + "message": "Tambahkan akun" }, "removeMasterPassword": { - "message": "Remove master password" + "message": "Hapus Kata Sandi Utama" }, "removedMasterPassword": { - "message": "Master password removed" + "message": "Sandi utama dihapus" }, "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$ menggunakan SSO dengan server kunci yang dihosting sendiri. Kata sandi utama tidak lagi diperlukan untuk masuk untuk anggota organisasi ini.", "placeholders": { "organization": { "content": "$1", @@ -1874,31 +1883,31 @@ } }, "leaveOrganization": { - "message": "Leave organization" + "message": "Tinggalkan organisasi" }, "leaveOrganizationConfirmation": { - "message": "Are you sure you want to leave this organization?" + "message": "Apakah Anda yakin ingin meninggalkan organisasi ini?" }, "leftOrganization": { - "message": "You have left the organization." + "message": "Anda telah keluar dari organisasi." }, "ssoKeyConnectorError": { - "message": "Key connector error: make sure key connector is available and working correctly." + "message": "Kesalahan konektor kunci: pastikan konektor kunci tersedia dan berfungsi dengan benar." }, "lockAllVaults": { - "message": "Lock all vaults" + "message": "Kunci semua brankas" }, "accountLimitReached": { - "message": "No more than 5 accounts may be logged in at the same time." + "message": "Tidak lebih dari 5 akun dapat masuk pada saat yang bersamaan." }, "accountPreferences": { - "message": "Preferences" + "message": "Preferensi" }, "appPreferences": { - "message": "App settings (all accounts)" + "message": "Setelan aplikasi (semua akun)" }, "accountSwitcherLimitReached": { - "message": "Account limit reached. Log out of an account to add another." + "message": "Batas akun tercapai. Keluar dari akun untuk menambahkan akun lain." }, "settingsTitle": { "message": "App settings for $EMAIL$", @@ -1913,7 +1922,7 @@ "message": "Switch account" }, "options": { - "message": "Options" + "message": "Pilihan" }, "sessionTimeout": { "message": "Your session has timed out. Please go back and try logging in again." @@ -1931,10 +1940,10 @@ } }, "locked": { - "message": "Locked" + "message": "Terkunci" }, "unlocked": { - "message": "Unlocked" + "message": "Tidak terkunci" }, "generator": { "message": "Generator" @@ -1968,19 +1977,19 @@ "message": "Use your domain's configured catch-all inbox." }, "random": { - "message": "Random" + "message": "Acak" }, "randomWord": { - "message": "Random word" + "message": "Kata acak" }, "websiteName": { - "message": "Website name" + "message": "Nama situs" }, "service": { - "message": "Service" + "message": "Layanan" }, "allVaults": { - "message": "All vaults" + "message": "Semua brankas" }, "searchOrganization": { "message": "Search organization" @@ -2020,7 +2029,7 @@ "message": "Mir" }, "vault": { - "message": "Vault" + "message": "Brankas" }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -2032,10 +2041,10 @@ "message": "Remember email" }, "notYou": { - "message": "Not you?" + "message": "Bukan Anda?" }, "newAroundHere": { - "message": "New around here?" + "message": "Pengguna baru?" }, "loggingInTo": { "message": "Logging in to $DOMAIN$", @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index b4f3e40b524..06c8ea27201 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabilitato" }, + "removePassword": { + "message": "Rimuovi la password" + }, + "removedPassword": { + "message": "Password rimossa" + }, + "removePasswordConfirmation": { + "message": "Sei sicuro di voler rimuovere la password?" + }, "maxAccessCountReached": { "message": "Numero massimo di accessi raggiunto" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Accedi con un altro dispositivo" + }, + "toggleCharacterCount": { + "message": "Attiva/Disattiva conteggio caratteri", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index 2bcdf30d0da..b169644cc20 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "無効" }, + "removePassword": { + "message": "パスワードを削除" + }, + "removedPassword": { + "message": "パスワードを削除しました" + }, + "removePasswordConfirmation": { + "message": "パスワードを削除してもよろしいですか?" + }, "maxAccessCountReached": { "message": "最大アクセス数に達しました" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "別のデバイスでログイン" + }, + "toggleCharacterCount": { + "message": "文字カウントを切り替える", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index 1e3ed274ccc..57ea12a961d 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index 1e3ed274ccc..57ea12a961d 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index 9be6a61531f..41a30c277c9 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "ಗರಿಷ್ಠ ಪ್ರವೇಶ ಎಣಿಕೆ ತಲುಪಿದೆ" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index d8ee402dc36..91f5ae75bc0 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "비활성화됨" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "최대 접근 횟수 도달" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index 295684eb821..f0df7485811 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -1569,31 +1569,31 @@ "message": "Nav apstiprināti izmantošanas nosacījumi un privātuma politika." }, "enableBrowserIntegration": { - "message": "Iespējot pārlūka saistīšanu" + "message": "Iespējot sasaistīšanu ar pārlūku" }, "enableBrowserIntegrationDesc": { "message": "Pārlūka saistīšana tiek izmantota pārlūka biometrijas nodrošināšanai." }, "enableDuckDuckGoBrowserIntegration": { - "message": "Atļaut DuckDuckGo pārlūka saistīšanu" + "message": "Atļaut sasaistīšanu ar DuckDuckGo pārlūku" }, "enableDuckDuckGoBrowserIntegrationDesc": { "message": "Izmantot Bitwarden glabātavu, kad pārlūko ar DuckDuckGo." }, "browserIntegrationUnsupportedTitle": { - "message": "Pārlūka saistīšana nav atbalstīta" + "message": "Sasaistīšana ar pārlūku nav atbalstīta" }, "browserIntegrationMasOnlyDesc": { - "message": "Diemžēl pārlūka saistīšana pagaidām ir nodrošināta tikai Mac App Store laidienā." + "message": "Diemžēl sasaistīšāna ar pārlūku pagaidām ir nodrošināta tikai Mac App Store laidienā." }, "browserIntegrationWindowsStoreDesc": { - "message": "Diemžēl pārlūka saistīšana pagaidām nav nodrošināta Windows veikala laidienā." + "message": "Diemžēl sasaistīšana ar pārlūku pagaidām nav nodrošināta Windows veikala laidienā." }, "browserIntegrationLinuxDesc": { - "message": "Diemžēl pārlūka saistīšana Linux laidienā pagaidām nav nodrošināta." + "message": "Diemžēl sasaistīšana ar pārlūku Linux laidienā pagaidām nav nodrošināta." }, "enableBrowserIntegrationFingerprint": { - "message": "Pieprasīt apstiprinājumu pārlūka saistīšanai" + "message": "Pieprasīt apstiprinājumu sasaistīšanai ar pārlūku" }, "enableBrowserIntegrationFingerprintDesc": { "message": "Iespējo papildus drošības slāni, pieprasot atpazīšanas vārdkopas pārbaudi, kad tiek izveidota saikne starp darbvirsmu un pārlūku. Kad iespējots, ir nepieciešama lietotāja mijiedarbīga un apstiprināšana katru reizi, kad tiek izveidots savienojums." @@ -1779,6 +1779,15 @@ "disabled": { "message": "Atspējots" }, + "removePassword": { + "message": "Noņemt paroli" + }, + "removedPassword": { + "message": "Parole noņemta" + }, + "removePasswordConfirmation": { + "message": "Vai tiešām noņemt paroli?" + }, "maxAccessCountReached": { "message": "Sasniegts lielākais pieļaujamais piekļuvju skaits" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Pierakstīties ar citu ierīci" + }, + "toggleCharacterCount": { + "message": "Pārslēgt rakstzīmju skaita attēlošanu", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index 6b1e5878487..fd472abecc1 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index 37ad33342b8..0bceb3bd481 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index da7b50c9599..2e38d650cd0 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Deaktivert" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Maksimalt antall tilganger nådd" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Logg inn med en annen enhet" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json new file mode 100644 index 00000000000..57ea12a961d --- /dev/null +++ b/apps/desktop/src/locales/ne/messages.json @@ -0,0 +1,2065 @@ +{ + "bitwarden": { + "message": "Bitwarden" + }, + "filters": { + "message": "Filters" + }, + "allItems": { + "message": "All items" + }, + "favorites": { + "message": "Favorites" + }, + "types": { + "message": "Types" + }, + "typeLogin": { + "message": "Login" + }, + "typeCard": { + "message": "Card" + }, + "typeIdentity": { + "message": "Identity" + }, + "typeSecureNote": { + "message": "Secure note" + }, + "folders": { + "message": "Folders" + }, + "collections": { + "message": "Collections" + }, + "searchVault": { + "message": "Search vault" + }, + "addItem": { + "message": "Add item" + }, + "shared": { + "message": "Shared" + }, + "share": { + "message": "Share" + }, + "moveToOrganization": { + "message": "Move to organization" + }, + "movedItemToOrg": { + "message": "$ITEMNAME$ moved to $ORGNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret Item" + }, + "orgname": { + "content": "$2", + "example": "Company Name" + } + } + }, + "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." + }, + "attachments": { + "message": "Attachments" + }, + "viewItem": { + "message": "View item" + }, + "name": { + "message": "Name" + }, + "uri": { + "message": "URI" + }, + "uriPosition": { + "message": "URI $POSITION$", + "description": "A listing of URIs. Ex: URI 1, URI 2, URI 3, etc.", + "placeholders": { + "position": { + "content": "$1", + "example": "2" + } + } + }, + "newUri": { + "message": "New URI" + }, + "username": { + "message": "Username" + }, + "password": { + "message": "Password" + }, + "passphrase": { + "message": "Passphrase" + }, + "editItem": { + "message": "Edit item" + }, + "emailAddress": { + "message": "Email address" + }, + "verificationCodeTotp": { + "message": "Verification code (TOTP)" + }, + "website": { + "message": "Website" + }, + "notes": { + "message": "Notes" + }, + "customFields": { + "message": "Custom fields" + }, + "launch": { + "message": "Launch" + }, + "copyValue": { + "message": "Copy value", + "description": "Copy value to clipboard" + }, + "minimizeOnCopyToClipboard": { + "message": "Minimize when copying to clipboard" + }, + "minimizeOnCopyToClipboardDesc": { + "message": "Minimize application when copying an item's data to the clipboard." + }, + "toggleVisibility": { + "message": "Toggle visibility" + }, + "toggleCollapse": { + "message": "Toggle collapse", + "description": "Toggling an expand/collapse state." + }, + "cardholderName": { + "message": "Cardholder name" + }, + "number": { + "message": "Number" + }, + "brand": { + "message": "Brand" + }, + "expiration": { + "message": "Expiration" + }, + "securityCode": { + "message": "Security code" + }, + "identityName": { + "message": "Identity name" + }, + "company": { + "message": "Company" + }, + "ssn": { + "message": "Social Security number" + }, + "passportNumber": { + "message": "Passport number" + }, + "licenseNumber": { + "message": "License number" + }, + "email": { + "message": "Email" + }, + "phone": { + "message": "Phone" + }, + "address": { + "message": "Address" + }, + "premiumRequired": { + "message": "Premium required" + }, + "premiumRequiredDesc": { + "message": "A Premium membership is required to use this feature." + }, + "errorOccurred": { + "message": "An error has occurred." + }, + "error": { + "message": "Error" + }, + "january": { + "message": "January" + }, + "february": { + "message": "February" + }, + "march": { + "message": "March" + }, + "april": { + "message": "April" + }, + "may": { + "message": "May" + }, + "june": { + "message": "June" + }, + "july": { + "message": "July" + }, + "august": { + "message": "August" + }, + "september": { + "message": "September" + }, + "october": { + "message": "October" + }, + "november": { + "message": "November" + }, + "december": { + "message": "December" + }, + "ex": { + "message": "ex.", + "description": "Short abbreviation for 'example'." + }, + "title": { + "message": "Title" + }, + "mr": { + "message": "Mr" + }, + "mrs": { + "message": "Mrs" + }, + "ms": { + "message": "Ms" + }, + "dr": { + "message": "Dr" + }, + "expirationMonth": { + "message": "Expiration month" + }, + "expirationYear": { + "message": "Expiration year" + }, + "select": { + "message": "Select" + }, + "other": { + "message": "Other" + }, + "generatePassword": { + "message": "Generate password" + }, + "type": { + "message": "Type" + }, + "firstName": { + "message": "First name" + }, + "middleName": { + "message": "Middle name" + }, + "lastName": { + "message": "Last name" + }, + "fullName": { + "message": "Full name" + }, + "address1": { + "message": "Address 1" + }, + "address2": { + "message": "Address 2" + }, + "address3": { + "message": "Address 3" + }, + "cityTown": { + "message": "City / Town" + }, + "stateProvince": { + "message": "State / Province" + }, + "zipPostalCode": { + "message": "Zip / Postal code" + }, + "country": { + "message": "Country" + }, + "save": { + "message": "Save" + }, + "cancel": { + "message": "Cancel" + }, + "delete": { + "message": "Delete" + }, + "favorite": { + "message": "Favorite" + }, + "edit": { + "message": "Edit" + }, + "authenticatorKeyTotp": { + "message": "Authenticator key (TOTP)" + }, + "folder": { + "message": "Folder" + }, + "newCustomField": { + "message": "New custom field" + }, + "value": { + "message": "Value" + }, + "dragToSort": { + "message": "Drag to sort" + }, + "cfTypeText": { + "message": "Text" + }, + "cfTypeHidden": { + "message": "Hidden" + }, + "cfTypeBoolean": { + "message": "Boolean" + }, + "cfTypeLinked": { + "message": "Linked", + "description": "This describes a field that is 'linked' (related) to another field." + }, + "linkedValue": { + "message": "Linked value", + "description": "This describes a value that is 'linked' (related) to another value." + }, + "remove": { + "message": "Remove" + }, + "nameRequired": { + "message": "Name is required." + }, + "addedItem": { + "message": "Item added" + }, + "editedItem": { + "message": "Item saved" + }, + "deleteItem": { + "message": "Delete item" + }, + "deleteFolder": { + "message": "Delete folder" + }, + "deleteAttachment": { + "message": "Delete attachment" + }, + "deleteItemConfirmation": { + "message": "Do you really want to send to the trash?" + }, + "deletedItem": { + "message": "Item sent to trash" + }, + "overwritePasswordConfirmation": { + "message": "Are you sure you want to overwrite the current password?" + }, + "overwriteUsername": { + "message": "Overwrite username" + }, + "overwriteUsernameConfirmation": { + "message": "Are you sure you want to overwrite the current username?" + }, + "noneFolder": { + "message": "No folder", + "description": "This is the folder for uncategorized items" + }, + "addFolder": { + "message": "Add folder" + }, + "editFolder": { + "message": "Edit folder" + }, + "regeneratePassword": { + "message": "Regenerate password" + }, + "copyPassword": { + "message": "Copy password" + }, + "copyUri": { + "message": "Copy URI" + }, + "copyVerificationCodeTotp": { + "message": "Copy verification code (TOTP)" + }, + "length": { + "message": "Length" + }, + "uppercase": { + "message": "Uppercase (A-Z)" + }, + "lowercase": { + "message": "Lowercase (a-z)" + }, + "numbers": { + "message": "Numbers (0-9)" + }, + "specialCharacters": { + "message": "Special characters (!@#$%^&*)" + }, + "numWords": { + "message": "Number of words" + }, + "wordSeparator": { + "message": "Word separator" + }, + "capitalize": { + "message": "Capitalize", + "description": "Make the first letter of a word uppercase." + }, + "includeNumber": { + "message": "Include number" + }, + "close": { + "message": "Close" + }, + "minNumbers": { + "message": "Minimum numbers" + }, + "minSpecial": { + "message": "Minimum special", + "description": "Minimum Special Characters" + }, + "ambiguous": { + "message": "Avoid ambiguous characters" + }, + "searchCollection": { + "message": "Search collection" + }, + "searchFolder": { + "message": "Search folder" + }, + "searchFavorites": { + "message": "Search favorites" + }, + "searchType": { + "message": "Search type", + "description": "Search item type" + }, + "newAttachment": { + "message": "Add new attachment" + }, + "deletedAttachment": { + "message": "Attachment deleted" + }, + "deleteAttachmentConfirmation": { + "message": "Are you sure you want to delete this attachment?" + }, + "attachmentSaved": { + "message": "Attachment saved" + }, + "file": { + "message": "File" + }, + "selectFile": { + "message": "Select a file" + }, + "maxFileSize": { + "message": "Maximum file size is 500 MB." + }, + "updateKey": { + "message": "You cannot use this feature until you update your encryption key." + }, + "editedFolder": { + "message": "Folder saved" + }, + "addedFolder": { + "message": "Folder added" + }, + "deleteFolderConfirmation": { + "message": "Are you sure you want to delete this folder?" + }, + "deletedFolder": { + "message": "Folder deleted" + }, + "loginOrCreateNewAccount": { + "message": "Log in or create a new account to access your secure vault." + }, + "createAccount": { + "message": "Create account" + }, + "logIn": { + "message": "Log in" + }, + "submit": { + "message": "Submit" + }, + "masterPass": { + "message": "Master password" + }, + "masterPassDesc": { + "message": "The master password is the password you use to access your vault. It is very important that you do not forget your master password. There is no way to recover the password in the event that you forget it." + }, + "masterPassHintDesc": { + "message": "A master password hint can help you remember your password if you forget it." + }, + "reTypeMasterPass": { + "message": "Re-type master password" + }, + "masterPassHint": { + "message": "Master password hint (optional)" + }, + "settings": { + "message": "Settings" + }, + "passwordHint": { + "message": "Password hint" + }, + "enterEmailToGetHint": { + "message": "Enter your account email address to receive your master password hint." + }, + "getMasterPasswordHint": { + "message": "Get master password hint" + }, + "emailRequired": { + "message": "Email address is required." + }, + "invalidEmail": { + "message": "Invalid email address." + }, + "masterPasswordRequired": { + "message": "Master password is required." + }, + "confirmMasterPasswordRequired": { + "message": "Master password retype is required." + }, + "masterPasswordMinlength": { + "message": "Master password must be at least 8 characters long." + }, + "masterPassDoesntMatch": { + "message": "Master password confirmation does not match." + }, + "newAccountCreated": { + "message": "Your new account has been created! You may now log in." + }, + "masterPassSent": { + "message": "We've sent you an email with your master password hint." + }, + "unexpectedError": { + "message": "An unexpected error has occurred." + }, + "itemInformation": { + "message": "Item information" + }, + "noItemsInList": { + "message": "There are no items to list." + }, + "sendVerificationCode": { + "message": "Send a verification code to your email" + }, + "sendCode": { + "message": "Send code" + }, + "codeSent": { + "message": "Code sent" + }, + "verificationCode": { + "message": "Verification code" + }, + "confirmIdentity": { + "message": "Confirm your identity to continue." + }, + "verificationCodeRequired": { + "message": "Verification code is required." + }, + "invalidVerificationCode": { + "message": "Invalid verification code" + }, + "continue": { + "message": "Continue" + }, + "enterVerificationCodeApp": { + "message": "Enter the 6 digit verification code from your authenticator app." + }, + "enterVerificationCodeEmail": { + "message": "Enter the 6 digit verification code that was emailed to $EMAIL$.", + "placeholders": { + "email": { + "content": "$1", + "example": "example@gmail.com" + } + } + }, + "verificationCodeEmailSent": { + "message": "Verification email sent to $EMAIL$.", + "placeholders": { + "email": { + "content": "$1", + "example": "example@gmail.com" + } + } + }, + "rememberMe": { + "message": "Remember me" + }, + "sendVerificationCodeEmailAgain": { + "message": "Send verification code email again" + }, + "useAnotherTwoStepMethod": { + "message": "Use another two-step login method" + }, + "insertYubiKey": { + "message": "Insert your YubiKey into your computer's USB port, then touch its button." + }, + "insertU2f": { + "message": "Insert your security key into your computer's USB port. If it has a button, touch it." + }, + "recoveryCodeDesc": { + "message": "Lost access to all of your two-factor providers? Use your recovery code to turn off all two-factor providers on your account." + }, + "recoveryCodeTitle": { + "message": "Recovery code" + }, + "authenticatorAppTitle": { + "message": "Authenticator app" + }, + "authenticatorAppDesc": { + "message": "Use an authenticator app (such as Authy or Google Authenticator) to generate time-based verification codes.", + "description": "'Authy' and 'Google Authenticator' are product names and should not be translated." + }, + "yubiKeyTitle": { + "message": "YubiKey OTP security key" + }, + "yubiKeyDesc": { + "message": "Use a YubiKey to access your account. Works with YubiKey 4, 4 Nano, 4C, and NEO devices." + }, + "duoDesc": { + "message": "Verify with Duo Security using the Duo Mobile app, SMS, phone call, or U2F security key.", + "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.", + "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." + }, + "emailTitle": { + "message": "Email" + }, + "emailDesc": { + "message": "Verification codes will be emailed to you." + }, + "loginUnavailable": { + "message": "Login unavailable" + }, + "noTwoStepProviders": { + "message": "This account has two-step login set up, however, none of the configured two-step providers are supported by this device." + }, + "noTwoStepProviders2": { + "message": "Please add additional providers that are better supported across devices (such as an authenticator app)." + }, + "twoStepOptions": { + "message": "Two-step login options" + }, + "selfHostedEnvironment": { + "message": "Self-hosted environment" + }, + "selfHostedEnvironmentFooter": { + "message": "Specify the base URL of your on-premises hosted Bitwarden installation." + }, + "customEnvironment": { + "message": "Custom environment" + }, + "customEnvironmentFooter": { + "message": "For advanced users. You can specify the base URL of each service independently." + }, + "baseUrl": { + "message": "Server URL" + }, + "apiUrl": { + "message": "API server URL" + }, + "webVaultUrl": { + "message": "Web vault server URL" + }, + "identityUrl": { + "message": "Identity server URL" + }, + "notificationsUrl": { + "message": "Notifications server URL" + }, + "iconsUrl": { + "message": "Icons server URL" + }, + "environmentSaved": { + "message": "Environment URLs saved" + }, + "ok": { + "message": "Ok" + }, + "yes": { + "message": "Yes" + }, + "no": { + "message": "No" + }, + "overwritePassword": { + "message": "Overwrite password" + }, + "learnMore": { + "message": "Learn more" + }, + "featureUnavailable": { + "message": "Feature unavailable" + }, + "loggedOut": { + "message": "Logged out" + }, + "loginExpired": { + "message": "Your login session has expired." + }, + "logOutConfirmation": { + "message": "Are you sure you want to log out?" + }, + "logOut": { + "message": "Log out" + }, + "addNewLogin": { + "message": "New login" + }, + "addNewItem": { + "message": "New item" + }, + "addNewFolder": { + "message": "New folder" + }, + "view": { + "message": "View" + }, + "account": { + "message": "Account" + }, + "loading": { + "message": "Loading..." + }, + "lockVault": { + "message": "Lock vault" + }, + "passwordGenerator": { + "message": "Password generator" + }, + "contactUs": { + "message": "Contact us" + }, + "getHelp": { + "message": "Get help" + }, + "fileBugReport": { + "message": "File a bug report" + }, + "blog": { + "message": "Blog" + }, + "followUs": { + "message": "Follow us" + }, + "syncVault": { + "message": "Sync vault" + }, + "changeMasterPass": { + "message": "Change master password" + }, + "changeMasterPasswordConfirmation": { + "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "fingerprintPhrase": { + "message": "Fingerprint phrase", + "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." + }, + "yourAccountsFingerprint": { + "message": "Your account's fingerprint phrase", + "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." + }, + "goToWebVault": { + "message": "Go to web vault" + }, + "getMobileApp": { + "message": "Get mobile app" + }, + "getBrowserExtension": { + "message": "Get browser extension" + }, + "syncingComplete": { + "message": "Syncing complete" + }, + "syncingFailed": { + "message": "Syncing failed" + }, + "yourVaultIsLocked": { + "message": "Your vault is locked. Verify your identity to continue." + }, + "unlock": { + "message": "Unlock" + }, + "loggedInAsOn": { + "message": "Logged in as $EMAIL$ on $HOSTNAME$.", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + }, + "hostname": { + "content": "$2", + "example": "bitwarden.com" + } + } + }, + "invalidMasterPassword": { + "message": "Invalid master password" + }, + "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?" + }, + "twoStepLogin": { + "message": "Two-step login" + }, + "vaultTimeout": { + "message": "Vault timeout" + }, + "vaultTimeoutDesc": { + "message": "Choose when your vault will take the vault timeout action." + }, + "immediately": { + "message": "Immediately" + }, + "tenSeconds": { + "message": "10 seconds" + }, + "twentySeconds": { + "message": "20 seconds" + }, + "thirtySeconds": { + "message": "30 seconds" + }, + "oneMinute": { + "message": "1 minute" + }, + "twoMinutes": { + "message": "2 minutes" + }, + "fiveMinutes": { + "message": "5 minutes" + }, + "fifteenMinutes": { + "message": "15 minutes" + }, + "thirtyMinutes": { + "message": "30 minutes" + }, + "oneHour": { + "message": "1 hour" + }, + "fourHours": { + "message": "4 hours" + }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, + "onLocked": { + "message": "On system lock" + }, + "onRestart": { + "message": "On restart" + }, + "never": { + "message": "Never" + }, + "security": { + "message": "Security" + }, + "clearClipboard": { + "message": "Clear clipboard", + "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.", + "description": "Clipboard is the operating system thing where you copy/paste data to on your device." + }, + "enableFavicon": { + "message": "Show website icons" + }, + "faviconDesc": { + "message": "Show a recognizable image next to each login." + }, + "enableMinToTray": { + "message": "Minimize to tray icon" + }, + "enableMinToTrayDesc": { + "message": "When minimizing the window, show an icon in the system tray instead." + }, + "enableMinToMenuBar": { + "message": "Minimize to menu bar" + }, + "enableMinToMenuBarDesc": { + "message": "When minimizing the window, show an icon in the menu bar instead." + }, + "enableCloseToTray": { + "message": "Close to tray icon" + }, + "enableCloseToTrayDesc": { + "message": "When closing the window, show an icon in the system tray instead." + }, + "enableCloseToMenuBar": { + "message": "Close to menu bar" + }, + "enableCloseToMenuBarDesc": { + "message": "When closing the window, show an icon in the menu bar instead." + }, + "enableTray": { + "message": "Show tray icon" + }, + "enableTrayDesc": { + "message": "Always show an icon in the system tray." + }, + "startToTray": { + "message": "Start to tray icon" + }, + "startToTrayDesc": { + "message": "When the application is first started, only show an icon in the system tray." + }, + "startToMenuBar": { + "message": "Start to menu bar" + }, + "startToMenuBarDesc": { + "message": "When the application is first started, only show an icon in the menu bar." + }, + "openAtLogin": { + "message": "Start automatically on login" + }, + "openAtLoginDesc": { + "message": "Start the Bitwarden desktop application automatically on login." + }, + "alwaysShowDock": { + "message": "Always show in the Dock" + }, + "alwaysShowDockDesc": { + "message": "Show the Bitwarden icon in the Dock even when minimized to the menu bar." + }, + "confirmTrayTitle": { + "message": "Confirm hiding tray" + }, + "confirmTrayDesc": { + "message": "Turning off this setting will also turn off all other tray related settings." + }, + "language": { + "message": "Language" + }, + "languageDesc": { + "message": "Change the language used by the application. Restart is required." + }, + "theme": { + "message": "Theme" + }, + "themeDesc": { + "message": "Change the application's color theme." + }, + "dark": { + "message": "Dark", + "description": "Dark color" + }, + "light": { + "message": "Light", + "description": "Light color" + }, + "copy": { + "message": "Copy", + "description": "Copy to clipboard" + }, + "checkForUpdates": { + "message": "Check for updates…" + }, + "version": { + "message": "Version $VERSION_NUM$", + "placeholders": { + "version_num": { + "content": "$1", + "example": "1.2.3" + } + } + }, + "restartToUpdate": { + "message": "Restart to update" + }, + "restartToUpdateDesc": { + "message": "Version $VERSION_NUM$ is ready to install. You must restart the application to complete the installation. Do you want to restart and update now?", + "placeholders": { + "version_num": { + "content": "$1", + "example": "1.2.3" + } + } + }, + "updateAvailable": { + "message": "Update available" + }, + "updateAvailableDesc": { + "message": "An update was found. Do you want to download it now?" + }, + "restart": { + "message": "Restart" + }, + "later": { + "message": "Later" + }, + "noUpdatesAvailable": { + "message": "No updates are currently available. You are using the latest version." + }, + "updateError": { + "message": "Update error" + }, + "unknown": { + "message": "Unknown" + }, + "copyUsername": { + "message": "Copy username" + }, + "copyNumber": { + "message": "Copy number", + "description": "Copy credit card number" + }, + "copySecurityCode": { + "message": "Copy security code", + "description": "Copy credit card security code (CVV)" + }, + "premiumMembership": { + "message": "Premium membership" + }, + "premiumManage": { + "message": "Manage membership" + }, + "premiumManageAlert": { + "message": "You can manage your membership on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "premiumRefresh": { + "message": "Refresh membership" + }, + "premiumNotCurrentMember": { + "message": "You are not currently a Premium member." + }, + "premiumSignUpAndGet": { + "message": "Sign up for a Premium membership and get:" + }, + "premiumSignUpStorage": { + "message": "1 GB encrypted storage for file attachments." + }, + "premiumSignUpTwoStep": { + "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + }, + "premiumSignUpReports": { + "message": "Password hygiene, account health, and data breach reports to keep your vault safe." + }, + "premiumSignUpTotp": { + "message": "TOTP verification code (2FA) generator for logins in your vault." + }, + "premiumSignUpSupport": { + "message": "Priority customer support." + }, + "premiumSignUpFuture": { + "message": "All future premium features. More coming soon!" + }, + "premiumPurchase": { + "message": "Purchase Premium" + }, + "premiumPurchaseAlert": { + "message": "You can purchase premium membership on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "premiumCurrentMember": { + "message": "You are a premium member!" + }, + "premiumCurrentMemberThanks": { + "message": "Thank you for supporting Bitwarden." + }, + "premiumPrice": { + "message": "All for just $PRICE$ /year!", + "placeholders": { + "price": { + "content": "$1", + "example": "$10" + } + } + }, + "refreshComplete": { + "message": "Refresh complete" + }, + "passwordHistory": { + "message": "Password history" + }, + "clear": { + "message": "Clear", + "description": "To clear something out. example: To clear browser history." + }, + "noPasswordsInList": { + "message": "There are no passwords to list." + }, + "undo": { + "message": "Undo" + }, + "redo": { + "message": "Redo" + }, + "cut": { + "message": "Cut", + "description": "Cut to clipboard" + }, + "paste": { + "message": "Paste", + "description": "Paste from clipboard" + }, + "selectAll": { + "message": "Select all" + }, + "zoomIn": { + "message": "Zoom in" + }, + "zoomOut": { + "message": "Zoom out" + }, + "resetZoom": { + "message": "Reset zoom" + }, + "toggleFullScreen": { + "message": "Toggle full screen" + }, + "reload": { + "message": "Reload" + }, + "toggleDevTools": { + "message": "Toggle developer tools" + }, + "minimize": { + "message": "Minimize", + "description": "Minimize window" + }, + "zoom": { + "message": "Zoom" + }, + "bringAllToFront": { + "message": "Bring all to front", + "description": "Bring all windows to front (foreground)" + }, + "aboutBitwarden": { + "message": "About Bitwarden" + }, + "services": { + "message": "Services" + }, + "hideBitwarden": { + "message": "Hide Bitwarden" + }, + "hideOthers": { + "message": "Hide others" + }, + "showAll": { + "message": "Show all" + }, + "quitBitwarden": { + "message": "Quit Bitwarden" + }, + "valueCopied": { + "message": "$VALUE$ copied", + "description": "Value has been copied to the clipboard.", + "placeholders": { + "value": { + "content": "$1", + "example": "Password" + } + } + }, + "help": { + "message": "Help" + }, + "window": { + "message": "Window" + }, + "checkPassword": { + "message": "Check if password has been exposed." + }, + "passwordExposed": { + "message": "This password has been exposed $VALUE$ time(s) in data breaches. You should change it.", + "placeholders": { + "value": { + "content": "$1", + "example": "2" + } + } + }, + "passwordSafe": { + "message": "This password was not found in any known data breaches. It should be safe to use." + }, + "baseDomain": { + "message": "Base domain", + "description": "Domain name. Ex. website.com" + }, + "domainName": { + "message": "Domain name", + "description": "Domain name. Ex. website.com" + }, + "host": { + "message": "Host", + "description": "A URL's host value. For example, the host of https://sub.domain.com:443 is 'sub.domain.com:443'." + }, + "exact": { + "message": "Exact" + }, + "startsWith": { + "message": "Starts with" + }, + "regEx": { + "message": "Regular expression", + "description": "A programming term, also known as 'RegEx'." + }, + "matchDetection": { + "message": "Match detection", + "description": "URI match detection for auto-fill." + }, + "defaultMatchDetection": { + "message": "Default match detection", + "description": "Default URI match detection for auto-fill." + }, + "toggleOptions": { + "message": "Toggle options" + }, + "organization": { + "message": "Organization", + "description": "An entity of multiple related people (ex. a team or business organization)." + }, + "default": { + "message": "Default" + }, + "exit": { + "message": "Exit" + }, + "showHide": { + "message": "Show / Hide", + "description": "Text for a button that toggles the visibility of the window. Shows the window when it is hidden or hides the window if it is currently open." + }, + "hideToTray": { + "message": "Hide to tray" + }, + "alwaysOnTop": { + "message": "Always on top", + "description": "Application window should always stay on top of other windows" + }, + "dateUpdated": { + "message": "Updated", + "description": "ex. Date this item was updated" + }, + "dateCreated": { + "message": "Created", + "description": "ex. Date this item was created" + }, + "datePasswordUpdated": { + "message": "Password updated", + "description": "ex. Date this password was updated" + }, + "exportVault": { + "message": "Export vault" + }, + "fileFormat": { + "message": "File format" + }, + "hCaptchaUrl": { + "message": "hCaptcha Url", + "description": "hCaptcha is the name of a website, should not be translated" + }, + "loadAccessibilityCookie": { + "message": "Load accessibility cookie" + }, + "registerAccessibilityUser": { + "message": "Register as an accessibility user at", + "description": "ex. Register as an accessibility user at hcaptcha.com" + }, + "copyPasteLink": { + "message": "Copy and paste the link sent to your email below" + }, + "enterhCaptchaUrl": { + "message": "Enter URL to load accessibility cookie for hCaptcha", + "description": "hCaptcha is the name of a website, should not be translated" + }, + "hCaptchaUrlRequired": { + "message": "hCaptcha Url is required", + "description": "hCaptcha is the name of a website, should not be translated" + }, + "invalidUrl": { + "message": "Invalid Url" + }, + "done": { + "message": "Done" + }, + "accessibilityCookieSaved": { + "message": "Accessibility cookie saved!" + }, + "noAccessibilityCookieSaved": { + "message": "No accessibility cookie saved" + }, + "warning": { + "message": "WARNING", + "description": "WARNING (should stay in capitalized letters if the language permits)" + }, + "confirmVaultExport": { + "message": "Confirm vault export" + }, + "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." + }, + "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." + }, + "encExportAccountWarningDesc": { + "message": "Account encryption keys are unique to each Bitwarden user account, so you can't import an encrypted export into a different account." + }, + "noOrganizationsList": { + "message": "You do not belong to any organizations. Organizations allow you to securely share items with other users." + }, + "noCollectionsInList": { + "message": "There are no collections to list." + }, + "ownership": { + "message": "Ownership" + }, + "whoOwnsThisItem": { + "message": "Who owns this item?" + }, + "strong": { + "message": "Strong", + "description": "ex. A strong password. Scale: Weak -> Good -> Strong" + }, + "good": { + "message": "Good", + "description": "ex. A good password. Scale: Weak -> Good -> Strong" + }, + "weak": { + "message": "Weak", + "description": "ex. A weak password. Scale: Weak -> Good -> Strong" + }, + "weakMasterPassword": { + "message": "Weak master password" + }, + "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?" + }, + "pin": { + "message": "PIN", + "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." + }, + "unlockWithPin": { + "message": "Unlock with 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." + }, + "pinRequired": { + "message": "PIN code is required." + }, + "invalidPin": { + "message": "Invalid PIN code." + }, + "unlockWithWindowsHello": { + "message": "Unlock with Windows Hello" + }, + "windowsHelloConsentMessage": { + "message": "Verify for Bitwarden." + }, + "unlockWithTouchId": { + "message": "Unlock with Touch ID" + }, + "touchIdConsentMessage": { + "message": "unlock your vault" + }, + "autoPromptWindowsHello": { + "message": "Ask for Windows Hello on launch" + }, + "autoPromptTouchId": { + "message": "Ask for Touch ID on launch" + }, + "lockWithMasterPassOnRestart": { + "message": "Lock with master password on restart" + }, + "deleteAccount": { + "message": "Delete account" + }, + "deleteAccountDesc": { + "message": "Proceed below to delete your account and all vault data." + }, + "deleteAccountWarning": { + "message": "Deleting your account is permanent. It cannot be undone." + }, + "accountDeleted": { + "message": "Account deleted" + }, + "accountDeletedDesc": { + "message": "Your account has been closed and all associated data has been deleted." + }, + "preferences": { + "message": "Preferences" + }, + "enableMenuBar": { + "message": "Show menu bar icon" + }, + "enableMenuBarDesc": { + "message": "Always show an icon in the menu bar." + }, + "hideToMenuBar": { + "message": "Hide to menu bar" + }, + "selectOneCollection": { + "message": "You must select at least one collection." + }, + "premiumUpdated": { + "message": "You've upgraded to Premium." + }, + "restore": { + "message": "Restore" + }, + "premiumManageAlertAppStore": { + "message": "You can manage your subscription from the App Store. Do you want to visit the App Store now?" + }, + "legal": { + "message": "Legal", + "description": "Noun. As in 'legal documents', like our terms of service and privacy policy." + }, + "termsOfService": { + "message": "Terms of Service" + }, + "privacyPolicy": { + "message": "Privacy Policy" + }, + "unsavedChangesConfirmation": { + "message": "Are you sure you want to leave? If you leave now then your current information will not be saved." + }, + "unsavedChangesTitle": { + "message": "Unsaved changes" + }, + "clone": { + "message": "Clone" + }, + "passwordGeneratorPolicyInEffect": { + "message": "One or more organization policies are affecting your generator settings." + }, + "vaultTimeoutAction": { + "message": "Vault timeout action" + }, + "vaultTimeoutActionLockDesc": { + "message": "Master password or other unlock method is required to access your vault again." + }, + "vaultTimeoutActionLogOutDesc": { + "message": "Re-authentication is required to access your vault again." + }, + "lock": { + "message": "Lock", + "description": "Verb form: to make secure or inaccesible by" + }, + "trash": { + "message": "Trash", + "description": "Noun: a special folder to hold deleted items" + }, + "searchTrash": { + "message": "Search trash" + }, + "permanentlyDeleteItem": { + "message": "Permanently delete item" + }, + "permanentlyDeleteItemConfirmation": { + "message": "Are you sure you want to permanently delete this item?" + }, + "permanentlyDeletedItem": { + "message": "Item permanently deleted" + }, + "restoreItem": { + "message": "Restore item" + }, + "restoreItemConfirmation": { + "message": "Are you sure you want to restore this item?" + }, + "restoredItem": { + "message": "Item restored" + }, + "permanentlyDelete": { + "message": "Permanently delete" + }, + "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?" + }, + "vaultTimeoutLogOutConfirmationTitle": { + "message": "Timeout action confirmation" + }, + "enterpriseSingleSignOn": { + "message": "Enterprise single sign-on" + }, + "setMasterPassword": { + "message": "Set master password" + }, + "ssoCompleteRegistration": { + "message": "In order to complete logging in with SSO, please set a master password to access and protect your vault." + }, + "newMasterPass": { + "message": "New master password" + }, + "confirmNewMasterPass": { + "message": "Confirm new master password" + }, + "masterPasswordPolicyInEffect": { + "message": "One or more organization policies require your master password to meet the following requirements:" + }, + "policyInEffectMinComplexity": { + "message": "Minimum complexity score of $SCORE$", + "placeholders": { + "score": { + "content": "$1", + "example": "4" + } + } + }, + "policyInEffectMinLength": { + "message": "Minimum length of $LENGTH$", + "placeholders": { + "length": { + "content": "$1", + "example": "14" + } + } + }, + "policyInEffectUppercase": { + "message": "Contain one or more uppercase characters" + }, + "policyInEffectLowercase": { + "message": "Contain one or more lowercase characters" + }, + "policyInEffectNumbers": { + "message": "Contain one or more numbers" + }, + "policyInEffectSpecial": { + "message": "Contain one or more of the following special characters $CHARS$", + "placeholders": { + "chars": { + "content": "$1", + "example": "!@#$%^&*" + } + } + }, + "masterPasswordPolicyRequirementsNotMet": { + "message": "Your new master password does not meet the policy requirements." + }, + "acceptPolicies": { + "message": "By checking this box you agree to the following:" + }, + "acceptPoliciesRequired": { + "message": "Terms of Service and Privacy Policy have not been acknowledged." + }, + "enableBrowserIntegration": { + "message": "Allow browser integration" + }, + "enableBrowserIntegrationDesc": { + "message": "Used for biometrics in browser." + }, + "enableDuckDuckGoBrowserIntegration": { + "message": "Allow DuckDuckGo browser integration" + }, + "enableDuckDuckGoBrowserIntegrationDesc": { + "message": "Use your Bitwarden vault when browsing with DuckDuckGo." + }, + "browserIntegrationUnsupportedTitle": { + "message": "Browser integration not supported" + }, + "browserIntegrationMasOnlyDesc": { + "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." + }, + "browserIntegrationWindowsStoreDesc": { + "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." + }, + "browserIntegrationLinuxDesc": { + "message": "Unfortunately browser integration is currently not supported in the linux version." + }, + "enableBrowserIntegrationFingerprint": { + "message": "Require verification for browser integration" + }, + "enableBrowserIntegrationFingerprintDesc": { + "message": "Add an additional layer of security by requiring fingerprint phrase confirmation when establishing a link between your desktop and browser. This requires user action and verification each time a connection is created." + }, + "approve": { + "message": "Approve" + }, + "verifyBrowserTitle": { + "message": "Verify browser connection" + }, + "verifyBrowserDesc": { + "message": "Please ensure the shown fingerprint is identical to the fingerprint showed in the browser extension." + }, + "verifyNativeMessagingConnectionTitle": { + "message": "$APPID$ wants to connect to Bitwarden", + "placeholders": { + "appid": { + "content": "$1", + "example": "My App" + } + } + }, + "verifyNativeMessagingConnectionDesc": { + "message": "Would you like to approve this request?" + }, + "verifyNativeMessagingConnectionWarning": { + "message": "If you did not initiate this request, do not approve it." + }, + "biometricsNotEnabledTitle": { + "message": "Biometrics not set up" + }, + "biometricsNotEnabledDesc": { + "message": "Browser biometrics requires desktop biometrics to be set up in the settings first." + }, + "personalOwnershipSubmitError": { + "message": "Due to an enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." + }, + "hintEqualsPassword": { + "message": "Your password hint cannot be the same as your password." + }, + "personalOwnershipPolicyInEffect": { + "message": "An organization policy is affecting your ownership options." + }, + "allSends": { + "message": "All Sends", + "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeFile": { + "message": "File" + }, + "sendTypeText": { + "message": "Text" + }, + "searchSends": { + "message": "Search Sends", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "editSend": { + "message": "Edit Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "myVault": { + "message": "My vault" + }, + "text": { + "message": "Text" + }, + "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." + }, + "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." + }, + "maxAccessCount": { + "message": "Maximum access count", + "description": "This text will be displayed after a Send has been accessed the maximum amount of times." + }, + "maxAccessCountDesc": { + "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." + }, + "currentAccessCount": { + "message": "Current access count" + }, + "disableSend": { + "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." + }, + "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." + }, + "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." + }, + "sendLink": { + "message": "Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendLinkLabel": { + "message": "Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "textHiddenByDefault": { + "message": "When accessing the Send, hide the text by default", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "createdSend": { + "message": "Send added", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "editedSend": { + "message": "Send saved", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "deletedSend": { + "message": "Send deleted", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "newPassword": { + "message": "New password" + }, + "whatTypeOfSend": { + "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." + }, + "createSend": { + "message": "New Send", + "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." + }, + "sendFileDesc": { + "message": "The file you want to send." + }, + "days": { + "message": "$DAYS$ days", + "placeholders": { + "days": { + "content": "$1", + "example": "1" + } + } + }, + "oneDay": { + "message": "1 day" + }, + "custom": { + "message": "Custom" + }, + "deleteSendConfirmation": { + "message": "Are you sure you want to delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "copySendLinkToClipboard": { + "message": "Copy Send link to clipboard", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "copySendLinkOnSave": { + "message": "Copy the link to share this Send to my clipboard upon save." + }, + "sendDisabled": { + "message": "Send removed", + "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.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "copyLink": { + "message": "Copy link" + }, + "disabled": { + "message": "Disabled" + }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, + "maxAccessCountReached": { + "message": "Max access count reached" + }, + "expired": { + "message": "Expired" + }, + "pendingDeletion": { + "message": "Pending deletion" + }, + "webAuthnAuthenticate": { + "message": "Authenticate WebAuthn" + }, + "hideEmail": { + "message": "Hide my email address from recipients." + }, + "sendOptionsPolicyInEffect": { + "message": "One or more organization policies are affecting your Send options." + }, + "emailVerificationRequired": { + "message": "Email verification required" + }, + "emailVerificationRequiredDesc": { + "message": "You must verify your email to use this feature." + }, + "passwordPrompt": { + "message": "Master password re-prompt" + }, + "passwordConfirmation": { + "message": "Master password confirmation" + }, + "passwordConfirmationDesc": { + "message": "This action is protected. To continue, please re-enter your master password to verify your identity." + }, + "updatedMasterPassword": { + "message": "Updated master password" + }, + "updateMasterPassword": { + "message": "Update master password" + }, + "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." + }, + "hours": { + "message": "Hours" + }, + "minutes": { + "message": "Minutes" + }, + "vaultTimeoutPolicyInEffect": { + "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "resetPasswordPolicyAutoEnroll": { + "message": "Automatic enrollment" + }, + "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." + }, + "vaultExportDisabled": { + "message": "Vault export removed" + }, + "personalVaultExportPolicyInEffect": { + "message": "One or more organization policies prevents you from exporting your personal vault." + }, + "addAccount": { + "message": "Add account" + }, + "removeMasterPassword": { + "message": "Remove master password" + }, + "removedMasterPassword": { + "message": "Master password removed" + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "leaveOrganization": { + "message": "Leave organization" + }, + "leaveOrganizationConfirmation": { + "message": "Are you sure you want to leave this organization?" + }, + "leftOrganization": { + "message": "You have left the organization." + }, + "ssoKeyConnectorError": { + "message": "Key connector error: make sure key connector is available and working correctly." + }, + "lockAllVaults": { + "message": "Lock all vaults" + }, + "accountLimitReached": { + "message": "No more than 5 accounts may be logged in at the same time." + }, + "accountPreferences": { + "message": "Preferences" + }, + "appPreferences": { + "message": "App settings (all accounts)" + }, + "accountSwitcherLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "settingsTitle": { + "message": "App settings for $EMAIL$", + "placeholders": { + "email": { + "content": "$1", + "example": "jdoe@example.com" + } + } + }, + "switchAccount": { + "message": "Switch account" + }, + "options": { + "message": "Options" + }, + "sessionTimeout": { + "message": "Your session has timed out. Please go back and try logging in again." + }, + "exportingPersonalVaultTitle": { + "message": "Exporting individual vault" + }, + "exportingPersonalVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, + "locked": { + "message": "Locked" + }, + "unlocked": { + "message": "Unlocked" + }, + "generator": { + "message": "Generator" + }, + "whatWouldYouLikeToGenerate": { + "message": "What would you like to generate?" + }, + "passwordType": { + "message": "Password type" + }, + "regenerateUsername": { + "message": "Regenerate username" + }, + "generateUsername": { + "message": "Generate username" + }, + "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" + }, + "plusAddressedEmailDesc": { + "message": "Use your email provider's sub-addressing capabilities." + }, + "catchallEmail": { + "message": "Catch-all email" + }, + "catchallEmailDesc": { + "message": "Use your domain's configured catch-all inbox." + }, + "random": { + "message": "Random" + }, + "randomWord": { + "message": "Random word" + }, + "websiteName": { + "message": "Website name" + }, + "service": { + "message": "Service" + }, + "allVaults": { + "message": "All vaults" + }, + "searchOrganization": { + "message": "Search organization" + }, + "searchMyVault": { + "message": "Search my vault" + }, + "forwardedEmail": { + "message": "Forwarded email alias" + }, + "forwardedEmailDesc": { + "message": "Generate an email alias with an external forwarding service." + }, + "hostname": { + "message": "Hostname", + "description": "Part of a URL." + }, + "apiAccessToken": { + "message": "API Access Token" + }, + "apiKey": { + "message": "API key" + }, + "premiumSubcriptionRequired": { + "message": "Premium subscription required" + }, + "organizationIsDisabled": { + "message": "Organization suspended" + }, + "disabledOrganizationFilterError": { + "message": "Items in suspended organizations cannot be accessed. Contact your organization owner for assistance." + }, + "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." + }, + "cardBrandMir": { + "message": "Mir" + }, + "vault": { + "message": "Vault" + }, + "loginWithMasterPassword": { + "message": "Log in with master password" + }, + "loggingInAs": { + "message": "Logging in as" + }, + "rememberEmail": { + "message": "Remember email" + }, + "notYou": { + "message": "Not you?" + }, + "newAroundHere": { + "message": "New around here?" + }, + "loggingInTo": { + "message": "Logging in to $DOMAIN$", + "placeholders": { + "domain": { + "content": "$1", + "example": "example.com" + } + } + }, + "logInWithAnotherDevice": { + "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." + } +} diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index 38b3a24d418..ca912821681 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Uitgeschakeld" }, + "removePassword": { + "message": "Wachtwoord verwijderen" + }, + "removedPassword": { + "message": "Wachtwoord verwijderd" + }, + "removePasswordConfirmation": { + "message": "Weet je zeker dat je het wachtwoord wilt verwijderen?" + }, "maxAccessCountReached": { "message": "Maximum aantal keren benaderd" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Inloggen met een ander apparaat" + }, + "toggleCharacterCount": { + "message": "Tekentelling in-/uitschakelen", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index d0b68b8ec5c..a3b8970c7c9 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index ea5a4e040f3..e25e9c5fcf4 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -308,7 +308,7 @@ "message": "Edytuj" }, "authenticatorKeyTotp": { - "message": "Klucz Uwierzytelniający (TOTP)" + "message": "Klucz uwierzytelniający (TOTP)" }, "folder": { "message": "Folder" @@ -734,7 +734,7 @@ "message": "Wyloguj się" }, "addNewLogin": { - "message": "Dodaj dane logowania" + "message": "Nowe dane logowania" }, "addNewItem": { "message": "Nowy element" @@ -836,7 +836,7 @@ "message": "Blokowanie sejfu" }, "vaultTimeoutDesc": { - "message": "Wybierz kiedy sejf zostanie zablokowany i wykonaj następującą akcję." + "message": "Wybierz, kiedy sejf zostanie zablokowany i wykonaj następującą akcję." }, "immediately": { "message": "Natychmiast" @@ -928,7 +928,7 @@ "message": "Po zamknięciu okna, pokaż ikonę w pasku menu." }, "enableTray": { - "message": "Włącz ikonę w zasobniku systemowym" + "message": "Pokaż ikonę w zasobniku systemowym" }, "enableTrayDesc": { "message": "Zawsze pokazuj ikonę w zasobniku systemowym." @@ -958,7 +958,7 @@ "message": "Pokaż ikonę Bitwarden w Docku po zminimalizowaniu do paska menu." }, "confirmTrayTitle": { - "message": "Potwierdź wyłączenie zasobnika systemowego" + "message": "Potwierdź ukrycie zasobnika systemowego" }, "confirmTrayDesc": { "message": "Wyłączenie tej opcji spowoduje wyłącznie wszystkich innych powiązanych ustawień z zasobnikiem systemowym." @@ -1169,7 +1169,7 @@ "message": "Ukryj Bitwarden" }, "hideOthers": { - "message": "Ukryj pozostałe" + "message": "Ukryj inne" }, "showAll": { "message": "Pokaż wszystkie" @@ -1462,10 +1462,10 @@ "message": "Sposób blokowania sejfu" }, "vaultTimeoutActionLockDesc": { - "message": "Po zablokowaniu sejfu, musisz ponownie wpisać hasło główne, aby uzyskać do niego dostęp." + "message": "Wpisz hasło główne lub odblokuj sejf inną metodą, aby uzyskać do niego dostęp." }, "vaultTimeoutActionLogOutDesc": { - "message": "Po wylogowaniu się z sejfu, musisz ponownie zalogować się, aby uzyskać do niego dostęp." + "message": "Zaloguj się ponownie, aby uzyskać dostęp do sejfu." }, "lock": { "message": "Zablokuj", @@ -1566,19 +1566,19 @@ "message": "Zaznaczając tę opcję, akceptujesz:" }, "acceptPoliciesRequired": { - "message": "Warunki użytkowania i polityka prywatności nie zostały zaakceptowane." + "message": "Regulamin i polityka prywatności nie zostały zaakceptowane." }, "enableBrowserIntegration": { "message": "Włącz połączenie z przeglądarką" }, "enableBrowserIntegrationDesc": { - "message": "Połączenie z przeglądarką jest używane do odblokowania danymi biometrycznymi." + "message": "Używane do odblokowania danymi biometrycznymi." }, "enableDuckDuckGoBrowserIntegration": { "message": "Włącz połączenie z przeglądarką DuckDuckGo" }, "enableDuckDuckGoBrowserIntegrationDesc": { - "message": "Korzystaj z sejfu Bitwarden podczas przeglądania za pomocą DuckDuckGo." + "message": "Korzystaj z sejfu Bitwarden podczas przeglądania za pomocą przeglądarki DuckDuckGo." }, "browserIntegrationUnsupportedTitle": { "message": "Połączenie z przeglądarką nie jest obsługiwane" @@ -1596,7 +1596,7 @@ "message": "Wymagaj weryfikacji połączenia z przeglądarką" }, "enableBrowserIntegrationFingerprintDesc": { - "message": "Wymagaj sprawdzenia identyfikatora podczas tworzenia połączenia między aplikacją a przeglądarką. Po włączeniu tej funkcji, musisz zweryfikować każde nawiązywane połączenie." + "message": "Dodaj dodatkową warstwę zabezpieczeń, weryfikując unikalny identyfikator konta podczas tworzenia linku między aplikacją a przeglądarką. Funkcja ta wymaga weryfikacji identyfikatora przy każdym nawiązaniu połączenia." }, "approve": { "message": "Zatwierdź" @@ -1608,7 +1608,7 @@ "message": "Upewnij się, że wyświetlony identyfikator jest identyczny z pokazanym w rozszerzeniu przeglądarki." }, "verifyNativeMessagingConnectionTitle": { - "message": "$APPID$ chce połączyć się z Bitwarden", + "message": "Aplikacja $APPID$ chce połączyć się z Bitwarden", "placeholders": { "appid": { "content": "$1", @@ -1629,7 +1629,7 @@ "message": "Aby włączyć dane biometryczne w przeglądarce, musisz włączyć tę samą funkcję w ustawianiach aplikacji." }, "personalOwnershipSubmitError": { - "message": "Ze względu na zasadę przedsiębiorstwa, nie możesz zapisywać elementów w osobistym sejfie. Zmień właściciela elementu na organizację i wybierz jedną z dostępnych kolekcji,." + "message": "Ze względu na zasadę przedsiębiorstwa, nie możesz zapisywać elementów w osobistym sejfie. Zmień właściciela elementu na organizację i wybierz jedną z dostępnych kolekcji." }, "hintEqualsPassword": { "message": "Podpowiedź do hasła nie może być taka sama jak hasło." @@ -1779,6 +1779,15 @@ "disabled": { "message": "Wyłączona" }, + "removePassword": { + "message": "Usuń hasło" + }, + "removedPassword": { + "message": "Hasło zostało usunięte" + }, + "removePasswordConfirmation": { + "message": "Czy na pewno chcesz usunąć hasło?" + }, "maxAccessCountReached": { "message": "Maksymalna liczba dostępów została osiągnięta" }, @@ -1798,7 +1807,7 @@ "message": "Co najmniej jedna zasada organizacji wpływa na ustawienia wysyłek." }, "emailVerificationRequired": { - "message": "Wymagana weryfikacja adresu e-mail" + "message": "Weryfikacja adresu e-mail jest wymagana" }, "emailVerificationRequiredDesc": { "message": "Musisz zweryfikować adres e-mail, aby używać tej funkcji." @@ -1844,7 +1853,7 @@ "message": "Czas blokowania sejfu przekracza limit określony przez organizację." }, "resetPasswordPolicyAutoEnroll": { - "message": "Automatyczne rejestrowanie użytkowników do resetowania hasła" + "message": "Automatyczne rejestrowanie użytkowników" }, "resetPasswordAutoEnrollInviteWarning": { "message": "Ta organizacja posługuje się zasadą, która automatycznie rejestruje użytkowników do resetowania hasła. Rejestracja umożliwia administratorom organizacji zmianę Twojego hasła głównego." @@ -1974,7 +1983,7 @@ "message": "Losowe słowo" }, "websiteName": { - "message": "Nazwa witryny" + "message": "Nazwa strony" }, "service": { "message": "Usługa" @@ -1989,7 +1998,7 @@ "message": "Szukaj w sejfie" }, "forwardedEmail": { - "message": "Alias przekazywanego e-maila" + "message": "Alias przekierowania" }, "forwardedEmailDesc": { "message": "Wygeneruj alias adresu e-mail z zewnętrznej usługi przekazywania." @@ -2002,19 +2011,19 @@ "message": "Token dostępu API" }, "apiKey": { - "message": "Klucz interfejsu API" + "message": "Klucz API" }, "premiumSubcriptionRequired": { "message": "Wymagana jest subskrypcja Premium" }, "organizationIsDisabled": { - "message": "Organizacja jest wyłączona." + "message": "Organizacja została zawieszona" }, "disabledOrganizationFilterError": { - "message": "Nie można uzyskać dostępu do elementów w wyłączonych organizacjach. Skontaktuj się z właścicielem organizacji, aby uzyskać pomoc." + "message": "Nie można uzyskać dostępu do elementów w zawieszonych organizacjach. Skontaktuj się z właścicielem organizacji, aby uzyskać pomoc." }, "neverLockWarning": { - "message": "Czy na pewno chcesz użyć opcji „Nigdy”? Ustawienie opcji blokady na „Nigdy” powoduje zapisanie klucza szyfrowania sejfu na urządzeniu. Jeśli korzystasz z tej opcji, upewnij się, że urządzenie jest odpowiednio chronione." + "message": "Czy na pewno chcesz użyć opcji „Nigdy”? Ustawienie opcji blokady na „Nigdy” spowoduje zapisanie klucza szyfrowania sejfu na urządzeniu. Jeśli korzystasz z tej opcji, upewnij się, że urządzenie jest odpowiednio chronione." }, "cardBrandMir": { "message": "Mir" @@ -2029,13 +2038,13 @@ "message": "Logowanie jako" }, "rememberEmail": { - "message": "Zapamiętaj e-mail" + "message": "Zapamiętaj adres e-mail" }, "notYou": { "message": "To nie Ty?" }, "newAroundHere": { - "message": "Jesteś tu nowy(a)?" + "message": "Nowy użytkownik?" }, "loggingInTo": { "message": "Logowanie do $DOMAIN$", @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Logowanie innym urządzeniem" + }, + "toggleCharacterCount": { + "message": "Pokaż / Ukryj licznik znaków", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index 39554984e43..bb3f3988203 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Desativado" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Número máximo de acessos atingido" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Fazer login com outro dispositivo" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index 4ef10f4de82..64aeafb9ed4 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Desativado" }, + "removePassword": { + "message": "Remover palavra-passe" + }, + "removedPassword": { + "message": "Palavra-passe removida" + }, + "removePasswordConfirmation": { + "message": "Tem a certeza de que deseja remover a palavra-passe?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Iniciar sessão com outro dispositivo" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index 005d5ee8200..4993288b3f6 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Dezactivat" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "S-a atins numărul maxim de accesări" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Autentificați-vă cu un alt dispozitiv" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index b2329fe2cf3..7b71d7e27e1 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -671,7 +671,7 @@ "message": "Настройки двухэтапной аутентификации" }, "selfHostedEnvironment": { - "message": "Окружение собственного хостинга" + "message": "Окружение пользовательского хостинга" }, "selfHostedEnvironmentFooter": { "message": "Укажите URL Bitwarden на вашем сервере." @@ -1468,7 +1468,7 @@ "message": "Чтобы получить доступ к хранилищу после выхода из него требуется повторная авторизация." }, "lock": { - "message": "Заблокировать", + "message": "Блокировка", "description": "Verb form: to make secure or inaccesible by" }, "trash": { @@ -1779,6 +1779,15 @@ "disabled": { "message": "Отключено" }, + "removePassword": { + "message": "Удалить пароль" + }, + "removedPassword": { + "message": "Пароль удален" + }, + "removePasswordConfirmation": { + "message": "Вы уверены, что хотите удалить пароль?" + }, "maxAccessCountReached": { "message": "Достигнут максимум обращений" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Войти с другого устройства" + }, + "toggleCharacterCount": { + "message": "Показать количество символов", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index c7ceef8ad34..eae67b21ce4 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index d6e3f2509f0..3e37ab0cde3 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Zakázané" }, + "removePassword": { + "message": "Odstrániť heslo" + }, + "removedPassword": { + "message": "Heslo bolo odstránené" + }, + "removePasswordConfirmation": { + "message": "Naozaj chcete odstrániť heslo?" + }, "maxAccessCountReached": { "message": "Bol dosiahnutý maximálny počet prístupov" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Prihlásenie pomocou iného zariadenia" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index 928a90498b6..27229e0789e 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index 1c26f5b9312..6e683a50386 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Онемогућено" }, + "removePassword": { + "message": "Уклони лозинку" + }, + "removedPassword": { + "message": "Лозинка је уклоњена" + }, + "removePasswordConfirmation": { + "message": "Да ли сте сигурни да желите уклонити лозинку?" + }, "maxAccessCountReached": { "message": "Достигнут максималан број приступа" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Пријавите се са другим уређајем" + }, + "toggleCharacterCount": { + "message": "Пребаци бројање слова", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index fd93b0e6829..724cee7610a 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Inaktiverad" }, + "removePassword": { + "message": "Ta bort lösenord" + }, + "removedPassword": { + "message": "Tog bort lösenord" + }, + "removePasswordConfirmation": { + "message": "Är du säker på att du vill ta bort lösenordet?" + }, "maxAccessCountReached": { "message": "Det maximala antalet åtkomster har uppnåtts" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Logga in med en annan enhet" + }, + "toggleCharacterCount": { + "message": "Växla teckenantal", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json new file mode 100644 index 00000000000..57ea12a961d --- /dev/null +++ b/apps/desktop/src/locales/te/messages.json @@ -0,0 +1,2065 @@ +{ + "bitwarden": { + "message": "Bitwarden" + }, + "filters": { + "message": "Filters" + }, + "allItems": { + "message": "All items" + }, + "favorites": { + "message": "Favorites" + }, + "types": { + "message": "Types" + }, + "typeLogin": { + "message": "Login" + }, + "typeCard": { + "message": "Card" + }, + "typeIdentity": { + "message": "Identity" + }, + "typeSecureNote": { + "message": "Secure note" + }, + "folders": { + "message": "Folders" + }, + "collections": { + "message": "Collections" + }, + "searchVault": { + "message": "Search vault" + }, + "addItem": { + "message": "Add item" + }, + "shared": { + "message": "Shared" + }, + "share": { + "message": "Share" + }, + "moveToOrganization": { + "message": "Move to organization" + }, + "movedItemToOrg": { + "message": "$ITEMNAME$ moved to $ORGNAME$", + "placeholders": { + "itemname": { + "content": "$1", + "example": "Secret Item" + }, + "orgname": { + "content": "$2", + "example": "Company Name" + } + } + }, + "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." + }, + "attachments": { + "message": "Attachments" + }, + "viewItem": { + "message": "View item" + }, + "name": { + "message": "Name" + }, + "uri": { + "message": "URI" + }, + "uriPosition": { + "message": "URI $POSITION$", + "description": "A listing of URIs. Ex: URI 1, URI 2, URI 3, etc.", + "placeholders": { + "position": { + "content": "$1", + "example": "2" + } + } + }, + "newUri": { + "message": "New URI" + }, + "username": { + "message": "Username" + }, + "password": { + "message": "Password" + }, + "passphrase": { + "message": "Passphrase" + }, + "editItem": { + "message": "Edit item" + }, + "emailAddress": { + "message": "Email address" + }, + "verificationCodeTotp": { + "message": "Verification code (TOTP)" + }, + "website": { + "message": "Website" + }, + "notes": { + "message": "Notes" + }, + "customFields": { + "message": "Custom fields" + }, + "launch": { + "message": "Launch" + }, + "copyValue": { + "message": "Copy value", + "description": "Copy value to clipboard" + }, + "minimizeOnCopyToClipboard": { + "message": "Minimize when copying to clipboard" + }, + "minimizeOnCopyToClipboardDesc": { + "message": "Minimize application when copying an item's data to the clipboard." + }, + "toggleVisibility": { + "message": "Toggle visibility" + }, + "toggleCollapse": { + "message": "Toggle collapse", + "description": "Toggling an expand/collapse state." + }, + "cardholderName": { + "message": "Cardholder name" + }, + "number": { + "message": "Number" + }, + "brand": { + "message": "Brand" + }, + "expiration": { + "message": "Expiration" + }, + "securityCode": { + "message": "Security code" + }, + "identityName": { + "message": "Identity name" + }, + "company": { + "message": "Company" + }, + "ssn": { + "message": "Social Security number" + }, + "passportNumber": { + "message": "Passport number" + }, + "licenseNumber": { + "message": "License number" + }, + "email": { + "message": "Email" + }, + "phone": { + "message": "Phone" + }, + "address": { + "message": "Address" + }, + "premiumRequired": { + "message": "Premium required" + }, + "premiumRequiredDesc": { + "message": "A Premium membership is required to use this feature." + }, + "errorOccurred": { + "message": "An error has occurred." + }, + "error": { + "message": "Error" + }, + "january": { + "message": "January" + }, + "february": { + "message": "February" + }, + "march": { + "message": "March" + }, + "april": { + "message": "April" + }, + "may": { + "message": "May" + }, + "june": { + "message": "June" + }, + "july": { + "message": "July" + }, + "august": { + "message": "August" + }, + "september": { + "message": "September" + }, + "october": { + "message": "October" + }, + "november": { + "message": "November" + }, + "december": { + "message": "December" + }, + "ex": { + "message": "ex.", + "description": "Short abbreviation for 'example'." + }, + "title": { + "message": "Title" + }, + "mr": { + "message": "Mr" + }, + "mrs": { + "message": "Mrs" + }, + "ms": { + "message": "Ms" + }, + "dr": { + "message": "Dr" + }, + "expirationMonth": { + "message": "Expiration month" + }, + "expirationYear": { + "message": "Expiration year" + }, + "select": { + "message": "Select" + }, + "other": { + "message": "Other" + }, + "generatePassword": { + "message": "Generate password" + }, + "type": { + "message": "Type" + }, + "firstName": { + "message": "First name" + }, + "middleName": { + "message": "Middle name" + }, + "lastName": { + "message": "Last name" + }, + "fullName": { + "message": "Full name" + }, + "address1": { + "message": "Address 1" + }, + "address2": { + "message": "Address 2" + }, + "address3": { + "message": "Address 3" + }, + "cityTown": { + "message": "City / Town" + }, + "stateProvince": { + "message": "State / Province" + }, + "zipPostalCode": { + "message": "Zip / Postal code" + }, + "country": { + "message": "Country" + }, + "save": { + "message": "Save" + }, + "cancel": { + "message": "Cancel" + }, + "delete": { + "message": "Delete" + }, + "favorite": { + "message": "Favorite" + }, + "edit": { + "message": "Edit" + }, + "authenticatorKeyTotp": { + "message": "Authenticator key (TOTP)" + }, + "folder": { + "message": "Folder" + }, + "newCustomField": { + "message": "New custom field" + }, + "value": { + "message": "Value" + }, + "dragToSort": { + "message": "Drag to sort" + }, + "cfTypeText": { + "message": "Text" + }, + "cfTypeHidden": { + "message": "Hidden" + }, + "cfTypeBoolean": { + "message": "Boolean" + }, + "cfTypeLinked": { + "message": "Linked", + "description": "This describes a field that is 'linked' (related) to another field." + }, + "linkedValue": { + "message": "Linked value", + "description": "This describes a value that is 'linked' (related) to another value." + }, + "remove": { + "message": "Remove" + }, + "nameRequired": { + "message": "Name is required." + }, + "addedItem": { + "message": "Item added" + }, + "editedItem": { + "message": "Item saved" + }, + "deleteItem": { + "message": "Delete item" + }, + "deleteFolder": { + "message": "Delete folder" + }, + "deleteAttachment": { + "message": "Delete attachment" + }, + "deleteItemConfirmation": { + "message": "Do you really want to send to the trash?" + }, + "deletedItem": { + "message": "Item sent to trash" + }, + "overwritePasswordConfirmation": { + "message": "Are you sure you want to overwrite the current password?" + }, + "overwriteUsername": { + "message": "Overwrite username" + }, + "overwriteUsernameConfirmation": { + "message": "Are you sure you want to overwrite the current username?" + }, + "noneFolder": { + "message": "No folder", + "description": "This is the folder for uncategorized items" + }, + "addFolder": { + "message": "Add folder" + }, + "editFolder": { + "message": "Edit folder" + }, + "regeneratePassword": { + "message": "Regenerate password" + }, + "copyPassword": { + "message": "Copy password" + }, + "copyUri": { + "message": "Copy URI" + }, + "copyVerificationCodeTotp": { + "message": "Copy verification code (TOTP)" + }, + "length": { + "message": "Length" + }, + "uppercase": { + "message": "Uppercase (A-Z)" + }, + "lowercase": { + "message": "Lowercase (a-z)" + }, + "numbers": { + "message": "Numbers (0-9)" + }, + "specialCharacters": { + "message": "Special characters (!@#$%^&*)" + }, + "numWords": { + "message": "Number of words" + }, + "wordSeparator": { + "message": "Word separator" + }, + "capitalize": { + "message": "Capitalize", + "description": "Make the first letter of a word uppercase." + }, + "includeNumber": { + "message": "Include number" + }, + "close": { + "message": "Close" + }, + "minNumbers": { + "message": "Minimum numbers" + }, + "minSpecial": { + "message": "Minimum special", + "description": "Minimum Special Characters" + }, + "ambiguous": { + "message": "Avoid ambiguous characters" + }, + "searchCollection": { + "message": "Search collection" + }, + "searchFolder": { + "message": "Search folder" + }, + "searchFavorites": { + "message": "Search favorites" + }, + "searchType": { + "message": "Search type", + "description": "Search item type" + }, + "newAttachment": { + "message": "Add new attachment" + }, + "deletedAttachment": { + "message": "Attachment deleted" + }, + "deleteAttachmentConfirmation": { + "message": "Are you sure you want to delete this attachment?" + }, + "attachmentSaved": { + "message": "Attachment saved" + }, + "file": { + "message": "File" + }, + "selectFile": { + "message": "Select a file" + }, + "maxFileSize": { + "message": "Maximum file size is 500 MB." + }, + "updateKey": { + "message": "You cannot use this feature until you update your encryption key." + }, + "editedFolder": { + "message": "Folder saved" + }, + "addedFolder": { + "message": "Folder added" + }, + "deleteFolderConfirmation": { + "message": "Are you sure you want to delete this folder?" + }, + "deletedFolder": { + "message": "Folder deleted" + }, + "loginOrCreateNewAccount": { + "message": "Log in or create a new account to access your secure vault." + }, + "createAccount": { + "message": "Create account" + }, + "logIn": { + "message": "Log in" + }, + "submit": { + "message": "Submit" + }, + "masterPass": { + "message": "Master password" + }, + "masterPassDesc": { + "message": "The master password is the password you use to access your vault. It is very important that you do not forget your master password. There is no way to recover the password in the event that you forget it." + }, + "masterPassHintDesc": { + "message": "A master password hint can help you remember your password if you forget it." + }, + "reTypeMasterPass": { + "message": "Re-type master password" + }, + "masterPassHint": { + "message": "Master password hint (optional)" + }, + "settings": { + "message": "Settings" + }, + "passwordHint": { + "message": "Password hint" + }, + "enterEmailToGetHint": { + "message": "Enter your account email address to receive your master password hint." + }, + "getMasterPasswordHint": { + "message": "Get master password hint" + }, + "emailRequired": { + "message": "Email address is required." + }, + "invalidEmail": { + "message": "Invalid email address." + }, + "masterPasswordRequired": { + "message": "Master password is required." + }, + "confirmMasterPasswordRequired": { + "message": "Master password retype is required." + }, + "masterPasswordMinlength": { + "message": "Master password must be at least 8 characters long." + }, + "masterPassDoesntMatch": { + "message": "Master password confirmation does not match." + }, + "newAccountCreated": { + "message": "Your new account has been created! You may now log in." + }, + "masterPassSent": { + "message": "We've sent you an email with your master password hint." + }, + "unexpectedError": { + "message": "An unexpected error has occurred." + }, + "itemInformation": { + "message": "Item information" + }, + "noItemsInList": { + "message": "There are no items to list." + }, + "sendVerificationCode": { + "message": "Send a verification code to your email" + }, + "sendCode": { + "message": "Send code" + }, + "codeSent": { + "message": "Code sent" + }, + "verificationCode": { + "message": "Verification code" + }, + "confirmIdentity": { + "message": "Confirm your identity to continue." + }, + "verificationCodeRequired": { + "message": "Verification code is required." + }, + "invalidVerificationCode": { + "message": "Invalid verification code" + }, + "continue": { + "message": "Continue" + }, + "enterVerificationCodeApp": { + "message": "Enter the 6 digit verification code from your authenticator app." + }, + "enterVerificationCodeEmail": { + "message": "Enter the 6 digit verification code that was emailed to $EMAIL$.", + "placeholders": { + "email": { + "content": "$1", + "example": "example@gmail.com" + } + } + }, + "verificationCodeEmailSent": { + "message": "Verification email sent to $EMAIL$.", + "placeholders": { + "email": { + "content": "$1", + "example": "example@gmail.com" + } + } + }, + "rememberMe": { + "message": "Remember me" + }, + "sendVerificationCodeEmailAgain": { + "message": "Send verification code email again" + }, + "useAnotherTwoStepMethod": { + "message": "Use another two-step login method" + }, + "insertYubiKey": { + "message": "Insert your YubiKey into your computer's USB port, then touch its button." + }, + "insertU2f": { + "message": "Insert your security key into your computer's USB port. If it has a button, touch it." + }, + "recoveryCodeDesc": { + "message": "Lost access to all of your two-factor providers? Use your recovery code to turn off all two-factor providers on your account." + }, + "recoveryCodeTitle": { + "message": "Recovery code" + }, + "authenticatorAppTitle": { + "message": "Authenticator app" + }, + "authenticatorAppDesc": { + "message": "Use an authenticator app (such as Authy or Google Authenticator) to generate time-based verification codes.", + "description": "'Authy' and 'Google Authenticator' are product names and should not be translated." + }, + "yubiKeyTitle": { + "message": "YubiKey OTP security key" + }, + "yubiKeyDesc": { + "message": "Use a YubiKey to access your account. Works with YubiKey 4, 4 Nano, 4C, and NEO devices." + }, + "duoDesc": { + "message": "Verify with Duo Security using the Duo Mobile app, SMS, phone call, or U2F security key.", + "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.", + "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." + }, + "emailTitle": { + "message": "Email" + }, + "emailDesc": { + "message": "Verification codes will be emailed to you." + }, + "loginUnavailable": { + "message": "Login unavailable" + }, + "noTwoStepProviders": { + "message": "This account has two-step login set up, however, none of the configured two-step providers are supported by this device." + }, + "noTwoStepProviders2": { + "message": "Please add additional providers that are better supported across devices (such as an authenticator app)." + }, + "twoStepOptions": { + "message": "Two-step login options" + }, + "selfHostedEnvironment": { + "message": "Self-hosted environment" + }, + "selfHostedEnvironmentFooter": { + "message": "Specify the base URL of your on-premises hosted Bitwarden installation." + }, + "customEnvironment": { + "message": "Custom environment" + }, + "customEnvironmentFooter": { + "message": "For advanced users. You can specify the base URL of each service independently." + }, + "baseUrl": { + "message": "Server URL" + }, + "apiUrl": { + "message": "API server URL" + }, + "webVaultUrl": { + "message": "Web vault server URL" + }, + "identityUrl": { + "message": "Identity server URL" + }, + "notificationsUrl": { + "message": "Notifications server URL" + }, + "iconsUrl": { + "message": "Icons server URL" + }, + "environmentSaved": { + "message": "Environment URLs saved" + }, + "ok": { + "message": "Ok" + }, + "yes": { + "message": "Yes" + }, + "no": { + "message": "No" + }, + "overwritePassword": { + "message": "Overwrite password" + }, + "learnMore": { + "message": "Learn more" + }, + "featureUnavailable": { + "message": "Feature unavailable" + }, + "loggedOut": { + "message": "Logged out" + }, + "loginExpired": { + "message": "Your login session has expired." + }, + "logOutConfirmation": { + "message": "Are you sure you want to log out?" + }, + "logOut": { + "message": "Log out" + }, + "addNewLogin": { + "message": "New login" + }, + "addNewItem": { + "message": "New item" + }, + "addNewFolder": { + "message": "New folder" + }, + "view": { + "message": "View" + }, + "account": { + "message": "Account" + }, + "loading": { + "message": "Loading..." + }, + "lockVault": { + "message": "Lock vault" + }, + "passwordGenerator": { + "message": "Password generator" + }, + "contactUs": { + "message": "Contact us" + }, + "getHelp": { + "message": "Get help" + }, + "fileBugReport": { + "message": "File a bug report" + }, + "blog": { + "message": "Blog" + }, + "followUs": { + "message": "Follow us" + }, + "syncVault": { + "message": "Sync vault" + }, + "changeMasterPass": { + "message": "Change master password" + }, + "changeMasterPasswordConfirmation": { + "message": "You can change your master password on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "fingerprintPhrase": { + "message": "Fingerprint phrase", + "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." + }, + "yourAccountsFingerprint": { + "message": "Your account's fingerprint phrase", + "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." + }, + "goToWebVault": { + "message": "Go to web vault" + }, + "getMobileApp": { + "message": "Get mobile app" + }, + "getBrowserExtension": { + "message": "Get browser extension" + }, + "syncingComplete": { + "message": "Syncing complete" + }, + "syncingFailed": { + "message": "Syncing failed" + }, + "yourVaultIsLocked": { + "message": "Your vault is locked. Verify your identity to continue." + }, + "unlock": { + "message": "Unlock" + }, + "loggedInAsOn": { + "message": "Logged in as $EMAIL$ on $HOSTNAME$.", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + }, + "hostname": { + "content": "$2", + "example": "bitwarden.com" + } + } + }, + "invalidMasterPassword": { + "message": "Invalid master password" + }, + "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?" + }, + "twoStepLogin": { + "message": "Two-step login" + }, + "vaultTimeout": { + "message": "Vault timeout" + }, + "vaultTimeoutDesc": { + "message": "Choose when your vault will take the vault timeout action." + }, + "immediately": { + "message": "Immediately" + }, + "tenSeconds": { + "message": "10 seconds" + }, + "twentySeconds": { + "message": "20 seconds" + }, + "thirtySeconds": { + "message": "30 seconds" + }, + "oneMinute": { + "message": "1 minute" + }, + "twoMinutes": { + "message": "2 minutes" + }, + "fiveMinutes": { + "message": "5 minutes" + }, + "fifteenMinutes": { + "message": "15 minutes" + }, + "thirtyMinutes": { + "message": "30 minutes" + }, + "oneHour": { + "message": "1 hour" + }, + "fourHours": { + "message": "4 hours" + }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, + "onLocked": { + "message": "On system lock" + }, + "onRestart": { + "message": "On restart" + }, + "never": { + "message": "Never" + }, + "security": { + "message": "Security" + }, + "clearClipboard": { + "message": "Clear clipboard", + "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.", + "description": "Clipboard is the operating system thing where you copy/paste data to on your device." + }, + "enableFavicon": { + "message": "Show website icons" + }, + "faviconDesc": { + "message": "Show a recognizable image next to each login." + }, + "enableMinToTray": { + "message": "Minimize to tray icon" + }, + "enableMinToTrayDesc": { + "message": "When minimizing the window, show an icon in the system tray instead." + }, + "enableMinToMenuBar": { + "message": "Minimize to menu bar" + }, + "enableMinToMenuBarDesc": { + "message": "When minimizing the window, show an icon in the menu bar instead." + }, + "enableCloseToTray": { + "message": "Close to tray icon" + }, + "enableCloseToTrayDesc": { + "message": "When closing the window, show an icon in the system tray instead." + }, + "enableCloseToMenuBar": { + "message": "Close to menu bar" + }, + "enableCloseToMenuBarDesc": { + "message": "When closing the window, show an icon in the menu bar instead." + }, + "enableTray": { + "message": "Show tray icon" + }, + "enableTrayDesc": { + "message": "Always show an icon in the system tray." + }, + "startToTray": { + "message": "Start to tray icon" + }, + "startToTrayDesc": { + "message": "When the application is first started, only show an icon in the system tray." + }, + "startToMenuBar": { + "message": "Start to menu bar" + }, + "startToMenuBarDesc": { + "message": "When the application is first started, only show an icon in the menu bar." + }, + "openAtLogin": { + "message": "Start automatically on login" + }, + "openAtLoginDesc": { + "message": "Start the Bitwarden desktop application automatically on login." + }, + "alwaysShowDock": { + "message": "Always show in the Dock" + }, + "alwaysShowDockDesc": { + "message": "Show the Bitwarden icon in the Dock even when minimized to the menu bar." + }, + "confirmTrayTitle": { + "message": "Confirm hiding tray" + }, + "confirmTrayDesc": { + "message": "Turning off this setting will also turn off all other tray related settings." + }, + "language": { + "message": "Language" + }, + "languageDesc": { + "message": "Change the language used by the application. Restart is required." + }, + "theme": { + "message": "Theme" + }, + "themeDesc": { + "message": "Change the application's color theme." + }, + "dark": { + "message": "Dark", + "description": "Dark color" + }, + "light": { + "message": "Light", + "description": "Light color" + }, + "copy": { + "message": "Copy", + "description": "Copy to clipboard" + }, + "checkForUpdates": { + "message": "Check for updates…" + }, + "version": { + "message": "Version $VERSION_NUM$", + "placeholders": { + "version_num": { + "content": "$1", + "example": "1.2.3" + } + } + }, + "restartToUpdate": { + "message": "Restart to update" + }, + "restartToUpdateDesc": { + "message": "Version $VERSION_NUM$ is ready to install. You must restart the application to complete the installation. Do you want to restart and update now?", + "placeholders": { + "version_num": { + "content": "$1", + "example": "1.2.3" + } + } + }, + "updateAvailable": { + "message": "Update available" + }, + "updateAvailableDesc": { + "message": "An update was found. Do you want to download it now?" + }, + "restart": { + "message": "Restart" + }, + "later": { + "message": "Later" + }, + "noUpdatesAvailable": { + "message": "No updates are currently available. You are using the latest version." + }, + "updateError": { + "message": "Update error" + }, + "unknown": { + "message": "Unknown" + }, + "copyUsername": { + "message": "Copy username" + }, + "copyNumber": { + "message": "Copy number", + "description": "Copy credit card number" + }, + "copySecurityCode": { + "message": "Copy security code", + "description": "Copy credit card security code (CVV)" + }, + "premiumMembership": { + "message": "Premium membership" + }, + "premiumManage": { + "message": "Manage membership" + }, + "premiumManageAlert": { + "message": "You can manage your membership on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "premiumRefresh": { + "message": "Refresh membership" + }, + "premiumNotCurrentMember": { + "message": "You are not currently a Premium member." + }, + "premiumSignUpAndGet": { + "message": "Sign up for a Premium membership and get:" + }, + "premiumSignUpStorage": { + "message": "1 GB encrypted storage for file attachments." + }, + "premiumSignUpTwoStep": { + "message": "Additional two-step login options such as YubiKey, FIDO U2F, and Duo." + }, + "premiumSignUpReports": { + "message": "Password hygiene, account health, and data breach reports to keep your vault safe." + }, + "premiumSignUpTotp": { + "message": "TOTP verification code (2FA) generator for logins in your vault." + }, + "premiumSignUpSupport": { + "message": "Priority customer support." + }, + "premiumSignUpFuture": { + "message": "All future premium features. More coming soon!" + }, + "premiumPurchase": { + "message": "Purchase Premium" + }, + "premiumPurchaseAlert": { + "message": "You can purchase premium membership on the bitwarden.com web vault. Do you want to visit the website now?" + }, + "premiumCurrentMember": { + "message": "You are a premium member!" + }, + "premiumCurrentMemberThanks": { + "message": "Thank you for supporting Bitwarden." + }, + "premiumPrice": { + "message": "All for just $PRICE$ /year!", + "placeholders": { + "price": { + "content": "$1", + "example": "$10" + } + } + }, + "refreshComplete": { + "message": "Refresh complete" + }, + "passwordHistory": { + "message": "Password history" + }, + "clear": { + "message": "Clear", + "description": "To clear something out. example: To clear browser history." + }, + "noPasswordsInList": { + "message": "There are no passwords to list." + }, + "undo": { + "message": "Undo" + }, + "redo": { + "message": "Redo" + }, + "cut": { + "message": "Cut", + "description": "Cut to clipboard" + }, + "paste": { + "message": "Paste", + "description": "Paste from clipboard" + }, + "selectAll": { + "message": "Select all" + }, + "zoomIn": { + "message": "Zoom in" + }, + "zoomOut": { + "message": "Zoom out" + }, + "resetZoom": { + "message": "Reset zoom" + }, + "toggleFullScreen": { + "message": "Toggle full screen" + }, + "reload": { + "message": "Reload" + }, + "toggleDevTools": { + "message": "Toggle developer tools" + }, + "minimize": { + "message": "Minimize", + "description": "Minimize window" + }, + "zoom": { + "message": "Zoom" + }, + "bringAllToFront": { + "message": "Bring all to front", + "description": "Bring all windows to front (foreground)" + }, + "aboutBitwarden": { + "message": "About Bitwarden" + }, + "services": { + "message": "Services" + }, + "hideBitwarden": { + "message": "Hide Bitwarden" + }, + "hideOthers": { + "message": "Hide others" + }, + "showAll": { + "message": "Show all" + }, + "quitBitwarden": { + "message": "Quit Bitwarden" + }, + "valueCopied": { + "message": "$VALUE$ copied", + "description": "Value has been copied to the clipboard.", + "placeholders": { + "value": { + "content": "$1", + "example": "Password" + } + } + }, + "help": { + "message": "Help" + }, + "window": { + "message": "Window" + }, + "checkPassword": { + "message": "Check if password has been exposed." + }, + "passwordExposed": { + "message": "This password has been exposed $VALUE$ time(s) in data breaches. You should change it.", + "placeholders": { + "value": { + "content": "$1", + "example": "2" + } + } + }, + "passwordSafe": { + "message": "This password was not found in any known data breaches. It should be safe to use." + }, + "baseDomain": { + "message": "Base domain", + "description": "Domain name. Ex. website.com" + }, + "domainName": { + "message": "Domain name", + "description": "Domain name. Ex. website.com" + }, + "host": { + "message": "Host", + "description": "A URL's host value. For example, the host of https://sub.domain.com:443 is 'sub.domain.com:443'." + }, + "exact": { + "message": "Exact" + }, + "startsWith": { + "message": "Starts with" + }, + "regEx": { + "message": "Regular expression", + "description": "A programming term, also known as 'RegEx'." + }, + "matchDetection": { + "message": "Match detection", + "description": "URI match detection for auto-fill." + }, + "defaultMatchDetection": { + "message": "Default match detection", + "description": "Default URI match detection for auto-fill." + }, + "toggleOptions": { + "message": "Toggle options" + }, + "organization": { + "message": "Organization", + "description": "An entity of multiple related people (ex. a team or business organization)." + }, + "default": { + "message": "Default" + }, + "exit": { + "message": "Exit" + }, + "showHide": { + "message": "Show / Hide", + "description": "Text for a button that toggles the visibility of the window. Shows the window when it is hidden or hides the window if it is currently open." + }, + "hideToTray": { + "message": "Hide to tray" + }, + "alwaysOnTop": { + "message": "Always on top", + "description": "Application window should always stay on top of other windows" + }, + "dateUpdated": { + "message": "Updated", + "description": "ex. Date this item was updated" + }, + "dateCreated": { + "message": "Created", + "description": "ex. Date this item was created" + }, + "datePasswordUpdated": { + "message": "Password updated", + "description": "ex. Date this password was updated" + }, + "exportVault": { + "message": "Export vault" + }, + "fileFormat": { + "message": "File format" + }, + "hCaptchaUrl": { + "message": "hCaptcha Url", + "description": "hCaptcha is the name of a website, should not be translated" + }, + "loadAccessibilityCookie": { + "message": "Load accessibility cookie" + }, + "registerAccessibilityUser": { + "message": "Register as an accessibility user at", + "description": "ex. Register as an accessibility user at hcaptcha.com" + }, + "copyPasteLink": { + "message": "Copy and paste the link sent to your email below" + }, + "enterhCaptchaUrl": { + "message": "Enter URL to load accessibility cookie for hCaptcha", + "description": "hCaptcha is the name of a website, should not be translated" + }, + "hCaptchaUrlRequired": { + "message": "hCaptcha Url is required", + "description": "hCaptcha is the name of a website, should not be translated" + }, + "invalidUrl": { + "message": "Invalid Url" + }, + "done": { + "message": "Done" + }, + "accessibilityCookieSaved": { + "message": "Accessibility cookie saved!" + }, + "noAccessibilityCookieSaved": { + "message": "No accessibility cookie saved" + }, + "warning": { + "message": "WARNING", + "description": "WARNING (should stay in capitalized letters if the language permits)" + }, + "confirmVaultExport": { + "message": "Confirm vault export" + }, + "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." + }, + "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." + }, + "encExportAccountWarningDesc": { + "message": "Account encryption keys are unique to each Bitwarden user account, so you can't import an encrypted export into a different account." + }, + "noOrganizationsList": { + "message": "You do not belong to any organizations. Organizations allow you to securely share items with other users." + }, + "noCollectionsInList": { + "message": "There are no collections to list." + }, + "ownership": { + "message": "Ownership" + }, + "whoOwnsThisItem": { + "message": "Who owns this item?" + }, + "strong": { + "message": "Strong", + "description": "ex. A strong password. Scale: Weak -> Good -> Strong" + }, + "good": { + "message": "Good", + "description": "ex. A good password. Scale: Weak -> Good -> Strong" + }, + "weak": { + "message": "Weak", + "description": "ex. A weak password. Scale: Weak -> Good -> Strong" + }, + "weakMasterPassword": { + "message": "Weak master password" + }, + "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?" + }, + "pin": { + "message": "PIN", + "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." + }, + "unlockWithPin": { + "message": "Unlock with 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." + }, + "pinRequired": { + "message": "PIN code is required." + }, + "invalidPin": { + "message": "Invalid PIN code." + }, + "unlockWithWindowsHello": { + "message": "Unlock with Windows Hello" + }, + "windowsHelloConsentMessage": { + "message": "Verify for Bitwarden." + }, + "unlockWithTouchId": { + "message": "Unlock with Touch ID" + }, + "touchIdConsentMessage": { + "message": "unlock your vault" + }, + "autoPromptWindowsHello": { + "message": "Ask for Windows Hello on launch" + }, + "autoPromptTouchId": { + "message": "Ask for Touch ID on launch" + }, + "lockWithMasterPassOnRestart": { + "message": "Lock with master password on restart" + }, + "deleteAccount": { + "message": "Delete account" + }, + "deleteAccountDesc": { + "message": "Proceed below to delete your account and all vault data." + }, + "deleteAccountWarning": { + "message": "Deleting your account is permanent. It cannot be undone." + }, + "accountDeleted": { + "message": "Account deleted" + }, + "accountDeletedDesc": { + "message": "Your account has been closed and all associated data has been deleted." + }, + "preferences": { + "message": "Preferences" + }, + "enableMenuBar": { + "message": "Show menu bar icon" + }, + "enableMenuBarDesc": { + "message": "Always show an icon in the menu bar." + }, + "hideToMenuBar": { + "message": "Hide to menu bar" + }, + "selectOneCollection": { + "message": "You must select at least one collection." + }, + "premiumUpdated": { + "message": "You've upgraded to Premium." + }, + "restore": { + "message": "Restore" + }, + "premiumManageAlertAppStore": { + "message": "You can manage your subscription from the App Store. Do you want to visit the App Store now?" + }, + "legal": { + "message": "Legal", + "description": "Noun. As in 'legal documents', like our terms of service and privacy policy." + }, + "termsOfService": { + "message": "Terms of Service" + }, + "privacyPolicy": { + "message": "Privacy Policy" + }, + "unsavedChangesConfirmation": { + "message": "Are you sure you want to leave? If you leave now then your current information will not be saved." + }, + "unsavedChangesTitle": { + "message": "Unsaved changes" + }, + "clone": { + "message": "Clone" + }, + "passwordGeneratorPolicyInEffect": { + "message": "One or more organization policies are affecting your generator settings." + }, + "vaultTimeoutAction": { + "message": "Vault timeout action" + }, + "vaultTimeoutActionLockDesc": { + "message": "Master password or other unlock method is required to access your vault again." + }, + "vaultTimeoutActionLogOutDesc": { + "message": "Re-authentication is required to access your vault again." + }, + "lock": { + "message": "Lock", + "description": "Verb form: to make secure or inaccesible by" + }, + "trash": { + "message": "Trash", + "description": "Noun: a special folder to hold deleted items" + }, + "searchTrash": { + "message": "Search trash" + }, + "permanentlyDeleteItem": { + "message": "Permanently delete item" + }, + "permanentlyDeleteItemConfirmation": { + "message": "Are you sure you want to permanently delete this item?" + }, + "permanentlyDeletedItem": { + "message": "Item permanently deleted" + }, + "restoreItem": { + "message": "Restore item" + }, + "restoreItemConfirmation": { + "message": "Are you sure you want to restore this item?" + }, + "restoredItem": { + "message": "Item restored" + }, + "permanentlyDelete": { + "message": "Permanently delete" + }, + "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?" + }, + "vaultTimeoutLogOutConfirmationTitle": { + "message": "Timeout action confirmation" + }, + "enterpriseSingleSignOn": { + "message": "Enterprise single sign-on" + }, + "setMasterPassword": { + "message": "Set master password" + }, + "ssoCompleteRegistration": { + "message": "In order to complete logging in with SSO, please set a master password to access and protect your vault." + }, + "newMasterPass": { + "message": "New master password" + }, + "confirmNewMasterPass": { + "message": "Confirm new master password" + }, + "masterPasswordPolicyInEffect": { + "message": "One or more organization policies require your master password to meet the following requirements:" + }, + "policyInEffectMinComplexity": { + "message": "Minimum complexity score of $SCORE$", + "placeholders": { + "score": { + "content": "$1", + "example": "4" + } + } + }, + "policyInEffectMinLength": { + "message": "Minimum length of $LENGTH$", + "placeholders": { + "length": { + "content": "$1", + "example": "14" + } + } + }, + "policyInEffectUppercase": { + "message": "Contain one or more uppercase characters" + }, + "policyInEffectLowercase": { + "message": "Contain one or more lowercase characters" + }, + "policyInEffectNumbers": { + "message": "Contain one or more numbers" + }, + "policyInEffectSpecial": { + "message": "Contain one or more of the following special characters $CHARS$", + "placeholders": { + "chars": { + "content": "$1", + "example": "!@#$%^&*" + } + } + }, + "masterPasswordPolicyRequirementsNotMet": { + "message": "Your new master password does not meet the policy requirements." + }, + "acceptPolicies": { + "message": "By checking this box you agree to the following:" + }, + "acceptPoliciesRequired": { + "message": "Terms of Service and Privacy Policy have not been acknowledged." + }, + "enableBrowserIntegration": { + "message": "Allow browser integration" + }, + "enableBrowserIntegrationDesc": { + "message": "Used for biometrics in browser." + }, + "enableDuckDuckGoBrowserIntegration": { + "message": "Allow DuckDuckGo browser integration" + }, + "enableDuckDuckGoBrowserIntegrationDesc": { + "message": "Use your Bitwarden vault when browsing with DuckDuckGo." + }, + "browserIntegrationUnsupportedTitle": { + "message": "Browser integration not supported" + }, + "browserIntegrationMasOnlyDesc": { + "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." + }, + "browserIntegrationWindowsStoreDesc": { + "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." + }, + "browserIntegrationLinuxDesc": { + "message": "Unfortunately browser integration is currently not supported in the linux version." + }, + "enableBrowserIntegrationFingerprint": { + "message": "Require verification for browser integration" + }, + "enableBrowserIntegrationFingerprintDesc": { + "message": "Add an additional layer of security by requiring fingerprint phrase confirmation when establishing a link between your desktop and browser. This requires user action and verification each time a connection is created." + }, + "approve": { + "message": "Approve" + }, + "verifyBrowserTitle": { + "message": "Verify browser connection" + }, + "verifyBrowserDesc": { + "message": "Please ensure the shown fingerprint is identical to the fingerprint showed in the browser extension." + }, + "verifyNativeMessagingConnectionTitle": { + "message": "$APPID$ wants to connect to Bitwarden", + "placeholders": { + "appid": { + "content": "$1", + "example": "My App" + } + } + }, + "verifyNativeMessagingConnectionDesc": { + "message": "Would you like to approve this request?" + }, + "verifyNativeMessagingConnectionWarning": { + "message": "If you did not initiate this request, do not approve it." + }, + "biometricsNotEnabledTitle": { + "message": "Biometrics not set up" + }, + "biometricsNotEnabledDesc": { + "message": "Browser biometrics requires desktop biometrics to be set up in the settings first." + }, + "personalOwnershipSubmitError": { + "message": "Due to an enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." + }, + "hintEqualsPassword": { + "message": "Your password hint cannot be the same as your password." + }, + "personalOwnershipPolicyInEffect": { + "message": "An organization policy is affecting your ownership options." + }, + "allSends": { + "message": "All Sends", + "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeFile": { + "message": "File" + }, + "sendTypeText": { + "message": "Text" + }, + "searchSends": { + "message": "Search Sends", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "editSend": { + "message": "Edit Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "myVault": { + "message": "My vault" + }, + "text": { + "message": "Text" + }, + "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." + }, + "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." + }, + "maxAccessCount": { + "message": "Maximum access count", + "description": "This text will be displayed after a Send has been accessed the maximum amount of times." + }, + "maxAccessCountDesc": { + "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." + }, + "currentAccessCount": { + "message": "Current access count" + }, + "disableSend": { + "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." + }, + "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." + }, + "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." + }, + "sendLink": { + "message": "Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendLinkLabel": { + "message": "Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "textHiddenByDefault": { + "message": "When accessing the Send, hide the text by default", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "createdSend": { + "message": "Send added", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "editedSend": { + "message": "Send saved", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "deletedSend": { + "message": "Send deleted", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "newPassword": { + "message": "New password" + }, + "whatTypeOfSend": { + "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." + }, + "createSend": { + "message": "New Send", + "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." + }, + "sendFileDesc": { + "message": "The file you want to send." + }, + "days": { + "message": "$DAYS$ days", + "placeholders": { + "days": { + "content": "$1", + "example": "1" + } + } + }, + "oneDay": { + "message": "1 day" + }, + "custom": { + "message": "Custom" + }, + "deleteSendConfirmation": { + "message": "Are you sure you want to delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "copySendLinkToClipboard": { + "message": "Copy Send link to clipboard", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "copySendLinkOnSave": { + "message": "Copy the link to share this Send to my clipboard upon save." + }, + "sendDisabled": { + "message": "Send removed", + "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.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "copyLink": { + "message": "Copy link" + }, + "disabled": { + "message": "Disabled" + }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, + "maxAccessCountReached": { + "message": "Max access count reached" + }, + "expired": { + "message": "Expired" + }, + "pendingDeletion": { + "message": "Pending deletion" + }, + "webAuthnAuthenticate": { + "message": "Authenticate WebAuthn" + }, + "hideEmail": { + "message": "Hide my email address from recipients." + }, + "sendOptionsPolicyInEffect": { + "message": "One or more organization policies are affecting your Send options." + }, + "emailVerificationRequired": { + "message": "Email verification required" + }, + "emailVerificationRequiredDesc": { + "message": "You must verify your email to use this feature." + }, + "passwordPrompt": { + "message": "Master password re-prompt" + }, + "passwordConfirmation": { + "message": "Master password confirmation" + }, + "passwordConfirmationDesc": { + "message": "This action is protected. To continue, please re-enter your master password to verify your identity." + }, + "updatedMasterPassword": { + "message": "Updated master password" + }, + "updateMasterPassword": { + "message": "Update master password" + }, + "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." + }, + "hours": { + "message": "Hours" + }, + "minutes": { + "message": "Minutes" + }, + "vaultTimeoutPolicyInEffect": { + "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "resetPasswordPolicyAutoEnroll": { + "message": "Automatic enrollment" + }, + "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." + }, + "vaultExportDisabled": { + "message": "Vault export removed" + }, + "personalVaultExportPolicyInEffect": { + "message": "One or more organization policies prevents you from exporting your personal vault." + }, + "addAccount": { + "message": "Add account" + }, + "removeMasterPassword": { + "message": "Remove master password" + }, + "removedMasterPassword": { + "message": "Master password removed" + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "leaveOrganization": { + "message": "Leave organization" + }, + "leaveOrganizationConfirmation": { + "message": "Are you sure you want to leave this organization?" + }, + "leftOrganization": { + "message": "You have left the organization." + }, + "ssoKeyConnectorError": { + "message": "Key connector error: make sure key connector is available and working correctly." + }, + "lockAllVaults": { + "message": "Lock all vaults" + }, + "accountLimitReached": { + "message": "No more than 5 accounts may be logged in at the same time." + }, + "accountPreferences": { + "message": "Preferences" + }, + "appPreferences": { + "message": "App settings (all accounts)" + }, + "accountSwitcherLimitReached": { + "message": "Account limit reached. Log out of an account to add another." + }, + "settingsTitle": { + "message": "App settings for $EMAIL$", + "placeholders": { + "email": { + "content": "$1", + "example": "jdoe@example.com" + } + } + }, + "switchAccount": { + "message": "Switch account" + }, + "options": { + "message": "Options" + }, + "sessionTimeout": { + "message": "Your session has timed out. Please go back and try logging in again." + }, + "exportingPersonalVaultTitle": { + "message": "Exporting individual vault" + }, + "exportingPersonalVaultDescription": { + "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included.", + "placeholders": { + "email": { + "content": "$1", + "example": "name@example.com" + } + } + }, + "locked": { + "message": "Locked" + }, + "unlocked": { + "message": "Unlocked" + }, + "generator": { + "message": "Generator" + }, + "whatWouldYouLikeToGenerate": { + "message": "What would you like to generate?" + }, + "passwordType": { + "message": "Password type" + }, + "regenerateUsername": { + "message": "Regenerate username" + }, + "generateUsername": { + "message": "Generate username" + }, + "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" + }, + "plusAddressedEmailDesc": { + "message": "Use your email provider's sub-addressing capabilities." + }, + "catchallEmail": { + "message": "Catch-all email" + }, + "catchallEmailDesc": { + "message": "Use your domain's configured catch-all inbox." + }, + "random": { + "message": "Random" + }, + "randomWord": { + "message": "Random word" + }, + "websiteName": { + "message": "Website name" + }, + "service": { + "message": "Service" + }, + "allVaults": { + "message": "All vaults" + }, + "searchOrganization": { + "message": "Search organization" + }, + "searchMyVault": { + "message": "Search my vault" + }, + "forwardedEmail": { + "message": "Forwarded email alias" + }, + "forwardedEmailDesc": { + "message": "Generate an email alias with an external forwarding service." + }, + "hostname": { + "message": "Hostname", + "description": "Part of a URL." + }, + "apiAccessToken": { + "message": "API Access Token" + }, + "apiKey": { + "message": "API key" + }, + "premiumSubcriptionRequired": { + "message": "Premium subscription required" + }, + "organizationIsDisabled": { + "message": "Organization suspended" + }, + "disabledOrganizationFilterError": { + "message": "Items in suspended organizations cannot be accessed. Contact your organization owner for assistance." + }, + "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." + }, + "cardBrandMir": { + "message": "Mir" + }, + "vault": { + "message": "Vault" + }, + "loginWithMasterPassword": { + "message": "Log in with master password" + }, + "loggingInAs": { + "message": "Logging in as" + }, + "rememberEmail": { + "message": "Remember email" + }, + "notYou": { + "message": "Not you?" + }, + "newAroundHere": { + "message": "New around here?" + }, + "loggingInTo": { + "message": "Logging in to $DOMAIN$", + "placeholders": { + "domain": { + "content": "$1", + "example": "example.com" + } + } + }, + "logInWithAnotherDevice": { + "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." + } +} diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index 44cefece66c..dbc6ab48ddc 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Disabled" }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" + }, "maxAccessCountReached": { "message": "Max access count reached" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index 87b6b6f07fc..267fdf4b438 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "Devre dışı" }, + "removePassword": { + "message": "Parolayı kaldır" + }, + "removedPassword": { + "message": "Parola kaldırıldı" + }, + "removePasswordConfirmation": { + "message": "Parolayı kaldırmak istediğinizden emin misiniz?" + }, "maxAccessCountReached": { "message": "Maksimum erişim sayısına ulaşıldı" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Başka bir cihazla giriş yap" + }, + "toggleCharacterCount": { + "message": "Karakter sayacını aç/kapat", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index a38af8ea000..74f2b12ae9f 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -6,7 +6,7 @@ "message": "Фільтри" }, "allItems": { - "message": "Всі елементи" + "message": "Усі записи" }, "favorites": { "message": "Обране" @@ -737,7 +737,7 @@ "message": "Новий вхід" }, "addNewItem": { - "message": "Новий елемент" + "message": "Новий запис" }, "addNewFolder": { "message": "Нова тека" @@ -937,7 +937,7 @@ "message": "Запускати в згорнутому вигляді" }, "startToTrayDesc": { - "message": "При першому запуску програми відображатиметься лише піктограма в системному лотку." + "message": "Під час першого запуску програми показувати лише піктограму в системному лотку." }, "startToMenuBar": { "message": "Запускати в панелі завдань" @@ -1331,7 +1331,7 @@ "message": "Ключі шифрування унікальні для кожного облікового запису користувача Bitwarden, тому ви не можете імпортувати зашифрований експорт до іншого облікового запису." }, "noOrganizationsList": { - "message": "Ви не входите до жодної організації. Організації дозволяють безпечно обмінюватися елементами з іншими користувачами." + "message": "Ви не входите до жодної організації. Організації дозволяють безпечно обмінюватися записами з іншими користувачами." }, "noCollectionsInList": { "message": "Немає збірок." @@ -1340,7 +1340,7 @@ "message": "Власник" }, "whoOwnsThisItem": { - "message": "Хто є власником цього елемента?" + "message": "Хто є власником цього запису?" }, "strong": { "message": "Надійний", @@ -1779,6 +1779,15 @@ "disabled": { "message": "Вимкнено" }, + "removePassword": { + "message": "Вилучити пароль" + }, + "removedPassword": { + "message": "Пароль вилучено" + }, + "removePasswordConfirmation": { + "message": "Ви дійсно хочете вилучити пароль?" + }, "maxAccessCountReached": { "message": "Досягнуто максимальну кількість доступів" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Увійти з іншого пристрою" + }, + "toggleCharacterCount": { + "message": "Перемкнути лічильник символів", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index 28075bd45d5..84ebfa0fadb 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -48,7 +48,7 @@ "message": "Chuyển tới Tổ chức" }, "movedItemToOrg": { - "message": "$ITEMNAME$ moved to $ORGNAME$", + "message": "$ITEMNAME$ đã di chuyển tới $ORGNAME$", "placeholders": { "itemname": { "content": "$1", @@ -752,7 +752,7 @@ "message": "Đang tải..." }, "lockVault": { - "message": "Lock vault" + "message": "Khóa kho" }, "passwordGenerator": { "message": "Tạo mật khẩu" @@ -805,7 +805,7 @@ "message": "Đồng bộ thất bại" }, "yourVaultIsLocked": { - "message": "Kho mật khẩu đã bị khóa. Xác minh mật khẩu chinhs của bạn để mở." + "message": "Kho mật khẩu đã bị khóa. Xác minh mật khẩu chính của bạn để mở." }, "unlock": { "message": "Mở khóa" @@ -901,7 +901,7 @@ "message": "Hiện biểu tượng trang web" }, "faviconDesc": { - "message": "Show a recognizable image next to each login." + "message": "Hiện một ảnh nhận dạng bên cạnh mỗi lần đăng nhập." }, "enableMinToTray": { "message": "Minimize to Tray Icon" @@ -946,7 +946,7 @@ "message": "When the application is first started, only show an icon in the menu bar." }, "openAtLogin": { - "message": "Start automatically on login" + "message": "Tự động bắt đầu khi đăng nhập" }, "openAtLoginDesc": { "message": "Start the Bitwarden desktop application automatically on login." @@ -958,7 +958,7 @@ "message": "Show the Bitwarden icon in the Dock even when minimized to the menu bar." }, "confirmTrayTitle": { - "message": "Confirm hiding tray" + "message": "Xác nhận ẩn khay" }, "confirmTrayDesc": { "message": "Turning off this setting will also turn off all other tray related settings." @@ -1213,7 +1213,7 @@ "description": "Domain name. Ex. website.com" }, "domainName": { - "message": "Domain name", + "message": "Tên miền", "description": "Domain name. Ex. website.com" }, "host": { @@ -1312,7 +1312,7 @@ "message": "Accessibility cookie saved!" }, "noAccessibilityCookieSaved": { - "message": "No accessibility cookie saved" + "message": "Không có cookie hỗ trợ tương tác nào được lưu" }, "warning": { "message": "CẢNH BÁO", @@ -1325,7 +1325,7 @@ "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." }, "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": "File xuất mã hóa dữ liệu xủa bạn bằng khóa giải mã. Nếu bạn thay đổi khóa giải mã, bạn nên xuất file lại vì bạn sẽ không thể giải mã file này." }, "encExportAccountWarningDesc": { "message": "Account encryption keys are unique to each Bitwarden user account, so you can't import an encrypted export into a different account." @@ -1569,31 +1569,31 @@ "message": "Điều khoản sử dụng và chính sách quyền riêng tư chưa được đồng ý." }, "enableBrowserIntegration": { - "message": "Allow browser integration" + "message": "Cho phép tích hợp với trình duyệt" }, "enableBrowserIntegrationDesc": { "message": "Used for biometrics in browser." }, "enableDuckDuckGoBrowserIntegration": { - "message": "Allow DuckDuckGo browser integration" + "message": "Cho phép tích hợp trình duyệt DuckDuckGo" }, "enableDuckDuckGoBrowserIntegrationDesc": { - "message": "Use your Bitwarden vault when browsing with DuckDuckGo." + "message": "Sử dụng kho Bitwarden của bạn khi tìm kiếm bằng DuckDuckGo." }, "browserIntegrationUnsupportedTitle": { - "message": "Browser integration not supported" + "message": "Không hỗ trợ tích hợp trình duyệt" }, "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." + "message": "Rất tiếc, tính năng tích hợp trình duyệt hiện chỉ được hỗ trợ trong phiên bản App Store trên Mac." }, "browserIntegrationWindowsStoreDesc": { - "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." + "message": "Rất tiếc, tính năng tích hợp trình duyệt hiện không được hỗ trợ trong phiên bản Microsoft Store." }, "browserIntegrationLinuxDesc": { - "message": "Unfortunately browser integration is currently not supported in the linux version." + "message": "Rất tiếc, tính năng tích hợp trình duyệt hiện không được hỗ trợ trong phiên bản linux." }, "enableBrowserIntegrationFingerprint": { - "message": "Require verification for browser integration" + "message": "Yêu cầu xác minh để tích hợp trình duyệt" }, "enableBrowserIntegrationFingerprintDesc": { "message": "Add an additional layer of security by requiring fingerprint phrase confirmation when establishing a link between your desktop and browser. This requires user action and verification each time a connection is created." @@ -1602,7 +1602,7 @@ "message": "Chấp nhận" }, "verifyBrowserTitle": { - "message": "Verify browser connection" + "message": "Xác minh kết nối trình duyệt" }, "verifyBrowserDesc": { "message": "Please ensure the shown fingerprint is identical to the fingerprint showed in the browser extension." @@ -1617,13 +1617,13 @@ } }, "verifyNativeMessagingConnectionDesc": { - "message": "Would you like to approve this request?" + "message": "Bạn có muốn chấp thuận yêu cầu này không?" }, "verifyNativeMessagingConnectionWarning": { - "message": "If you did not initiate this request, do not approve it." + "message": "Nếu bạn không gửi yêu cầu này, đừng chấp thuận." }, "biometricsNotEnabledTitle": { - "message": "Biometrics not set up" + "message": "Sinh trắc học chưa được thiết lập" }, "biometricsNotEnabledDesc": { "message": "Browser biometrics requires desktop biometrics to be set up in the settings first." @@ -1632,7 +1632,7 @@ "message": "Due to an enterprise policy, you are restricted from saving items to your individual vault. Change the ownership option to an organization and choose from available collections." }, "hintEqualsPassword": { - "message": "Your password hint cannot be the same as your password." + "message": "Gợi ý mật khẩu không được trùng với mật khẩu của bạn." }, "personalOwnershipPolicyInEffect": { "message": "An organization policy is affecting your ownership options." @@ -1662,7 +1662,7 @@ "message": "Văn bản" }, "deletionDate": { - "message": "Deletion date" + "message": "Ngày xóa" }, "deletionDateDesc": { "message": "Send sẽ được xóa vĩnh viễn vào ngày và giờ được chỉ định.", @@ -1703,7 +1703,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinkLabel": { - "message": "Send link", + "message": "Gửi liên kết", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "textHiddenByDefault": { @@ -1752,14 +1752,14 @@ "message": "1 ngày" }, "custom": { - "message": "Custom" + "message": "Tùy chỉnh" }, "deleteSendConfirmation": { "message": "Bạn có chắc chắn muốn xóa Send này?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLinkToClipboard": { - "message": "Copy Send link to clipboard", + "message": "Sao chép liên kết tới Khay nhớ tạm", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLinkOnSave": { @@ -1777,58 +1777,67 @@ "message": "Sao chép liên kết" }, "disabled": { - "message": "Disabled" + "message": "Đã tắt" + }, + "removePassword": { + "message": "Remove password" + }, + "removedPassword": { + "message": "Password removed" + }, + "removePasswordConfirmation": { + "message": "Are you sure you want to remove the password?" }, "maxAccessCountReached": { - "message": "Max access count reached" + "message": "Đã đạt đến số lượng truy cập tối đa" }, "expired": { - "message": "Expired" + "message": "Đã hết hạn" }, "pendingDeletion": { "message": "Đang chờ xóa" }, "webAuthnAuthenticate": { - "message": "Authenticate WebAuthn" + "message": "Xác thực WebAuthn" }, "hideEmail": { - "message": "Hide my email address from recipients." + "message": "Ẩn địa chỉ email của tôi khỏi người nhận." }, "sendOptionsPolicyInEffect": { - "message": "One or more organization policies are affecting your Send options." + "message": "Có một hoặc vài chính sách của tổ chức đang làm ảnh hưởng đến cài đặt tạo mật khẩu của bạn." }, "emailVerificationRequired": { - "message": "Email verification required" + "message": "Yêu cầu xác nhận danh tính qua Email" }, "emailVerificationRequiredDesc": { - "message": "You must verify your email to use this feature." + "message": "Bạn phải xác minh email của mình để sử dụng tính năng này." }, "passwordPrompt": { - "message": "Master password re-prompt" + "message": "Nhắc lại mật khẩu chính" }, "passwordConfirmation": { - "message": "Master password confirmation" + "message": "Xác nhận mật khẩu chính" }, "passwordConfirmationDesc": { - "message": "This action is protected. To continue, please re-enter your master password to verify your identity." + "message": "Hành động này được bảo vệ. Để tiếp tục, vui lòng nhập lại mật khẩu chính của bạn để xác minh danh tính của bạn." }, "updatedMasterPassword": { - "message": "Updated master password" + "message": "Mật khẩu chính đã được cập nhật" }, "updateMasterPassword": { - "message": "Update master password" + "message": "Cập nhật Mật khẩu chính" }, "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": "Mật khẩu chính của bạn gần đây đã được thay đổi bởi một quản trị viên trong tổ chức của bạn. Để truy cập Kho, bạn phải cập nhật nó ngay bây giờ. Tiếp tục sẽ đăng xuất bạn khỏi phiên hiện tại của bạn, yêu cầu bạn đăng nhập lại. Các phiên hoạt động trên các thiết bị khác có thể tiếp tục hoạt động trong tối đa một giờ." }, "hours": { - "message": "Hours" + "message": "Giờ" }, "minutes": { - "message": "Minutes" + "message": "Phút" }, "vaultTimeoutPolicyInEffect": { - "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)", + "message": "Chính sách tổ chức của bạn đang ảnh hưởng đến thời gian chờ Kho của bạn. Thời gian chờ kho tối đa được phép là $HOURS$ giờ và $MINUTES$ phút", "placeholders": { "hours": { "content": "$1", @@ -1841,10 +1850,10 @@ } }, "vaultTimeoutTooLarge": { - "message": "Your vault timeout exceeds the restrictions set by your organization." + "message": "Thời gian chờ Kho của bạn vượt quá các hạn chế do tổ chức của bạn đặt ra." }, "resetPasswordPolicyAutoEnroll": { - "message": "Automatic enrollment" + "message": "Đăng ký tự động" }, "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." @@ -1856,13 +1865,13 @@ "message": "One or more organization policies prevents you from exporting your personal vault." }, "addAccount": { - "message": "Add account" + "message": "Thêm tài khoản" }, "removeMasterPassword": { - "message": "Remove master password" + "message": "Xóa mật khẩu chính" }, "removedMasterPassword": { - "message": "Master password removed" + "message": "Đã xóa mật khẩu chính" }, "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.", @@ -1874,7 +1883,7 @@ } }, "leaveOrganization": { - "message": "Leave organization" + "message": "Rời khỏi tổ chức" }, "leaveOrganizationConfirmation": { "message": "Are you sure you want to leave this organization?" @@ -1892,7 +1901,7 @@ "message": "No more than 5 accounts may be logged in at the same time." }, "accountPreferences": { - "message": "Preferences" + "message": "Tuỳ chỉnh" }, "appPreferences": { "message": "App settings (all accounts)" @@ -1910,10 +1919,10 @@ } }, "switchAccount": { - "message": "Switch account" + "message": "Chuyển tài khoản" }, "options": { - "message": "Options" + "message": "Tùy chọn" }, "sessionTimeout": { "message": "Your session has timed out. Please go back and try logging in again." @@ -1937,19 +1946,19 @@ "message": "Đã mở khóa" }, "generator": { - "message": "Generator" + "message": "Tạo" }, "whatWouldYouLikeToGenerate": { - "message": "What would you like to generate?" + "message": "Bạn muốn tạo gì?" }, "passwordType": { - "message": "Password type" + "message": "Loại mật khẩu" }, "regenerateUsername": { "message": "Regenerate username" }, "generateUsername": { - "message": "Generate username" + "message": "Tạo tên tài khoản" }, "usernameType": { "message": "Username type" @@ -1968,22 +1977,22 @@ "message": "Use your domain's configured catch-all inbox." }, "random": { - "message": "Random" + "message": "Ngẫu nhiên" }, "randomWord": { - "message": "Random word" + "message": "Từ ngẫu nhiên" }, "websiteName": { - "message": "Website name" + "message": "Tên website" }, "service": { - "message": "Service" + "message": "Dịch vụ" }, "allVaults": { - "message": "All vaults" + "message": "Tất cả kho" }, "searchOrganization": { - "message": "Search organization" + "message": "Tìm tổ chức" }, "searchMyVault": { "message": "Search my vault" @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "Log in with another device" + }, + "toggleCharacterCount": { + "message": "Toggle character count", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index 0d3c5c1903c..530a3e76e77 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -1779,6 +1779,15 @@ "disabled": { "message": "已禁用" }, + "removePassword": { + "message": "移除密码" + }, + "removedPassword": { + "message": "密码已移除" + }, + "removePasswordConfirmation": { + "message": "确定要移除此密码吗?" + }, "maxAccessCountReached": { "message": "已达最大访问次数" }, @@ -2035,7 +2044,7 @@ "message": "不是你?" }, "newAroundHere": { - "message": "新建在这里?" + "message": "初来乍到吗?" }, "loggingInTo": { "message": "正在登录到 $DOMAIN$", @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "使用其他设备登录" + }, + "toggleCharacterCount": { + "message": "字符计数开关", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index 4eb5624dbd9..f419931029d 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -958,7 +958,7 @@ "message": "即使最小化到選單列,也在 Docker 中顯示 Bitwarden 圖示。" }, "confirmTrayTitle": { - "message": "確認禁用系統匣" + "message": "確認隱藏到系統匣" }, "confirmTrayDesc": { "message": "禁用此設定也將禁用所有其他系統匣相關設定。" @@ -1779,6 +1779,15 @@ "disabled": { "message": "已停用" }, + "removePassword": { + "message": "移除密碼" + }, + "removedPassword": { + "message": "已移除密碼" + }, + "removePasswordConfirmation": { + "message": "您確定要移除密碼嗎?" + }, "maxAccessCountReached": { "message": "已達最大存取次數" }, @@ -2048,5 +2057,9 @@ }, "logInWithAnotherDevice": { "message": "使用其他裝置登入" + }, + "toggleCharacterCount": { + "message": "切換字元計數", + "description": "'Character count' describes a feature that displays a number next to each character of the password." } } diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index d54d46487a2..f5a078cdcc5 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -6,20 +6,20 @@ import { StateFactory } from "@bitwarden/common/factories/stateFactory"; import { GlobalState } from "@bitwarden/common/models/domain/global-state"; import { MemoryStorageService } from "@bitwarden/common/services/memoryStorage.service"; import { StateService } from "@bitwarden/common/services/state.service"; -import { ElectronLogService } from "@bitwarden/electron/services/electronLog.service"; -import { ElectronMainMessagingService } from "@bitwarden/electron/services/electronMainMessaging.service"; -import { ElectronStorageService } from "@bitwarden/electron/services/electronStorage.service"; -import { TrayMain } from "@bitwarden/electron/tray.main"; -import { UpdaterMain } from "@bitwarden/electron/updater.main"; -import { WindowMain } from "@bitwarden/electron/window.main"; import { BiometricMain } from "./main/biometric/biometric.main"; -import { DesktopCredentialStorageListener } from "./main/desktopCredentialStorageListener"; +import { DesktopCredentialStorageListener } from "./main/desktop-credential-storage-listener"; import { MenuMain } from "./main/menu/menu.main"; import { MessagingMain } from "./main/messaging.main"; -import { NativeMessagingMain } from "./main/nativeMessaging.main"; -import { PowerMonitorMain } from "./main/powerMonitor.main"; +import { NativeMessagingMain } from "./main/native-messaging.main"; +import { PowerMonitorMain } from "./main/power-monitor.main"; +import { TrayMain } from "./main/tray.main"; +import { UpdaterMain } from "./main/updater.main"; +import { WindowMain } from "./main/window.main"; import { Account } from "./models/account"; +import { ElectronLogService } from "./services/electron-log.service"; +import { ElectronMainMessagingService } from "./services/electron-main-messaging.service"; +import { ElectronStorageService } from "./services/electron-storage.service"; import { I18nService } from "./services/i18n.service"; export class Main { @@ -105,15 +105,7 @@ export class Main { (win) => this.trayMain.setupWindowListeners(win) ); this.messagingMain = new MessagingMain(this, this.stateService); - this.updaterMain = new UpdaterMain( - this.i18nService, - this.windowMain, - "clients", - null, - null, - null, - "bitwarden" - ); + this.updaterMain = new UpdaterMain(this.i18nService, this.windowMain, "bitwarden"); this.menuMain = new MenuMain(this); this.powerMonitorMain = new PowerMonitorMain(this); this.trayMain = new TrayMain(this.windowMain, this.i18nService, this.stateService); diff --git a/apps/desktop/src/main/biometric/biometric.windows.main.ts b/apps/desktop/src/main/biometric/biometric.windows.main.ts index 01e3b563f2f..207e6c38b56 100644 --- a/apps/desktop/src/main/biometric/biometric.windows.main.ts +++ b/apps/desktop/src/main/biometric/biometric.windows.main.ts @@ -4,7 +4,8 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { StateService } from "@bitwarden/common/abstractions/state.service"; import { biometrics } from "@bitwarden/desktop-native"; -import { WindowMain } from "@bitwarden/electron/window.main"; + +import { WindowMain } from "../window.main"; import { BiometricMain } from "./biometric.main"; diff --git a/apps/desktop/src/main/desktopCredentialStorageListener.ts b/apps/desktop/src/main/desktop-credential-storage-listener.ts similarity index 100% rename from apps/desktop/src/main/desktopCredentialStorageListener.ts rename to apps/desktop/src/main/desktop-credential-storage-listener.ts diff --git a/apps/desktop/src/main/menu/menu.about.ts b/apps/desktop/src/main/menu/menu.about.ts index 64f1adad0ce..33d4f12ea55 100644 --- a/apps/desktop/src/main/menu/menu.about.ts +++ b/apps/desktop/src/main/menu/menu.about.ts @@ -1,8 +1,9 @@ import { BrowserWindow, clipboard, dialog, MenuItemConstructorOptions } from "electron"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; -import { UpdaterMain } from "@bitwarden/electron/updater.main"; -import { isMacAppStore, isSnapStore, isWindowsStore } from "@bitwarden/electron/utils"; + +import { isMacAppStore, isSnapStore, isWindowsStore } from "../../utils"; +import { UpdaterMain } from "../updater.main"; import { IMenubarMenu } from "./menubar"; diff --git a/apps/desktop/src/main/menu/menu.account.ts b/apps/desktop/src/main/menu/menu.account.ts index b7c4b1f693f..31e0797ebb6 100644 --- a/apps/desktop/src/main/menu/menu.account.ts +++ b/apps/desktop/src/main/menu/menu.account.ts @@ -2,7 +2,8 @@ import { BrowserWindow, dialog, MenuItemConstructorOptions, shell } from "electr import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; -import { isMacAppStore, isWindowsStore } from "@bitwarden/electron/utils"; + +import { isMacAppStore, isWindowsStore } from "../../utils"; import { IMenubarMenu } from "./menubar"; diff --git a/apps/desktop/src/main/menu/menu.bitwarden.ts b/apps/desktop/src/main/menu/menu.bitwarden.ts index d25454d9a49..7a20b381f58 100644 --- a/apps/desktop/src/main/menu/menu.bitwarden.ts +++ b/apps/desktop/src/main/menu/menu.bitwarden.ts @@ -2,8 +2,9 @@ import { BrowserWindow, MenuItemConstructorOptions } from "electron"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; -import { UpdaterMain } from "@bitwarden/electron/updater.main"; -import { isMac } from "@bitwarden/electron/utils"; + +import { isMac } from "../../utils"; +import { UpdaterMain } from "../updater.main"; import { FirstMenu } from "./menu.first"; import { MenuAccount } from "./menu.updater"; diff --git a/apps/desktop/src/main/menu/menu.file.ts b/apps/desktop/src/main/menu/menu.file.ts index 71b4a236922..6a7c3ba16dd 100644 --- a/apps/desktop/src/main/menu/menu.file.ts +++ b/apps/desktop/src/main/menu/menu.file.ts @@ -2,8 +2,9 @@ import { BrowserWindow, MenuItemConstructorOptions } from "electron"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; -import { UpdaterMain } from "@bitwarden/electron/updater.main"; -import { isMac, isMacAppStore } from "@bitwarden/electron/utils"; + +import { isMac, isMacAppStore } from "../../utils"; +import { UpdaterMain } from "../updater.main"; import { FirstMenu } from "./menu.first"; import { MenuAccount } from "./menu.updater"; diff --git a/apps/desktop/src/main/menu/menu.first.ts b/apps/desktop/src/main/menu/menu.first.ts index 5786b4e68cf..06f24b0570e 100644 --- a/apps/desktop/src/main/menu/menu.first.ts +++ b/apps/desktop/src/main/menu/menu.first.ts @@ -2,8 +2,9 @@ import { BrowserWindow, dialog, MenuItem, MenuItemConstructorOptions } from "ele import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; -import { UpdaterMain } from "@bitwarden/electron/updater.main"; -import { isMacAppStore, isSnapStore, isWindowsStore } from "@bitwarden/electron/utils"; + +import { isMacAppStore, isSnapStore, isWindowsStore } from "../../utils"; +import { UpdaterMain } from "../updater.main"; import { MenuAccount } from "./menu.updater"; diff --git a/apps/desktop/src/main/menu/menu.help.ts b/apps/desktop/src/main/menu/menu.help.ts index 60fce1ce494..2abab06dfab 100644 --- a/apps/desktop/src/main/menu/menu.help.ts +++ b/apps/desktop/src/main/menu/menu.help.ts @@ -1,7 +1,8 @@ import { shell, MenuItemConstructorOptions } from "electron"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; -import { isMacAppStore, isWindowsStore } from "@bitwarden/electron/utils"; + +import { isMacAppStore, isWindowsStore } from "../../utils"; import { AboutMenu } from "./menu.about"; import { IMenubarMenu } from "./menubar"; @@ -165,6 +166,7 @@ export class HelpMenu implements IMenubarMenu { { id: "android", label: "Android", + visible: !isMacAppStore(), // Apple Guideline 2.3.10 - Accurate Metadata click: () => { shell.openExternal( "https://play.google.com/store/apps/" + "details?id=com.x8bit.bitwarden" diff --git a/apps/desktop/src/main/menu/menu.main.ts b/apps/desktop/src/main/menu/menu.main.ts index 66670e196cc..6a2bd3531b5 100644 --- a/apps/desktop/src/main/menu/menu.main.ts +++ b/apps/desktop/src/main/menu/menu.main.ts @@ -1,17 +1,22 @@ import { app, Menu } from "electron"; -import { BaseMenu } from "@bitwarden/electron/baseMenu"; +import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { Main } from "../../main"; +import { WindowMain } from "../window.main"; import { MenuUpdateRequest } from "./menu.updater"; import { Menubar } from "./menubar"; const cloudWebVaultUrl = "https://vault.bitwarden.com"; -export class MenuMain extends BaseMenu { +export class MenuMain { + private i18nService: I18nService; + private windowMain: WindowMain; + constructor(private main: Main) { - super(main.i18nService, main.windowMain); + this.i18nService = main.i18nService; + this.windowMain = main.windowMain; } async init() { @@ -49,4 +54,84 @@ export class MenuMain extends BaseMenu { } return webVaultUrl; } + + private initContextMenu() { + if (this.windowMain.win == null) { + return; + } + + const selectionMenu = Menu.buildFromTemplate([ + { + label: this.i18nService.t("copy"), + role: "copy", + }, + { type: "separator" }, + { + label: this.i18nService.t("selectAll"), + role: "selectAll", + }, + ]); + + const inputMenu = Menu.buildFromTemplate([ + { + label: this.i18nService.t("undo"), + role: "undo", + }, + { + label: this.i18nService.t("redo"), + role: "redo", + }, + { type: "separator" }, + { + label: this.i18nService.t("cut"), + role: "cut", + enabled: false, + }, + { + label: this.i18nService.t("copy"), + role: "copy", + enabled: false, + }, + { + label: this.i18nService.t("paste"), + role: "paste", + }, + { type: "separator" }, + { + label: this.i18nService.t("selectAll"), + role: "selectAll", + }, + ]); + + const inputSelectionMenu = Menu.buildFromTemplate([ + { + label: this.i18nService.t("cut"), + role: "cut", + }, + { + label: this.i18nService.t("copy"), + role: "copy", + }, + { + label: this.i18nService.t("paste"), + role: "paste", + }, + { type: "separator" }, + { + label: this.i18nService.t("selectAll"), + role: "selectAll", + }, + ]); + + this.windowMain.win.webContents.on("context-menu", (e, props) => { + const selected = props.selectionText && props.selectionText.trim() !== ""; + if (props.isEditable && selected) { + inputSelectionMenu.popup({ window: this.windowMain.win }); + } else if (props.isEditable) { + inputMenu.popup({ window: this.windowMain.win }); + } else if (selected) { + selectionMenu.popup({ window: this.windowMain.win }); + } + }); + } } diff --git a/apps/desktop/src/main/menu/menu.window.ts b/apps/desktop/src/main/menu/menu.window.ts index 3746e22b726..8f588781a56 100644 --- a/apps/desktop/src/main/menu/menu.window.ts +++ b/apps/desktop/src/main/menu/menu.window.ts @@ -2,8 +2,9 @@ import { MenuItemConstructorOptions } from "electron"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; -import { isMac } from "@bitwarden/electron/utils"; -import { WindowMain } from "@bitwarden/electron/window.main"; + +import { isMac } from "../../utils"; +import { WindowMain } from "../window.main"; import { IMenubarMenu } from "./menubar"; diff --git a/apps/desktop/src/main/menu/menubar.ts b/apps/desktop/src/main/menu/menubar.ts index 441b9ac6550..5048c60dc09 100644 --- a/apps/desktop/src/main/menu/menubar.ts +++ b/apps/desktop/src/main/menu/menubar.ts @@ -2,9 +2,10 @@ import { Menu, MenuItemConstructorOptions } from "electron"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; -import { UpdaterMain } from "@bitwarden/electron/updater.main"; -import { isMac } from "@bitwarden/electron/utils"; -import { WindowMain } from "@bitwarden/electron/window.main"; + +import { isMac } from "../../utils"; +import { UpdaterMain } from "../updater.main"; +import { WindowMain } from "../window.main"; import { AboutMenu } from "./menu.about"; import { AccountMenu } from "./menu.account"; diff --git a/apps/desktop/src/main/nativeMessaging.main.ts b/apps/desktop/src/main/native-messaging.main.ts similarity index 99% rename from apps/desktop/src/main/nativeMessaging.main.ts rename to apps/desktop/src/main/native-messaging.main.ts index 0559d2ce96c..ae549ace384 100644 --- a/apps/desktop/src/main/nativeMessaging.main.ts +++ b/apps/desktop/src/main/native-messaging.main.ts @@ -8,7 +8,8 @@ import { ipcMain } from "electron"; import * as ipc from "node-ipc"; import { LogService } from "@bitwarden/common/abstractions/log.service"; -import { WindowMain } from "@bitwarden/electron/window.main"; + +import { WindowMain } from "./window.main"; export class NativeMessagingMain { private connected: Socket[] = []; diff --git a/apps/desktop/src/main/powerMonitor.main.ts b/apps/desktop/src/main/power-monitor.main.ts similarity index 94% rename from apps/desktop/src/main/powerMonitor.main.ts rename to apps/desktop/src/main/power-monitor.main.ts index 438a0e6f9ec..56e6139ccf1 100644 --- a/apps/desktop/src/main/powerMonitor.main.ts +++ b/apps/desktop/src/main/power-monitor.main.ts @@ -1,8 +1,7 @@ import { powerMonitor } from "electron"; -import { isSnapStore } from "@bitwarden/electron/utils"; - import { Main } from "../main"; +import { isSnapStore } from "../utils"; // tslint:disable-next-line const IdleLockSeconds = 5 * 60; // 5 minutes diff --git a/libs/electron/src/tray.main.ts b/apps/desktop/src/main/tray.main.ts similarity index 100% rename from libs/electron/src/tray.main.ts rename to apps/desktop/src/main/tray.main.ts diff --git a/libs/electron/src/updater.main.ts b/apps/desktop/src/main/updater.main.ts similarity index 86% rename from libs/electron/src/updater.main.ts rename to apps/desktop/src/main/updater.main.ts index 638c5efeda4..314ea076b22 100644 --- a/libs/electron/src/updater.main.ts +++ b/apps/desktop/src/main/updater.main.ts @@ -4,10 +4,11 @@ import { autoUpdater } from "electron-updater"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; -import { isAppImage, isDev, isMacAppStore, isWindowsPortable, isWindowsStore } from "./utils"; +import { isAppImage, isDev, isMacAppStore, isWindowsPortable, isWindowsStore } from "../utils"; + import { WindowMain } from "./window.main"; -const UpdaterCheckInitalDelay = 5 * 1000; // 5 seconds +const UpdaterCheckInitialDelay = 5 * 1000; // 5 seconds const UpdaterCheckInterval = 12 * 60 * 60 * 1000; // 12 hours export class UpdaterMain { @@ -18,10 +19,6 @@ export class UpdaterMain { constructor( private i18nService: I18nService, private windowMain: WindowMain, - private gitHubProject: string, - private onCheckingForUpdate: () => void = null, - private onReset: () => void = null, - private onUpdateDownloaded: () => void = null, private projectName: string ) { autoUpdater.logger = log; @@ -36,13 +33,10 @@ export class UpdaterMain { } async init() { - global.setTimeout(async () => await this.checkForUpdate(), UpdaterCheckInitalDelay); + global.setTimeout(async () => await this.checkForUpdate(), UpdaterCheckInitialDelay); global.setInterval(async () => await this.checkForUpdate(), UpdaterCheckInterval); autoUpdater.on("checking-for-update", () => { - if (this.onCheckingForUpdate != null) { - this.onCheckingForUpdate(); - } this.doingUpdateCheck = true; }); @@ -87,10 +81,6 @@ export class UpdaterMain { }); autoUpdater.on("update-downloaded", async (info) => { - if (this.onUpdateDownloaded != null) { - this.onUpdateDownloaded(); - } - if (this.windowMain.win == null) { return; } @@ -132,7 +122,7 @@ export class UpdaterMain { if (!this.canUpdate) { if (withFeedback) { - shell.openExternal("https://github.com/bitwarden/" + this.gitHubProject + "/releases"); + shell.openExternal("https://github.com/bitwarden/clients/releases"); } return; @@ -147,9 +137,6 @@ export class UpdaterMain { } private reset() { - if (this.onReset != null) { - this.onReset(); - } autoUpdater.autoDownload = true; this.doingUpdateCheck = false; } diff --git a/libs/electron/src/window.main.ts b/apps/desktop/src/main/window.main.ts similarity index 94% rename from libs/electron/src/window.main.ts rename to apps/desktop/src/main/window.main.ts index 47f7e4e9cd3..b1d88a821f4 100644 --- a/libs/electron/src/window.main.ts +++ b/apps/desktop/src/main/window.main.ts @@ -5,17 +5,19 @@ import { app, BrowserWindow, screen } from "electron"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { StateService } from "@bitwarden/common/abstractions/state.service"; +import { WindowState } from "@bitwarden/common/models/domain/window-state"; -import { cleanUserAgent, isDev, isMacAppStore, isSnapStore } from "./utils"; +import { cleanUserAgent, isDev, isMacAppStore, isSnapStore } from "../utils"; const mainWindowSizeKey = "mainWindowSize"; const WindowEventHandlingDelay = 100; export class WindowMain { win: BrowserWindow; isQuitting = false; + isClosing = false; private windowStateChangeTimer: NodeJS.Timer; - private windowStates: { [key: string]: any } = {}; + private windowStates: { [key: string]: WindowState } = {}; private enableAlwaysOnTop = false; constructor( @@ -128,6 +130,10 @@ export class WindowMain { }, }); + this.win.webContents.on("dom-ready", () => { + this.win.webContents.zoomFactor = this.windowStates[mainWindowSizeKey].zoomFactor ?? 1.0; + }); + if (this.windowStates[mainWindowSizeKey].isMaximized) { this.win.maximize(); } @@ -154,6 +160,7 @@ export class WindowMain { // Emitted when the window is closed. this.win.on("closed", async () => { + this.isClosing = false; await this.updateWindowState(mainWindowSizeKey, this.win); // Dereference the window object, usually you would store window @@ -163,6 +170,7 @@ export class WindowMain { }); this.win.on("close", async () => { + this.isClosing = true; await this.updateWindowState(mainWindowSizeKey, this.win); }); @@ -217,7 +225,7 @@ export class WindowMain { if (this.windowStates[configKey] == null) { this.windowStates[configKey] = await this.stateService.getWindow(); if (this.windowStates[configKey] == null) { - this.windowStates[configKey] = {}; + this.windowStates[configKey] = {}; } } @@ -231,6 +239,10 @@ export class WindowMain { this.windowStates[configKey].height = bounds.height; } + if (this.isClosing) { + this.windowStates[configKey].zoomFactor = win.webContents.zoomFactor; + } + await this.stateService.setWindow(this.windowStates[configKey]); } catch (e) { this.logService.error(e); diff --git a/apps/desktop/src/models/nativeMessaging/decryptedCommandData.ts b/apps/desktop/src/models/native-messaging/decrypted-command-data.ts similarity index 60% rename from apps/desktop/src/models/nativeMessaging/decryptedCommandData.ts rename to apps/desktop/src/models/native-messaging/decrypted-command-data.ts index 0aea170f6e6..eeab234d7a4 100644 --- a/apps/desktop/src/models/nativeMessaging/decryptedCommandData.ts +++ b/apps/desktop/src/models/native-messaging/decrypted-command-data.ts @@ -1,4 +1,4 @@ -import { EncryptedCommand } from "./encryptedCommand"; +import { EncryptedCommand } from "./encrypted-command"; export type DecryptedCommandData = { command: EncryptedCommand; diff --git a/apps/desktop/src/models/nativeMessaging/encryptedCommand.ts b/apps/desktop/src/models/native-messaging/encrypted-command.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedCommand.ts rename to apps/desktop/src/models/native-messaging/encrypted-command.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialCreatePayload.ts b/apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-create-payload.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialCreatePayload.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-create-payload.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialRetrievePayload.ts b/apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-retrieve-payload.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialRetrievePayload.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-retrieve-payload.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload.ts b/apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-update-payload.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-payloads/credential-update-payload.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/passwordGeneratePayload.ts b/apps/desktop/src/models/native-messaging/encrypted-message-payloads/password-generate-payload.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessagePayloads/passwordGeneratePayload.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-payloads/password-generate-payload.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-response.ts similarity index 76% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-response.ts index bed85b65e96..5f8deb05a30 100644 --- a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponse.ts +++ b/apps/desktop/src/models/native-messaging/encrypted-message-response.ts @@ -1,6 +1,6 @@ import { EncString } from "@bitwarden/common/models/domain/enc-string"; -import { MessageCommon } from "./messageCommon"; +import { MessageCommon } from "./message-common"; export type EncryptedMessageResponse = MessageCommon & { encryptedPayload: EncString; diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/accountStatusResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/account-status-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/accountStatusResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/account-status-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cannotDecryptErrorResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/cannot-decrypt-error-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cannotDecryptErrorResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/cannot-decrypt-error-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cipherResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/cipher-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/cipherResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/cipher-response.ts diff --git a/apps/desktop/src/models/native-messaging/encrypted-message-responses/encrypted-message-response.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/encrypted-message-response.ts new file mode 100644 index 00000000000..a0d35a7adbd --- /dev/null +++ b/apps/desktop/src/models/native-messaging/encrypted-message-responses/encrypted-message-response.ts @@ -0,0 +1,16 @@ +import { AccountStatusResponse } from "./account-status-response"; +import { CannotDecryptErrorResponse } from "./cannot-decrypt-error-response"; +import { CipherResponse } from "./cipher-response"; +import { FailureStatusResponse } from "./failure-status-response"; +import { GenerateResponse } from "./generate-response"; +import { SuccessStatusResponse } from "./success-status-response"; +import { UserStatusErrorResponse } from "./user-status-error-response"; + +export type EncyptedMessageResponse = + | AccountStatusResponse[] + | CannotDecryptErrorResponse + | CipherResponse[] + | FailureStatusResponse + | GenerateResponse + | SuccessStatusResponse + | UserStatusErrorResponse; diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/failureStatusResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/failure-status-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/failureStatusResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/failure-status-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/generateResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/generate-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/generateResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/generate-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/successStatusResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/success-status-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/successStatusResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/success-status-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/userStatusErrorResponse.ts b/apps/desktop/src/models/native-messaging/encrypted-message-responses/user-status-error-response.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/userStatusErrorResponse.ts rename to apps/desktop/src/models/native-messaging/encrypted-message-responses/user-status-error-response.ts diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessage.ts b/apps/desktop/src/models/native-messaging/encrypted-message.ts similarity index 80% rename from apps/desktop/src/models/nativeMessaging/encryptedMessage.ts rename to apps/desktop/src/models/native-messaging/encrypted-message.ts index bd0b5177a40..fc06214875e 100644 --- a/apps/desktop/src/models/nativeMessaging/encryptedMessage.ts +++ b/apps/desktop/src/models/native-messaging/encrypted-message.ts @@ -1,6 +1,6 @@ import { EncString } from "@bitwarden/common/models/domain/enc-string"; -import { MessageCommon } from "./messageCommon"; +import { MessageCommon } from "./message-common"; export type EncryptedMessage = MessageCommon & { // Will decrypt to a DecryptedCommandData object diff --git a/apps/desktop/src/models/native-messaging/index.ts b/apps/desktop/src/models/native-messaging/index.ts new file mode 100644 index 00000000000..7f6701886af --- /dev/null +++ b/apps/desktop/src/models/native-messaging/index.ts @@ -0,0 +1,25 @@ +export * from "./encrypted-message-payloads/credential-create-payload"; +export * from "./encrypted-message-payloads/credential-retrieve-payload"; +export * from "./encrypted-message-payloads/credential-update-payload"; +export * from "./encrypted-message-payloads/password-generate-payload"; + +export * from "./encrypted-message-responses/account-status-response"; +export * from "./encrypted-message-responses/cannot-decrypt-error-response"; +export * from "./encrypted-message-responses/cipher-response"; +export * from "./encrypted-message-responses/encrypted-message-response"; +export * from "./encrypted-message-responses/failure-status-response"; +export * from "./encrypted-message-responses/generate-response"; +export * from "./encrypted-message-responses/success-status-response"; +export * from "./encrypted-message-responses/user-status-error-response"; + +export * from "./decrypted-command-data"; +export * from "./encrypted-command"; +export * from "./encrypted-message"; +export * from "./encrypted-message-response"; +export * from "./legacy-message"; +export * from "./legacy-message-wrapper"; +export * from "./message"; +export * from "./message-common"; +export * from "./unencrypted-command"; +export * from "./unencrypted-message"; +export * from "./unencrypted-message-response"; diff --git a/apps/desktop/src/models/nativeMessaging/legacyMessageWrapper.ts b/apps/desktop/src/models/native-messaging/legacy-message-wrapper.ts similarity index 77% rename from apps/desktop/src/models/nativeMessaging/legacyMessageWrapper.ts rename to apps/desktop/src/models/native-messaging/legacy-message-wrapper.ts index 5ef10e73289..03068374069 100644 --- a/apps/desktop/src/models/nativeMessaging/legacyMessageWrapper.ts +++ b/apps/desktop/src/models/native-messaging/legacy-message-wrapper.ts @@ -1,6 +1,6 @@ import { EncString } from "@bitwarden/common/models/domain/enc-string"; -import { LegacyMessage } from "./legacyMessage"; +import { LegacyMessage } from "./legacy-message"; export type LegacyMessageWrapper = { message: LegacyMessage | EncString; diff --git a/apps/desktop/src/models/nativeMessaging/legacyMessage.ts b/apps/desktop/src/models/native-messaging/legacy-message.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/legacyMessage.ts rename to apps/desktop/src/models/native-messaging/legacy-message.ts diff --git a/apps/desktop/src/models/nativeMessaging/messageCommon.ts b/apps/desktop/src/models/native-messaging/message-common.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/messageCommon.ts rename to apps/desktop/src/models/native-messaging/message-common.ts diff --git a/apps/desktop/src/models/native-messaging/message.ts b/apps/desktop/src/models/native-messaging/message.ts new file mode 100644 index 00000000000..b76666bf142 --- /dev/null +++ b/apps/desktop/src/models/native-messaging/message.ts @@ -0,0 +1,4 @@ +import { EncryptedMessage } from "./encrypted-message"; +import { UnencryptedMessage } from "./unencrypted-message"; + +export type Message = UnencryptedMessage | EncryptedMessage; diff --git a/apps/desktop/src/models/nativeMessaging/unencryptedCommand.ts b/apps/desktop/src/models/native-messaging/unencrypted-command.ts similarity index 100% rename from apps/desktop/src/models/nativeMessaging/unencryptedCommand.ts rename to apps/desktop/src/models/native-messaging/unencrypted-command.ts diff --git a/apps/desktop/src/models/nativeMessaging/unencryptedMessageResponse.ts b/apps/desktop/src/models/native-messaging/unencrypted-message-response.ts similarity index 85% rename from apps/desktop/src/models/nativeMessaging/unencryptedMessageResponse.ts rename to apps/desktop/src/models/native-messaging/unencrypted-message-response.ts index a564b1324e2..b4f71ec6253 100644 --- a/apps/desktop/src/models/nativeMessaging/unencryptedMessageResponse.ts +++ b/apps/desktop/src/models/native-messaging/unencrypted-message-response.ts @@ -1,4 +1,4 @@ -import { MessageCommon } from "./messageCommon"; +import { MessageCommon } from "./message-common"; export type UnencryptedMessageResponse = MessageCommon & ( diff --git a/apps/desktop/src/models/nativeMessaging/unencryptedMessage.ts b/apps/desktop/src/models/native-messaging/unencrypted-message.ts similarity index 58% rename from apps/desktop/src/models/nativeMessaging/unencryptedMessage.ts rename to apps/desktop/src/models/native-messaging/unencrypted-message.ts index a665f312a98..d4ddb83ad68 100644 --- a/apps/desktop/src/models/nativeMessaging/unencryptedMessage.ts +++ b/apps/desktop/src/models/native-messaging/unencrypted-message.ts @@ -1,5 +1,5 @@ -import { MessageCommon } from "./messageCommon"; -import { UnencryptedCommand } from "./unencryptedCommand"; +import { MessageCommon } from "./message-common"; +import { UnencryptedCommand } from "./unencrypted-command"; export type UnencryptedMessage = MessageCommon & { command: UnencryptedCommand; diff --git a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse.ts b/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse.ts deleted file mode 100644 index a10da2d8571..00000000000 --- a/apps/desktop/src/models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { AccountStatusResponse } from "./accountStatusResponse"; -import { CannotDecryptErrorResponse } from "./cannotDecryptErrorResponse"; -import { CipherResponse } from "./cipherResponse"; -import { FailureStatusResponse } from "./failureStatusResponse"; -import { GenerateResponse } from "./generateResponse"; -import { SuccessStatusResponse } from "./successStatusResponse"; -import { UserStatusErrorResponse } from "./userStatusErrorResponse"; - -export type EncyptedMessageResponse = - | AccountStatusResponse[] - | CannotDecryptErrorResponse - | CipherResponse[] - | FailureStatusResponse - | GenerateResponse - | SuccessStatusResponse - | UserStatusErrorResponse; diff --git a/apps/desktop/src/models/nativeMessaging/index.ts b/apps/desktop/src/models/nativeMessaging/index.ts deleted file mode 100644 index 1b0148c3f90..00000000000 --- a/apps/desktop/src/models/nativeMessaging/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -export * from "./encryptedMessagePayloads/credentialCreatePayload"; -export * from "./encryptedMessagePayloads/credentialRetrievePayload"; -export * from "./encryptedMessagePayloads/credentialUpdatePayload"; -export * from "./encryptedMessagePayloads/passwordGeneratePayload"; - -export * from "./encryptedMessageResponses/accountStatusResponse"; -export * from "./encryptedMessageResponses/cannotDecryptErrorResponse"; -export * from "./encryptedMessageResponses/cipherResponse"; -export * from "./encryptedMessageResponses/encryptedMessageResponse"; -export * from "./encryptedMessageResponses/failureStatusResponse"; -export * from "./encryptedMessageResponses/generateResponse"; -export * from "./encryptedMessageResponses/successStatusResponse"; -export * from "./encryptedMessageResponses/userStatusErrorResponse"; - -export * from "./decryptedCommandData"; -export * from "./encryptedCommand"; -export * from "./encryptedMessage"; -export * from "./encryptedMessageResponse"; -export * from "./legacyMessage"; -export * from "./legacyMessageWrapper"; -export * from "./message"; -export * from "./messageCommon"; -export * from "./unencryptedCommand"; -export * from "./unencryptedMessage"; -export * from "./unencryptedMessageResponse"; diff --git a/apps/desktop/src/models/nativeMessaging/message.ts b/apps/desktop/src/models/nativeMessaging/message.ts deleted file mode 100644 index 25d96aa218a..00000000000 --- a/apps/desktop/src/models/nativeMessaging/message.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { EncryptedMessage } from "./encryptedMessage"; -import { UnencryptedMessage } from "./unencryptedMessage"; - -export type Message = UnencryptedMessage | EncryptedMessage; diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index fc77537579f..e2abaf062ff 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitwarden/desktop", - "version": "2022.10.2", + "version": "2023.1.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@bitwarden/desktop", - "version": "2022.10.2", + "version": "2023.1.2", "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-native": "file:../desktop_native" diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index f7e3af532c3..4b37292ba00 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -2,7 +2,7 @@ "name": "@bitwarden/desktop", "productName": "Bitwarden", "description": "A secure and free password manager for all of your devices.", - "version": "2022.10.2", + "version": "2023.1.2", "author": "Bitwarden Inc. (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/apps/desktop/src/scss/box.scss b/apps/desktop/src/scss/box.scss index 64cbf370abf..e67f4cdf4f7 100644 --- a/apps/desktop/src/scss/box.scss +++ b/apps/desktop/src/scss/box.scss @@ -58,7 +58,7 @@ .icon { display: flex; align-items: flex-end; - margin-left: 5px; + margin-right: 5px; @include themify($themes) { color: themed("headingColor"); @@ -366,6 +366,11 @@ display: flex; margin-left: 5px; + &.action-buttons-fixed { + align-self: start; + margin-top: 2px; + } + .row-btn { @extend .icon-btn; } diff --git a/apps/desktop/src/scss/misc.scss b/apps/desktop/src/scss/misc.scss index 5818b194710..c5aabb5b072 100644 --- a/apps/desktop/src/scss/misc.scss +++ b/apps/desktop/src/scss/misc.scss @@ -215,8 +215,8 @@ p.lead { } } -.generated-wrapper { - word-break: break-all; +.password-wrapper { + overflow-wrap: break-word; white-space: pre-wrap; min-width: 0; } @@ -233,6 +233,30 @@ p.lead { } } +.password-character { + display: inline-flex; + flex-direction: column; + align-items: center; + width: 30px; + height: 36px; + font-weight: 600; + + &:nth-child(odd) { + @include themify($themes) { + background-color: themed("backgroundColorAlt2"); + } + } +} + +.password-count { + white-space: nowrap; + font-size: 8px; + + @include themify($themes) { + color: themed("passwordCountText") !important; + } +} + #duo-frame { background: url("../images/loading.svg") 0 0 no-repeat; height: 330px; @@ -337,31 +361,31 @@ form, margin-left: -18px; } } +} - .help-block { - margin-top: 3px; - display: block; +.help-block { + margin-top: 3px; + display: block; + + @include themify($themes) { + color: themed("mutedColor"); + } + + a { + @extend .btn; + @extend .link; + + padding: 0; + font-size: inherit; + font-weight: bold; @include themify($themes) { color: themed("mutedColor"); } - a { - @extend .btn; - @extend .link; - - padding: 0; - font-size: inherit; - font-weight: bold; - + &:hover { @include themify($themes) { - color: themed("mutedColor"); - } - - &:hover { - @include themify($themes) { - color: darken(themed("mutedColor"), 6%); - } + color: darken(themed("mutedColor"), 6%); } } } @@ -534,3 +558,9 @@ img, app-vault-view .box-footer { user-select: auto; } + +/* tweak for inconsistent line heights in cipher view */ +.box-footer button, +.box-footer a { + line-height: 1; +} diff --git a/apps/desktop/src/scss/variables.scss b/apps/desktop/src/scss/variables.scss index 0f13d18c7a1..67955db6b0b 100644 --- a/apps/desktop/src/scss/variables.scss +++ b/apps/desktop/src/scss/variables.scss @@ -88,6 +88,7 @@ $themes: ( logoSuffix: "dark", passwordNumberColor: #007fde, passwordSpecialColor: #c40800, + passwordCountText: #212529, calloutBorderColor: $border-color-dark, calloutBackgroundColor: $background-color, accountSwitcherBackgroundColor: $background-color, @@ -142,6 +143,7 @@ $themes: ( logoSuffix: "white", passwordNumberColor: #52bdfb, passwordSpecialColor: #ff7c70, + passwordCountText: #ffffff, calloutBorderColor: #2f2f2f, calloutBackgroundColor: #363636, accountSwitcherBackgroundColor: #2f2f2f, @@ -196,6 +198,7 @@ $themes: ( logoSuffix: "white", passwordNumberColor: $nord8, passwordSpecialColor: $nord12, + passwordCountText: $nord5, calloutBorderColor: $nord1, calloutBackgroundColor: $nord2, accountSwitcherBackgroundColor: $nord0, diff --git a/libs/electron/src/services/electronCrypto.service.ts b/apps/desktop/src/services/electron-crypto.service.ts similarity index 100% rename from libs/electron/src/services/electronCrypto.service.ts rename to apps/desktop/src/services/electron-crypto.service.ts diff --git a/libs/electron/spec/services/electronLog.service.spec.ts b/apps/desktop/src/services/electron-log.service.spec.ts similarity index 75% rename from libs/electron/spec/services/electronLog.service.spec.ts rename to apps/desktop/src/services/electron-log.service.spec.ts index 5802478fb70..5b12edc9450 100644 --- a/libs/electron/spec/services/electronLog.service.spec.ts +++ b/apps/desktop/src/services/electron-log.service.spec.ts @@ -1,4 +1,4 @@ -import { ElectronLogService } from "@bitwarden/electron/services/electronLog.service"; +import { ElectronLogService } from "./electron-log.service"; describe("ElectronLogService", () => { it("sets dev based on electron method", () => { diff --git a/libs/electron/src/services/electronLog.service.ts b/apps/desktop/src/services/electron-log.service.ts similarity index 100% rename from libs/electron/src/services/electronLog.service.ts rename to apps/desktop/src/services/electron-log.service.ts diff --git a/libs/electron/src/services/electronMainMessaging.service.ts b/apps/desktop/src/services/electron-main-messaging.service.ts similarity index 97% rename from libs/electron/src/services/electronMainMessaging.service.ts rename to apps/desktop/src/services/electron-main-messaging.service.ts index a025e32e373..b8d754b925b 100644 --- a/libs/electron/src/services/electronMainMessaging.service.ts +++ b/apps/desktop/src/services/electron-main-messaging.service.ts @@ -3,8 +3,8 @@ import { app, dialog, ipcMain, Menu, MenuItem, nativeTheme, session } from "elec import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; import { ThemeType } from "@bitwarden/common/enums/themeType"; +import { WindowMain } from "../main/window.main"; import { RendererMenuItem } from "../utils"; -import { WindowMain } from "../window.main"; export class ElectronMainMessagingService implements MessagingService { constructor(private windowMain: WindowMain, private onMessage: (message: any) => void) { diff --git a/libs/electron/src/services/electronPlatformUtils.service.ts b/apps/desktop/src/services/electron-platform-utils.service.ts similarity index 97% rename from libs/electron/src/services/electronPlatformUtils.service.ts rename to apps/desktop/src/services/electron-platform-utils.service.ts index 1b710b85f2f..8af865d8452 100644 --- a/libs/electron/src/services/electronPlatformUtils.service.ts +++ b/apps/desktop/src/services/electron-platform-utils.service.ts @@ -87,6 +87,10 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService { return ipcRenderer.invoke("appVersion"); } + async getApplicationVersionNumber(): Promise { + return (await this.getApplicationVersion()).split(/[+|-]/)[0].trim(); + } + // Temporarily restricted to only Windows until https://github.com/electron/electron/pull/28349 // has been merged and an updated electron build is available. supportsWebAuthn(win: Window): boolean { diff --git a/libs/electron/src/services/electronRendererMessaging.service.ts b/apps/desktop/src/services/electron-renderer-messaging.service.ts similarity index 100% rename from libs/electron/src/services/electronRendererMessaging.service.ts rename to apps/desktop/src/services/electron-renderer-messaging.service.ts diff --git a/libs/electron/src/services/electronRendererSecureStorage.service.ts b/apps/desktop/src/services/electron-renderer-secure-storage.service.ts similarity index 100% rename from libs/electron/src/services/electronRendererSecureStorage.service.ts rename to apps/desktop/src/services/electron-renderer-secure-storage.service.ts diff --git a/libs/electron/src/services/electronRendererStorage.service.ts b/apps/desktop/src/services/electron-renderer-storage.service.ts similarity index 100% rename from libs/electron/src/services/electronRendererStorage.service.ts rename to apps/desktop/src/services/electron-renderer-storage.service.ts diff --git a/libs/electron/src/services/electronStorage.service.ts b/apps/desktop/src/services/electron-storage.service.ts similarity index 100% rename from libs/electron/src/services/electronStorage.service.ts rename to apps/desktop/src/services/electron-storage.service.ts diff --git a/apps/desktop/src/services/encryptedMessageHandlerService.ts b/apps/desktop/src/services/encrypted-message-handler.service.ts similarity index 84% rename from apps/desktop/src/services/encryptedMessageHandlerService.ts rename to apps/desktop/src/services/encrypted-message-handler.service.ts index f5ac1e5fcef..5f45118768e 100644 --- a/apps/desktop/src/services/encryptedMessageHandlerService.ts +++ b/apps/desktop/src/services/encrypted-message-handler.service.ts @@ -12,18 +12,18 @@ import { CipherView } from "@bitwarden/common/models/view/cipher.view"; import { LoginUriView } from "@bitwarden/common/models/view/login-uri.view"; import { LoginView } from "@bitwarden/common/models/view/login.view"; -import { DecryptedCommandData } from "../models/nativeMessaging/decryptedCommandData"; -import { CredentialCreatePayload } from "../models/nativeMessaging/encryptedMessagePayloads/credentialCreatePayload"; -import { CredentialRetrievePayload } from "../models/nativeMessaging/encryptedMessagePayloads/credentialRetrievePayload"; -import { CredentialUpdatePayload } from "../models/nativeMessaging/encryptedMessagePayloads/credentialUpdatePayload"; -import { PasswordGeneratePayload } from "../models/nativeMessaging/encryptedMessagePayloads/passwordGeneratePayload"; -import { AccountStatusResponse } from "../models/nativeMessaging/encryptedMessageResponses/accountStatusResponse"; -import { CipherResponse } from "../models/nativeMessaging/encryptedMessageResponses/cipherResponse"; -import { EncyptedMessageResponse } from "../models/nativeMessaging/encryptedMessageResponses/encryptedMessageResponse"; -import { FailureStatusResponse } from "../models/nativeMessaging/encryptedMessageResponses/failureStatusResponse"; -import { GenerateResponse } from "../models/nativeMessaging/encryptedMessageResponses/generateResponse"; -import { SuccessStatusResponse } from "../models/nativeMessaging/encryptedMessageResponses/successStatusResponse"; -import { UserStatusErrorResponse } from "../models/nativeMessaging/encryptedMessageResponses/userStatusErrorResponse"; +import { DecryptedCommandData } from "../models/native-messaging/decrypted-command-data"; +import { CredentialCreatePayload } from "../models/native-messaging/encrypted-message-payloads/credential-create-payload"; +import { CredentialRetrievePayload } from "../models/native-messaging/encrypted-message-payloads/credential-retrieve-payload"; +import { CredentialUpdatePayload } from "../models/native-messaging/encrypted-message-payloads/credential-update-payload"; +import { PasswordGeneratePayload } from "../models/native-messaging/encrypted-message-payloads/password-generate-payload"; +import { AccountStatusResponse } from "../models/native-messaging/encrypted-message-responses/account-status-response"; +import { CipherResponse } from "../models/native-messaging/encrypted-message-responses/cipher-response"; +import { EncyptedMessageResponse } from "../models/native-messaging/encrypted-message-responses/encrypted-message-response"; +import { FailureStatusResponse } from "../models/native-messaging/encrypted-message-responses/failure-status-response"; +import { GenerateResponse } from "../models/native-messaging/encrypted-message-responses/generate-response"; +import { SuccessStatusResponse } from "../models/native-messaging/encrypted-message-responses/success-status-response"; +import { UserStatusErrorResponse } from "../models/native-messaging/encrypted-message-responses/user-status-error-response"; import { StateService } from "./state.service"; diff --git a/apps/desktop/src/services/nativeMessageHandler.service.ts b/apps/desktop/src/services/native-message-handler.service.ts similarity index 91% rename from apps/desktop/src/services/nativeMessageHandler.service.ts rename to apps/desktop/src/services/native-message-handler.service.ts index 8a578957bfe..fd00bf33f8e 100644 --- a/apps/desktop/src/services/nativeMessageHandler.service.ts +++ b/apps/desktop/src/services/native-message-handler.service.ts @@ -12,14 +12,14 @@ import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; import { StateService } from "@bitwarden/common/services/state.service"; -import { DecryptedCommandData } from "../models/nativeMessaging/decryptedCommandData"; -import { EncryptedMessage } from "../models/nativeMessaging/encryptedMessage"; -import { EncryptedMessageResponse } from "../models/nativeMessaging/encryptedMessageResponse"; -import { Message } from "../models/nativeMessaging/message"; -import { UnencryptedMessage } from "../models/nativeMessaging/unencryptedMessage"; -import { UnencryptedMessageResponse } from "../models/nativeMessaging/unencryptedMessageResponse"; +import { DecryptedCommandData } from "../models/native-messaging/decrypted-command-data"; +import { EncryptedMessage } from "../models/native-messaging/encrypted-message"; +import { EncryptedMessageResponse } from "../models/native-messaging/encrypted-message-response"; +import { Message } from "../models/native-messaging/message"; +import { UnencryptedMessage } from "../models/native-messaging/unencrypted-message"; +import { UnencryptedMessageResponse } from "../models/native-messaging/unencrypted-message-response"; -import { EncryptedMessageHandlerService } from "./encryptedMessageHandlerService"; +import { EncryptedMessageHandlerService } from "./encrypted-message-handler.service"; const EncryptionAlgorithm = "sha1"; @@ -114,7 +114,7 @@ export class NativeMessageHandlerService { const secret = await this.cryptoFunctionService.randomBytes(64); this.ddgSharedSecret = new SymmetricCryptoKey(secret); - const sharedKeyB64 = new SymmetricCryptoKey(secret).toJSON().keyB64; + const sharedKeyB64 = new SymmetricCryptoKey(secret).keyB64; await this.stateService.setDuckDuckGoSharedKey(sharedKeyB64); diff --git a/apps/desktop/src/services/nativeMessaging.service.ts b/apps/desktop/src/services/native-messaging.service.ts similarity index 95% rename from apps/desktop/src/services/nativeMessaging.service.ts rename to apps/desktop/src/services/native-messaging.service.ts index 83e3705dc7d..0a2b443bfbf 100644 --- a/apps/desktop/src/services/nativeMessaging.service.ts +++ b/apps/desktop/src/services/native-messaging.service.ts @@ -15,11 +15,11 @@ import { Utils } from "@bitwarden/common/misc/utils"; import { EncString } from "@bitwarden/common/models/domain/enc-string"; import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-crypto-key"; -import { LegacyMessage } from "../models/nativeMessaging/legacyMessage"; -import { LegacyMessageWrapper } from "../models/nativeMessaging/legacyMessageWrapper"; -import { Message } from "../models/nativeMessaging/message"; +import { LegacyMessage } from "../models/native-messaging/legacy-message"; +import { LegacyMessageWrapper } from "../models/native-messaging/legacy-message-wrapper"; +import { Message } from "../models/native-messaging/message"; -import { NativeMessageHandlerService } from "./nativeMessageHandler.service"; +import { NativeMessageHandlerService } from "./native-message-handler.service"; const MessageValidTimeout = 10 * 1000; const EncryptionAlgorithm = "sha1"; diff --git a/apps/desktop/src/services/passwordReprompt.service.ts b/apps/desktop/src/services/password-reprompt.service.ts similarity index 100% rename from apps/desktop/src/services/passwordReprompt.service.ts rename to apps/desktop/src/services/password-reprompt.service.ts diff --git a/libs/electron/spec/utils.spec.ts b/apps/desktop/src/utils.spec.ts similarity index 96% rename from libs/electron/spec/utils.spec.ts rename to apps/desktop/src/utils.spec.ts index 96af43eb752..e9fbad3df9e 100644 --- a/libs/electron/spec/utils.spec.ts +++ b/apps/desktop/src/utils.spec.ts @@ -1,4 +1,4 @@ -import { cleanUserAgent } from "@bitwarden/electron/utils"; +import { cleanUserAgent } from "./utils"; const expectedUserAgent = `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${process.versions.chrome} Safari/537.36`; diff --git a/libs/electron/src/utils.ts b/apps/desktop/src/utils.ts similarity index 100% rename from libs/electron/src/utils.ts rename to apps/desktop/src/utils.ts diff --git a/apps/desktop/stores/chocolatey/bitwarden.nuspec b/apps/desktop/stores/chocolatey/bitwarden.nuspec index 779bf16c61e..9e701522a8e 100644 --- a/apps/desktop/stores/chocolatey/bitwarden.nuspec +++ b/apps/desktop/stores/chocolatey/bitwarden.nuspec @@ -10,7 +10,7 @@ Bitwarden Inc. https://bitwarden.com/ https://raw.githubusercontent.com/bitwarden/brand/master/icons/256x256.png - Copyright © 2015-2022 Bitwarden Inc. + Copyright © 2015-2023 Bitwarden Inc. https://github.com/bitwarden/clients/ https://bitwarden.com/help/ https://github.com/bitwarden/clients/issues diff --git a/apps/desktop/tsconfig.json b/apps/desktop/tsconfig.json index f2f28c73065..9ae70b05f9a 100644 --- a/apps/desktop/tsconfig.json +++ b/apps/desktop/tsconfig.json @@ -11,11 +11,11 @@ "baseUrl": ".", "paths": { "@bitwarden/common/*": ["../../libs/common/src/*"], - "@bitwarden/angular/*": ["../../libs/angular/src/*"], - "@bitwarden/electron/*": ["../../libs/electron/src/*"] + "@bitwarden/angular/*": ["../../libs/angular/src/*"] } }, "angularCompilerOptions": { + "strictTemplates": true, "preserveWhitespaces": true }, "include": ["src", "../../libs/common/src/services/**/*.worker.ts"] diff --git a/apps/web/README.md b/apps/web/README.md index 1193b139a4d..f43a9dc1614 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -21,4 +21,4 @@ ## Documentation -Please refer to the [Web vault section](https://contributing.bitwarden.com/clients/web-vault/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. +Please refer to the [Web vault section](https://contributing.bitwarden.com/getting-started/clients/web-vault/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started. diff --git a/apps/web/config/base.json b/apps/web/config/base.json index 8eb8a311335..ed0bc0a850d 100644 --- a/apps/web/config/base.json +++ b/apps/web/config/base.json @@ -11,6 +11,7 @@ "allowedHosts": "auto" }, "flags": { + "secretsManager": false, "showPasswordless": false } } diff --git a/apps/web/config/cloud.json b/apps/web/config/cloud.json index 5bd5e6b0608..ebf13af61e9 100644 --- a/apps/web/config/cloud.json +++ b/apps/web/config/cloud.json @@ -16,7 +16,7 @@ "proxyEvents": "https://events.bitwarden.com" }, "flags": { - "showTrial": true, - "showPasswordless": false + "secretsManager": false, + "showPasswordless": true } } diff --git a/apps/web/config/development.json b/apps/web/config/development.json index f460a1659aa..7aeffe55d0d 100644 --- a/apps/web/config/development.json +++ b/apps/web/config/development.json @@ -10,7 +10,7 @@ "proxyNotifications": "http://localhost:61840" }, "flags": { - "showTrial": true, + "secretsManager": true, "showPasswordless": true } } diff --git a/apps/web/config/ee.json b/apps/web/config/ee.json index 3ba61fda596..cd36ab15c5e 100644 --- a/apps/web/config/ee.json +++ b/apps/web/config/ee.json @@ -6,7 +6,5 @@ "proxyNotifications": "http://localhost:61841", "port": 8081 }, - "flags": { - "showTrial": false - } + "flags": {} } diff --git a/apps/web/config/qa.json b/apps/web/config/qa.json index a0d1b0e88c3..3632c696856 100644 --- a/apps/web/config/qa.json +++ b/apps/web/config/qa.json @@ -10,7 +10,7 @@ "proxyEvents": "https://events.qa.bitwarden.pw" }, "flags": { - "showTrial": true, + "secretsManager": true, "showPasswordless": true } } diff --git a/apps/web/config/selfhosted.json b/apps/web/config/selfhosted.json index b37a9226043..21a9d93a998 100644 --- a/apps/web/config/selfhosted.json +++ b/apps/web/config/selfhosted.json @@ -7,7 +7,7 @@ "port": 8081 }, "flags": { - "showTrial": false, + "secretsManager": false, "showPasswordless": false } } diff --git a/apps/web/package.json b/apps/web/package.json index 7098100f60f..a2764f4b79c 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/web-vault", - "version": "2022.10.2", + "version": "2023.1.1", "scripts": { "build:oss": "webpack", "build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js", diff --git a/apps/web/src/404.html b/apps/web/src/404.html index 6cf5e363d58..5a650b3673a 100644 --- a/apps/web/src/404.html +++ b/apps/web/src/404.html @@ -47,6 +47,6 @@ contact us.

- + diff --git a/apps/web/src/app/accounts/accept-organization.component.html b/apps/web/src/app/accounts/accept-organization.component.html index 25d90d93e0c..3aef47df22b 100644 --- a/apps/web/src/app/accounts/accept-organization.component.html +++ b/apps/web/src/app/accounts/accept-organization.component.html @@ -11,7 +11,7 @@

-
+

{{ "joinOrganization" | i18n }}

diff --git a/apps/web/src/app/accounts/accept-organization.component.ts b/apps/web/src/app/accounts/accept-organization.component.ts index be3826fe18c..4153c9c516a 100644 --- a/apps/web/src/app/accounts/accept-organization.component.ts +++ b/apps/web/src/app/accounts/accept-organization.component.ts @@ -1,10 +1,12 @@ import { Component } from "@angular/core"; import { ActivatedRoute, Params, Router } from "@angular/router"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; +import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; +import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; +import { OrganizationUserAcceptRequest } from "@bitwarden/common/abstractions/organization-user/requests"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/abstractions/policy/policy-api.service.abstraction"; @@ -12,7 +14,6 @@ import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.serv import { StateService } from "@bitwarden/common/abstractions/state.service"; import { Utils } from "@bitwarden/common/misc/utils"; import { Policy } from "@bitwarden/common/models/domain/policy"; -import { OrganizationUserAcceptRequest } from "@bitwarden/common/models/request/organization-user-accept.request"; import { BaseAcceptComponent } from "../common/base.accept.component"; @@ -30,26 +31,37 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent { platformUtilsService: PlatformUtilsService, i18nService: I18nService, route: ActivatedRoute, - private apiService: ApiService, stateService: StateService, private cryptoService: CryptoService, private policyApiService: PolicyApiServiceAbstraction, private policyService: PolicyService, private logService: LogService, - private organizationApiService: OrganizationApiServiceAbstraction + private organizationApiService: OrganizationApiServiceAbstraction, + private organizationUserService: OrganizationUserService, + private messagingService: MessagingService ) { super(router, platformUtilsService, i18nService, route, stateService); } async authedHandler(qParams: Params): Promise { + const needsReAuth = (await this.stateService.getOrganizationInvitation()) != null; + if (!needsReAuth) { + // Accepting an org invite requires authentication from a logged out state + this.messagingService.send("logout", { redirect: false }); + await this.prepareOrganizationInvitation(qParams); + return; + } + + // User has already logged in and passed the Master Password policy check this.actionPromise = this.prepareAcceptRequest(qParams).then(async (request) => { - await this.apiService.postOrganizationUserAccept( + await this.organizationUserService.postOrganizationUserAccept( qParams.organizationId, qParams.organizationUserId, request ); }); + await this.stateService.setOrganizationInvitation(null); await this.actionPromise; this.platformUtilService.showToast( "success", @@ -58,17 +70,11 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent { { timeout: 10000 } ); - await this.stateService.setOrganizationInvitation(null); this.router.navigate(["/vault"]); } async unauthedHandler(qParams: Params): Promise { - this.orgName = qParams.organizationName; - if (this.orgName != null) { - // Fix URL encoding of space issue with Angular - this.orgName = this.orgName.replace(/\+/g, " "); - } - await this.stateService.setOrganizationInvitation(qParams); + await this.prepareOrganizationInvitation(qParams); } private async prepareAcceptRequest(qParams: Params): Promise { @@ -119,4 +125,13 @@ export class AcceptOrganizationComponent extends BaseAcceptComponent { return false; } + + private async prepareOrganizationInvitation(qParams: Params): Promise { + this.orgName = qParams.organizationName; + if (this.orgName != null) { + // Fix URL encoding of space issue with Angular + this.orgName = this.orgName.replace(/\+/g, " "); + } + await this.stateService.setOrganizationInvitation(qParams); + } } diff --git a/apps/web/src/app/accounts/login/login-with-device.component.ts b/apps/web/src/app/accounts/login/login-with-device.component.ts index fabc2552208..82852000832 100644 --- a/apps/web/src/app/accounts/login/login-with-device.component.ts +++ b/apps/web/src/app/accounts/login/login-with-device.component.ts @@ -142,7 +142,7 @@ export class LoginWithDeviceComponent this.router.navigate([this.forcePasswordResetRoute]); } } else { - await this.setRememberEmailValues(); + await this.loginService.saveEmailSettings(); if (this.onSuccessfulLogin != null) { this.onSuccessfulLogin(); } @@ -202,12 +202,4 @@ export class LoginWithDeviceComponent localHashedPassword ); } - - private async setRememberEmailValues() { - const rememberEmail = this.loginService.getRememberEmail(); - const rememberedEmail = this.loginService.getEmail(); - await this.stateService.setRememberEmail(rememberEmail); - await this.stateService.setRememberedEmail(rememberEmail ? rememberedEmail : null); - this.loginService.clearValues(); - } } diff --git a/apps/web/src/app/accounts/login/login.component.html b/apps/web/src/app/accounts/login/login.component.html index 3b7724559fe..00f54324590 100644 --- a/apps/web/src/app/accounts/login/login.component.html +++ b/apps/web/src/app/accounts/login/login.component.html @@ -32,18 +32,10 @@
-
- -
- - {{ "rememberEmail" | i18n }} - + + + {{ "rememberEmail" | i18n }} +
@@ -63,7 +55,8 @@

{{ "newAroundHere" | i18n }} - {{ "createAccount" | i18n }} + + {{ "createAccount" | i18n }}

@@ -77,18 +70,12 @@ {{ "masterPass" | i18n }} - + {{ "getMasterPasswordHint" | i18n @@ -120,7 +107,7 @@ @@ -112,23 +112,31 @@
-
+
+
- - + {{ "logIn" | i18n }} - diff --git a/apps/web/src/app/accounts/register-form/register-form.component.ts b/apps/web/src/app/accounts/register-form/register-form.component.ts index 6cd62ea13e4..242ce39baf3 100644 --- a/apps/web/src/app/accounts/register-form/register-form.component.ts +++ b/apps/web/src/app/accounts/register-form/register-form.component.ts @@ -4,6 +4,7 @@ import { Router } from "@angular/router"; import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { AuthService } from "@bitwarden/common/abstractions/auth.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; @@ -41,7 +42,8 @@ export class RegisterFormComponent extends BaseRegisterComponent { passwordGenerationService: PasswordGenerationService, private policyService: PolicyService, environmentService: EnvironmentService, - logService: LogService + logService: LogService, + auditService: AuditService ) { super( formValidationErrorService, @@ -55,7 +57,8 @@ export class RegisterFormComponent extends BaseRegisterComponent { platformUtilsService, passwordGenerationService, environmentService, - logService + logService, + auditService ); } diff --git a/apps/web/src/app/accounts/register.component.html b/apps/web/src/app/accounts/register.component.html deleted file mode 100644 index ed492a6bb89..00000000000 --- a/apps/web/src/app/accounts/register.component.html +++ /dev/null @@ -1,210 +0,0 @@ -
- -
-
-
-
- -
-
-
-
- -
-
-
-
- -
-

The Bitwarden Password Manager

-

- Trusted by millions of individuals, teams, and organizations worldwide for secure - password storage and sharing. -

-

Store logins, secure notes, and more

-

Collaborate and share securely

-

Access anywhere on any device

-

Create your account to get started

-
- - -
-

- Start Your Teams
Enterprise Free Trial Now -

-

- Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure - password storage and sharing. -

-

Collaborate and share securely

-

Deploy and manage quickly and easily

-

Access anywhere on any device

-

Create your account to get started

-
- - -
-

- Start Your Teams
Enterprise Free Trial Now -

-

- Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure - password storage and sharing. -

-

Collaborate and share securely

-

Deploy and manage quickly and easily

-

Access anywhere on any device

-

Create your account to get started

-
- - -
-

Start Your Premium Account Now

-

- Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure - password storage and sharing. -

-

Store logins, secure notes, and more

-

Secure your account with advanced two-step login

-

Access anywhere on any device

-

Create your account to get started

-
-
-
-
-
-
-

{{ "createAccount" | i18n }}

-
-
- - {{ "createOrganizationCreatePersonalAccount" | i18n }} - - -
-
-
-
-
-
-
-
-
-
-
- - cnet logo - -
-
- "No more excuses; start using Bitwarden today. The identity you save could be your - own. The money definitely will be." -
-
-
- -
-
-
- - Forbes Logo - -
-
- “Bitwarden boasts the backing of some of the world's best security experts and an - attractive, easy-to-use interface” -
-
-
-
-
- US News 360 Reviews Best Password Manager -
-
- US News 360 Reviews Best Password Manager -
-
-
-
diff --git a/apps/web/src/app/accounts/register.component.ts b/apps/web/src/app/accounts/register.component.ts deleted file mode 100644 index c7c3b7dfc58..00000000000 --- a/apps/web/src/app/accounts/register.component.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { Component, OnDestroy, OnInit } from "@angular/core"; -import { UntypedFormBuilder } from "@angular/forms"; -import { ActivatedRoute, Router } from "@angular/router"; -import { Subject, takeUntil } from "rxjs"; -import { first } from "rxjs/operators"; - -import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { AuthService } from "@bitwarden/common/abstractions/auth.service"; -import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; -import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service"; -import { FormValidationErrorsService } from "@bitwarden/common/abstractions/formValidationErrors.service"; -import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; -import { LogService } from "@bitwarden/common/abstractions/log.service"; -import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; -import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; -import { PolicyApiServiceAbstraction } from "@bitwarden/common/abstractions/policy/policy-api.service.abstraction"; -import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction"; -import { StateService } from "@bitwarden/common/abstractions/state.service"; -import { PolicyData } from "@bitwarden/common/models/data/policy.data"; -import { MasterPasswordPolicyOptions } from "@bitwarden/common/models/domain/master-password-policy-options"; -import { Policy } from "@bitwarden/common/models/domain/policy"; -import { ReferenceEventRequest } from "@bitwarden/common/models/request/reference-event.request"; - -import { RouterService } from "../core"; - -@Component({ - selector: "app-register", - templateUrl: "register.component.html", -}) -export class RegisterComponent extends BaseRegisterComponent implements OnInit, OnDestroy { - email = ""; - showCreateOrgMessage = false; - layout = ""; - enforcedPolicyOptions: MasterPasswordPolicyOptions; - - private policies: Policy[]; - private destroy$ = new Subject(); - - constructor( - formValidationErrorService: FormValidationErrorsService, - formBuilder: UntypedFormBuilder, - authService: AuthService, - router: Router, - i18nService: I18nService, - cryptoService: CryptoService, - apiService: ApiService, - private route: ActivatedRoute, - stateService: StateService, - platformUtilsService: PlatformUtilsService, - passwordGenerationService: PasswordGenerationService, - private policyApiService: PolicyApiServiceAbstraction, - private policyService: PolicyService, - environmentService: EnvironmentService, - logService: LogService, - private routerService: RouterService - ) { - super( - formValidationErrorService, - formBuilder, - authService, - router, - i18nService, - cryptoService, - apiService, - stateService, - platformUtilsService, - passwordGenerationService, - environmentService, - logService - ); - } - - async ngOnInit() { - // eslint-disable-next-line rxjs-angular/prefer-takeuntil - this.route.queryParams.pipe(first()).subscribe((qParams) => { - this.referenceData = new ReferenceEventRequest(); - if (qParams.email != null && qParams.email.indexOf("@") > -1) { - this.email = qParams.email; - } - if (qParams.premium != null) { - this.routerService.setPreviousUrl("/settings/premium"); - } else if (qParams.org != null) { - this.showCreateOrgMessage = true; - this.referenceData.flow = qParams.org; - const route = this.router.createUrlTree(["create-organization"], { - queryParams: { plan: qParams.org }, - }); - this.routerService.setPreviousUrl(route.toString()); - } - if (qParams.layout != null) { - this.layout = this.referenceData.layout = qParams.layout; - } - if (qParams.reference != null) { - this.referenceData.id = qParams.reference; - } else { - this.referenceData.id = ("; " + document.cookie) - .split("; reference=") - .pop() - .split(";") - .shift(); - } - // Are they coming from an email for sponsoring a families organization - if (qParams.sponsorshipToken != null) { - // After logging in redirect them to setup the families sponsorship - const route = this.router.createUrlTree(["setup/families-for-enterprise"], { - queryParams: { plan: qParams.sponsorshipToken }, - }); - this.routerService.setPreviousUrl(route.toString()); - } - if (this.referenceData.id === "") { - this.referenceData.id = null; - } - }); - const invite = await this.stateService.getOrganizationInvitation(); - if (invite != null) { - try { - const policies = await this.policyApiService.getPoliciesByToken( - invite.organizationId, - invite.token, - invite.email, - invite.organizationUserId - ); - if (policies.data != null) { - const policiesData = policies.data.map((p) => new PolicyData(p)); - this.policies = policiesData.map((p) => new Policy(p)); - } - } catch (e) { - this.logService.error(e); - } - } - - if (this.policies != null) { - this.policyService - .masterPasswordPolicyOptions$(this.policies) - .pipe(takeUntil(this.destroy$)) - .subscribe((enforcedPasswordPolicyOptions) => { - this.enforcedPolicyOptions = enforcedPasswordPolicyOptions; - }); - } - - await super.ngOnInit(); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } -} diff --git a/apps/web/src/app/accounts/set-password.component.ts b/apps/web/src/app/accounts/set-password.component.ts index 7e337314121..2e6deb2f5fb 100644 --- a/apps/web/src/app/accounts/set-password.component.ts +++ b/apps/web/src/app/accounts/set-password.component.ts @@ -6,6 +6,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; +import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; @@ -32,7 +33,8 @@ export class SetPasswordComponent extends BaseSetPasswordComponent { syncService: SyncService, route: ActivatedRoute, stateService: StateService, - organizationApiService: OrganizationApiServiceAbstraction + organizationApiService: OrganizationApiServiceAbstraction, + organizationUserService: OrganizationUserService ) { super( i18nService, @@ -47,7 +49,8 @@ export class SetPasswordComponent extends BaseSetPasswordComponent { syncService, route, stateService, - organizationApiService + organizationApiService, + organizationUserService ); } } diff --git a/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.html b/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.html index 2b7f07254b6..924f122495b 100644 --- a/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.html +++ b/apps/web/src/app/accounts/trial-initiation/trial-initiation.component.html @@ -23,26 +23,39 @@
- - - - - - - + + + + + + + - - - + + +
-
+
+
+ +
+
+

@@ -62,7 +75,7 @@

diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 9bd0fda6fcb..12f499a01cc 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -12,7 +12,7 @@ import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.s import { CipherService } from "@bitwarden/common/abstractions/cipher.service"; import { CollectionService } from "@bitwarden/common/abstractions/collection.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; -import { EventService } from "@bitwarden/common/abstractions/event.service"; +import { EventUploadService } from "@bitwarden/common/abstractions/event/event-upload.service"; import { InternalFolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service"; @@ -74,7 +74,7 @@ export class AppComponent implements OnDestroy, OnInit { private notificationsService: NotificationsService, private routerService: RouterService, private stateService: StateService, - private eventService: EventService, + private eventUploadService: EventUploadService, private policyService: InternalPolicyService, protected policyListService: PolicyListService, private keyConnectorService: KeyConnectorService @@ -112,7 +112,7 @@ export class AppComponent implements OnDestroy, OnInit { this.router.navigate(["/"]); break; case "logout": - this.logOut(!!message.expired); + this.logOut(!!message.expired, message.redirect); break; case "lockVault": await this.vaultTimeoutService.lock(); @@ -220,11 +220,10 @@ export class AppComponent implements OnDestroy, OnInit { this.destroy$.complete(); } - private async logOut(expired: boolean) { - await this.eventService.uploadEvents(); + private async logOut(expired: boolean, redirect = true) { + await this.eventUploadService.uploadEvents(); const userId = await this.stateService.getUserId(); await Promise.all([ - this.eventService.clearEvents(), this.syncService.setLastSync(new Date(0)), this.cryptoService.clearKeys(), this.settingsService.clear(userId), @@ -248,7 +247,9 @@ export class AppComponent implements OnDestroy, OnInit { await this.stateService.clean({ userId: userId }); Swal.close(); - this.router.navigate(["/"]); + if (redirect) { + this.router.navigate(["/"]); + } }); } diff --git a/apps/web/src/app/app.module.ts b/apps/web/src/app/app.module.ts index 0f29ea14fa7..5d1afd21224 100644 --- a/apps/web/src/app/app.module.ts +++ b/apps/web/src/app/app.module.ts @@ -6,7 +6,7 @@ import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { InfiniteScrollModule } from "ngx-infinite-scroll"; import { AppComponent } from "./app.component"; -import { CoreModule } from "./core/core.module"; +import { CoreModule } from "./core"; import { OssRoutingModule } from "./oss-routing.module"; import { OssModule } from "./oss.module"; import { WildcardRoutingModule } from "./wildcard-routing.module"; diff --git a/apps/web/src/app/common/base.accept.component.ts b/apps/web/src/app/common/base.accept.component.ts index dcca9e061c1..e09e62a2711 100644 --- a/apps/web/src/app/common/base.accept.component.ts +++ b/apps/web/src/app/common/base.accept.component.ts @@ -35,6 +35,7 @@ export abstract class BaseAcceptComponent implements OnInit { let errorMessage: string = null; if (!error) { this.authed = await this.stateService.getIsAuthenticated(); + this.email = qParams.email; if (this.authed) { try { @@ -44,7 +45,6 @@ export abstract class BaseAcceptComponent implements OnInit { errorMessage = e.message; } } else { - this.email = qParams.email; await this.unauthedHandler(qParams); } } diff --git a/apps/web/src/app/common/base.people.component.ts b/apps/web/src/app/common/base.people.component.ts index c3ee6bb209d..7464a41877f 100644 --- a/apps/web/src/app/common/base.people.component.ts +++ b/apps/web/src/app/common/base.people.component.ts @@ -17,9 +17,9 @@ import { ProviderUserStatusType } from "@bitwarden/common/enums/providerUserStat import { ProviderUserType } from "@bitwarden/common/enums/providerUserType"; import { Utils } from "@bitwarden/common/misc/utils"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; -import { OrganizationUserUserDetailsResponse } from "@bitwarden/common/models/response/organization-user.response"; import { ProviderUserUserDetailsResponse } from "@bitwarden/common/models/response/provider/provider-user.response"; +import { OrganizationUserView } from "../organizations/core/views/organization-user.view"; import { UserConfirmComponent } from "../organizations/manage/user-confirm.component"; type StatusType = OrganizationUserStatusType | ProviderUserStatusType; @@ -28,7 +28,7 @@ const MaxCheckedCount = 500; @Directive() export abstract class BasePeopleComponent< - UserType extends ProviderUserUserDetailsResponse | OrganizationUserUserDetailsResponse + UserType extends ProviderUserUserDetailsResponse | OrganizationUserView > { @ViewChild("confirmTemplate", { read: ViewContainerRef, static: true }) confirmModalRef: ViewContainerRef; @@ -110,7 +110,7 @@ export abstract class BasePeopleComponent< ) {} abstract edit(user: UserType): void; - abstract getUsers(): Promise>; + abstract getUsers(): Promise | UserType[]>; abstract deleteUser(id: string): Promise; abstract revokeUser(id: string): Promise; abstract restoreUser(id: string): Promise; @@ -125,9 +125,14 @@ export abstract class BasePeopleComponent< this.statusMap.set(status, []); } - this.allUsers = response.data != null && response.data.length > 0 ? response.data : []; + if (response instanceof ListResponse) { + this.allUsers = response.data != null && response.data.length > 0 ? response.data : []; + } else if (Array.isArray(response)) { + this.allUsers = response; + } + this.allUsers.sort( - Utils.getSortFunction( + Utils.getSortFunction( this.i18nService, "email" ) @@ -176,7 +181,7 @@ export abstract class BasePeopleComponent< this.didScroll = this.pagedUsers.length > this.pageSize; } - checkUser(user: OrganizationUserUserDetailsResponse, select?: boolean) { + checkUser(user: UserType, select?: boolean) { (user as any).checked = select == null ? !(user as any).checked : select; } @@ -401,6 +406,12 @@ export abstract class BasePeopleComponent< this.users.splice(index, 1); this.resetPaging(); } + + index = this.allUsers.indexOf(user); + if (index > -1) { + this.allUsers.splice(index, 1); + } + if (this.statusMap.has(user.status)) { index = this.statusMap.get(user.status).indexOf(user); if (index > -1) { diff --git a/apps/web/src/app/components/dynamic-avatar.component.ts b/apps/web/src/app/components/dynamic-avatar.component.ts new file mode 100644 index 00000000000..ccc1c57cf48 --- /dev/null +++ b/apps/web/src/app/components/dynamic-avatar.component.ts @@ -0,0 +1,41 @@ +import { Component, Input, OnDestroy } from "@angular/core"; +import { Observable, Subject } from "rxjs"; + +import { AvatarUpdateService } from "@bitwarden/common/abstractions/account/avatar-update.service"; +type SizeTypes = "xlarge" | "large" | "default" | "small" | "xsmall"; +@Component({ + selector: "dynamic-avatar", + template: ` + + + `, +}) +export class DynamicAvatarComponent implements OnDestroy { + @Input() border = false; + @Input() id: string; + @Input() text: string; + @Input() title: string; + @Input() size: SizeTypes = "default"; + color$: Observable; + private destroy$ = new Subject(); + + constructor(private accountUpdateService: AvatarUpdateService) { + if (this.text) { + this.text = this.text.toUpperCase(); + } + this.color$ = this.accountUpdateService.avatarUpdate$; + } + + async ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } +} diff --git a/apps/web/src/app/components/nested-checkbox.component.html b/apps/web/src/app/components/nested-checkbox.component.html deleted file mode 100644 index 9f585e66427..00000000000 --- a/apps/web/src/app/components/nested-checkbox.component.html +++ /dev/null @@ -1,30 +0,0 @@ -
-
- - -
-
-
- - -
-
-
diff --git a/apps/web/src/app/components/nested-checkbox.component.ts b/apps/web/src/app/components/nested-checkbox.component.ts deleted file mode 100644 index eebd01e25d1..00000000000 --- a/apps/web/src/app/components/nested-checkbox.component.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Component, EventEmitter, Input, Output } from "@angular/core"; - -import { Utils } from "@bitwarden/common/misc/utils"; - -@Component({ - selector: "app-nested-checkbox", - templateUrl: "nested-checkbox.component.html", -}) -export class NestedCheckboxComponent { - @Input() parentId: string; - @Input() checkboxes: { id: string; get: () => boolean; set: (v: boolean) => void }[]; - @Output() onSavedUser = new EventEmitter(); - @Output() onDeletedUser = new EventEmitter(); - - get parentIndeterminate() { - return !this.parentChecked && this.checkboxes.some((c) => c.get()); - } - - get parentChecked() { - return this.checkboxes.every((c) => c.get()); - } - - set parentChecked(value: boolean) { - this.checkboxes.forEach((c) => { - c.set(value); - }); - } - - pascalize(s: string) { - return Utils.camelToPascalCase(s); - } -} diff --git a/apps/web/src/app/components/organization-switcher.component.html b/apps/web/src/app/components/organization-switcher.component.html index ca3995bf463..235dcf713b6 100644 --- a/apps/web/src/app/components/organization-switcher.component.html +++ b/apps/web/src/app/components/organization-switcher.component.html @@ -6,7 +6,7 @@ [appA11yTitle]="'organizationPicker' | i18n" [bitMenuTriggerFor]="orgPickerMenu" > - +
{{ activeOrganization.name }} diff --git a/apps/web/src/app/components/organization-switcher.component.ts b/apps/web/src/app/components/organization-switcher.component.ts index 70ea6385fe2..a7aafa15da7 100644 --- a/apps/web/src/app/components/organization-switcher.component.ts +++ b/apps/web/src/app/components/organization-switcher.component.ts @@ -4,6 +4,7 @@ import { map, Observable } from "rxjs"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { canAccessAdmin, + isNotProviderUser, OrganizationService, } from "@bitwarden/common/abstractions/organization/organization.service.abstraction"; import { Utils } from "@bitwarden/common/misc/utils"; @@ -23,6 +24,7 @@ export class OrganizationSwitcherComponent implements OnInit { async ngOnInit() { this.organizations$ = this.organizationService.organizations$.pipe( + map((orgs) => orgs.filter(isNotProviderUser)), canAccessAdmin(this.i18nService), map((orgs) => orgs.sort(Utils.getSortFunction(this.i18nService, "name"))) ); diff --git a/apps/web/src/app/components/selectable-avatar.component.ts b/apps/web/src/app/components/selectable-avatar.component.ts new file mode 100644 index 00000000000..deb573c5db0 --- /dev/null +++ b/apps/web/src/app/components/selectable-avatar.component.ts @@ -0,0 +1,54 @@ +import { Component, EventEmitter, Input, Output } from "@angular/core"; + +@Component({ + selector: "selectable-avatar", + template: ` + + + `, +}) +export class SelectableAvatarComponent { + @Input() id: string; + @Input() text: string; + @Input() title: string; + @Input() color: string; + @Input() border = false; + @Input() selected = false; + @Output() select = new EventEmitter(); + + onFire() { + this.select.emit(this.color); + } + + get classList() { + return ["tw-rounded-full tw-inline-block"] + .concat(["tw-cursor-pointer", "tw-outline", "tw-outline-solid", "tw-outline-offset-1"]) + .concat( + this.selected + ? ["tw-outline-[3px]", "tw-outline-primary-500"] + : [ + "tw-outline-0", + "hover:tw-outline-1", + "hover:tw-outline-primary-300", + "focus:tw-outline-2", + "focus:tw-outline-primary-500", + ] + ); + } +} diff --git a/libs/angular/src/components/user-verification.component.html b/apps/web/src/app/components/user-verification.component.html similarity index 100% rename from libs/angular/src/components/user-verification.component.html rename to apps/web/src/app/components/user-verification.component.html diff --git a/apps/web/src/app/components/user-verification.component.ts b/apps/web/src/app/components/user-verification.component.ts new file mode 100644 index 00000000000..2eb89c28333 --- /dev/null +++ b/apps/web/src/app/components/user-verification.component.ts @@ -0,0 +1,23 @@ +import { animate, style, transition, trigger } from "@angular/animations"; +import { Component } from "@angular/core"; +import { NG_VALUE_ACCESSOR } from "@angular/forms"; + +import { UserVerificationComponent as BaseComponent } from "@bitwarden/angular/components/user-verification.component"; + +@Component({ + selector: "app-user-verification", + templateUrl: "user-verification.component.html", + providers: [ + { + provide: NG_VALUE_ACCESSOR, + multi: true, + useExisting: UserVerificationComponent, + }, + ], + animations: [ + trigger("sent", [ + transition(":enter", [style({ opacity: 0 }), animate("100ms", style({ opacity: 1 }))]), + ]), + ], +}) +export class UserVerificationComponent extends BaseComponent {} diff --git a/apps/web/src/app/core/core.module.ts b/apps/web/src/app/core/core.module.ts index ac349561c8f..8a37e5f44ac 100644 --- a/apps/web/src/app/core/core.module.ts +++ b/apps/web/src/app/core/core.module.ts @@ -103,6 +103,7 @@ import { WebPlatformUtilsService } from "./web-platform-utils.service"; { provide: LoginServiceAbstraction, useClass: LoginService, + deps: [StateService], }, ], }) diff --git a/apps/web/src/app/core/index.ts b/apps/web/src/app/core/index.ts index f9a92f361cc..80c1a44d50f 100644 --- a/apps/web/src/app/core/index.ts +++ b/apps/web/src/app/core/index.ts @@ -1,7 +1,4 @@ -// Do not export this here or it will import MultithreadEncryptService (via JslibServicesModule) into test code. -// MultithreadEncryptService contains ES2020 features (import.meta) which are not supported in Node and Jest. -// Revisit this when Node & Jest get stable support for ESM. -// export * from "./core.module"; +export * from "./core.module"; export * from "./event.service"; export * from "./policy-list.service"; export * from "./router.service"; diff --git a/apps/web/src/app/core/init.service.ts b/apps/web/src/app/core/init.service.ts index 75089529f22..8baa7c0ddc6 100644 --- a/apps/web/src/app/core/init.service.ts +++ b/apps/web/src/app/core/init.service.ts @@ -8,14 +8,14 @@ import { EnvironmentService as EnvironmentServiceAbstraction, Urls, } from "@bitwarden/common/abstractions/environment.service"; -import { EventService as EventLoggingServiceAbstraction } from "@bitwarden/common/abstractions/event.service"; +import { EventUploadService as EventUploadServiceAbstraction } from "@bitwarden/common/abstractions/event/event-upload.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service"; import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/abstractions/state.service"; import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/abstractions/twoFactor.service"; import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout/vaultTimeout.service"; import { ContainerService } from "@bitwarden/common/services/container.service"; -import { EventService as EventLoggingService } from "@bitwarden/common/services/event.service"; +import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service"; import { VaultTimeoutService as VaultTimeoutService } from "@bitwarden/common/services/vaultTimeout/vaultTimeout.service"; import { I18nService } from "./i18n.service"; @@ -28,7 +28,7 @@ export class InitService { private notificationsService: NotificationsServiceAbstraction, private vaultTimeoutService: VaultTimeoutServiceAbstraction, private i18nService: I18nServiceAbstraction, - private eventLoggingService: EventLoggingServiceAbstraction, + private eventUploadService: EventUploadServiceAbstraction, private twoFactorService: TwoFactorServiceAbstraction, private stateService: StateServiceAbstraction, private cryptoService: CryptoServiceAbstraction, @@ -48,7 +48,7 @@ export class InitService { (this.vaultTimeoutService as VaultTimeoutService).init(true); const locale = await this.stateService.getLocale(); await (this.i18nService as I18nService).init(locale); - (this.eventLoggingService as EventLoggingService).init(true); + (this.eventUploadService as EventUploadService).init(true); this.twoFactorService.init(); const htmlEl = this.win.document.documentElement; htmlEl.classList.add("locale_" + this.i18nService.translationLocale); diff --git a/apps/web/src/app/core/state/state.service.ts b/apps/web/src/app/core/state/state.service.ts index 7c1eefe2701..0b7a56790f4 100644 --- a/apps/web/src/app/core/state/state.service.ts +++ b/apps/web/src/app/core/state/state.service.ts @@ -8,7 +8,10 @@ import { } from "@bitwarden/angular/services/injection-tokens"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { StateMigrationService } from "@bitwarden/common/abstractions/stateMigration.service"; -import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service"; +import { + AbstractMemoryStorageService, + AbstractStorageService, +} from "@bitwarden/common/abstractions/storage.service"; import { StateFactory } from "@bitwarden/common/factories/stateFactory"; import { CipherData } from "@bitwarden/common/models/data/cipher.data"; import { CollectionData } from "@bitwarden/common/models/data/collection.data"; @@ -25,7 +28,7 @@ export class StateService extends BaseStateService { constructor( storageService: AbstractStorageService, @Inject(SECURE_STORAGE) secureStorageService: AbstractStorageService, - @Inject(MEMORY_STORAGE) memoryStorageService: AbstractStorageService, + @Inject(MEMORY_STORAGE) memoryStorageService: AbstractMemoryStorageService, logService: LogService, stateMigrationService: StateMigrationService, @Inject(STATE_FACTORY) stateFactory: StateFactory, @@ -48,23 +51,6 @@ export class StateService extends BaseStateService { await super.addAccount(account); } - async getRememberEmail(options?: StorageOptions) { - return ( - await this.getGlobals(this.reconcileOptions(options, await this.defaultOnDiskLocalOptions())) - )?.rememberEmail; - } - - async setRememberEmail(value: boolean, options?: StorageOptions): Promise { - const globals = await this.getGlobals( - this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) - ); - globals.rememberEmail = value; - await this.saveGlobals( - globals, - this.reconcileOptions(options, await this.defaultOnDiskLocalOptions()) - ); - } - async getEncryptedCiphers(options?: StorageOptions): Promise<{ [id: string]: CipherData }> { options = this.reconcileOptions(options, await this.defaultInMemoryOptions()); return await super.getEncryptedCiphers(options); diff --git a/apps/web/src/app/core/web-platform-utils.service.spec.ts b/apps/web/src/app/core/web-platform-utils.service.spec.ts new file mode 100644 index 00000000000..5b0271b8227 --- /dev/null +++ b/apps/web/src/app/core/web-platform-utils.service.spec.ts @@ -0,0 +1,115 @@ +import { WebPlatformUtilsService } from "./web-platform-utils.service"; + +describe("Web Platform Utils Service", () => { + let webPlatformUtilsService: WebPlatformUtilsService; + + beforeEach(() => { + webPlatformUtilsService = new WebPlatformUtilsService(null, null, null); + }); + + afterEach(() => { + delete process.env.APPLICATION_VERSION; + }); + + describe("getApplicationVersion", () => { + test("null", async () => { + delete process.env.APPLICATION_VERSION; + + const result = await webPlatformUtilsService.getApplicationVersion(); + expect(result).toBe("-"); + }); + + test("", async () => { + process.env.APPLICATION_VERSION = ""; + + const result = await webPlatformUtilsService.getApplicationVersion(); + expect(result).toBe("-"); + }); + + test("{version number}", async () => { + process.env.APPLICATION_VERSION = "2022.10.2"; + + const result = await webPlatformUtilsService.getApplicationVersion(); + expect(result).toBe("2022.10.2"); + }); + + test("{version number} - {git hash}", async () => { + process.env.APPLICATION_VERSION = "2022.10.2 - 5f8c1c1"; + + const result = await webPlatformUtilsService.getApplicationVersion(); + expect(result).toBe("2022.10.2 - 5f8c1c1"); + }); + + test("{version number}-{git hash}", async () => { + process.env.APPLICATION_VERSION = "2022.10.2-5f8c1c1"; + + const result = await webPlatformUtilsService.getApplicationVersion(); + expect(result).toBe("2022.10.2-5f8c1c1"); + }); + + test("{version number} + {git hash}", async () => { + process.env.APPLICATION_VERSION = "2022.10.2 + 5f8c1c1"; + + const result = await webPlatformUtilsService.getApplicationVersion(); + expect(result).toBe("2022.10.2 + 5f8c1c1"); + }); + + test("{version number}+{git hash}", async () => { + process.env.APPLICATION_VERSION = "2022.10.2+5f8c1c1"; + + const result = await webPlatformUtilsService.getApplicationVersion(); + expect(result).toBe("2022.10.2+5f8c1c1"); + }); + }); + + describe("getApplicationVersionNumber", () => { + test("null", async () => { + delete process.env.APPLICATION_VERSION; + + const result = await webPlatformUtilsService.getApplicationVersionNumber(); + expect(result).toBe(""); + }); + + test("", async () => { + process.env.APPLICATION_VERSION = ""; + + const result = await webPlatformUtilsService.getApplicationVersionNumber(); + expect(result).toBe(""); + }); + + test("{version number}", async () => { + process.env.APPLICATION_VERSION = "2022.10.2"; + + const result = await webPlatformUtilsService.getApplicationVersionNumber(); + expect(result).toBe("2022.10.2"); + }); + + test("{version number} - {git hash}", async () => { + process.env.APPLICATION_VERSION = "2022.10.2 - 5f8c1c1"; + + const result = await webPlatformUtilsService.getApplicationVersionNumber(); + expect(result).toBe("2022.10.2"); + }); + + test("{version number}-{git hash}", async () => { + process.env.APPLICATION_VERSION = "2022.10.2-5f8c1c1"; + + const result = await webPlatformUtilsService.getApplicationVersionNumber(); + expect(result).toBe("2022.10.2"); + }); + + test("{version number} + {git hash}", async () => { + process.env.APPLICATION_VERSION = "2022.10.2 + 5f8c1c1"; + + const result = await webPlatformUtilsService.getApplicationVersionNumber(); + expect(result).toBe("2022.10.2"); + }); + + test("{version number}+{git hash}", async () => { + process.env.APPLICATION_VERSION = "2022.10.2+5f8c1c1"; + + const result = await webPlatformUtilsService.getApplicationVersionNumber(); + expect(result).toBe("2022.10.2"); + }); + }); +}); diff --git a/apps/web/src/app/core/web-platform-utils.service.ts b/apps/web/src/app/core/web-platform-utils.service.ts index 115d53401a9..2d682a35aaa 100644 --- a/apps/web/src/app/core/web-platform-utils.service.ts +++ b/apps/web/src/app/core/web-platform-utils.service.ts @@ -108,6 +108,10 @@ export class WebPlatformUtilsService implements PlatformUtilsService { return Promise.resolve(process.env.APPLICATION_VERSION || "-"); } + async getApplicationVersionNumber(): Promise { + return (await this.getApplicationVersion()).split(RegExp("[+|-]"))[0].trim(); + } + supportsWebAuthn(win: Window): boolean { return typeof PublicKeyCredential !== "undefined"; } @@ -136,7 +140,8 @@ export class WebPlatformUtilsService implements PlatformUtilsService { confirmText?: string, cancelText?: string, type?: string, - bodyIsHtml = false + bodyIsHtml = false, + target?: string ) { let iconClasses: string = null; if (type != null) { @@ -178,6 +183,7 @@ export class WebPlatformUtilsService implements PlatformUtilsService { cancelButtonText: cancelText, showConfirmButton: true, confirmButtonText: confirmText == null ? this.i18nService.t("ok") : confirmText, + target: target != null ? target : "body", }); if (bootstrapModal != null) { diff --git a/apps/web/src/app/layouts/navbar.component.html b/apps/web/src/app/layouts/navbar.component.html index b7e5a69fb6b..e1513f050c6 100644 --- a/apps/web/src/app/layouts/navbar.component.html +++ b/apps/web/src/app/layouts/navbar.component.html @@ -38,13 +38,14 @@
+
+
diff --git a/apps/web/src/app/settings/change-avatar.component.ts b/apps/web/src/app/settings/change-avatar.component.ts new file mode 100644 index 00000000000..ba6a234cbfa --- /dev/null +++ b/apps/web/src/app/settings/change-avatar.component.ts @@ -0,0 +1,138 @@ +import { + Component, + ElementRef, + EventEmitter, + Input, + OnDestroy, + OnInit, + Output, + ViewChild, + ViewEncapsulation, +} from "@angular/core"; +import { BehaviorSubject, debounceTime, Subject, takeUntil } from "rxjs"; + +import { AvatarUpdateService } from "@bitwarden/common/abstractions/account/avatar-update.service"; +import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; +import { Utils } from "@bitwarden/common/misc/utils"; +import { ProfileResponse } from "@bitwarden/common/models/response/profile.response"; + +@Component({ + selector: "app-change-avatar", + templateUrl: "change-avatar.component.html", + encapsulation: ViewEncapsulation.None, +}) +export class ChangeAvatarComponent implements OnInit, OnDestroy { + @Input() profile: ProfileResponse; + + @Output() changeColor: EventEmitter = new EventEmitter(); + @Output() onSaved = new EventEmitter(); + + @ViewChild("colorPicker") colorPickerElement: ElementRef; + + loading = false; + error: string; + defaultColorPalette: NamedAvatarColor[] = [ + { name: "brightBlue", color: "#16cbfc" }, + { name: "green", color: "#94cc4b" }, + { name: "orange", color: "#ffb520" }, + { name: "lavender", color: "#e5beed" }, + { name: "yellow", color: "#fcff41" }, + { name: "indigo", color: "#acbdf7" }, + { name: "teal", color: "#8ecdc5" }, + { name: "salmon", color: "#ffa3a3" }, + { name: "pink", color: "#ffa2d4" }, + ]; + customColorSelected = false; + currentSelection: string; + + protected customColor$ = new BehaviorSubject(null); + protected customTextColor$ = new BehaviorSubject("#000000"); + private destroy$ = new Subject(); + + constructor( + private i18nService: I18nService, + private platformUtilsService: PlatformUtilsService, + private logService: LogService, + private accountUpdateService: AvatarUpdateService + ) {} + + async ngOnInit() { + //localize the default colors + this.defaultColorPalette.forEach((c) => (c.name = this.i18nService.t(c.name))); + + this.customColor$ + .pipe(debounceTime(200), takeUntil(this.destroy$)) + .subscribe((color: string | null) => { + if (color == null) { + return; + } + this.customTextColor$.next(Utils.pickTextColorBasedOnBgColor(color)); + this.customColorSelected = true; + this.currentSelection = color; + }); + + this.setSelection(await this.accountUpdateService.loadColorFromState()); + } + + async showCustomPicker() { + this.customColorSelected = true; + this.colorPickerElement.nativeElement.click(); + this.setSelection(this.customColor$.value); + } + + async generateAvatarColor() { + Utils.stringToColor(this.profile.name.toString()); + } + + async submit() { + try { + if (Utils.validateHexColor(this.currentSelection) || this.currentSelection == null) { + await this.accountUpdateService.pushUpdate(this.currentSelection); + this.changeColor.emit(this.currentSelection); + this.platformUtilsService.showToast("success", null, this.i18nService.t("avatarUpdated")); + } else { + this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred")); + } + } catch (e) { + this.logService.error(e); + this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred")); + } + } + + async ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + + async setSelection(color: string | null) { + this.defaultColorPalette.filter((x) => x.selected).forEach((c) => (c.selected = false)); + + if (color == null) { + return; + } + + color = color.toLowerCase(); + + this.customColorSelected = false; + //Allow for toggle + if (this.currentSelection === color) { + this.currentSelection = null; + } else { + const selectedColorIndex = this.defaultColorPalette.findIndex((c) => c.color === color); + if (selectedColorIndex !== -1) { + this.defaultColorPalette[selectedColorIndex].selected = true; + this.currentSelection = color; + } else { + this.customColor$.next(color); + } + } + } +} + +export class NamedAvatarColor { + name: string; + color: string; + selected? = false; +} diff --git a/apps/web/src/app/settings/change-password.component.ts b/apps/web/src/app/settings/change-password.component.ts index e6e1709d229..8d0266410ff 100644 --- a/apps/web/src/app/settings/change-password.component.ts +++ b/apps/web/src/app/settings/change-password.component.ts @@ -10,6 +10,8 @@ import { FolderService } from "@bitwarden/common/abstractions/folder/folder.serv import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { KeyConnectorService } from "@bitwarden/common/abstractions/keyConnector.service"; import { MessagingService } from "@bitwarden/common/abstractions/messaging.service"; +import { OrganizationUserService } from "@bitwarden/common/abstractions/organization-user/organization-user.service"; +import { OrganizationUserResetPasswordEnrollmentRequest } from "@bitwarden/common/abstractions/organization-user/requests"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/abstractions/organization/organization-api.service.abstraction"; import { OrganizationService } from "@bitwarden/common/abstractions/organization/organization.service.abstraction"; import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service"; @@ -25,7 +27,6 @@ import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-cr import { CipherWithIdRequest } from "@bitwarden/common/models/request/cipher-with-id.request"; import { EmergencyAccessUpdateRequest } from "@bitwarden/common/models/request/emergency-access-update.request"; import { FolderWithIdRequest } from "@bitwarden/common/models/request/folder-with-id.request"; -import { OrganizationUserResetPasswordEnrollmentRequest } from "@bitwarden/common/models/request/organization-user-reset-password-enrollment.request"; import { PasswordRequest } from "@bitwarden/common/models/request/password.request"; import { SendWithIdRequest } from "@bitwarden/common/models/request/send-with-id.request"; import { UpdateKeyRequest } from "@bitwarden/common/models/request/update-key.request"; @@ -55,7 +56,8 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent { private organizationService: OrganizationService, private keyConnectorService: KeyConnectorService, private router: Router, - private organizationApiService: OrganizationApiServiceAbstraction + private organizationApiService: OrganizationApiServiceAbstraction, + private organizationUserService: OrganizationUserService ) { super( i18nService, @@ -280,7 +282,11 @@ export class ChangePasswordComponent extends BaseChangePasswordComponent { request.masterPasswordHash = masterPasswordHash; request.resetPasswordKey = encryptedKey.encryptedString; - await this.apiService.putOrganizationUserResetPasswordEnrollment(org.id, org.userId, request); + await this.organizationUserService.putOrganizationUserResetPasswordEnrollment( + org.id, + org.userId, + request + ); } } } diff --git a/apps/web/src/app/settings/emergency-access-add-edit.component.html b/apps/web/src/app/settings/emergency-access-add-edit.component.html index b438cee937c..b3590f8fb0e 100644 --- a/apps/web/src/app/settings/emergency-access-add-edit.component.html +++ b/apps/web/src/app/settings/emergency-access-add-edit.component.html @@ -121,16 +121,16 @@ (click)="delete()" appA11yTitle="{{ 'delete' | i18n }}" *ngIf="editMode" - [disabled]="deleteBtn.loading" + [disabled]="$any(deleteBtn).loading" > diff --git a/apps/web/src/app/settings/emergency-access.component.html b/apps/web/src/app/settings/emergency-access.component.html index fbf646d5666..ee0fa015634 100644 --- a/apps/web/src/app/settings/emergency-access.component.html +++ b/apps/web/src/app/settings/emergency-access.component.html @@ -34,7 +34,12 @@ - + {{ c.email }} @@ -142,7 +147,12 @@ - + {{ c.email }} diff --git a/apps/web/src/app/settings/emergency-add-edit.component.ts b/apps/web/src/app/settings/emergency-add-edit.component.ts index ca7997eaa49..3e97067d6f9 100644 --- a/apps/web/src/app/settings/emergency-add-edit.component.ts +++ b/apps/web/src/app/settings/emergency-add-edit.component.ts @@ -3,7 +3,7 @@ import { Component } from "@angular/core"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { CipherService } from "@bitwarden/common/abstractions/cipher.service"; import { CollectionService } from "@bitwarden/common/abstractions/collection.service"; -import { EventService } from "@bitwarden/common/abstractions/event.service"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { FolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; @@ -26,6 +26,7 @@ import { AddEditComponent as BaseAddEditComponent } from "../vault/add-edit.comp export class EmergencyAddEditComponent extends BaseAddEditComponent { originalCipher: Cipher = null; viewOnly = true; + protected override componentName = "app-org-vault-add-edit"; constructor( cipherService: CipherService, @@ -38,7 +39,7 @@ export class EmergencyAddEditComponent extends BaseAddEditComponent { totpService: TotpService, passwordGenerationService: PasswordGenerationService, messagingService: MessagingService, - eventService: EventService, + eventCollectionService: EventCollectionService, policyService: PolicyService, passwordRepromptService: PasswordRepromptService, organizationService: OrganizationService, @@ -55,7 +56,7 @@ export class EmergencyAddEditComponent extends BaseAddEditComponent { totpService, passwordGenerationService, messagingService, - eventService, + eventCollectionService, policyService, organizationService, logService, diff --git a/apps/web/src/app/settings/organization-plans.component.html b/apps/web/src/app/settings/organization-plans.component.html index ac6bee95ab7..3ba90d3bd59 100644 --- a/apps/web/src/app/settings/organization-plans.component.html +++ b/apps/web/src/app/settings/organization-plans.component.html @@ -240,14 +240,6 @@ {{ "monthAbbr" | i18n }} = {{ additionalStorageTotal(selectablePlan) | currency: "$" }} /{{ "year" | i18n }} - - {{ "premiumAccess" | i18n }}: - {{ selectablePlan.premiumAccessOptionCost / 12 | currency: "$" }} × 12 - {{ "monthAbbr" | i18n }} - = - {{ 40 | currency: "$" }} - /{{ "year" | i18n }} - {{ "monthly" | i18n }} @@ -272,17 +264,6 @@ {{ "monthAbbr" | i18n }} = {{ additionalStorageTotal(selectablePlan) | currency: "$" }} /{{ "month" | i18n }} - - {{ "premiumAccess" | i18n }}: - {{ selectablePlan.premiumAccessOptionCost | currency: "$" }} {{ "monthAbbr" | i18n }} = - {{ 40 | currency: "$" }} - /{{ "month" | i18n }} -
diff --git a/apps/web/src/app/settings/organization-plans.component.ts b/apps/web/src/app/settings/organization-plans.component.ts index ad1c01d235e..5c449894231 100644 --- a/apps/web/src/app/settings/organization-plans.component.ts +++ b/apps/web/src/app/settings/organization-plans.component.ts @@ -52,8 +52,25 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { @Input() showFree = true; @Input() showCancel = false; @Input() acceptingSponsorship = false; - @Input() product: ProductType = ProductType.Free; - @Input() plan: PlanType = PlanType.Free; + @Input() + get product(): ProductType { + return this._product; + } + set product(product: ProductType) { + this._product = product; + this.formGroup?.controls?.product?.setValue(product); + } + private _product = ProductType.Free; + + @Input() + get plan(): PlanType { + return this._plan; + } + set plan(plan: PlanType) { + this._plan = plan; + this.formGroup?.controls?.plan?.setValue(plan); + } + private _plan = PlanType.Free; @Input() providerId: string; @Output() onSuccess = new EventEmitter(); @Output() onCanceled = new EventEmitter(); @@ -121,7 +138,7 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { } if (this.createOrganization) { - this.formGroup.controls.name.addValidators(Validators.required); + this.formGroup.controls.name.addValidators([Validators.required, Validators.maxLength(50)]); this.formGroup.controls.billingEmail.addValidators(Validators.required); } diff --git a/apps/web/src/app/settings/premium.component.html b/apps/web/src/app/settings/premium.component.html index 13266fd9ca5..7fc2daae23b 100644 --- a/apps/web/src/app/settings/premium.component.html +++ b/apps/web/src/app/settings/premium.component.html @@ -45,7 +45,10 @@

- {{ "premiumPrice" | i18n: (premiumPrice | currency: "$") }} + {{ "premiumPriceWithFamilyPlan" | i18n: (premiumPrice | currency: "$"):familyPlanMaxUserCount }} + {{ + "bitwardenFamiliesPlan" | i18n + }}

- + +

@@ -55,3 +65,4 @@ {{ "save" | i18n }} + diff --git a/apps/web/src/app/settings/profile.component.ts b/apps/web/src/app/settings/profile.component.ts index 229f2f5b007..1c4b1198c4f 100644 --- a/apps/web/src/app/settings/profile.component.ts +++ b/apps/web/src/app/settings/profile.component.ts @@ -1,5 +1,7 @@ -import { Component, OnInit } from "@angular/core"; +import { ViewChild, ViewContainerRef, Component, OnDestroy, OnInit } from "@angular/core"; +import { Subject, takeUntil } from "rxjs"; +import { ModalService } from "@bitwarden/angular/services/modal.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { CryptoService } from "@bitwarden/common/abstractions/crypto.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; @@ -10,16 +12,21 @@ import { StateService } from "@bitwarden/common/abstractions/state.service"; import { UpdateProfileRequest } from "@bitwarden/common/models/request/update-profile.request"; import { ProfileResponse } from "@bitwarden/common/models/response/profile.response"; +import { ChangeAvatarComponent } from "./change-avatar.component"; + @Component({ selector: "app-profile", templateUrl: "profile.component.html", }) -export class ProfileComponent implements OnInit { +export class ProfileComponent implements OnInit, OnDestroy { loading = true; profile: ProfileResponse; fingerprint: string; formPromise: Promise; + @ViewChild("avatarModalTemplate", { read: ViewContainerRef, static: true }) + avatarModalRef: ViewContainerRef; + private destroy$ = new Subject(); constructor( private apiService: ApiService, @@ -28,7 +35,8 @@ export class ProfileComponent implements OnInit { private cryptoService: CryptoService, private logService: LogService, private keyConnectorService: KeyConnectorService, - private stateService: StateService + private stateService: StateService, + private modalService: ModalService ) {} async ngOnInit() { @@ -42,6 +50,24 @@ export class ProfileComponent implements OnInit { } } + async ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + + async openChangeAvatar() { + const modalOpened = await this.modalService.openViewRef( + ChangeAvatarComponent, + this.avatarModalRef, + (modal) => { + modal.profile = this.profile; + modal.changeColor.pipe(takeUntil(this.destroy$)).subscribe(() => { + modalOpened[0].close(); + }); + } + ); + } + async submit() { try { const request = new UpdateProfileRequest(this.profile.name, this.profile.masterPasswordHint); diff --git a/apps/web/src/app/settings/purge-vault.component.ts b/apps/web/src/app/settings/purge-vault.component.ts index e10782914f0..52cb89e4c61 100644 --- a/apps/web/src/app/settings/purge-vault.component.ts +++ b/apps/web/src/app/settings/purge-vault.component.ts @@ -5,6 +5,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; +import { SyncService } from "@bitwarden/common/abstractions/sync/sync.service.abstraction"; import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction"; import { Verification } from "@bitwarden/common/types/verification"; @@ -24,7 +25,8 @@ export class PurgeVaultComponent { private platformUtilsService: PlatformUtilsService, private userVerificationService: UserVerificationService, private router: Router, - private logService: LogService + private logService: LogService, + private syncService: SyncService ) {} async submit() { @@ -34,6 +36,7 @@ export class PurgeVaultComponent { .then((request) => this.apiService.postPurgeCiphers(request, this.organizationId)); await this.formPromise; this.platformUtilsService.showToast("success", null, this.i18nService.t("vaultPurged")); + this.syncService.fullSync(true); if (this.organizationId != null) { this.router.navigate(["organizations", this.organizationId, "vault"]); } else { diff --git a/apps/web/src/app/settings/sponsoring-org-row.component.html b/apps/web/src/app/settings/sponsoring-org-row.component.html index 30abad44752..bbff3399590 100644 --- a/apps/web/src/app/settings/sponsoring-org-row.component.html +++ b/apps/web/src/app/settings/sponsoring-org-row.component.html @@ -25,7 +25,7 @@ *ngIf="!isSelfHosted && !sponsoringOrg.familySponsorshipValidUntil" [appApiAction]="resendEmailPromise" class="dropdown-item btn-submit" - [disabled]="resendEmailBtn.loading" + [disabled]="$any(resendEmailBtn).loading" (click)="resendEmail()" [attr.aria-label]="'resendEmailLabel' | i18n: sponsoringOrg.familySponsorshipFriendlyName" > @@ -36,7 +36,7 @@ #revokeSponsorshipBtn [appApiAction]="revokeSponsorshipPromise" class="dropdown-item text-danger btn-submit" - [disabled]="revokeSponsorshipBtn.loading" + [disabled]="$any(revokeSponsorshipBtn).loading" (click)="revokeSponsorship()" [attr.aria-label]="'revokeAccount' | i18n: sponsoringOrg.familySponsorshipFriendlyName" > diff --git a/apps/web/src/app/settings/two-factor-authenticator.component.html b/apps/web/src/app/settings/two-factor-authenticator.component.html index 3ed1b2e573e..748c6e0bd60 100644 --- a/apps/web/src/app/settings/two-factor-authenticator.component.html +++ b/apps/web/src/app/settings/two-factor-authenticator.component.html @@ -18,7 +18,7 @@ diff --git a/apps/web/src/app/settings/two-factor-authenticator.component.ts b/apps/web/src/app/settings/two-factor-authenticator.component.ts index 652ee24a144..90825f98bec 100644 --- a/apps/web/src/app/settings/two-factor-authenticator.component.ts +++ b/apps/web/src/app/settings/two-factor-authenticator.component.ts @@ -7,6 +7,7 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti import { StateService } from "@bitwarden/common/abstractions/state.service"; import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/enums/twoFactorProviderType"; +import { Utils } from "@bitwarden/common/misc/utils"; import { UpdateTwoFactorAuthenticatorRequest } from "@bitwarden/common/models/request/update-two-factor-authenticator.request"; import { TwoFactorAuthenticatorResponse } from "@bitwarden/common/models/response/two-factor-authenticator.response"; import { AuthResponse } from "@bitwarden/common/types/authResponse"; @@ -99,7 +100,7 @@ export class TwoFactorAuthenticatorComponent element: document.getElementById("qr"), value: "otpauth://totp/Bitwarden:" + - encodeURIComponent(email) + + Utils.encodeRFC3986URIComponent(email) + "?secret=" + encodeURIComponent(this.key) + "&issuer=Bitwarden", diff --git a/apps/web/src/app/settings/two-factor-duo.component.html b/apps/web/src/app/settings/two-factor-duo.component.html index 02e3e9c5950..fa76cf362ce 100644 --- a/apps/web/src/app/settings/two-factor-duo.component.html +++ b/apps/web/src/app/settings/two-factor-duo.component.html @@ -18,7 +18,7 @@ diff --git a/apps/web/src/app/settings/two-factor-email.component.html b/apps/web/src/app/settings/two-factor-email.component.html index 5f98fb0d93d..a885aa96244 100644 --- a/apps/web/src/app/settings/two-factor-email.component.html +++ b/apps/web/src/app/settings/two-factor-email.component.html @@ -18,7 +18,7 @@ @@ -61,7 +61,7 @@ class="btn btn-outline-primary btn-sm btn-submit align-self-start" (click)="sendEmail()" [appApiAction]="emailPromise" - [disabled]="sendBtn.loading" + [disabled]="$any(sendBtn).loading" >