diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index dd3b6445edd..fadaabf57bb 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -18,6 +18,9 @@ - **NEVER** commit secrets, credentials, or sensitive information. +- **CRITICAL**: Tailwind CSS classes MUST use the `tw-` prefix (e.g., `tw-flex`, `tw-p-4`). + - Missing prefix breaks styling completely. + - **NEVER** log decrypted data, encryption keys, or PII - No vault data in error messages or console logs diff --git a/.claude/prompts/review-code.md b/.claude/prompts/review-code.md deleted file mode 100644 index 1888b7cd503..00000000000 --- a/.claude/prompts/review-code.md +++ /dev/null @@ -1,57 +0,0 @@ -# Bitwarden Clients Repo Code Review - Careful Consideration Required - -## Think Twice Before Recommending - -Angular has multiple valid patterns. Before suggesting changes: - -- **Consider the context** - Is this code part of an active modernization effort? -- **Check for established patterns** - Look for similar implementations in the codebase -- **Avoid premature optimization** - Don't suggest refactoring stable, working code without clear benefit -- **Respect incremental progress** - Teams may be modernizing gradually with feature flags - -## Angular Modernization - Handle with Care - -**Control Flow Syntax (@if, @for, @switch):** - -- When you see legacy structural directives (*ngIf, *ngFor), consider whether modernization is in scope -- Do not mandate changes to stable code unless part of the PR's objective -- If suggesting modernization, acknowledge it's optional unless required by PR goals - -**Standalone Components:** - -- New components should be standalone whenever feasible, but do not flag existing NgModule components as issues -- Legacy patterns exist for valid reasons - consider modernization effort vs benefit - -**Typed Forms:** - -- Recommend typed forms for NEW form code -- Don't suggest rewriting working untyped forms unless they're being modified - -## Tailwind CSS - Critical Pattern - -**tw- prefix is mandatory** - This is non-negotiable and should be flagged as ❌ major finding: - -- Missing tw- prefix breaks styling completely -- Check ALL Tailwind classes in modified files - -## Rust SDK Adoption - Tread Carefully - -When reviewing cipher operations: - -- Look for breaking changes in the TypeScript → Rust boundary -- Verify error handling matches established patterns -- Don't suggest alternative SDK patterns without strong justification - -## Component Library First - -Before suggesting custom implementations: - -- Check if Bitwarden's component library already provides the functionality -- Prefer existing components over custom Tailwind styling -- Don't add UI complexity that the component library already solves - -## When in Doubt - -- **Ask questions** (💭) rather than making definitive recommendations -- **Flag for human review** (⚠️) if you're uncertain -- **Acknowledge alternatives** exist when suggesting improvements diff --git a/.github/renovate.json5 b/.github/renovate.json5 index acd181310d6..b402d01e209 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -132,6 +132,7 @@ "@yao-pkg/pkg", "anyhow", "arboard", + "ashpd", "babel-loader", "base64-loader", "base64", @@ -142,6 +143,7 @@ "core-foundation", "copy-webpack-plugin", "css-loader", + "ctor", "dirs", "electron", "electron-builder", @@ -157,6 +159,7 @@ "html-webpack-injector", "html-webpack-plugin", "interprocess", + "itertools", "json5", "keytar", "libc", @@ -178,6 +181,7 @@ "sass", "sass-loader", "scopeguard", + "secmem-proc", "security-framework", "security-framework-sys", "semver", @@ -186,6 +190,7 @@ "simplelog", "style-loader", "sysinfo", + "thiserror", "tokio", "tokio-util", "tracing", @@ -209,6 +214,7 @@ "windows-registry", "zbus", "zbus_polkit", + "zeroizing-alloc", ], description: "Platform owned dependencies", commitMessagePrefix: "[deps] Platform:", @@ -284,6 +290,7 @@ "@types/jsdom", "@types/papaparse", "@types/zxcvbn", + "aes-gcm", "async-trait", "clap", "jsdom", @@ -336,6 +343,7 @@ "aes", "big-integer", "cbc", + "chacha20poly1305", "linux-keyutils", "memsec", "node-forge", @@ -444,6 +452,7 @@ matchPackageNames: [ "anyhow", "arboard", + "ashpd", "babel-loader", "base64-loader", "base64", @@ -453,6 +462,7 @@ "core-foundation", "copy-webpack-plugin", "css-loader", + "ctor", "dirs", "electron-builder", "electron-log", @@ -487,6 +497,7 @@ "sass", "sass-loader", "scopeguard", + "secmem-proc", "security-framework", "security-framework-sys", "semver", @@ -495,6 +506,7 @@ "simplelog", "style-loader", "sysinfo", + "thiserror", "tokio", "tokio-util", "tracing", @@ -516,6 +528,7 @@ "windows-registry", "zbus", "zbus_polkit", + "zeroizing-alloc", ], matchUpdateTypes: ["minor", "patch"], dependencyDashboardApproval: true, diff --git a/.github/whitelist-capital-letters.txt b/.github/whitelist-capital-letters.txt index db5097e5268..b9f904d7613 100644 --- a/.github/whitelist-capital-letters.txt +++ b/.github/whitelist-capital-letters.txt @@ -19,8 +19,6 @@ ./apps/cli/stores/chocolatey/tools/VERIFICATION.txt ./apps/browser/store/windows/AppxManifest.xml ./apps/browser/src/background/nativeMessaging.background.ts -./apps/browser/src/models/browserComponentState.ts -./apps/browser/src/models/browserGroupingsComponentState.ts ./apps/browser/src/models/biometricErrors.ts ./apps/browser/src/browser/safariApp.ts ./apps/browser/src/safari/desktop/ViewController.swift diff --git a/.github/workflows/alert-ddg-files-modified.yml b/.github/workflows/alert-ddg-files-modified.yml index 90c055a97b8..35eb0515c10 100644 --- a/.github/workflows/alert-ddg-files-modified.yml +++ b/.github/workflows/alert-ddg-files-modified.yml @@ -14,7 +14,7 @@ jobs: pull-requests: write steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 0 persist-credentials: false diff --git a/.github/workflows/auto-branch-updater.yml b/.github/workflows/auto-branch-updater.yml index 02176b3169e..be9cd338e82 100644 --- a/.github/workflows/auto-branch-updater.yml +++ b/.github/workflows/auto-branch-updater.yml @@ -30,7 +30,7 @@ jobs: run: echo "branch=${GITHUB_REF#refs/heads/}" >> "$GITHUB_OUTPUT" - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: 'eu-web-${{ steps.setup.outputs.branch }}' fetch-depth: 0 diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index ab932c561ba..b5859516eaa 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -55,7 +55,7 @@ jobs: has_secrets: ${{ steps.check-secrets.outputs.has_secrets }} steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -94,7 +94,7 @@ jobs: working-directory: apps/browser steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -146,7 +146,7 @@ jobs: _NODE_VERSION: ${{ needs.setup.outputs.node_version }} steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -254,7 +254,7 @@ jobs: artifact_name: "dist-opera-MV3" steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -386,7 +386,7 @@ jobs: _NODE_VERSION: ${{ needs.setup.outputs.node_version }} steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -542,7 +542,7 @@ jobs: - build-safari steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index 964cbc834c5..704a9810b27 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -59,7 +59,7 @@ jobs: has_secrets: ${{ steps.check-secrets.outputs.has_secrets }} steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -114,7 +114,7 @@ jobs: steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -311,7 +311,7 @@ jobs: _WIN_PKG_VERSION: 3.5 steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -520,7 +520,7 @@ jobs: _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 5a7703adb78..8e43127770c 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -55,7 +55,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -88,7 +88,7 @@ jobs: working-directory: apps/desktop steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: true @@ -173,24 +173,14 @@ jobs: working-directory: apps/desktop steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 1 ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - - name: Free disk space for build - run: | - sudo rm -rf /usr/share/dotnet - sudo rm -rf /usr/share/swift - sudo rm -rf /usr/local/.ghcup - sudo rm -rf /usr/share/miniconda - sudo rm -rf /usr/share/az_* - sudo rm -rf /usr/local/julia* - sudo rm -rf /usr/lib/mono - sudo rm -rf /usr/lib/heroku - sudo rm -rf /usr/local/aws-cli - sudo rm -rf /usr/local/aws-sam-cli + - name: Free disk space + uses: bitwarden/gh-actions/free-disk-space@main - name: Set up Node uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 @@ -265,7 +255,7 @@ jobs: # Note: It is important that we use the release build because some compute heavy # operations such as key derivation for oo7 on linux are too slow in debug mode run: | - node build.js --release + node build.js --target=x86_64-unknown-linux-gnu --release - name: Build application run: npm run dist:lin @@ -343,7 +333,7 @@ jobs: working-directory: apps/desktop steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -428,7 +418,7 @@ jobs: # Note: It is important that we use the release build because some compute heavy # operations such as key derivation for oo7 on linux are too slow in debug mode run: | - node build.js --release + node build.js --target=aarch64-unknown-linux-gnu --release - name: Check index.d.ts generated if: github.event_name == 'pull_request' && steps.cache.outputs.cache-hit != 'true' @@ -491,7 +481,7 @@ jobs: NODE_OPTIONS: --max_old_space_size=4096 steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -759,7 +749,7 @@ jobs: NODE_OPTIONS: --max_old_space_size=4096 steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -1004,7 +994,7 @@ jobs: working-directory: apps/desktop steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -1244,7 +1234,7 @@ jobs: working-directory: apps/desktop steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -1519,7 +1509,7 @@ jobs: working-directory: apps/desktop steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -1860,7 +1850,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 02ab7727c24..7d302fb453b 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -64,7 +64,7 @@ jobs: has_secrets: ${{ steps.check-secrets.outputs.has_secrets }} steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -144,7 +144,7 @@ jobs: _VERSION: ${{ needs.setup.outputs.version }} steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false @@ -174,7 +174,7 @@ jobs: echo "server_ref=$SERVER_REF" >> "$GITHUB_OUTPUT" - name: Check out Server repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: path: server repository: bitwarden/server @@ -367,7 +367,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index 44ea21276e2..c7d80b82baa 100644 --- a/.github/workflows/chromatic.yml +++ b/.github/workflows/chromatic.yml @@ -31,7 +31,7 @@ jobs: steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 diff --git a/.github/workflows/crowdin-pull.yml b/.github/workflows/crowdin-pull.yml index 5475c4dd692..e99034c499a 100644 --- a/.github/workflows/crowdin-pull.yml +++ b/.github/workflows/crowdin-pull.yml @@ -58,7 +58,7 @@ jobs: permission-pull-requests: write # for generating pull requests - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: token: ${{ steps.app-token.outputs.token }} persist-credentials: false diff --git a/.github/workflows/lint-crowdin-config.yml b/.github/workflows/lint-crowdin-config.yml index b0efeb50823..dff253a8da2 100644 --- a/.github/workflows/lint-crowdin-config.yml +++ b/.github/workflows/lint-crowdin-config.yml @@ -22,7 +22,7 @@ jobs: ] steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 1 persist-credentials: false diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f2e6db96b30..3aeb75dcbf6 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -95,7 +95,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -142,9 +142,9 @@ jobs: run: cargo +nightly udeps --workspace --all-features --all-targets - name: Install cargo-deny - uses: taiki-e/install-action@81ee1d48d9194cdcab880cbdc7d36e87d39874cb # v2.62.45 + uses: taiki-e/install-action@073d46cba2cde38f6698c798566c1b3e24feeb44 # v2.62.67 with: - tool: cargo-deny@0.18.5 + tool: cargo-deny@0.18.6 - name: Run cargo deny working-directory: ./apps/desktop/desktop_native diff --git a/.github/workflows/locales-lint.yml b/.github/workflows/locales-lint.yml index 8335d6aacad..e431854aea2 100644 --- a/.github/workflows/locales-lint.yml +++ b/.github/workflows/locales-lint.yml @@ -17,11 +17,11 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Checkout base branch repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.event.pull_request.base.sha }} path: base diff --git a/.github/workflows/nx.yml b/.github/workflows/nx.yml index 0f01aa27899..1e23c31b033 100644 --- a/.github/workflows/nx.yml +++ b/.github/workflows/nx.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout repository - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 0 persist-credentials: false diff --git a/.github/workflows/publish-cli.yml b/.github/workflows/publish-cli.yml index 8fcd1fe7c98..ef287b0de08 100644 --- a/.github/workflows/publish-cli.yml +++ b/.github/workflows/publish-cli.yml @@ -103,7 +103,7 @@ jobs: _PKG_VERSION: ${{ needs.setup.outputs.release_version }} steps: - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -151,7 +151,7 @@ jobs: _PKG_VERSION: ${{ needs.setup.outputs.release_version }} steps: - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -203,7 +203,7 @@ jobs: _PKG_VERSION: ${{ needs.setup.outputs.release_version }} steps: - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false diff --git a/.github/workflows/publish-desktop.yml b/.github/workflows/publish-desktop.yml index 3d512d49559..f013abbbb3b 100644 --- a/.github/workflows/publish-desktop.yml +++ b/.github/workflows/publish-desktop.yml @@ -204,7 +204,7 @@ jobs: _RELEASE_TAG: ${{ needs.setup.outputs.tag_name }} steps: - name: Checkout Repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -258,7 +258,7 @@ jobs: _RELEASE_TAG: ${{ needs.setup.outputs.tag_name }} steps: - name: Checkout Repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -315,7 +315,7 @@ jobs: _RELEASE_TAG: ${{ needs.setup.outputs.tag_name }} steps: - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false diff --git a/.github/workflows/publish-web.yml b/.github/workflows/publish-web.yml index fb1de5a1bc5..be0087800f7 100644 --- a/.github/workflows/publish-web.yml +++ b/.github/workflows/publish-web.yml @@ -28,7 +28,7 @@ jobs: contents: read steps: - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -74,7 +74,7 @@ jobs: echo "Github Release Option: $_RELEASE_OPTION" - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false diff --git a/.github/workflows/release-browser.yml b/.github/workflows/release-browser.yml index ff5fb669faf..f7e45919308 100644 --- a/.github/workflows/release-browser.yml +++ b/.github/workflows/release-browser.yml @@ -28,7 +28,7 @@ jobs: release_version: ${{ steps.version.outputs.version }} steps: - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -61,7 +61,7 @@ jobs: contents: read steps: - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml index 08045b8d3c7..3f7b7e326d9 100644 --- a/.github/workflows/release-cli.yml +++ b/.github/workflows/release-cli.yml @@ -29,7 +29,7 @@ jobs: release_version: ${{ steps.version.outputs.version }} steps: - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 2239cb1268f..ec529d7b4d8 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -31,7 +31,7 @@ jobs: release_channel: ${{ steps.release_channel.outputs.channel }} steps: - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false diff --git a/.github/workflows/release-web.yml b/.github/workflows/release-web.yml index fc0ac340234..f6feb3386a7 100644 --- a/.github/workflows/release-web.yml +++ b/.github/workflows/release-web.yml @@ -25,7 +25,7 @@ jobs: tag_version: ${{ steps.version.outputs.tag }} steps: - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false diff --git a/.github/workflows/repository-management.yml b/.github/workflows/repository-management.yml index 0a343be878c..b2edf0171db 100644 --- a/.github/workflows/repository-management.yml +++ b/.github/workflows/repository-management.yml @@ -105,7 +105,7 @@ jobs: permission-contents: write # for committing and pushing to current branch - name: Check out branch - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ github.ref }} token: ${{ steps.app-token.outputs.token }} @@ -471,7 +471,7 @@ jobs: permission-contents: write # for creating and pushing new branch - name: Check out target ref - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: ${{ inputs.target_ref }} token: ${{ steps.app-token.outputs.token }} diff --git a/.github/workflows/review-code.yml b/.github/workflows/review-code.yml index 0e0597fccf0..000f4020961 100644 --- a/.github/workflows/review-code.yml +++ b/.github/workflows/review-code.yml @@ -2,7 +2,7 @@ name: Code Review on: pull_request: - types: [opened, synchronize, reopened, ready_for_review] + types: [opened, synchronize, reopened] permissions: {} diff --git a/.github/workflows/sdk-breaking-change-check.yml b/.github/workflows/sdk-breaking-change-check.yml index 14547b3942f..ecc803ebd5c 100644 --- a/.github/workflows/sdk-breaking-change-check.yml +++ b/.github/workflows/sdk-breaking-change-check.yml @@ -64,7 +64,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Check out clients repository - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false diff --git a/.github/workflows/test-browser-interactions.yml b/.github/workflows/test-browser-interactions.yml index c8f4c959c52..6e236f2352c 100644 --- a/.github/workflows/test-browser-interactions.yml +++ b/.github/workflows/test-browser-interactions.yml @@ -18,7 +18,7 @@ jobs: id-token: write steps: - name: Checkout code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 0 persist-credentials: false diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e3ba6112b7d..e8f062ea345 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -103,7 +103,7 @@ jobs: sudo apt-get install -y gnome-keyring dbus-x11 - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -137,7 +137,7 @@ jobs: runs-on: macos-14 steps: - name: Checkout - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false @@ -173,7 +173,7 @@ jobs: - rust-coverage steps: - name: Check out repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false diff --git a/.github/workflows/version-auto-bump.yml b/.github/workflows/version-auto-bump.yml index 65f004149de..d66c48fcf58 100644 --- a/.github/workflows/version-auto-bump.yml +++ b/.github/workflows/version-auto-bump.yml @@ -39,7 +39,7 @@ jobs: permission-contents: write # for committing and pushing to the current branch - name: Check out target ref - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: ref: main token: ${{ steps.app-token.outputs.token }} diff --git a/apps/browser/package.json b/apps/browser/package.json index cf2be624a22..7055aabf4fd 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/browser", - "version": "2025.12.0", + "version": "2025.12.1", "scripts": { "build": "npm run build:chrome", "build:bit": "npm run build:bit:chrome", diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index f1e2da53768..25cc1e31425 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "التصدير من" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "صيغة الملف" diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index 81c9fb6a131..d7257bab478 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Buradan xaricə köçür" }, - "export": { - "message": "Xaricə köçür" + "exportVerb": { + "message": "Xaricə köçür", + "description": "The verb form of the word Export" }, - "import": { - "message": "Daxilə köçür" + "exportNoun": { + "message": "Xaricə köçürmə", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Daxilə köçürmə", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Daxilə köçür", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Fayl formatı" @@ -2488,7 +2498,7 @@ } }, "topLayerHijackWarning": { - "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + "message": "Bu səhifə Bitwarden təcrübəsinə müdaxilə edir. Bitwarden daxili menyusu, təhlükəsizlik tədbiri olaraq müvəqqəti sıradan çıxarılıb." }, "setMasterPassword": { "message": "Ana parolu ayarla" @@ -4114,7 +4124,7 @@ "message": "Avto-doldurula bilmir" }, "cannotAutofillExactMatch": { - "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + "message": "İlkin uyuşma 'Tam Uyuşur' olaraq ayarlanıb. Hazırkı veb sayt, bu element üçün saxlanılmış giriş məlumatları ilə tam uyuşmur." }, "okay": { "message": "Oldu" @@ -4802,13 +4812,13 @@ "message": "Hesab güvənliyi" }, "phishingBlocker": { - "message": "Phishing Blocker" + "message": "Fişinq əngəlləyici" }, "enablePhishingDetection": { - "message": "Phishing detection" + "message": "Fişinq aşkarlama" }, "enablePhishingDetectionDesc": { - "message": "Display warning before accessing suspected phishing sites" + "message": "Şübhəli fişinq saytlarına erişməzdən əvvəl xəbərdarlıq nümayiş etdir" }, "notifications": { "message": "Bildirişlər" diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index afe26551760..fd4dbb780da 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Экспартаванне з" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Фармат файла" diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index adaa86369c6..1dc20d6ab65 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Изнасяне от" }, - "export": { - "message": "Изнасяне" + "exportVerb": { + "message": "Изнасяне", + "description": "The verb form of the word Export" }, - "import": { - "message": "Внасяне" + "exportNoun": { + "message": "Изнасяне", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Внасяне", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Внасяне", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Формат на файла" @@ -4802,13 +4812,13 @@ "message": "Защита на регистрацията" }, "phishingBlocker": { - "message": "Phishing Blocker" + "message": "Блокатор на измами" }, "enablePhishingDetection": { - "message": "Phishing detection" + "message": "Разпознаване на измами" }, "enablePhishingDetectionDesc": { - "message": "Display warning before accessing suspected phishing sites" + "message": "Показване на предупреждение преди зареждане на уеб сайтове подозирани за измамни" }, "notifications": { "message": "Известия" diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 9b2248da59b..baf98bcb50a 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "ফাইলের ধরণ" diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index 5c3c01a0ab5..df473b0192d 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "File format" diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 61a12bfb50c..4bbfb9a418e 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Exporta des de" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Format de fitxer" diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index 309c7361de2..e40edbb8091 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Exportovat z" }, - "export": { - "message": "Exportovat" + "exportVerb": { + "message": "Exportovat", + "description": "The verb form of the word Export" }, - "import": { - "message": "Importovat" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Importovat", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Formát souboru" diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index b5d090cc505..896eee5af4f 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Fformat y ffeil" diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 0627c97766f..ad6e5b0e90d 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Eksportér fra" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Filformat" diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index dc972697dc6..5ba6dd419a1 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export aus" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Exportieren", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Importieren", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Dateiformat" @@ -4802,13 +4812,13 @@ "message": "Kontosicherheit" }, "phishingBlocker": { - "message": "Phishing Blocker" + "message": "Phishing-Blocker" }, "enablePhishingDetection": { - "message": "Phishing detection" + "message": "Phishing-Erkennung" }, "enablePhishingDetectionDesc": { - "message": "Display warning before accessing suspected phishing sites" + "message": "Warnung vor dem Zugriff auf verdächtige Phishing-Seiten anzeigen" }, "notifications": { "message": "Benachrichtigungen" diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index e4b7b28a512..29593380dd9 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Εξαγωγή από" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Τύπος αρχείου" diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 99e95da0df5..59748f92206 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "File format" @@ -6032,5 +6042,8 @@ }, "whyAmISeeingThis": { "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 1abb01fcb14..f64bf387a5e 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "File format" diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 8b5d976a5e9..34f2f0b1105 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "File format" diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 80139915ff0..276d0dbedb2 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Exportar desde" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Formato de archivo" diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 113336c18cb..3efb771966d 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Failivorming" diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 87469554bea..d8f8c5230c6 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Fitxategiaren formatua" diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 5d4ff7a6d2b..c264b292761 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "برون ریزی از" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "فرمت پرونده" diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 06157846f90..38003bb31b2 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Vie lähteestä" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Tiedostomuoto" diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 918acdac775..38702b940c4 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Format ng file" diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index cb611df41a1..afc71011900 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Exporter à partir de" }, - "export": { - "message": "Exporter" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Importer" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Format de fichier" diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index e83baff136f..7bfe70bded5 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Exportar dende" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Formato de ficheiro" diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 64dddc4db04..fd4e010ce60 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "ייצא מ־" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "פורמט הקובץ" diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 4602d099b59..dc91aa89197 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "File Format" diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 3a42b22eb45..c472bfa50cd 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Izvezi iz" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Format datoteke" diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index dc34f3b2166..438c574bba9 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Exportálás innen:" }, - "export": { - "message": "Exportálás" + "exportVerb": { + "message": "Exportálás", + "description": "The verb form of the word Export" }, - "import": { - "message": "Importálás" + "exportNoun": { + "message": "Exportálás", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Importálás", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Importálás", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Fájlformátum" diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 36df2e733f8..4d636a5a79d 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Ekspor dari" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Format Berkas" diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index a64b3e0f351..2615640df0a 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Esporta da" }, - "export": { - "message": "Esporta" + "exportVerb": { + "message": "Esporta", + "description": "The verb form of the word Export" }, - "import": { - "message": "Importa" + "exportNoun": { + "message": "Esporta", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Importa", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Importa", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Formato file" @@ -4802,13 +4812,13 @@ "message": "Sicurezza dell'account" }, "phishingBlocker": { - "message": "Phishing Blocker" + "message": "Blocco phishing" }, "enablePhishingDetection": { - "message": "Phishing detection" + "message": "Rilevazione phishing" }, "enablePhishingDetectionDesc": { - "message": "Display warning before accessing suspected phishing sites" + "message": "Mostra un avviso prima di accedere ai siti sospetti di phishing" }, "notifications": { "message": "Notifiche" diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index e89c58ae4c3..91c006fccca 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "エクスポート元" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "ファイル形式" diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index 6e1a5c556d9..85c2b04f469 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "File format" diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 02945c6ff9b..04386b72930 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "File format" diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 5d37dbf41b7..c29ecc2d04f 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "ಕಡತದ ಮಾದರಿ" diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 247133a4295..ccf3d96d366 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "~(으)로부터 내보내기" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "파일 형식" diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index 25c830574c6..a1e3e287892 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Failo formatas" diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 65c4591db80..95e5d2399e6 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Izgūt no" }, - "export": { - "message": "Izgūt" + "exportVerb": { + "message": "Izgūt", + "description": "The verb form of the word Export" }, - "import": { - "message": "Ievietot" + "exportNoun": { + "message": "Izgūšana", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Ievietošana", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Ievietot", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Datnes veids" @@ -4802,13 +4812,13 @@ "message": "Konta drošība" }, "phishingBlocker": { - "message": "Phishing Blocker" + "message": "Pikšķerēšanas aizturētājs" }, "enablePhishingDetection": { - "message": "Phishing detection" + "message": "Pikšķerēšanas noteikšana" }, "enablePhishingDetectionDesc": { - "message": "Display warning before accessing suspected phishing sites" + "message": "Attēlot brīdinājumu pirms piekļūšanas iespējamām piekšķerēšanas vietnēm" }, "notifications": { "message": "Paziņojumi" diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index 895ae7baa01..3257c4c745c 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "ഫയൽ ഫോർമാറ്റ്" diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 2f2f9bbefe8..38487d00f9d 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "File format" diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 02945c6ff9b..04386b72930 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "File format" diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 027e28d1f2c..9056ce4ad8e 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Eksporter fra" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Filformat" diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 02945c6ff9b..04386b72930 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "File format" diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index f91f28e5c1c..ea040cb057b 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Exporteren vanuit" }, - "export": { - "message": "Exporteren" + "exportVerb": { + "message": "Exporteren", + "description": "The verb form of the word Export" }, - "import": { - "message": "Importeren" + "exportNoun": { + "message": "Exporteren", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Importeren", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Importeren", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Bestandsindeling" diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 02945c6ff9b..04386b72930 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "File format" diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 02945c6ff9b..04386b72930 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "File format" diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index c8e452d7190..67e7a2a2a00 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Eksportuj z" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Eksportuj", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Importuj", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Format pliku" @@ -1422,7 +1432,7 @@ "message": "Enter your master password" }, "updateSettings": { - "message": "Update settings" + "message": "Zaktualizuj ustawienia" }, "later": { "message": "Później" diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index 20c1a7e098d..c7ecfe3f81d 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Exportar de" }, - "export": { - "message": "Exportar" + "exportVerb": { + "message": "Exportar", + "description": "The verb form of the word Export" }, - "import": { - "message": "Importar" + "exportNoun": { + "message": "Exportação", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Importação", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Importar", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Formato do arquivo" diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index e0cbf43a813..d766e8f95fb 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Exportar de" }, - "export": { - "message": "Exportar" + "exportVerb": { + "message": "Exportar", + "description": "The verb form of the word Export" }, - "import": { - "message": "Importar" + "exportNoun": { + "message": "Exportação", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Importar", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Importação", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Formato do ficheiro" diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index d6d32804dcb..f0f71168074 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Format fișier" diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 575b29350fd..3185edea5d5 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Экспорт из" }, - "export": { - "message": "Экспорт" + "exportVerb": { + "message": "Экспортировать", + "description": "The verb form of the word Export" }, - "import": { - "message": "Импорт" + "exportNoun": { + "message": "Экспорт", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Импорт", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Импортировать", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Формат файла" diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index d4fb646f471..7ef837967c9 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "ගොනු ආකෘතිය" diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index c32da5e7adb..ffda610b8f0 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Exportovať z" }, - "export": { - "message": "Exportovať" + "exportVerb": { + "message": "Exportovať", + "description": "The verb form of the word Export" }, - "import": { - "message": "Importovať" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Importovať", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Formát súboru" @@ -1476,7 +1486,7 @@ "message": "Vyberte súbor" }, "itemsTransferred": { - "message": "Items transferred" + "message": "Položky boli prenesené" }, "maxFileSize": { "message": "Maximálna veľkosť súboru je 500 MB." @@ -3398,10 +3408,10 @@ "message": "Použiť možnosti subadresovania svojho poskytovateľa e-mailu." }, "catchallEmail": { - "message": "Catch-all e-mail" + "message": "Doménový kôš" }, "catchallEmailDesc": { - "message": "Použiť doručenú poštu typu catch-all nastavenú na doméne." + "message": "Použiť nastavený doménový kôš." }, "random": { "message": "Náhodné" diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index fcfa6857588..2806020d5f0 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Format datoteke" diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index fa3e918bc01..4a23b7e7141 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Извоз од" }, - "export": { - "message": "Извези" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Увоз" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Формат датотеке" diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index bb1e65f82fa..d04202f5c0b 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Exportera från" }, - "export": { - "message": "Exportera" + "exportVerb": { + "message": "Exportera", + "description": "The verb form of the word Export" }, - "import": { - "message": "Importera" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Importera", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Filformat" @@ -4802,13 +4812,13 @@ "message": "Kontosäkerhet" }, "phishingBlocker": { - "message": "Phishing Blocker" + "message": "Nätfiskeblockerare" }, "enablePhishingDetection": { - "message": "Phishing detection" + "message": "Nätfiskedetektering" }, "enablePhishingDetectionDesc": { - "message": "Display warning before accessing suspected phishing sites" + "message": "Visa varning innan du öppnar misstänkta nätfiskeplatser" }, "notifications": { "message": "Aviseringar" diff --git a/apps/browser/src/_locales/ta/messages.json b/apps/browser/src/_locales/ta/messages.json index 9856e53591d..1e25b5157ef 100644 --- a/apps/browser/src/_locales/ta/messages.json +++ b/apps/browser/src/_locales/ta/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "இதிலிருந்து ஏற்றுமதிசெய்" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "கோப்பு வடிவம்" diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 02945c6ff9b..04386b72930 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Export from" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "File format" diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index a07b23793b7..49bda58b558 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -6,7 +6,7 @@ "message": "โลโก้ Bitwarden" }, "extName": { - "message": "Bitwarden - จัดการรหัสผ่าน", + "message": "Bitwarden Password Manager", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { @@ -14,7 +14,7 @@ "description": "Extension description, MUST be less than 112 characters (Safari restriction)" }, "loginOrCreateNewAccount": { - "message": "ล็อกอิน หรือ สร้างบัญชีใหม่ เพื่อใช้งานตู้นิรภัยของคุณ" + "message": "เข้าสู่ระบบหรือสร้างบัญชีใหม่เพื่อเข้าถึงตู้นิรภัยของคุณ" }, "inviteAccepted": { "message": "ตอบรับคำเชิญแล้ว" @@ -23,28 +23,28 @@ "message": "สร้างบัญชี" }, "newToBitwarden": { - "message": "เพิ่งเริ่มใช้ Bitwarden ใช่ไหม?" + "message": "เพิ่งเคยใช้งาน Bitwarden ใช่หรือไม่" }, "logInWithPasskey": { "message": "เข้าสู่ระบบด้วยพาสคีย์" }, "useSingleSignOn": { - "message": "ใช้การลงชื่อเพียงครั้งเดียว" + "message": "ใช้การลงชื่อเข้าใช้แบบ SSO" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "องค์กรของคุณบังคับใช้ Single Sign-On" }, "welcomeBack": { - "message": "ยินดีต้อนรับกลับมา" + "message": "ยินดีต้อนรับกลับ" }, "setAStrongPassword": { "message": "ตั้งรหัสผ่านที่รัดกุม" }, "finishCreatingYourAccountBySettingAPassword": { - "message": "ดำเนินการสร้างบัญชีของคุณให้เสร็จสมบูรณ์โดยการตั้งรหัสผ่าน" + "message": "ตั้งรหัสผ่านเพื่อเสร็จสิ้นการสร้างบัญชี" }, "enterpriseSingleSignOn": { - "message": "Enterprise Single Sign-On" + "message": "SSO สำหรับองค์กร" }, "cancel": { "message": "ยกเลิก" @@ -53,22 +53,22 @@ "message": "ปิด" }, "submit": { - "message": "ส่งข้อมูล" + "message": "ส่ง" }, "emailAddress": { "message": "ที่อยู่อีเมล" }, "masterPass": { - "message": "Master Password" + "message": "รหัสผ่านหลัก" }, "masterPassDesc": { - "message": "รหัสผ่านหลัก คือ รหัสผ่านที่ใช้เข้าถึงตู้นิรภัยของคุณ สิ่งสำคัญมาก คือ คุณจะต้องไม่ลืมรหัสผ่านหลักโดยเด็ดขาด เพราะหากคุณลืมแล้วล่ะก็ จะไม่มีวิธีที่สามารถกู้รหัสผ่านของคุณได้เลย" + "message": "รหัสผ่านหลักคือรหัสที่คุณใช้เข้าถึงตู้นิรภัย สิ่งสำคัญคือห้ามลืมรหัสผ่านหลักเด็ดขาด เนื่องจากไม่มีวิธีกู้คืนรหัสผ่านหากคุณลืม" }, "masterPassHintDesc": { - "message": "คำใบ้เกี่ยวกับรหัสผ่านหลักสามารถช่วยให้คุณนึกรหัสผ่านหลักออกได้หากลืม" + "message": "คำใบ้รหัสผ่านหลักช่วยเตือนความจำหากคุณลืมรหัสผ่าน" }, "masterPassHintText": { - "message": "หากคุณลืมรหัสผ่าน ระบบสามารถส่งคำใบ้รหัสผ่านไปยังอีเมลของคุณได้ จำกัด $CURRENT$/$MAXIMUM$ ตัวอักษร", + "message": "หากลืมรหัสผ่าน ระบบจะส่งคำใบ้ไปที่อีเมลของคุณ สูงสุด $CURRENT$/$MAXIMUM$ ตัวอักษร", "placeholders": { "current": { "content": "$1", @@ -81,13 +81,13 @@ } }, "reTypeMasterPass": { - "message": "Re-type Master Password" + "message": "ป้อนรหัสผ่านหลักอีกครั้ง" }, "masterPassHint": { - "message": "Master Password Hint (optional)" + "message": "คำใบ้รหัสผ่านหลัก (ไม่บังคับ)" }, "passwordStrengthScore": { - "message": "คะแนนความรัดกุมของรหัสผ่าน $SCORE$", + "message": "ระดับความรัดกุมของรหัสผ่าน $SCORE$", "placeholders": { "score": { "content": "$1", @@ -96,10 +96,10 @@ } }, "joinOrganization": { - "message": "Join organization" + "message": "เข้าร่วมองค์กร" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "เข้าร่วม $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -108,7 +108,7 @@ } }, "finishJoiningThisOrganizationBySettingAMasterPassword": { - "message": "Finish joining this organization by setting a master password." + "message": "ตั้งรหัสผ่านหลักเพื่อเสร็จสิ้นการเข้าร่วมองค์กรนี้" }, "tab": { "message": "แท็บ" @@ -117,7 +117,7 @@ "message": "ตู้นิรภัย" }, "myVault": { - "message": "My Vault" + "message": "ตู้นิรภัยของฉัน" }, "allVaults": { "message": "ตู้นิรภัยทั้งหมด" @@ -135,10 +135,10 @@ "message": "คัดลอกรหัสผ่าน" }, "copyPassphrase": { - "message": "Copy passphrase" + "message": "คัดลอกวลีรหัสผ่าน" }, "copyNote": { - "message": "Copy Note" + "message": "คัดลอกโน้ต" }, "copyUri": { "message": "คัดลอก URI" @@ -150,34 +150,34 @@ "message": "คัดลอกหมายเลข" }, "copySecurityCode": { - "message": "คัดลอกรหัสรักษาความปลอดภัย" + "message": "คัดลอกรหัสความปลอดภัย" }, "copyName": { - "message": "Copy name" + "message": "คัดลอกชื่อ" }, "copyCompany": { - "message": "Copy company" + "message": "คัดลอกบริษัท" }, "copySSN": { - "message": "Copy Social Security number" + "message": "คัดลอกหมายเลขประกันสังคม" }, "copyPassportNumber": { - "message": "Copy passport number" + "message": "คัดลอกเลขหนังสือเดินทาง" }, "copyLicenseNumber": { - "message": "Copy license number" + "message": "คัดลอกเลขใบขับขี่" }, "copyPrivateKey": { - "message": "Copy private key" + "message": "คัดลอกกุญแจส่วนตัว" }, "copyPublicKey": { - "message": "Copy public key" + "message": "คัดลอกกุญแจสาธารณะ" }, "copyFingerprint": { - "message": "Copy fingerprint" + "message": "คัดลอกลายนิ้วมือ" }, "copyCustomField": { - "message": "Copy $FIELD$", + "message": "คัดลอก $FIELD$", "placeholders": { "field": { "content": "$1", @@ -186,186 +186,186 @@ } }, "copyWebsite": { - "message": "Copy website" + "message": "คัดลอกเว็บไซต์" }, "copyNotes": { - "message": "Copy notes" + "message": "คัดลอกโน้ต" }, "copy": { - "message": "Copy", + "message": "คัดลอก", "description": "Copy to clipboard" }, "fill": { - "message": "Fill", + "message": "ป้อน", "description": "This string is used on the vault page to indicate autofilling. Horizontal space is limited in the interface here so try and keep translations as concise as possible." }, "autoFill": { - "message": "กรอกข้อมูลอัตโนมัติ" + "message": "ป้อนอัตโนมัติ" }, "autoFillLogin": { - "message": "Autofill login" + "message": "ป้อนข้อมูลเข้าสู่ระบบอัตโนมัติ" }, "autoFillCard": { - "message": "Autofill card" + "message": "ป้อนข้อมูลบัตรอัตโนมัติ" }, "autoFillIdentity": { - "message": "Autofill identity" + "message": "ป้อนข้อมูลระบุตัวตนอัตโนมัติ" }, "fillVerificationCode": { - "message": "Fill verification code" + "message": "ป้อนรหัสยืนยัน" }, "fillVerificationCodeAria": { - "message": "Fill Verification Code", + "message": "ป้อนรหัสยืนยัน", "description": "Aria label for the heading displayed the inline menu for totp code autofill" }, "generatePasswordCopied": { - "message": "Generate Password (copied)" + "message": "สร้างรหัสผ่าน (คัดลอกแล้ว)" }, "copyElementIdentifier": { - "message": "คัดลอกชื่อของช่องที่กำหนดเอง" + "message": "คัดลอกชื่อฟิลด์ที่กำหนดเอง" }, "noMatchingLogins": { - "message": "ไม่พบข้อมูลล็อกอินที่ตรงกัน" + "message": "ไม่พบข้อมูลเข้าสู่ระบบที่ตรงกัน" }, "noCards": { - "message": "No cards" + "message": "ไม่มีบัตร" }, "noIdentities": { - "message": "No identities" + "message": "ไม่มีข้อมูลระบุตัวตน" }, "addLoginMenu": { - "message": "Add login" + "message": "เพิ่มข้อมูลเข้าสู่ระบบ" }, "addCardMenu": { - "message": "Add card" + "message": "เพิ่มบัตร" }, "addIdentityMenu": { - "message": "Add identity" + "message": "เพิ่มข้อมูลระบุตัวตน" }, "unlockVaultMenu": { - "message": "ปลดล็อกกตู้นิรภัยของคุณ" + "message": "ปลดล็อกตู้นิรภัย" }, "loginToVaultMenu": { - "message": "ลงชื่อเข้าใช้ตู้นิรภัยของคุณ" + "message": "เข้าสู่ระบบตู้นิรภัย" }, "autoFillInfo": { - "message": "ไม่พบข้อมูลล็อกอินเพื่อใช้กรอกข้อมูลอัตโนมัติ สำหรับแท็บปัจจุบันของเบราว์เซอร์" + "message": "ไม่มีข้อมูลเข้าสู่ระบบสำหรับป้อนในแท็บเบราว์เซอร์ปัจจุบัน" }, "addLogin": { - "message": "Add a Login" + "message": "เพิ่มข้อมูลเข้าสู่ระบบ" }, "addItem": { "message": "เพิ่มรายการ" }, "accountEmail": { - "message": "Account email" + "message": "อีเมลบัญชี" }, "requestHint": { - "message": "Request hint" + "message": "ขอคำใบ้" }, "requestPasswordHint": { - "message": "Request password hint" + "message": "ขอคำใบ้รหัสผ่าน" }, "enterYourAccountEmailAddressAndYourPasswordHintWillBeSentToYou": { - "message": "กรอกที่อยู่อีเมลบัญชีของคุณ แล้วระบบจะส่งคำใบ้รหัสผ่านไปให้คุณ" + "message": "กรอกอีเมลบัญชี แล้วระบบจะส่งคำใบ้รหัสผ่านไปให้" }, "getMasterPasswordHint": { - "message": "รับคำใบ้เกี่ยวกับรหัสผ่านหลักของคุณ" + "message": "รับคำใบ้รหัสผ่านหลัก" }, "continue": { - "message": "ดำเนินการต่อไป" + "message": "ดำเนินการต่อ" }, "sendVerificationCode": { - "message": "ส่งโค้ดยืนยันไปยังอีเมลของคุณ" + "message": "ส่งรหัสยืนยันไปที่อีเมล" }, "sendCode": { - "message": "ส่งโค้ด" + "message": "ส่งรหัส" }, "codeSent": { - "message": "ส่งโค้ดแล้ว" + "message": "ส่งรหัสแล้ว" }, "verificationCode": { - "message": "Verification Code" + "message": "รหัสยืนยัน" }, "confirmIdentity": { - "message": "ยืนยันตัวตนของคุณเพื่อดำเนินการต่อ" + "message": "ยืนยันตัวตนเพื่อดำเนินการต่อ" }, "changeMasterPassword": { - "message": "Change Master Password" + "message": "เปลี่ยนรหัสผ่านหลัก" }, "continueToWebApp": { - "message": "Continue to web app?" + "message": "ไปที่เว็บแอปหรือไม่" }, "continueToWebAppDesc": { - "message": "Explore more features of your Bitwarden account on the web app." + "message": "สำรวจฟีเจอร์เพิ่มเติมของบัญชี Bitwarden บนเว็บแอป" }, "continueToHelpCenter": { - "message": "Continue to Help Center?" + "message": "ไปที่ศูนย์ช่วยเหลือหรือไม่" }, "continueToHelpCenterDesc": { - "message": "Learn more about how to use Bitwarden on the Help Center." + "message": "เรียนรู้วิธีการใช้งาน Bitwarden เพิ่มเติมได้ที่ศูนย์ช่วยเหลือ" }, "continueToBrowserExtensionStore": { - "message": "Continue to browser extension store?" + "message": "ไปที่ร้านค้าส่วนขยายหรือไม่" }, "continueToBrowserExtensionStoreDesc": { - "message": "Help others find out if Bitwarden is right for them. Visit your browser's extension store and leave a rating now." + "message": "ช่วยบอกต่อว่า Bitwarden ดีอย่างไร แวะไปที่ร้านค้าส่วนขยายของเบราว์เซอร์และให้คะแนนเลย" }, "changeMasterPasswordOnWebConfirmation": { - "message": "You can change your master password on the Bitwarden web app." + "message": "คุณสามารถเปลี่ยนรหัสผ่านหลักได้ที่เว็บแอป Bitwarden" }, "fingerprintPhrase": { - "message": "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." }, "yourAccountsFingerprint": { - "message": "ข้อความลายนิ้วมือของบัญชีของคุณ", + "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": { - "message": "Two-step Login" + "message": "การยืนยันตัวตนสองขั้นตอน" }, "logOut": { "message": "ออกจากระบบ" }, "aboutBitwarden": { - "message": "About Bitwarden" + "message": "เกี่ยวกับ Bitwarden" }, "about": { "message": "เกี่ยวกับ" }, "moreFromBitwarden": { - "message": "More from Bitwarden" + "message": "เพิ่มเติมจาก Bitwarden" }, "continueToBitwardenDotCom": { - "message": "Continue to bitwarden.com?" + "message": "ไปที่ bitwarden.com หรือไม่" }, "bitwardenForBusiness": { - "message": "Bitwarden for Business" + "message": "Bitwarden สำหรับธุรกิจ" }, "bitwardenAuthenticator": { "message": "Bitwarden Authenticator" }, "continueToAuthenticatorPageDesc": { - "message": "Bitwarden Authenticator allows you to store authenticator keys and generate TOTP codes for 2-step verification flows. Learn more on the bitwarden.com website" + "message": "Bitwarden Authenticator ช่วยจัดเก็บคีย์และสร้างรหัส TOTP สำหรับการยืนยันตัวตนสองขั้นตอน เรียนรู้เพิ่มเติมที่เว็บไซต์ bitwarden.com" }, "bitwardenSecretsManager": { "message": "Bitwarden Secrets Manager" }, "continueToSecretsManagerPageDesc": { - "message": "Securely store, manage, and share developer secrets with Bitwarden Secrets Manager. Learn more on the bitwarden.com website." + "message": "จัดเก็บ จัดการ และแชร์ความลับสำหรับนักพัฒนาอย่างปลอดภัยด้วย Bitwarden Secrets Manager เรียนรู้เพิ่มเติมที่เว็บไซต์ bitwarden.com" }, "passwordlessDotDev": { "message": "Passwordless.dev" }, "continueToPasswordlessDotDevPageDesc": { - "message": "Create smooth and secure login experiences free from traditional passwords with Passwordless.dev. Learn more on the bitwarden.com website." + "message": "สร้างประสบการณ์การเข้าสู่ระบบที่ลื่นไหลและปลอดภัย ปราศจากรหัสผ่านแบบเดิม ๆ ด้วย Passwordless.dev เรียนรู้เพิ่มเติมที่เว็บไซต์ bitwarden.com" }, "freeBitwardenFamilies": { - "message": "Free Bitwarden Families" + "message": "Bitwarden Families ฟรี" }, "freeBitwardenFamiliesPageDesc": { - "message": "You are eligible for Free Bitwarden Families. Redeem this offer today in the web app." + "message": "คุณได้รับสิทธิ์ใช้งาน Bitwarden Families ฟรี รับสิทธิ์ข้อเสนอนี้ได้ทันทีในเว็บแอป" }, "version": { "message": "เวอร์ชัน" @@ -386,7 +386,7 @@ "message": "แก้ไขโฟลเดอร์" }, "editFolderWithName": { - "message": "Edit folder: $FOLDERNAME$", + "message": "แก้ไขโฟลเดอร์: $FOLDERNAME$", "placeholders": { "foldername": { "content": "$1", @@ -395,22 +395,22 @@ } }, "newFolder": { - "message": "New folder" + "message": "โฟลเดอร์ใหม่" }, "folderName": { - "message": "Folder name" + "message": "ชื่อโฟลเดอร์" }, "folderHintText": { - "message": "Nest a folder by adding the parent folder's name followed by a “/”. Example: Social/Forums" + "message": "ซ้อนโฟลเดอร์โดยการใส่ชื่อโฟลเดอร์หลักตามด้วยเครื่องหมาย “/” ตัวอย่าง: โซเชียล/ฟอรัม" }, "noFoldersAdded": { - "message": "No folders added" + "message": "ยังไม่ได้เพิ่มโฟลเดอร์" }, "createFoldersToOrganize": { - "message": "Create folders to organize your vault items" + "message": "สร้างโฟลเดอร์เพื่อจัดระเบียบรายการในตู้นิรภัย" }, "deleteFolderPermanently": { - "message": "Are you sure you want to permanently delete this folder?" + "message": "ยืนยันที่จะลบโฟลเดอร์นี้ถาวรหรือไม่" }, "deleteFolder": { "message": "ลบโฟลเดอร์" @@ -419,65 +419,65 @@ "message": "โฟลเดอร์" }, "noFolders": { - "message": "ไม่มีโฟลเดอร์" + "message": "ไม่มีโฟลเดอร์ที่จะแสดง" }, "helpFeedback": { - "message": "Help & Feedback" + "message": "ความช่วยเหลือและข้อเสนอแนะ" }, "helpCenter": { - "message": "Bitwarden Help center" + "message": "ศูนย์ช่วยเหลือ Bitwarden" }, "communityForums": { - "message": "Explore Bitwarden community forums" + "message": "สำรวจฟอรัมชุมชน Bitwarden" }, "contactSupport": { - "message": "Contact Bitwarden support" + "message": "ติดต่อฝ่ายสนับสนุน Bitwarden" }, "sync": { "message": "ซิงค์" }, "syncNow": { - "message": "Sync now" + "message": "ซิงค์ทันที" }, "lastSync": { - "message": "Last Sync:" + "message": "ซิงค์ล่าสุด:" }, "passGen": { - "message": "Password Generator" + "message": "ตัวสร้างรหัสผ่าน" }, "generator": { - "message": "สุ่มรหัส", + "message": "ตัวสร้าง", "description": "Short for 'credential generator'." }, "passGenInfo": { - "message": "สร้างรหัสผ่านที่รัดกุมและไม่ซ้ำใครโดยอัตโนมัติสำหรับการเข้าสู่ระบบของคุณ" + "message": "สร้างรหัสผ่านที่รัดกุมและไม่ซ้ำกันสำหรับข้อมูลเข้าสู่ระบบของคุณโดยอัตโนมัติ" }, "bitWebVaultApp": { - "message": "Bitwarden web app" + "message": "เว็บแอป Bitwarden" }, "select": { "message": "เลือก" }, "generatePassword": { - "message": "Generate Password" + "message": "สร้างรหัสผ่าน" }, "generatePassphrase": { - "message": "Generate passphrase" + "message": "สร้างวลีรหัสผ่าน" }, "passwordGenerated": { - "message": "Password generated" + "message": "สร้างรหัสผ่านแล้ว" }, "passphraseGenerated": { - "message": "Passphrase generated" + "message": "สร้างวลีรหัสผ่านแล้ว" }, "usernameGenerated": { - "message": "Username generated" + "message": "สร้างชื่อผู้ใช้แล้ว" }, "emailGenerated": { - "message": "Email generated" + "message": "สร้างอีเมลแล้ว" }, "regeneratePassword": { - "message": "Regenerate Password" + "message": "สร้างรหัสผ่านใหม่" }, "options": { "message": "ตัวเลือก" @@ -486,11 +486,11 @@ "message": "ความยาว" }, "include": { - "message": "Include", + "message": "รวม", "description": "Card header for password generator include block" }, "uppercaseDescription": { - "message": "Include uppercase characters", + "message": "รวมตัวอักษรพิมพ์ใหญ่", "description": "Tooltip for the password generator uppercase character checkbox" }, "uppercaseLabel": { @@ -498,7 +498,7 @@ "description": "Label for the password generator uppercase character checkbox" }, "lowercaseDescription": { - "message": "Include lowercase characters", + "message": "รวมตัวอักษรพิมพ์เล็ก", "description": "Full description for the password generator lowercase character checkbox" }, "lowercaseLabel": { @@ -506,7 +506,7 @@ "description": "Label for the password generator lowercase character checkbox" }, "numbersDescription": { - "message": "Include numbers", + "message": "รวมตัวเลข", "description": "Full description for the password generator numbers checkbox" }, "numbersLabel": { @@ -514,100 +514,100 @@ "description": "Label for the password generator numbers checkbox" }, "specialCharactersDescription": { - "message": "Include special characters", + "message": "รวมอักขระพิเศษ", "description": "Full description for the password generator special characters checkbox" }, "numWords": { - "message": "Number of Words" + "message": "จำนวนคำ" }, "wordSeparator": { - "message": "Word Separator" + "message": "ตัวคั่นคำ" }, "capitalize": { "message": "ขึ้นต้นด้วยตัวพิมพ์ใหญ่", "description": "Make the first letter of a work uppercase." }, "includeNumber": { - "message": "ต่อท้ายด้วยตัวเลข" + "message": "รวมตัวเลข" }, "minNumbers": { - "message": "Minimum Numbers" + "message": "จำนวนตัวเลขขั้นต่ำ" }, "minSpecial": { - "message": "Minimum Special" + "message": "จำนวนอักขระพิเศษขั้นต่ำ" }, "avoidAmbiguous": { - "message": "Avoid ambiguous characters", + "message": "หลีกเลี่ยงอักขระที่กำกวม", "description": "Label for the avoid ambiguous characters checkbox." }, "generatorPolicyInEffect": { - "message": "Enterprise policy requirements have been applied to your generator options.", + "message": "มีการใช้นโยบายองค์กรกับตัวเลือกของตัวสร้างรหัสผ่าน", "description": "Indicates that a policy limits the credential generator screen." }, "searchVault": { "message": "ค้นหาในตู้นิรภัย" }, "resetSearch": { - "message": "Reset search" + "message": "รีเซ็ตการค้นหา" }, "archiveNoun": { - "message": "Archive", + "message": "รายการจัดเก็บถาวร", "description": "Noun" }, "archiveVerb": { - "message": "Archive", + "message": "จัดเก็บถาวร", "description": "Verb" }, "unArchive": { - "message": "Unarchive" + "message": "เลิกจัดเก็บถาวร" }, "itemsInArchive": { - "message": "Items in archive" + "message": "รายการในที่จัดเก็บถาวร" }, "noItemsInArchive": { - "message": "No items in archive" + "message": "ไม่มีรายการจัดเก็บถาวร" }, "noItemsInArchiveDesc": { - "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + "message": "รายการที่จัดเก็บถาวรจะปรากฏที่นี่ และจะไม่ถูกรวมในผลการค้นหาทั่วไปหรือคำแนะนำการป้อนอัตโนมัติ" }, "itemWasSentToArchive": { - "message": "Item was sent to archive" + "message": "ย้ายรายการไปที่จัดเก็บถาวรแล้ว" }, "itemUnarchived": { - "message": "Item was unarchived" + "message": "เลิกจัดเก็บถาวรรายการแล้ว" }, "archiveItem": { - "message": "Archive item" + "message": "จัดเก็บรายการถาวร" }, "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "message": "รายการที่จัดเก็บถาวรจะไม่ถูกรวมในผลการค้นหาทั่วไปและคำแนะนำการป้อนอัตโนมัติ ยืนยันที่จะจัดเก็บรายการนี้ถาวรหรือไม่" }, "upgradeToUseArchive": { - "message": "A premium membership is required to use Archive." + "message": "ต้องเป็นสมาชิกพรีเมียมจึงจะใช้งานฟีเจอร์จัดเก็บถาวรได้" }, "edit": { "message": "แก้ไข" }, "view": { - "message": "แสดง" + "message": "ดู" }, "viewAll": { - "message": "View all" + "message": "ดูทั้งหมด" }, "showAll": { - "message": "Show all" + "message": "แสดงทั้งหมด" }, "viewLess": { - "message": "View less" + "message": "ดูน้อยลง" }, "viewLogin": { - "message": "View login" + "message": "ดูข้อมูลเข้าสู่ระบบ" }, "noItemsInList": { - "message": "ไม่มีรายการ" + "message": "ไม่มีรายการที่จะแสดง" }, "itemInformation": { - "message": "Item Information" + "message": "ข้อมูลรายการ" }, "username": { "message": "ชื่อผู้ใช้" @@ -616,28 +616,28 @@ "message": "รหัสผ่าน" }, "totp": { - "message": "Authenticator secret" + "message": "รหัสลับยืนยันตัวตน" }, "passphrase": { - "message": "ข้อความรหัสผ่าน" + "message": "วลีรหัสผ่าน" }, "favorite": { "message": "รายการโปรด" }, "unfavorite": { - "message": "Unfavorite" + "message": "เลิกเป็นรายการโปรด" }, "itemAddedToFavorites": { - "message": "Item added to favorites" + "message": "เพิ่มรายการในรายการโปรดแล้ว" }, "itemRemovedFromFavorites": { - "message": "Item removed from favorites" + "message": "ลบรายการออกจากรายการโปรดแล้ว" }, "notes": { "message": "โน้ต" }, "privateNote": { - "message": "Private note" + "message": "โน้ตส่วนตัว" }, "note": { "message": "โน้ต" @@ -655,13 +655,13 @@ "message": "ดูรายการ" }, "launch": { - "message": "เริ่ม" + "message": "เปิด" }, "launchWebsite": { - "message": "Launch website" + "message": "เปิดเว็บไซต์" }, "launchWebsiteName": { - "message": "Launch website $ITEMNAME$", + "message": "เปิดเว็บไซต์ $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -673,7 +673,7 @@ "message": "เว็บไซต์" }, "toggleVisibility": { - "message": "Toggle Visibility" + "message": "สลับการแสดงผล" }, "manage": { "message": "จัดการ" @@ -682,55 +682,55 @@ "message": "อื่น ๆ" }, "unlockMethods": { - "message": "Unlock options" + "message": "ตัวเลือกการปลดล็อก" }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Set up an unlock method to change your vault timeout action." + "message": "ตั้งค่าวิธีการปลดล็อกเพื่อเปลี่ยนการดำเนินการเมื่อตู้นิรภัยหมดเวลา" }, "unlockMethodNeeded": { - "message": "Set up an unlock method in Settings" + "message": "ตั้งค่าวิธีการปลดล็อกในการตั้งค่า" }, "sessionTimeoutHeader": { - "message": "Session timeout" + "message": "เซสชันหมดเวลา" }, "vaultTimeoutHeader": { - "message": "Vault timeout" + "message": "ตู้นิรภัยหมดเวลา" }, "otherOptions": { - "message": "Other options" + "message": "ตัวเลือกอื่น ๆ" }, "rateExtension": { - "message": "Rate the Extension" + "message": "ให้คะแนนส่วนขยาย" }, "browserNotSupportClipboard": { - "message": "เว็บเบราว์เซอร์ของคุณไม่รองรับการคัดลอกคลิปบอร์ดอย่างง่าย คัดลอกด้วยตนเองแทน" + "message": "เว็บเบราว์เซอร์ของคุณไม่รองรับการคัดลอกไปยังคลิปบอร์ดแบบง่าย โปรดคัดลอกด้วยตนเองแทน" }, "verifyYourIdentity": { - "message": "Verify your identity" + "message": "ยืนยันตัวตนของคุณ" }, "weDontRecognizeThisDevice": { - "message": "We don't recognize this device. Enter the code sent to your email to verify your identity." + "message": "เราไม่รู้จักอุปกรณ์นี้ ป้อนรหัสที่ส่งไปยังอีเมลของคุณเพื่อยืนยันตัวตน" }, "continueLoggingIn": { - "message": "Continue logging in" + "message": "ดำเนินการเข้าสู่ระบบต่อ" }, "yourVaultIsLocked": { - "message": "ตู้เซฟของคุณถูกล็อก ยืนยันตัวตนของคุณเพื่อดำเนินการต่อ" + "message": "ตู้นิรภัยล็อกอยู่ ยืนยันตัวตนเพื่อดำเนินการต่อ" }, "yourVaultIsLockedV2": { - "message": "ห้องนิรภัยของคุณถูกล็อก" + "message": "ตู้นิรภัยล็อกอยู่" }, "yourAccountIsLocked": { - "message": "Your account is locked" + "message": "บัญชีของคุณถูกล็อก" }, "or": { - "message": "or" + "message": "หรือ" }, "unlock": { - "message": "ปลดล็อค" + "message": "ปลดล็อก" }, "loggedInAsOn": { - "message": "ล็อกอินด้วย $EMAIL$ บน $HOSTNAME$", + "message": "เข้าสู่ระบบในชื่อ $EMAIL$ บน $HOSTNAME$", "placeholders": { "email": { "content": "$1", @@ -746,7 +746,7 @@ "message": "รหัสผ่านหลักไม่ถูกต้อง" }, "invalidMasterPasswordConfirmEmailAndHost": { - "message": "Invalid master password. Confirm your email is correct and your account was created on $HOST$.", + "message": "รหัสผ่านหลักไม่ถูกต้อง โปรดยืนยันว่าอีเมลของคุณถูกต้องและบัญชีถูกสร้างขึ้นบน $HOST$", "placeholders": { "host": { "content": "$1", @@ -755,16 +755,16 @@ } }, "vaultTimeout": { - "message": "ระยะเวลาล็อกตู้เซฟ" + "message": "ระยะเวลาหมดเวลาตู้นิรภัย" }, "vaultTimeout1": { - "message": "Timeout" + "message": "หมดเวลา" }, "lockNow": { - "message": "Lock Now" + "message": "ล็อกทันที" }, "lockAll": { - "message": "Lock all" + "message": "ล็อกทั้งหมด" }, "immediately": { "message": "ทันที" @@ -800,52 +800,52 @@ "message": "4 ชั่วโมง" }, "onLocked": { - "message": "On Locked" + "message": "เมื่อล็อกระบบ" }, "onIdle": { - "message": "On system idle" + "message": "เมื่อระบบไม่ได้ใช้งาน" }, "onSleep": { - "message": "On system sleep" + "message": "เมื่อระบบสลีป" }, "onRestart": { - "message": "On Restart" + "message": "เมื่อรีสตาร์ตเบราว์เซอร์" }, "never": { - "message": "ไม่อีกเลย" + "message": "ไม่เลย" }, "security": { "message": "ความปลอดภัย" }, "confirmMasterPassword": { - "message": "Confirm master password" + "message": "ยืนยันรหัสผ่านหลัก" }, "masterPassword": { - "message": "Master password" + "message": "รหัสผ่านหลัก" }, "masterPassImportant": { - "message": "Your master password cannot be recovered if you forget it!" + "message": "รหัสผ่านหลักไม่สามารถกู้คืนได้หากคุณลืม!" }, "masterPassHintLabel": { - "message": "Master password hint" + "message": "คำใบ้รหัสผ่านหลัก" }, "errorOccurred": { - "message": "พบข้อผิดพลาด" + "message": "เกิดข้อผิดพลาด" }, "emailRequired": { - "message": "ต้องระบุอีเมล" + "message": "จำเป็นต้องระบุที่อยู่อีเมล" }, "invalidEmail": { "message": "ที่อยู่อีเมลไม่ถูกต้อง" }, "masterPasswordRequired": { - "message": "ต้องใช้รหัสผ่านหลัก" + "message": "จำเป็นต้องระบุรหัสผ่านหลัก" }, "confirmMasterPasswordRequired": { - "message": "ต้องพิมพ์รหัสผ่านหลักอีกครั้ง" + "message": "จำเป็นต้องป้อนรหัสผ่านหลักซ้ำ" }, "masterPasswordMinlength": { - "message": "Master password must be at least $VALUE$ characters long.", + "message": "รหัสผ่านหลักต้องมีความยาวอย่างน้อย $VALUE$ ตัวอักษร", "description": "The Master Password must be at least a specific number of characters long.", "placeholders": { "value": { @@ -855,37 +855,37 @@ } }, "masterPassDoesntMatch": { - "message": "การยืนยันรหัสผ่านหลักไม่ตรงกัน" + "message": "ยืนยันรหัสผ่านหลักไม่ตรงกัน" }, "newAccountCreated": { - "message": "บัญชีใหม่ของคุณถูกสร้างขึ้นแล้ว! ตอนนี้คุณสามารถเข้าสู่ระบบ" + "message": "สร้างบัญชีใหม่แล้ว! คุณสามารถเข้าสู่ระบบได้ทันที" }, "newAccountCreated2": { - "message": "Your new account has been created!" + "message": "สร้างบัญชีใหม่แล้ว!" }, "youHaveBeenLoggedIn": { - "message": "You have been logged in!" + "message": "เข้าสู่ระบบแล้ว!" }, "youSuccessfullyLoggedIn": { - "message": "You successfully logged in" + "message": "คุณเข้าสู่ระบบสำเร็จ" }, "youMayCloseThisWindow": { - "message": "You may close this window" + "message": "คุณสามารถปิดหน้าต่างนี้ได้" }, "masterPassSent": { - "message": "เราได้ส่งอีเมลพร้อมคำใบ้รหัสผ่านหลักของคุณออกไปแล้ว" + "message": "ส่งอีเมลแจ้งคำใบ้รหัสผ่านหลักให้คุณแล้ว" }, "verificationCodeRequired": { - "message": "ต้องระบุโค้ดยืนยัน" + "message": "จำเป็นต้องระบุรหัสยืนยัน" }, "webauthnCancelOrTimeout": { - "message": "The authentication was cancelled or took too long. Please try again." + "message": "การยืนยันตัวตนถูกยกเลิกหรือใช้เวลานานเกินไป โปรดลองอีกครั้ง" }, "invalidVerificationCode": { - "message": "โค้ดยืนยันไม่ถูกต้อง" + "message": "รหัสยืนยันไม่ถูกต้อง" }, "valueCopied": { - "message": "$VALUE$ copied", + "message": "คัดลอก $VALUE$ แล้ว", "description": "Value has been copied to the clipboard.", "placeholders": { "value": { @@ -895,127 +895,127 @@ } }, "autofillError": { - "message": "Unable to auto-fill the selected login on this page. Copy/paste your username and/or password instead." + "message": "ไม่สามารถป้อนข้อมูลรายการที่เลือกบนหน้านี้ได้อัตโนมัติ โปรดคัดลอกและวางข้อมูลแทน" }, "totpCaptureError": { - "message": "Unable to scan QR code from the current webpage" + "message": "ไม่สามารถสแกน QR Code จากหน้าเว็บปัจจุบัน" }, "totpCaptureSuccess": { - "message": "Authenticator key added" + "message": "เพิ่มคีย์ยืนยันตัวตนแล้ว" }, "totpCapture": { - "message": "Scan authenticator QR code from current webpage" + "message": "สแกน QR Code สำหรับแอปยืนยันตัวตนจากหน้าเว็บปัจจุบัน" }, "totpHelperTitle": { - "message": "Make 2-step verification seamless" + "message": "ทำให้การยืนยันตัวตน 2 ขั้นตอนราบรื่นยิ่งขึ้น" }, "totpHelper": { - "message": "Bitwarden can store and fill 2-step verification codes. Copy and paste the key into this field." + "message": "Bitwarden สามารถจัดเก็บและป้อนรหัสยืนยัน 2 ขั้นตอนได้ คัดลอกและวางคีย์ลงในช่องนี้" }, "totpHelperWithCapture": { - "message": "Bitwarden can store and fill 2-step verification codes. Select the camera icon to take a screenshot of this website's authenticator QR code, or copy and paste the key into this field." + "message": "Bitwarden สามารถจัดเก็บและป้อนรหัสยืนยัน 2 ขั้นตอนได้ เลือกไอคอนกล้องเพื่อถ่ายภาพหน้าจอ QR Code ของแอปยืนยันตัวตนจากเว็บไซต์นี้ หรือคัดลอกและวางคีย์ลงในช่องนี้" }, "learnMoreAboutAuthenticators": { - "message": "Learn more about authenticators" + "message": "เรียนรู้เพิ่มเติมเกี่ยวกับแอปยืนยันตัวตน" }, "copyTOTP": { - "message": "Copy Authenticator key (TOTP)" + "message": "คัดลอกคีย์ยืนยันตัวตน (TOTP)" }, "loggedOut": { - "message": "ออกจากระบบ" + "message": "ออกจากระบบแล้ว" }, "loggedOutDesc": { - "message": "You have been logged out of your account." + "message": "คุณออกจากระบบบัญชีแล้ว" }, "loginExpired": { - "message": "เซสชันของคุณหมดอายุแล้ว" + "message": "เซสชันการเข้าสู่ระบบหมดอายุ" }, "logIn": { - "message": "Log in" + "message": "เข้าสู่ระบบ" }, "logInToBitwarden": { - "message": "Log in to Bitwarden" + "message": "เข้าสู่ระบบ Bitwarden" }, "enterTheCodeSentToYourEmail": { - "message": "Enter the code sent to your email" + "message": "ป้อนรหัสที่ส่งไปยังอีเมลของคุณ" }, "enterTheCodeFromYourAuthenticatorApp": { - "message": "Enter the code from your authenticator app" + "message": "ป้อนรหัสจากแอปยืนยันตัวตน" }, "pressYourYubiKeyToAuthenticate": { - "message": "Press your YubiKey to authenticate" + "message": "กด YubiKey ของคุณเพื่อยืนยันตัวตน" }, "duoTwoFactorRequiredPageSubtitle": { - "message": "Duo two-step login is required for your account. Follow the steps below to finish logging in." + "message": "บัญชีของคุณจำเป็นต้องเข้าสู่ระบบ 2 ขั้นตอนผ่าน Duo ทำตามขั้นตอนด้านล่างเพื่อเสร็จสิ้นการเข้าสู่ระบบ" }, "followTheStepsBelowToFinishLoggingIn": { - "message": "Follow the steps below to finish logging in." + "message": "ทำตามขั้นตอนด้านล่างเพื่อเสร็จสิ้นการเข้าสู่ระบบ" }, "followTheStepsBelowToFinishLoggingInWithSecurityKey": { - "message": "Follow the steps below to finish logging in with your security key." + "message": "ทำตามขั้นตอนด้านล่างเพื่อเสร็จสิ้นการเข้าสู่ระบบด้วยคีย์ความปลอดภัย" }, "restartRegistration": { - "message": "Restart registration" + "message": "เริ่มการลงทะเบียนใหม่" }, "expiredLink": { - "message": "Expired link" + "message": "ลิงก์หมดอายุ" }, "pleaseRestartRegistrationOrTryLoggingIn": { - "message": "Please restart registration or try logging in." + "message": "โปรดเริ่มการลงทะเบียนใหม่หรือลองเข้าสู่ระบบ" }, "youMayAlreadyHaveAnAccount": { - "message": "You may already have an account" + "message": "คุณอาจมีบัญชีอยู่แล้ว" }, "logOutConfirmation": { - "message": "คุณต้องการล็อกเอาต์ใช่หรือไม่?" + "message": "ยืนยันที่จะออกจากระบบหรือไม่" }, "yes": { "message": "ใช่" }, "no": { - "message": "ไม่ใช่" + "message": "ไม่" }, "location": { - "message": "Location" + "message": "ตำแหน่งที่ตั้ง" }, "unexpectedError": { - "message": "An unexpected error has occured." + "message": "เกิดข้อผิดพลาดที่ไม่คาดคิด" }, "nameRequired": { - "message": "ต้องระบุชื่อ" + "message": "จำเป็นต้องระบุชื่อ" }, "addedFolder": { "message": "เพิ่มโฟลเดอร์แล้ว" }, "twoStepLoginConfirmation": { - "message": "Two-step login makes your account more secure by requiring you to enter a security code from an authenticator app whenever you log in. Two-step login can be enabled on the bitwarden.com web vault. Do you want to visit the website now?" + "message": "การเข้าสู่ระบบ 2 ขั้นตอนช่วยให้บัญชีปลอดภัยยิ่งขึ้น โดยกำหนดให้คุณยืนยันการเข้าสู่ระบบด้วยอุปกรณ์อื่น เช่น คีย์ความปลอดภัย แอปยืนยันตัวตน SMS โทรศัพท์ หรืออีเมล สามารถตั้งค่าได้ที่เว็บตู้นิรภัย bitwarden.com ต้องการไปที่เว็บไซต์ตอนนี้หรือไม่" }, "twoStepLoginConfirmationContent": { - "message": "Make your account more secure by setting up two-step login in the Bitwarden web app." + "message": "ทำให้บัญชีปลอดภัยยิ่งขึ้นด้วยการตั้งค่าการเข้าสู่ระบบ 2 ขั้นตอนในเว็บแอป Bitwarden" }, "twoStepLoginConfirmationTitle": { - "message": "Continue to web app?" + "message": "ไปที่เว็บแอปหรือไม่" }, "editedFolder": { - "message": "Edited Folder" + "message": "บันทึกโฟลเดอร์แล้ว" }, "deleteFolderConfirmation": { - "message": "คุณแน่ใจหรือไม่ว่าต้องการลบโฟลเดอร์นี้" + "message": "ยืนยันที่จะลบโฟลเดอร์นี้หรือไม่" }, "deletedFolder": { "message": "ลบโฟลเดอร์แล้ว" }, "gettingStartedTutorial": { - "message": "Getting Started Tutorial" + "message": "บทแนะนำการเริ่มต้นใช้งาน" }, "gettingStartedTutorialVideo": { - "message": "ดูบทช่วยสอนการเริ่มต้นของเราเพื่อเรียนรู้วิธีใช้ประโยชน์สูงสุดจากส่วนขยายเบราว์เซอร์" + "message": "ดูวิดีโอแนะนำการเริ่มต้นใช้งานเพื่อเรียนรู้วิธีใช้งานส่วนขยายเบราว์เซอร์ให้คุ้มค่าที่สุด" }, "syncingComplete": { - "message": "การซิงก์เสร็จสมบูรณ์" + "message": "ซิงค์เสร็จสมบูรณ์" }, "syncingFailed": { - "message": "การซิงก์ล้มเหลว" + "message": "การซิงค์ล้มเหลว" }, "passwordCopied": { "message": "คัดลอกรหัสผ่านแล้ว" @@ -1034,23 +1034,23 @@ } }, "newUri": { - "message": "เพิ่ม URI ใหม่" + "message": "URI ใหม่" }, "addDomain": { - "message": "Add domain", + "message": "เพิ่มโดเมน", "description": "'Domain' here refers to an internet domain name (e.g. 'bitwarden.com') and the message in whole described the act of putting a domain value into the context." }, "addedItem": { "message": "เพิ่มรายการแล้ว" }, "editedItem": { - "message": "แก้ไขรายการแล้ว" + "message": "บันทึกรายการแล้ว" }, "savedWebsite": { - "message": "Saved website" + "message": "เว็บไซต์ที่บันทึก" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "เว็บไซต์ที่บันทึก ( $COUNT$ )", "placeholders": { "count": { "content": "$1", @@ -1059,88 +1059,88 @@ } }, "deleteItemConfirmation": { - "message": "คุณต้องการส่งไปยังถังขยะใช่หรือไม่?" + "message": "ยืนยันที่จะย้ายไปถังขยะหรือไม่" }, "deletedItem": { - "message": "ส่งรายการไปยังถังขยะแล้ว" + "message": "ย้ายรายการไปถังขยะแล้ว" }, "overwritePassword": { - "message": "Overwrite Password" + "message": "เขียนทับรหัสผ่าน" }, "overwritePasswordConfirmation": { - "message": "คุณต้องการเขียนทับรหัสผ่านปัจจุบันใช่หรือไม่?" + "message": "ยืนยันที่จะเขียนทับรหัสผ่านปัจจุบันหรือไม่" }, "overwriteUsername": { "message": "เขียนทับชื่อผู้ใช้" }, "overwriteUsernameConfirmation": { - "message": "คุณแน่ใจหรือไม่ว่าต้องการเขียนทับชื่อผู้ใช้ปัจจุบัน" + "message": "ยืนยันที่จะเขียนทับชื่อผู้ใช้ปัจจุบันหรือไม่" }, "searchFolder": { - "message": "ค้นหาในโพลเดอร์" + "message": "ค้นหาโฟลเดอร์" }, "searchCollection": { - "message": "คอลเลกชันการค้นหา" + "message": "ค้นหาคอลเลกชัน" }, "searchType": { "message": "ประเภทการค้นหา" }, "noneFolder": { - "message": "No Folder", + "message": "ไม่มีโฟลเดอร์", "description": "This is the folder for uncategorized items" }, "enableAddLoginNotification": { - "message": "ถามเพื่อให้เพิ่มการเข้าสู่ระบบ" + "message": "ถามเพื่อเพิ่มข้อมูลเข้าสู่ระบบ" }, "vaultSaveOptionsTitle": { - "message": "Save to vault options" + "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." + "message": "ถามเพื่อเพิ่มรายการหากไม่พบข้อมูลในตู้นิรภัย" }, "addLoginNotificationDescAlt": { - "message": "หากไม่พบรายการในห้องนิรภัยของคุณ ระบบจะถามเพื่อเพิ่มรายการ มีผลกับทุกบัญชีที่ลงชื่อเข้าใช้" + "message": "ถามเพื่อเพิ่มรายการหากไม่พบข้อมูลในตู้นิรภัย (สำหรับทุกบัญชีที่เข้าสู่ระบบ)" }, "showCardsInVaultViewV2": { - "message": "Always show cards as Autofill suggestions on Vault view" + "message": "แสดงบัตรเป็นคำแนะนำการป้อนอัตโนมัติในมุมมองตู้นิรภัยเสมอ" }, "showCardsCurrentTab": { - "message": "แสดงการ์ดบนหน้าแท็บ" + "message": "แสดงบัตรในหน้าแท็บ" }, "showCardsCurrentTabDesc": { - "message": "บัตรรายการในหน้าแท็บเพื่อให้ป้อนอัตโนมัติได้ง่าย" + "message": "แสดงรายการบัตรในหน้าแท็บเพื่อให้ป้อนอัตโนมัติได้ง่าย" }, "showIdentitiesInVaultViewV2": { - "message": "Always show identities as Autofill suggestions on Vault view" + "message": "แสดงข้อมูลระบุตัวตนเป็นคำแนะนำการป้อนอัตโนมัติในมุมมองตู้นิรภัยเสมอ" }, "showIdentitiesCurrentTab": { - "message": "แสดงตัวตนบนหน้าแท็บ" + "message": "แสดงข้อมูลระบุตัวตนในหน้าแท็บ" }, "showIdentitiesCurrentTabDesc": { - "message": "แสดงรายการข้อมูลประจำตัวในหน้าแท็บเพื่อให้ป้อนอัตโนมัติได้ง่าย" + "message": "แสดงรายการข้อมูลระบุตัวตนในหน้าแท็บเพื่อให้ป้อนอัตโนมัติได้ง่าย" }, "clickToAutofillOnVault": { - "message": "Click items to autofill on Vault view" + "message": "คลิกรายการเพื่อป้อนข้อมูลอัตโนมัติในมุมมองตู้นิรภัย" }, "clickToAutofill": { - "message": "Click items in autofill suggestion to fill" + "message": "คลิกรายการในคำแนะนำเพื่อป้อนข้อมูล" }, "clearClipboard": { "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": "Should bitwarden remember this password for you?" + "message": "ต้องการให้ Bitwarden จำรหัสผ่านนี้หรือไม่" }, "notificationAddSave": { - "message": "Yes, Save Now" + "message": "บันทึก" }, "notificationViewAria": { - "message": "View $ITEMNAME$, opens in new window", + "message": "ดู $ITEMNAME$ เปิดในหน้าต่างใหม่", "placeholders": { "itemName": { "content": "$1" @@ -1149,18 +1149,18 @@ "description": "Aria label for the view button in notification bar confirmation message" }, "notificationNewItemAria": { - "message": "New Item, opens in new window", + "message": "รายการใหม่ เปิดในหน้าต่างใหม่", "description": "Aria label for the new item button in notification bar confirmation message when error is prompted" }, "notificationEditTooltip": { - "message": "Edit before saving", + "message": "แก้ไขก่อนบันทึก", "description": "Tooltip and Aria label for edit button on cipher item" }, "newNotification": { - "message": "New notification" + "message": "การแจ้งเตือนใหม่" }, "labelWithNotification": { - "message": "$LABEL$: New notification", + "message": "$LABEL$: การแจ้งเตือนใหม่", "description": "Label for the notification with a new login suggestion.", "placeholders": { "label": { @@ -1170,15 +1170,15 @@ } }, "notificationLoginSaveConfirmation": { - "message": "saved to Bitwarden.", + "message": "บันทึกลง Bitwarden แล้ว", "description": "Shown to user after item is saved." }, "notificationLoginUpdatedConfirmation": { - "message": "updated in Bitwarden.", + "message": "อัปเดตใน Bitwarden แล้ว", "description": "Shown to user after item is updated." }, "selectItemAriaLabel": { - "message": "Select $ITEMTYPE$, $ITEMNAME$", + "message": "เลือก $ITEMTYPE$, $ITEMNAME$", "description": "Used by screen readers. $1 is the item type (like vault or folder), $2 is the selected item name.", "placeholders": { "itemType": { @@ -1190,35 +1190,35 @@ } }, "saveAsNewLoginAction": { - "message": "Save as new login", + "message": "บันทึกเป็นข้อมูลเข้าสู่ระบบใหม่", "description": "Button text for saving login details as a new entry." }, "updateLoginAction": { - "message": "Update login", + "message": "อัปเดตข้อมูลเข้าสู่ระบบ", "description": "Button text for updating an existing login entry." }, "unlockToSave": { - "message": "Unlock to save this login", + "message": "ปลดล็อกเพื่อบันทึกข้อมูลเข้าสู่ระบบนี้", "description": "User prompt to take action in order to save the login they just entered." }, "saveLogin": { - "message": "Save login", + "message": "บันทึกข้อมูลเข้าสู่ระบบ", "description": "Prompt asking the user if they want to save their login details." }, "updateLogin": { - "message": "Update existing login", + "message": "อัปเดตข้อมูลเข้าสู่ระบบเดิม", "description": "Prompt asking the user if they want to update an existing login entry." }, "loginSaveSuccess": { - "message": "Login saved", + "message": "บันทึกข้อมูลเข้าสู่ระบบแล้ว", "description": "Message displayed when login details are successfully saved." }, "loginUpdateSuccess": { - "message": "Login updated", + "message": "อัปเดตข้อมูลเข้าสู่ระบบแล้ว", "description": "Message displayed when login details are successfully updated." }, "loginUpdateTaskSuccess": { - "message": "Great job! You took the steps to make you and $ORGANIZATION$ more secure.", + "message": "เยี่ยมมาก! คุณได้ดำเนินการเพื่อทำให้คุณและ $ORGANIZATION$ ปลอดภัยยิ่งขึ้น", "placeholders": { "organization": { "content": "$1" @@ -1227,7 +1227,7 @@ "description": "Shown to user after login is updated." }, "loginUpdateTaskSuccessAdditional": { - "message": "Thank you for making $ORGANIZATION$ more secure. You have $TASK_COUNT$ more passwords to update.", + "message": "ขอบคุณที่ช่วยให้ $ORGANIZATION$ ปลอดภัยยิ่งขึ้น คุณยังมีรหัสผ่านที่ต้องอัปเดตอีก $TASK_COUNT$ รายการ", "placeholders": { "organization": { "content": "$1" @@ -1239,77 +1239,77 @@ "description": "Shown to user after login is updated." }, "nextSecurityTaskAction": { - "message": "Change next password", + "message": "เปลี่ยนรหัสผ่านถัดไป", "description": "Message prompting user to undertake completion of another security task." }, "saveFailure": { - "message": "Error saving", + "message": "เกิดข้อผิดพลาดในการบันทึก", "description": "Error message shown when the system fails to save login details." }, "saveFailureDetails": { - "message": "Oh no! We couldn't save this. Try entering the details manually.", + "message": "แย่แล้ว! เราไม่สามารถบันทึกรายการนี้ได้ ลองป้อนรายละเอียดด้วยตนเอง", "description": "Detailed error message shown when saving login details fails." }, "changePasswordWarning": { - "message": "After changing your password, you will need to log in with your new password. Active sessions on other devices will be logged out within one hour." + "message": "หลังจากเปลี่ยนรหัสผ่าน คุณจะต้องเข้าสู่ระบบด้วยรหัสผ่านใหม่ เซสชันที่ใช้งานอยู่บนอุปกรณ์อื่นจะถูกออกจากระบบภายใน 1 ชั่วโมง" }, "accountRecoveryUpdateMasterPasswordSubtitle": { - "message": "Change your master password to complete account recovery." + "message": "เปลี่ยนรหัสผ่านหลักเพื่อเสร็จสิ้นการกู้คืนบัญชี" }, "enableChangedPasswordNotification": { - "message": "ขอให้ปรับปรุงการเข้าสู่ระบบที่มีอยู่" + "message": "ถามเพื่ออัปเดตข้อมูลเข้าสู่ระบบที่มีอยู่" }, "changedPasswordNotificationDesc": { - "message": "Ask to update a login's password when a change is detected on a website." + "message": "ถามเพื่ออัปเดตรหัสผ่านของข้อมูลเข้าสู่ระบบเมื่อตรวจพบการเปลี่ยนแปลงบนเว็บไซต์" }, "changedPasswordNotificationDescAlt": { - "message": "Ask to update a login's password when a change is detected on a website. Applies to all logged in accounts." + "message": "ถามเพื่ออัปเดตรหัสผ่านของข้อมูลเข้าสู่ระบบเมื่อตรวจพบการเปลี่ยนแปลงบนเว็บไซต์ (สำหรับทุกบัญชีที่เข้าสู่ระบบ)" }, "enableUsePasskeys": { - "message": "Ask to save and use passkeys" + "message": "ถามเพื่อบันทึกและใช้พาสคีย์" }, "usePasskeysDesc": { - "message": "Ask to save new passkeys or log in with passkeys stored in your vault. Applies to all logged in accounts." + "message": "ถามเพื่อบันทึกพาสคีย์ใหม่หรือเข้าสู่ระบบด้วยพาสคีย์ที่เก็บในตู้นิรภัย (สำหรับทุกบัญชีที่เข้าสู่ระบบ)" }, "notificationChangeDesc": { - "message": "คุณต้องการอัปเดตรหัสผ่านนี้ใน Bitwarden หรือไม่?" + "message": "คุณต้องการอัปเดตรหัสผ่านนี้ใน Bitwarden หรือไม่" }, "notificationChangeSave": { - "message": "Yes, Update Now" + "message": "อัปเดต" }, "notificationUnlockDesc": { - "message": "Unlock your Bitwarden vault to complete the autofill request." + "message": "ปลดล็อกตู้นิรภัย Bitwarden เพื่อดำเนินการตามคำขอกรอกข้อมูลอัตโนมัติ" }, "notificationUnlock": { - "message": "Unlock" + "message": "ปลดล็อก" }, "additionalOptions": { - "message": "Additional options" + "message": "ตัวเลือกเพิ่มเติม" }, "enableContextMenuItem": { "message": "แสดงตัวเลือกเมนูบริบท" }, "contextMenuItemDesc": { - "message": "ใช้การคลิกสำรองเพื่อเข้าถึงการสร้างรหัสผ่านและการเข้าสู่ระบบที่ตรงกันสำหรับเว็บไซต์ " + "message": "ใช้การคลิกขวาเพื่อเข้าถึงการสร้างรหัสผ่านและข้อมูลเข้าสู่ระบบที่ตรงกันสำหรับเว็บไซต์" }, "contextMenuItemDescAlt": { - "message": "Use a secondary click to access password generation and matching logins for the website. Applies to all logged in accounts." + "message": "ใช้การคลิกขวาเพื่อเข้าถึงการสร้างรหัสผ่านและข้อมูลเข้าสู่ระบบที่ตรงกันสำหรับเว็บไซต์ (สำหรับทุกบัญชีที่เข้าสู่ระบบ)" }, "defaultUriMatchDetection": { - "message": "การตรวจจับการจับคู่ URI เริ่มต้น", + "message": "การตรวจสอบการจับคู่ URI เริ่มต้น", "description": "Default URI match detection for autofill." }, "defaultUriMatchDetectionDesc": { - "message": "เลือกวิธีเริ่มต้นในการจัดการการตรวจหาการจับคู่ URI สำหรับการเข้าสู่ระบบเมื่อดำเนินการต่างๆ เช่น การป้อนอัตโนมัติ" + "message": "เลือกวิธีเริ่มต้นในการจัดการการจับคู่ URI สำหรับข้อมูลเข้าสู่ระบบเมื่อดำเนินการ เช่น การป้อนอัตโนมัติ" }, "theme": { "message": "ธีม" }, "themeDesc": { - "message": "Change the application's color theme." + "message": "เปลี่ยนธีมสีของแอปพลิเคชัน" }, "themeDescAlt": { - "message": "Change the application's color theme. Applies to all logged in accounts." + "message": "เปลี่ยนธีมสีของแอปพลิเคชัน (สำหรับทุกบัญชีที่เข้าสู่ระบบ)" }, "dark": { "message": "มืด", @@ -1320,75 +1320,85 @@ "description": "Light color" }, "exportFrom": { - "message": "Export from" + "message": "ส่งออกจาก" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "ส่งออก", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "ส่งออก", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "นำเข้า", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "นำเข้า", + "description": "The verb form of the word Import" }, "fileFormat": { - "message": "File Format" + "message": "รูปแบบไฟล์" }, "fileEncryptedExportWarningDesc": { - "message": "This file export will be password protected and require the file password to decrypt." + "message": "ไฟล์ส่งออกนี้จะได้รับการป้องกันด้วยรหัสผ่าน และต้องใช้รหัสผ่านไฟล์เพื่อถอดรหัส" }, "filePassword": { - "message": "File password" + "message": "รหัสผ่านไฟล์" }, "exportPasswordDescription": { - "message": "This password will be used to export and import this file" + "message": "รหัสผ่านนี้จะถูกใช้เพื่อส่งออกและนำเข้าไฟล์นี้" }, "accountRestrictedOptionDescription": { - "message": "Use your account encryption key, derived from your account's username and Master Password, to encrypt the export and restrict import to only the current Bitwarden account." + "message": "ใช้กุญแจเข้ารหัสบัญชีของคุณ ซึ่งได้มาจากชื่อผู้ใช้และรหัสผ่านหลัก เพื่อเข้ารหัสไฟล์ส่งออก และจำกัดการนำเข้าเฉพาะบัญชี Bitwarden ปัจจุบันเท่านั้น" }, "passwordProtectedOptionDescription": { - "message": "Set a file password to encrypt the export and import it to any Bitwarden account using the password for decryption." + "message": "ตั้งรหัสผ่านไฟล์เพื่อเข้ารหัสไฟล์ส่งออก และสามารถนำเข้าสู่บัญชี Bitwarden ใดก็ได้โดยใช้รหัสผ่านเพื่อถอดรหัส" }, "exportTypeHeading": { - "message": "Export type" + "message": "ประเภทการส่งออก" }, "accountRestricted": { - "message": "Account restricted" + "message": "จำกัดเฉพาะบัญชี" }, "filePasswordAndConfirmFilePasswordDoNotMatch": { - "message": "“File password” and “Confirm file password“ do not match." + "message": "“รหัสผ่านไฟล์” และ “ยืนยันรหัสผ่านไฟล์” ไม่ตรงกัน" }, "warning": { "message": "คำเตือน", "description": "WARNING (should stay in capitalized letters if the language permits)" }, "warningCapitalized": { - "message": "Warning", + "message": "คำเตือน", "description": "Warning (should maintain locale-relevant capitalization)" }, "confirmVaultExport": { "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": "แชร์แล้ว" }, "bitwardenForBusinessPageDesc": { - "message": "Bitwarden for Business allows you to share your vault items with others by using an organization. Learn more on the bitwarden.com website." + "message": "Bitwarden สำหรับธุรกิจช่วยให้คุณแชร์รายการในตู้นิรภัยกับผู้อื่นโดยใช้องค์กร เรียนรู้เพิ่มเติมที่เว็บไซต์ bitwarden.com" }, "moveToOrganization": { - "message": "ย้ายไปยังแบบองค์กร" + "message": "ย้ายไปที่องค์กร" }, "movedItemToOrg": { - "message": "ย้าย $ITEMNAME$ ไปยัง $ORGNAME$ แล้ว", + "message": "ย้าย $ITEMNAME$ ไปที่ $ORGNAME$ แล้ว", "placeholders": { "itemname": { "content": "$1", @@ -1401,40 +1411,40 @@ } }, "moveToOrgDesc": { - "message": "เลือกองค์กรที่คุณต้องการย้ายรายการนี้ไป การย้ายไปยังองค์กรจะโอนความเป็นเจ้าของรายการไปยังองค์กรนั้น คุณจะไม่ได้เป็นเจ้าของโดยตรงของรายการนี้อีกต่อไปเมื่อมีการย้ายแล้ว" + "message": "เลือกองค์กรที่คุณต้องการย้ายรายการนี้ไป การย้ายไปที่องค์กรจะโอนกรรมสิทธิ์ของรายการนั้นไปยังองค์กร คุณจะไม่เป็นเจ้าของโดยตรงของรายการนี้อีกต่อไปหลังจากย้ายแล้ว" }, "learnMore": { "message": "เรียนรู้เพิ่มเติม" }, "migrationsFailed": { - "message": "An error occurred updating the encryption settings." + "message": "เกิดข้อผิดพลาดในการอัปเดตการตั้งค่าการเข้ารหัส" }, "updateEncryptionSettingsTitle": { - "message": "Update your encryption settings" + "message": "อัปเดตการตั้งค่าการเข้ารหัส" }, "updateEncryptionSettingsDesc": { - "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + "message": "การตั้งค่าการเข้ารหัสใหม่ที่แนะนำจะช่วยปรับปรุงความปลอดภัยของบัญชี ป้อนรหัสผ่านหลักเพื่ออัปเดตทันที" }, "confirmIdentityToContinue": { - "message": "Confirm your identity to continue" + "message": "ยืนยันตัวตนเพื่อดำเนินการต่อ" }, "enterYourMasterPassword": { - "message": "Enter your master password" + "message": "ป้อนรหัสผ่านหลัก" }, "updateSettings": { - "message": "Update settings" + "message": "อัปเดตการตั้งค่า" }, "later": { - "message": "Later" + "message": "ไว้ทีหลัง" }, "authenticatorKeyTotp": { - "message": "Authenticator Key (TOTP)" + "message": "คีย์ยืนยันตัวตน (TOTP)" }, "verificationCodeTotp": { - "message": "Verification Code (TOTP)" + "message": "รหัสยืนยัน (TOTP)" }, "copyVerificationCode": { - "message": "Copy Verification Code" + "message": "คัดลอกรหัสยืนยัน" }, "attachments": { "message": "ไฟล์แนบ" @@ -1443,7 +1453,7 @@ "message": "ลบไฟล์แนบ" }, "deleteAttachmentConfirmation": { - "message": "คุณต้องการลบไฟล์แนบนี้ใช่หรือไม่?" + "message": "ยืนยันที่จะลบไฟล์แนบนี้หรือไม่" }, "deletedAttachment": { "message": "ลบไฟล์แนบแล้ว" @@ -1458,58 +1468,58 @@ "message": "บันทึกไฟล์แนบแล้ว" }, "fixEncryption": { - "message": "Fix encryption" + "message": "ซ่อมแซมการเข้ารหัส" }, "fixEncryptionTooltip": { - "message": "This file is using an outdated encryption method." + "message": "ไฟล์นี้ใช้วิธีการเข้ารหัสที่ล้าสมัย" }, "attachmentUpdated": { - "message": "Attachment updated" + "message": "อัปเดตไฟล์แนบแล้ว" }, "file": { "message": "ไฟล์" }, "fileToShare": { - "message": "File to share" + "message": "ไฟล์ที่จะแชร์" }, "selectFile": { "message": "เลือกไฟล์" }, "itemsTransferred": { - "message": "Items transferred" + "message": "รายการที่โอนย้าย" }, "maxFileSize": { - "message": "ขนาดไฟล์สูงสุด คือ 500 MB" + "message": "ขนาดไฟล์สูงสุดคือ 500 MB" }, "featureUnavailable": { - "message": "Feature Unavailable" + "message": "ฟีเจอร์ไม่พร้อมใช้งาน" }, "legacyEncryptionUnsupported": { - "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + "message": "ไม่รองรับการเข้ารหัสแบบเก่าอีกต่อไป โปรดติดต่อฝ่ายสนับสนุนเพื่อกู้คืนบัญชีของคุณ" }, "premiumMembership": { - "message": "Premium Membership" + "message": "สมาชิกพรีเมียม" }, "premiumManage": { - "message": "Manage Membership" + "message": "จัดการการเป็นสมาชิก" }, "premiumManageAlert": { - "message": "คุณสามารถจัดการการเป็นสมาชิกของคุณได้ที่ bitwarden.com web vault คุณต้องการเข้าชมเว็บไซต์ตอนนี้หรือไม่?" + "message": "คุณสามารถจัดการการเป็นสมาชิกได้ที่เว็บตู้นิรภัย bitwarden.com ต้องการไปที่เว็บไซต์ตอนนี้หรือไม่" }, "premiumRefresh": { - "message": "Refresh Membership" + "message": "รีเฟรชสถานะสมาชิก" }, "premiumNotCurrentMember": { - "message": "คุณยังไม่ได้เป็นสมาชิกพรีเมียม" + "message": "ปัจจุบันคุณไม่ได้เป็นสมาชิกพรีเมียม" }, "premiumSignUpAndGet": { - "message": "สมัครสมาชิกพรีเมี่ยมและรับ:" + "message": "สมัครสมาชิกพรีเมียมแล้วรับ:" }, "ppremiumSignUpStorage": { - "message": "1 GB of encrypted file storage." + "message": "พื้นที่จัดเก็บไฟล์แนบเข้ารหัสขนาด 1 GB" }, "premiumSignUpStorageV2": { - "message": "$SIZE$ encrypted storage for file attachments.", + "message": "พื้นที่จัดเก็บไฟล์แนบเข้ารหัสขนาด $SIZE$", "placeholders": { "size": { "content": "$1", @@ -1518,40 +1528,40 @@ } }, "premiumSignUpEmergency": { - "message": "Emergency access." + "message": "การเข้าถึงฉุกเฉิน" }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "ตัวเลือกการเข้าสู่ระบบ 2 ขั้นตอนแบบพิเศษ เช่น YubiKey และ Duo" }, "ppremiumSignUpReports": { - "message": "สุขอนามัยของรหัสผ่าน ความสมบูรณ์ของบัญชี และรายงานการละเมิดข้อมูลเพื่อให้ตู้นิรภัยของคุณปลอดภัย" + "message": "รายงานความปลอดภัยของรหัสผ่าน สุขภาพบัญชี และข้อมูลรั่วไหล เพื่อรักษาตู้นิรภัยให้ปลอดภัย" }, "ppremiumSignUpTotp": { - "message": "ตัวสร้างรหัสยืนยัน TOTP (2FA) สำหรับการเข้าสู่ระบบในตู้นิรภัยของคุณ" + "message": "ตัวสร้างรหัสยืนยัน TOTP (2FA) สำหรับข้อมูลเข้าสู่ระบบในตู้นิรภัย" }, "ppremiumSignUpSupport": { - "message": "Priority customer support." + "message": "บริการลูกค้าสัมพันธ์ระดับพรีเมียม" }, "ppremiumSignUpFuture": { - "message": "All future Premium features. More coming soon!" + "message": "ฟีเจอร์พรีเมียมในอนาคตทั้งหมด และอื่น ๆ ที่กำลังจะมาเร็ว ๆ นี้!" }, "premiumPurchase": { - "message": "Purchase Premium" + "message": "ซื้อพรีเมียม" }, "premiumPurchaseAlertV2": { - "message": "You can purchase Premium from your account settings on the Bitwarden web app." + "message": "คุณสามารถซื้อพรีเมียมได้จากการตั้งค่าบัญชีในเว็บแอป Bitwarden" }, "premiumCurrentMember": { - "message": "You are a Premium member!" + "message": "คุณเป็นสมาชิกพรีเมียมแล้ว!" }, "premiumCurrentMemberThanks": { - "message": "Thank you for supporting bitwarden." + "message": "ขอบคุณที่สนับสนุน Bitwarden" }, "premiumFeatures": { - "message": "Upgrade to Premium and receive:" + "message": "อัปเกรดเป็นพรีเมียมเพื่อรับ:" }, "premiumPrice": { - "message": "All for just $PRICE$ /year!", + "message": "ทั้งหมดนี้เพียง $PRICE$ /ปี!", "placeholders": { "price": { "content": "$1", @@ -1560,7 +1570,7 @@ } }, "premiumPriceV2": { - "message": "All for just $PRICE$ per year!", + "message": "ทั้งหมดนี้เพียง $PRICE$ ต่อปี!", "placeholders": { "price": { "content": "$1", @@ -1569,25 +1579,25 @@ } }, "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 autofill the login." + "message": "หากข้อมูลเข้าสู่ระบบมีคีย์ยืนยันตัวตน ให้คัดลอกรหัสยืนยัน TOTP ไปยังคลิปบอร์ดเมื่อคุณป้อนข้อมูลเข้าสู่ระบบอัตโนมัติ" }, "enableAutoBiometricsPrompt": { - "message": "Ask for biometrics on launch" + "message": "ถามหาไบโอเมตริกเมื่อเปิดแอป" }, "authenticationTimeout": { - "message": "Authentication timeout" + "message": "การยืนยันตัวตนหมดเวลา" }, "authenticationSessionTimedOut": { - "message": "The authentication session timed out. Please restart the login process." + "message": "เซสชันการยืนยันตัวตนหมดเวลา โปรดเริ่มกระบวนการเข้าสู่ระบบใหม่" }, "verificationCodeEmailSent": { - "message": "ส่งโค้ดยืนยันไปยังอีเมล $EMAIL$ แล้ว", + "message": "ส่งอีเมลยืนยันไปที่ $EMAIL$ แล้ว", "placeholders": { "email": { "content": "$1", @@ -1596,148 +1606,148 @@ } }, "dontAskAgainOnThisDeviceFor30Days": { - "message": "Don't ask again on this device for 30 days" + "message": "ไม่ต้องถามอีกบนอุปกรณ์นี้เป็นเวลา 30 วัน" }, "selectAnotherMethod": { - "message": "Select another method", + "message": "เลือกวิธีอื่น", "description": "Select another two-step login method" }, "useYourRecoveryCode": { - "message": "Use your recovery code" + "message": "ใช้รหัสกู้คืนของคุณ" }, "insertU2f": { - "message": "Insert your security key into your computer's USB port. If it has a button, touch it." + "message": "เสียบคีย์ความปลอดภัยเข้ากับพอร์ต USB ของคอมพิวเตอร์ หากมีปุ่มให้แตะที่ปุ่ม" }, "openInNewTab": { - "message": "Open in new tab" + "message": "เปิดในแท็บใหม่" }, "webAuthnAuthenticate": { - "message": "Authenticate WebAuthn" + "message": "ยืนยันตัวตน WebAuthn" }, "readSecurityKey": { - "message": "Read security key" + "message": "อ่านคีย์ความปลอดภัย" }, "readingPasskeyLoading": { - "message": "Reading passkey..." + "message": "กำลังอ่านพาสคีย์..." }, "passkeyAuthenticationFailed": { - "message": "Passkey authentication failed" + "message": "การยืนยันตัวตนด้วยพาสคีย์ล้มเหลว" }, "useADifferentLogInMethod": { - "message": "Use a different log in method" + "message": "ใช้วิธีเข้าสู่ระบบอื่น" }, "awaitingSecurityKeyInteraction": { - "message": "Awaiting security key interaction..." + "message": "กำลังรอการตอบสนองจากคีย์ความปลอดภัย..." }, "loginUnavailable": { - "message": "Login Unavailable" + "message": "ไม่สามารถเข้าสู่ระบบได้" }, "noTwoStepProviders": { - "message": "This account has two-step login enabled, however, none of the configured two-step providers are supported by this web browser." + "message": "บัญชีนี้ตั้งค่าการเข้าสู่ระบบ 2 ขั้นตอนไว้ แต่เว็บเบราว์เซอร์นี้ไม่รองรับผู้ให้บริการ 2 ขั้นตอนใด ๆ ที่กำหนดค่าไว้" }, "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": "ตัวเลือกการเข้าสู่ระบบ 2 ขั้นตอน" }, "selectTwoStepLoginMethod": { - "message": "Select two-step login method" + "message": "เลือกวิธีการเข้าสู่ระบบ 2 ขั้นตอน" }, "recoveryCodeTitle": { - "message": "Recovery Code" + "message": "รหัสกู้คืน" }, "authenticatorAppTitle": { - "message": "Authenticator App" + "message": "แอปยืนยันตัวตน" }, "authenticatorAppDescV2": { - "message": "Enter a code generated by an authenticator app like Bitwarden Authenticator.", + "message": "ป้อนรหัสที่สร้างโดยแอปยืนยันตัวตน เช่น Bitwarden Authenticator", "description": "'Bitwarden Authenticator' is a product name and should not be translated." }, "yubiKeyTitleV2": { - "message": "Yubico OTP Security Key" + "message": "คีย์ความปลอดภัย Yubico 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" }, "duoDescV2": { - "message": "Enter a code generated by Duo Security.", + "message": "ป้อนรหัสที่สร้างโดย Duo Security", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { - "message": "Verify with Duo Security for your organization using the Duo Mobile app, SMS, phone call, or U2F security key.", + "message": "ยืนยันตัวตนกับ Duo Security สำหรับองค์กรของคุณโดยใช้แอป Duo Mobile, SMS, โทรศัพท์ หรือคีย์ความปลอดภัย U2F", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "webAuthnTitle": { "message": "FIDO2 WebAuthn" }, "webAuthnDesc": { - "message": "ใช้กุญแจความปลอดภัยที่รองรับ WebAuthn ใดก็ได้เพื่อเข้าถึงบัญชีของคุณ" + "message": "ใช้คีย์ความปลอดภัยที่รองรับ WebAuthn เพื่อเข้าถึงบัญชีของคุณ" }, "emailTitle": { "message": "อีเมล" }, "emailDescV2": { - "message": "Enter a code sent to your email." + "message": "ป้อนรหัสที่ส่งไปยังอีเมลของคุณ" }, "selfHostedEnvironment": { - "message": "Self-hosted Environment" + "message": "สภาพแวดล้อมโฮสต์เอง" }, "selfHostedBaseUrlHint": { - "message": "Specify the base URL of your on-premises hosted Bitwarden installation. Example: https://bitwarden.company.com" + "message": "ระบุ URL เซิร์ฟเวอร์ของการติดตั้ง Bitwarden ที่คุณโฮสต์เอง ตัวอย่าง: https://bitwarden.company.com" }, "selfHostedCustomEnvHeader": { - "message": "For advanced configuration, you can specify the base URL of each service independently." + "message": "สำหรับการกำหนดค่าขั้นสูง คุณสามารถระบุ URL ของแต่ละบริการได้อย่างอิสระ" }, "selfHostedEnvFormInvalid": { - "message": "You must add either the base Server URL or at least one custom environment." + "message": "คุณต้องเพิ่ม URL เซิร์ฟเวอร์หลัก หรือสภาพแวดล้อมที่กำหนดเองอย่างน้อยหนึ่งรายการ" }, "selfHostedEnvMustUseHttps": { - "message": "URLs must use HTTPS." + "message": "URL ต้องใช้ HTTPS" }, "customEnvironment": { - "message": "Custom Environment" + "message": "สภาพแวดล้อมที่กำหนดเอง" }, "baseUrl": { - "message": "URL ของเซิร์ฟเวอร์" + "message": "URL เซิร์ฟเวอร์" }, "selfHostBaseUrl": { - "message": "Self-host server URL", + "message": "URL เซิร์ฟเวอร์โฮสต์เอง", "description": "Label for field requesting a self-hosted integration service URL" }, "apiUrl": { - "message": "API Server URL" + "message": "URL เซิร์ฟเวอร์ API" }, "webVaultUrl": { - "message": "Web Vault Server URL" + "message": "URL เซิร์ฟเวอร์เว็บตู้นิรภัย" }, "identityUrl": { - "message": "Identity Server URL" + "message": "URL เซิร์ฟเวอร์ข้อมูลระบุตัวตน" }, "notificationsUrl": { - "message": "Notifications Server URL" + "message": "URL เซิร์ฟเวอร์การแจ้งเตือน" }, "iconsUrl": { - "message": "Icons Server URL" + "message": "URL เซิร์ฟเวอร์ไอคอน" }, "environmentSaved": { - "message": "Environment URLs saved" + "message": "บันทึก URL สภาพแวดล้อมแล้ว" }, "showAutoFillMenuOnFormFields": { - "message": "Show autofill menu on form fields", + "message": "แสดงเมนูป้อนอัตโนมัติบนช่องกรอกข้อมูล", "description": "Represents the message for allowing the user to enable the autofill overlay" }, "autofillSuggestionsSectionTitle": { - "message": "คำแนะนำการกรอกข้อมูลอัตโนมัติ" + "message": "คำแนะนำการป้อนอัตโนมัติ" }, "autofillSpotlightTitle": { - "message": "Easily find autofill suggestions" + "message": "ค้นหาคำแนะนำการป้อนอัตโนมัติได้ง่ายขึ้น" }, "autofillSpotlightDesc": { - "message": "Turn off your browser's autofill settings, so they don't conflict with Bitwarden." + "message": "ปิดการตั้งค่าป้อนอัตโนมัติของเบราว์เซอร์ เพื่อไม่ให้ขัดแย้งกับ Bitwarden" }, "turnOffBrowserAutofill": { - "message": "Turn off $BROWSER$ autofill", + "message": "ปิดการป้อนอัตโนมัติของ $BROWSER$", "placeholders": { "browser": { "content": "$1", @@ -1746,162 +1756,162 @@ } }, "turnOffAutofill": { - "message": "Turn off autofill" + "message": "ปิดการป้อนอัตโนมัติ" }, "confirmAutofill": { - "message": "Confirm autofill" + "message": "ยืนยันการป้อนอัตโนมัติ" }, "confirmAutofillDesc": { - "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + "message": "เว็บไซต์นี้ไม่ตรงกับรายละเอียดการเข้าสู่ระบบที่คุณบันทึกไว้ ก่อนป้อนข้อมูล โปรดตรวจสอบให้แน่ใจว่าเป็นเว็บไซต์ที่เชื่อถือได้" }, "showInlineMenuLabel": { - "message": "Show autofill suggestions on form fields" + "message": "แสดงคำแนะนำการป้อนอัตโนมัติบนช่องกรอกข้อมูล" }, "howDoesBitwardenProtectFromPhishing": { - "message": "How does Bitwarden protect your data from phishing?" + "message": "Bitwarden ปกป้องข้อมูลของคุณจากการฟิชชิงได้อย่างไร" }, "currentWebsite": { - "message": "Current website" + "message": "เว็บไซต์ปัจจุบัน" }, "autofillAndAddWebsite": { - "message": "Autofill and add this website" + "message": "ป้อนอัตโนมัติและเพิ่มเว็บไซต์นี้" }, "autofillWithoutAdding": { - "message": "Autofill without adding" + "message": "ป้อนอัตโนมัติโดยไม่เพิ่ม" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "ไม่ต้องป้อนอัตโนมัติ" }, "showInlineMenuIdentitiesLabel": { - "message": "Display identities as suggestions" + "message": "แสดงข้อมูลระบุตัวตนเป็นคำแนะนำ" }, "showInlineMenuCardsLabel": { - "message": "Display cards as suggestions" + "message": "แสดงบัตรเป็นคำแนะนำ" }, "showInlineMenuOnIconSelectionLabel": { - "message": "Display suggestions when icon is selected" + "message": "แสดงคำแนะนำเมื่อเลือกไอคอน" }, "showInlineMenuOnFormFieldsDescAlt": { - "message": "Applies to all logged in accounts." + "message": "สำหรับทุกบัญชีที่เข้าสู่ระบบ" }, "turnOffBrowserBuiltInPasswordManagerSettings": { - "message": "Turn off your browser's built in password manager settings to avoid conflicts." + "message": "ปิดการตั้งค่าตัวจัดการรหัสผ่านในตัวของเบราว์เซอร์เพื่อหลีกเลี่ยงความขัดแย้ง" }, "turnOffBrowserBuiltInPasswordManagerSettingsLink": { - "message": "Edit browser settings." + "message": "แก้ไขการตั้งค่าเบราว์เซอร์" }, "autofillOverlayVisibilityOff": { - "message": "Off", + "message": "ปิด", "description": "Overlay setting select option for disabling autofill overlay" }, "autofillOverlayVisibilityOnFieldFocus": { - "message": "When field is selected (on focus)", + "message": "เมื่อเลือกช่อง (โฟกัส)", "description": "Overlay appearance select option for showing the field on focus of the input element" }, "autofillOverlayVisibilityOnButtonClick": { - "message": "When autofill icon is selected", + "message": "เมื่อเลือกไอคอนป้อนอัตโนมัติ", "description": "Overlay appearance select option for showing the field on click of the overlay icon" }, "enableAutoFillOnPageLoadSectionTitle": { - "message": "Autofill on page load" + "message": "ป้อนอัตโนมัติเมื่อโหลดหน้าเว็บ" }, "enableAutoFillOnPageLoad": { - "message": "Enable Auto-fill On Page Load." + "message": "ป้อนอัตโนมัติเมื่อโหลดหน้าเว็บ" }, "enableAutoFillOnPageLoadDesc": { - "message": "If a login form is detected, autofill when the web page loads." + "message": "หากตรวจพบแบบฟอร์มเข้าสู่ระบบ ให้ป้อนข้อมูลอัตโนมัติเมื่อหน้าเว็บโหลดเสร็จ" }, "experimentalFeature": { - "message": "Compromised or untrusted websites can exploit autofill on page load." + "message": "เว็บไซต์ที่ไม่น่าเชื่อถือหรือถูกแฮ็กอาจใช้ประโยชน์จากการป้อนอัตโนมัติเมื่อโหลดหน้าเว็บได้" }, "learnMoreAboutAutofillOnPageLoadLinkText": { - "message": "Learn more about risks" + "message": "เรียนรู้เพิ่มเติมเกี่ยวกับความเสี่ยง" }, "learnMoreAboutAutofill": { - "message": "Learn more about autofill" + "message": "เรียนรู้เพิ่มเติมเกี่ยวกับการป้อนอัตโนมัติ" }, "defaultAutoFillOnPageLoad": { - "message": "Default autofill setting for login items" + "message": "การตั้งค่าเริ่มต้นสำหรับการป้อนข้อมูลเข้าสู่ระบบอัตโนมัติ" }, "defaultAutoFillOnPageLoadDesc": { - "message": "You can turn off autofill on page load for individual login items from the item's Edit view." + "message": "คุณสามารถปิดการป้อนอัตโนมัติเมื่อโหลดหน้าเว็บสำหรับแต่ละรายการได้จากหน้าแก้ไขรายการ" }, "autoFillOnPageLoadUseDefault": { - "message": "Use default setting" + "message": "ใช้การตั้งค่าเริ่มต้น" }, "autoFillOnPageLoadYes": { - "message": "Autofill on page load" + "message": "ป้อนอัตโนมัติเมื่อโหลดหน้าเว็บ" }, "autoFillOnPageLoadNo": { - "message": "Do not autofill on page load" + "message": "ไม่ป้อนอัตโนมัติเมื่อโหลดหน้าเว็บ" }, "commandOpenPopup": { - "message": "Open vault popup" + "message": "เปิดป๊อปอัปตู้นิรภัย" }, "commandOpenSidebar": { - "message": "Open vault in sidebar" + "message": "เปิดตู้นิรภัยในแถบด้านข้าง" }, "commandAutofillLoginDesc": { - "message": "Autofill the last used login for the current website" + "message": "ป้อนข้อมูลเข้าสู่ระบบที่ใช้ล่าสุดสำหรับเว็บไซต์ปัจจุบันโดยอัตโนมัติ" }, "commandAutofillCardDesc": { - "message": "Autofill the last used card for the current website" + "message": "ป้อนข้อมูลบัตรที่ใช้ล่าสุดสำหรับเว็บไซต์ปัจจุบันโดยอัตโนมัติ" }, "commandAutofillIdentityDesc": { - "message": "Autofill the last used identity for the current website" + "message": "ป้อนข้อมูลระบุตัวตนที่ใช้ล่าสุดสำหรับเว็บไซต์ปัจจุบันโดยอัตโนมัติ" }, "commandGeneratePasswordDesc": { - "message": "Generate and copy a new random password to the clipboard." + "message": "สร้างและคัดลอกรหัสผ่านแบบสุ่มใหม่ไปยังคลิปบอร์ด" }, "commandLockVaultDesc": { - "message": "ล็อกตู้เซฟ" + "message": "ล็อกตู้นิรภัย" }, "customFields": { - "message": "Custom Fields" + "message": "ฟิลด์ที่กำหนดเอง" }, "copyValue": { - "message": "Copy Value" + "message": "คัดลอกค่า" }, "value": { "message": "ค่า" }, "newCustomField": { - "message": "New Custom Field" + "message": "ฟิลด์ที่กำหนดเองใหม่" }, "dragToSort": { - "message": "Drag to sort" + "message": "ลากเพื่อเรียงลำดับ" }, "dragToReorder": { - "message": "Drag to reorder" + "message": "ลากเพื่อจัดลำดับใหม่" }, "cfTypeText": { "message": "ข้อความ" }, "cfTypeHidden": { - "message": "Hidden" + "message": "ซ่อน" }, "cfTypeBoolean": { - "message": "Boolean" + "message": "บูลีน" }, "cfTypeCheckbox": { - "message": "Checkbox" + "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": "การคลิกนอกหน้าต่างป๊อปอัปเพื่อตรวจสอบรหัสยืนยันในอีเมลจะทำให้ป๊อปอัปปิดลง คุณต้องการเปิดป๊อปอัปนี้ในหน้าต่างใหม่เพื่อไม่ให้ปิดหรือไม่" }, "showIconsChangePasswordUrls": { - "message": "Show website icons and retrieve change password URLs" + "message": "แสดงไอคอนเว็บไซต์และดึง URL เปลี่ยนรหัสผ่าน" }, "cardholderName": { - "message": "Cardholder Name" + "message": "ชื่อผู้ถือบัตร" }, "number": { "message": "หมายเลข" @@ -1910,13 +1920,13 @@ "message": "แบรนด์" }, "expirationMonth": { - "message": "Expiration Month" + "message": "เดือนที่หมดอายุ" }, "expirationYear": { - "message": "Expiration Year" + "message": "ปีที่หมดอายุ" }, "monthly": { - "message": "month" + "message": "เดือน" }, "expiration": { "message": "วันหมดอายุ" @@ -1958,13 +1968,13 @@ "message": "ธันวาคม" }, "securityCode": { - "message": "Security Code" + "message": "รหัสความปลอดภัย" }, "cardNumber": { - "message": "card number" + "message": "หมายเลขบัตร" }, "ex": { - "message": "ex." + "message": "ตัวอย่าง" }, "title": { "message": "คำนำหน้า" @@ -1982,22 +1992,22 @@ "message": "ดร." }, "mx": { - "message": "Mx" + "message": "คุณ" }, "firstName": { - "message": "First Name" + "message": "ชื่อจริง" }, "middleName": { - "message": "Middle Name" + "message": "ชื่อกลาง" }, "lastName": { - "message": "Last Name" + "message": "นามสกุล" }, "fullName": { "message": "ชื่อเต็ม" }, "identityName": { - "message": "Identity Name" + "message": "ชื่อข้อมูลระบุตัวตน" }, "company": { "message": "บริษัท" @@ -2009,7 +2019,7 @@ "message": "หมายเลขหนังสือเดินทาง" }, "licenseNumber": { - "message": "หมายเลขใบอนุญาต" + "message": "หมายเลขใบขับขี่" }, "email": { "message": "อีเมล" @@ -2030,7 +2040,7 @@ "message": "ที่อยู่ 3" }, "cityTown": { - "message": "เมือง" + "message": "เมือง / ตำบล" }, "stateProvince": { "message": "รัฐ / จังหวัด" @@ -2042,116 +2052,116 @@ "message": "ประเทศ" }, "type": { - "message": "ชนิด" + "message": "ประเภท" }, "typeLogin": { - "message": "ล็อกอิน" + "message": "ข้อมูลเข้าสู่ระบบ" }, "typeLogins": { - "message": "ล็อกอิน" + "message": "ข้อมูลเข้าสู่ระบบ" }, "typeSecureNote": { - "message": "Secure Note" + "message": "โน้ตความปลอดภัย" }, "typeCard": { - "message": "บัตรเครดิต" + "message": "บัตร" }, "typeIdentity": { "message": "ข้อมูลระบุตัวตน" }, "typeSshKey": { - "message": "SSH key" + "message": "คีย์ SSH" }, "typeNote": { - "message": "Note" + "message": "โน้ต" }, "newItemHeaderLogin": { - "message": "New Login", + "message": "ข้อมูลเข้าสู่ระบบใหม่", "description": "Header for new login item type" }, "newItemHeaderCard": { - "message": "New Card", + "message": "บัตรใหม่", "description": "Header for new card item type" }, "newItemHeaderIdentity": { - "message": "New Identity", + "message": "ข้อมูลระบุตัวตนใหม่", "description": "Header for new identity item type" }, "newItemHeaderNote": { - "message": "New Note", + "message": "โน้ตใหม่", "description": "Header for new note item type" }, "newItemHeaderSshKey": { - "message": "New SSH key", + "message": "คีย์ SSH ใหม่", "description": "Header for new SSH key item type" }, "newItemHeaderTextSend": { - "message": "New Text Send", + "message": "ข้อความ Send ใหม่", "description": "Header for new text send" }, "newItemHeaderFileSend": { - "message": "New File Send", + "message": "ไฟล์ Send ใหม่", "description": "Header for new file send" }, "editItemHeaderLogin": { - "message": "Edit Login", + "message": "แก้ไขข้อมูลเข้าสู่ระบบ", "description": "Header for edit login item type" }, "editItemHeaderCard": { - "message": "Edit Card", + "message": "แก้ไขบัตร", "description": "Header for edit card item type" }, "editItemHeaderIdentity": { - "message": "Edit Identity", + "message": "แก้ไขข้อมูลระบุตัวตน", "description": "Header for edit identity item type" }, "editItemHeaderNote": { - "message": "Edit Note", + "message": "แก้ไขโน้ต", "description": "Header for edit note item type" }, "editItemHeaderSshKey": { - "message": "Edit SSH key", + "message": "แก้ไขคีย์ SSH", "description": "Header for edit SSH key item type" }, "editItemHeaderTextSend": { - "message": "Edit Text Send", + "message": "แก้ไขข้อความ Send", "description": "Header for edit text send" }, "editItemHeaderFileSend": { - "message": "Edit File Send", + "message": "แก้ไขไฟล์ Send", "description": "Header for edit file send" }, "viewItemHeaderLogin": { - "message": "View Login", + "message": "ดูข้อมูลเข้าสู่ระบบ", "description": "Header for view login item type" }, "viewItemHeaderCard": { - "message": "View Card", + "message": "ดูบัตร", "description": "Header for view card item type" }, "viewItemHeaderIdentity": { - "message": "View Identity", + "message": "ดูข้อมูลระบุตัวตน", "description": "Header for view identity item type" }, "viewItemHeaderNote": { - "message": "View Note", + "message": "ดูโน้ต", "description": "Header for view note item type" }, "viewItemHeaderSshKey": { - "message": "View SSH key", + "message": "ดูคีย์ SSH", "description": "Header for view SSH key item type" }, "passwordHistory": { - "message": "ประวัติของรหัสผ่าน" + "message": "ประวัติรหัสผ่าน" }, "generatorHistory": { - "message": "Generator history" + "message": "ประวัติตัวสร้างรหัสผ่าน" }, "clearGeneratorHistoryTitle": { - "message": "Clear generator history" + "message": "ล้างประวัติตัวสร้างรหัสผ่าน" }, "cleargGeneratorHistoryDescription": { - "message": "If you continue, all entries will be permanently deleted from generator's history. Are you sure you want to continue?" + "message": "หากดำเนินการต่อ รายการทั้งหมดจะถูกลบออกจากประวัติของตัวสร้างอย่างถาวร ยืนยันที่จะดำเนินการต่อหรือไม่" }, "back": { "message": "ย้อนกลับ" @@ -2160,7 +2170,7 @@ "message": "คอลเลกชัน" }, "nCollections": { - "message": "$COUNT$ collections", + "message": "$COUNT$ คอลเลกชัน", "placeholders": { "count": { "content": "$1", @@ -2172,7 +2182,7 @@ "message": "รายการโปรด" }, "popOutNewWindow": { - "message": "เปิดหน้าต่างใหม่" + "message": "แยกหน้าต่างใหม่" }, "refresh": { "message": "รีเฟรช" @@ -2184,23 +2194,23 @@ "message": "ข้อมูลระบุตัวตน" }, "logins": { - "message": "เข้าสู่ระบบ" + "message": "ข้อมูลเข้าสู่ระบบ" }, "secureNotes": { - "message": "Secure Notes" + "message": "โน้ตความปลอดภัย" }, "sshKeys": { - "message": "SSH Keys" + "message": "คีย์ SSH" }, "clear": { - "message": "ลบทิ้ง", + "message": "ล้าง", "description": "To clear something out. example: To clear browser history." }, "checkPassword": { - "message": "ตรวจสอบว่ารหัสผ่านถูกเปิดเผยหรือไม่" + "message": "ตรวจสอบว่ารหัสผ่านรั่วไหลหรือไม่" }, "passwordExposed": { - "message": "This password has been exposed $VALUE$ time(s) in data breaches. You should change it.", + "message": "รหัสผ่านนี้รั่วไหล $VALUE$ ครั้งในเหตุการณ์ข้อมูลรั่วไหล คุณควรเปลี่ยนรหัสผ่านนี้", "placeholders": { "value": { "content": "$1", @@ -2209,14 +2219,14 @@ } }, "passwordSafe": { - "message": "ไม่พบรหัสผ่านนี้ในการละเมิดข้อมูลที่มี ควรใช้อย่างปลอดภัย" + "message": "ไม่พบรหัสผ่านนี้ในเหตุการณ์ข้อมูลรั่วไหลที่รู้จัก ควรจะปลอดภัยต่อการใช้งาน" }, "baseDomain": { - "message": "โดเมนพื้นฐาน", + "message": "โดเมนฐาน", "description": "Domain name. Ex. website.com" }, "baseDomainOptionRecommended": { - "message": "Base domain (recommended)", + "message": "โดเมนฐาน (แนะนำ)", "description": "Domain name. Ex. website.com" }, "domainName": { @@ -2228,25 +2238,25 @@ "description": "A URL's host value. For example, the host of https://sub.domain.com:443 is 'sub.domain.com:443'." }, "exact": { - "message": "ถูกต้อง" + "message": "ตรงกันทุกตัวอักษร" }, "startsWith": { - "message": "เริ่มต้นด้วย" + "message": "ขึ้นต้นด้วย" }, "regEx": { - "message": "นิพจน์ทั่วไป", + "message": "Regular expression", "description": "A programming term, also known as 'RegEx'." }, "matchDetection": { - "message": "Match Detection", + "message": "การตรวจสอบการจับคู่", "description": "URI match detection for autofill." }, "defaultMatchDetection": { - "message": "การตรวจจับการจับคู่เริ่มต้น", + "message": "การตรวจสอบการจับคู่เริ่มต้น", "description": "Default URI match detection for autofill." }, "toggleOptions": { - "message": "Toggle Options" + "message": "สลับตัวเลือก" }, "toggleCurrentUris": { "message": "สลับ URI ปัจจุบัน", @@ -2261,7 +2271,7 @@ "description": "An entity of multiple related people (ex. a team or business organization)." }, "types": { - "message": "ชนิด" + "message": "ประเภท" }, "allItems": { "message": "รายการทั้งหมด" @@ -2270,64 +2280,64 @@ "message": "ไม่มีรหัสผ่านที่จะแสดง" }, "clearHistory": { - "message": "Clear history" + "message": "ล้างประวัติ" }, "nothingToShow": { - "message": "Nothing to show" + "message": "ไม่มีข้อมูลที่จะแสดง" }, "nothingGeneratedRecently": { - "message": "You haven't generated anything recently" + "message": "คุณไม่ได้สร้างรหัสผ่านใด ๆ ในช่วงนี้" }, "remove": { - "message": "ลบ" + "message": "เอาออก" }, "default": { "message": "ค่าเริ่มต้น" }, "dateUpdated": { - "message": "อัปเดตแล้ว", + "message": "อัปเดต", "description": "ex. Date this item was updated" }, "dateCreated": { - "message": "สร้างเมื่อ", + "message": "สร้าง", "description": "ex. Date this item was created" }, "datePasswordUpdated": { - "message": "อัปเดต Password แล้ว", + "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": "ไม่มีคอลเลกชันที่จะแสดง" }, "ownership": { - "message": "เจ้าของ" + "message": "ความเป็นเจ้าของ" }, "whoOwnsThisItem": { - "message": "ใครเป็นเจ้าของรายการนี้?" + "message": "ใครเป็นเจ้าของรายการนี้" }, "strong": { - "message": "แข็งแรง", + "message": "รัดกุม", "description": "ex. A strong password. Scale: Weak -> Good -> Strong" }, "good": { - "message": "ไม่เลว", + "message": "ดี", "description": "ex. A good password. Scale: Weak -> Good -> Strong" }, "weak": { - "message": "ง่ายเกินไป", + "message": "อ่อน", "description": "ex. A weak password. Scale: Weak -> Good -> Strong" }, "weakMasterPassword": { - "message": "Weak Master Password" + "message": "รหัสผ่านหลักไม่ปลอดภัย" }, "weakMasterPasswordDesc": { - "message": "รหัสผ่านหลักที่คุณเลือกนั้นไม่รัดกุม คุณควรใช้รหัสผ่านหลักที่รัดกุม (หรือวลีรหัสผ่าน) เพื่อปกป้องบัญชี Bitwarden ของคุณอย่างเหมาะสม คุณแน่ใจหรือไม่ว่าต้องการใช้รหัสผ่านหลักนี้" + "message": "รหัสผ่านหลักที่คุณเลือกไม่ปลอดภัย คุณควรใช้รหัสผ่านหลักที่รัดกุม (หรือวลีรหัสผ่าน) เพื่อปกป้องบัญชี Bitwarden ของคุณอย่างเหมาะสม ยืนยันที่จะใช้รหัสผ่านหลักนี้หรือไม่" }, "pin": { "message": "PIN", @@ -2337,43 +2347,43 @@ "message": "ปลดล็อกด้วย PIN" }, "setYourPinTitle": { - "message": "ตั้ง PIN" + "message": "ตั้งค่า PIN" }, "setYourPinButton": { - "message": "ตั้ง PIN" + "message": "ตั้งค่า PIN" }, "setYourPinCode": { - "message": "ตั้ง PIN เพื่อใช้ปลดล็อก Bitwarden ทั้งนี้ หากคุณล็อกเอาต์ออกจากแอปโดยสมบูรณ์จะเป็นการลบการตั้งค่า PIN ของคุณด้วย" + "message": "ตั้งรหัส PIN สำหรับปลดล็อก Bitwarden การตั้งค่า PIN จะถูกรีเซ็ตหากคุณออกจากระบบแอปพลิเคชันโดยสมบูรณ์" }, "setPinCode": { - "message": "ตั้ง PIN เพื่อใช้ปลดล็อก Bitwarden และหากคุณล็อกเอาต์ออกจากแอปโดยสมบูรณ์จะเป็นการลบการตั้งค่า PIN ของคุณ" + "message": "คุณสามารถใช้ PIN นี้เพื่อปลดล็อก Bitwarden PIN จะถูกรีเซ็ตหากคุณออกจากระบบแอปพลิเคชันโดยสมบูรณ์" }, "pinRequired": { - "message": "ต้องระบุ PIN" + "message": "จำเป็นต้องระบุรหัส PIN" }, "invalidPin": { - "message": "PIN ไม่ถูกต้อง" + "message": "รหัส PIN ไม่ถูกต้อง" }, "tooManyInvalidPinEntryAttemptsLoggingOut": { - "message": "Too many invalid PIN entry attempts. Logging out." + "message": "พยายามป้อน PIN ผิดหลายครั้งเกินไป กำลังออกจากระบบ" }, "unlockWithBiometrics": { "message": "ปลดล็อกด้วยไบโอเมตริก" }, "unlockWithMasterPassword": { - "message": "เข้าสู่ระบบด้วยรหัสผ่านหลัก" + "message": "ปลดล็อกด้วยรหัสผ่านหลัก" }, "awaitDesktop": { - "message": "Awaiting confirmation from desktop" + "message": "กำลังรอการยืนยันจากเดสก์ท็อป" }, "awaitDesktopDesc": { - "message": "โปรดยืนยันการใช้ไบโอเมตริกในแอปพลิเคชันเดสก์ท็อป Bitwarden เพื่อตั้งค่าไบโอเมตริกสำหรับเบราว์เซอร์" + "message": "โปรดยืนยันโดยใช้ไบโอเมตริกในแอปพลิเคชัน Bitwarden บนเดสก์ท็อปเพื่อตั้งค่าไบโอเมตริกสำหรับเบราว์เซอร์" }, "lockWithMasterPassOnRestart": { - "message": "ล็อคด้วยรหัสผ่านหลักเมื่อรีสตาร์ทเบราว์เซอร์" + "message": "ล็อกด้วยรหัสผ่านหลักเมื่อรีสตาร์ตเบราว์เซอร์" }, "lockWithMasterPassOnRestart1": { - "message": "กำหนดให้ป้อนรหัสผ่านหลักเมื่อรีสตาร์ทเบราว์เซอร์" + "message": "ต้องใช้รหัสผ่านหลักเมื่อรีสตาร์ตเบราว์เซอร์" }, "selectOneCollection": { "message": "คุณต้องเลือกอย่างน้อยหนึ่งคอลเลกชัน" @@ -2385,42 +2395,42 @@ "message": "โคลน" }, "passwordGenerator": { - "message": "Password generator" + "message": "ตัวสร้างรหัสผ่าน" }, "usernameGenerator": { - "message": "Username generator" + "message": "ตัวสร้างชื่อผู้ใช้" }, "useThisEmail": { - "message": "Use this email" + "message": "ใช้อีเมลนี้" }, "useThisPassword": { - "message": "Use this password" + "message": "ใช้รหัสผ่านนี้" }, "useThisPassphrase": { - "message": "Use this passphrase" + "message": "ใช้วลีรหัสผ่านนี้" }, "useThisUsername": { - "message": "Use this username" + "message": "ใช้ชื่อผู้ใช้นี้" }, "securePasswordGenerated": { - "message": "Secure password generated! Don't forget to also update your password on the website." + "message": "สร้างรหัสผ่านที่รัดกุมแล้ว! อย่าลืมอัปเดตรหัสผ่านบนเว็บไซต์ด้วย" }, "useGeneratorHelpTextPartOne": { - "message": "Use the generator", + "message": "ใช้ตัวสร้าง", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "useGeneratorHelpTextPartTwo": { - "message": "to create a strong unique password", + "message": "เพื่อสร้างรหัสผ่านที่รัดกุมและไม่ซ้ำกัน", "description": "This will be used as part of a larger sentence, broken up to include the generator icon. The full sentence will read 'Use the generator [GENERATOR_ICON] to create a strong unique password'" }, "vaultCustomization": { - "message": "Vault customization" + "message": "การปรับแต่งตู้นิรภัย" }, "vaultTimeoutAction": { - "message": "การดำเนินการหลังหมดเวลาล็อคตู้เซฟ" + "message": "การดำเนินการเมื่อตู้นิรภัยหมดเวลา" }, "vaultTimeoutAction1": { - "message": "Timeout action" + "message": "การดำเนินการเมื่อหมดเวลา" }, "lock": { "message": "ล็อก", @@ -2434,52 +2444,52 @@ "message": "ค้นหาในถังขยะ" }, "permanentlyDeleteItem": { - "message": "ลบรายการอย่างถาวร" + "message": "ลบรายการถาวร" }, "permanentlyDeleteItemConfirmation": { - "message": "คุณแน่ใจหรือไม่ว่าต้องการลบรายการนี้อย่างถาวร?" + "message": "ยืนยันที่จะลบรายการนี้ถาวรหรือไม่" }, "permanentlyDeletedItem": { - "message": "ลบรายการอย่างถาวรแล้ว" + "message": "ลบรายการถาวรแล้ว" }, "restoreItem": { "message": "กู้คืนรายการ" }, "restoredItem": { - "message": "คืนค่ารายการแล้ว" + "message": "กู้คืนรายการแล้ว" }, "alreadyHaveAccount": { - "message": "Already have an account?" + "message": "มีบัญชีอยู่แล้วใช่หรือไม่" }, "vaultTimeoutLogOutConfirmation": { - "message": "การออกจากระบบจะลบการเข้าถึงตู้นิรภัยของคุณทั้งหมด และต้องมีการตรวจสอบสิทธิ์ออนไลน์หลังจากหมดเวลา คุณแน่ใจหรือไม่ว่าต้องการใช้การตั้งค่านี้" + "message": "การออกจากระบบจะทำให้สิทธิ์เข้าถึงตู้นิรภัยทั้งหมดถูกยกเลิก และต้องยืนยันตัวตนออนไลน์ใหม่หลังจากหมดเวลา ยืนยันที่จะใช้การตั้งค่านี้หรือไม่" }, "vaultTimeoutLogOutConfirmationTitle": { - "message": "การยืนยันการดำเนินการหมดเวลา" + "message": "ยืนยันการดำเนินการเมื่อหมดเวลา" }, "autoFillAndSave": { - "message": "กรอกอัตโนมัติและบันทึก" + "message": "ป้อนอัตโนมัติและบันทึก" }, "fillAndSave": { - "message": "Fill and save" + "message": "ป้อนและบันทึก" }, "autoFillSuccessAndSavedUri": { - "message": "เติมรายการอัตโนมัติและบันทึก URI แล้ว" + "message": "ป้อนข้อมูลรายการอัตโนมัติและบันทึก URI แล้ว" }, "autoFillSuccess": { - "message": "รายการเติมอัตโนมัติ " + "message": "ป้อนข้อมูลรายการอัตโนมัติแล้ว" }, "insecurePageWarning": { - "message": "Warning: This is an unsecured HTTP page, and any information you submit can potentially be seen and changed by others. This Login was originally saved on a secure (HTTPS) page." + "message": "คำเตือน: หน้านี้เป็นหน้า HTTP ที่ไม่ปลอดภัย ข้อมูลที่คุณส่งอาจถูกดักจับหรือแก้ไขโดยผู้อื่นได้ ข้อมูลเข้าสู่ระบบนี้ถูกบันทึกไว้บนหน้าเว็บที่ปลอดภัย (HTTPS)" }, "insecurePageWarningFillPrompt": { - "message": "Do you still wish to fill this login?" + "message": "ยังต้องการป้อนข้อมูลเข้าสู่ระบบนี้หรือไม่" }, "autofillIframeWarning": { - "message": "The form is hosted by a different domain than the URI of your saved login. Choose OK to autofill anyway, or Cancel to stop." + "message": "แบบฟอร์มนี้โฮสต์โดยโดเมนที่ต่างจาก URI ของข้อมูลเข้าสู่ระบบที่คุณบันทึกไว้ เลือก ตกลง เพื่อป้อนข้อมูลอัตโนมัติ หรือ ยกเลิก เพื่อหยุด" }, "autofillIframeWarningTip": { - "message": "To prevent this warning in the future, save this URI, $HOSTNAME$, to your Bitwarden login item for this site.", + "message": "เพื่อป้องกันการแจ้งเตือนนี้ในอนาคต ให้บันทึก URI $HOSTNAME$ ลงในรายการข้อมูลเข้าสู่ระบบ Bitwarden สำหรับไซต์นี้", "placeholders": { "hostname": { "content": "$1", @@ -2488,22 +2498,22 @@ } }, "topLayerHijackWarning": { - "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + "message": "หน้านี้รบกวนการทำงานของ Bitwarden เมนูในบรรทัดของ Bitwarden ถูกปิดใช้งานชั่วคราวเพื่อความปลอดภัย" }, "setMasterPassword": { "message": "ตั้งรหัสผ่านหลัก" }, "currentMasterPass": { - "message": "Current master password" + "message": "รหัสผ่านหลักปัจจุบัน" }, "newMasterPass": { - "message": "New master password" + "message": "รหัสผ่านหลักใหม่" }, "confirmNewMasterPass": { - "message": "Confirm new master password" + "message": "ยืนยันรหัสผ่านหลักใหม่" }, "masterPasswordPolicyInEffect": { - "message": "นโยบายองค์กรอย่างน้อยหนึ่งนโยบายกำหนดให้รหัสผ่านหลักของคุณเป็นไปตามข้อกำหนดต่อไปนี้:" + "message": "นโยบายองค์กรอย่างน้อยหนึ่งรายการกำหนดให้รหัสผ่านหลักของคุณต้องเป็นไปตามข้อกำหนดดังนี้:" }, "policyInEffectMinComplexity": { "message": "คะแนนความซับซ้อนขั้นต่ำ $SCORE$", @@ -2515,7 +2525,7 @@ } }, "policyInEffectMinLength": { - "message": "ความยาวอย่างน้อย $LENGTH$ อักขระ", + "message": "ความยาวขั้นต่ำ $LENGTH$ ตัวอักษร", "placeholders": { "length": { "content": "$1", @@ -2524,16 +2534,16 @@ } }, "policyInEffectUppercase": { - "message": "มีตัวพิมพ์ใหญ่อย่างน้อย 1 ตัว" + "message": "มีตัวอักษรพิมพ์ใหญ่อย่างน้อยหนึ่งตัว" }, "policyInEffectLowercase": { - "message": "มีตัวพิมพ์เล็กอย่างน้อย 1 ตัว" + "message": "มีตัวอักษรพิมพ์เล็กอย่างน้อยหนึ่งตัว" }, "policyInEffectNumbers": { - "message": "มีตัวเลขอย่างน้อย 1 ตัว" + "message": "มีตัวเลขอย่างน้อยหนึ่งตัว" }, "policyInEffectSpecial": { - "message": "มีอักขระพิเศษต่อไปนี้อย่างน้อย 1 อักขระ: $CHARS$ ", + "message": "มีอักขระพิเศษต่อไปนี้อย่างน้อยหนึ่งตัว $CHARS$", "placeholders": { "chars": { "content": "$1", @@ -2542,186 +2552,186 @@ } }, "masterPasswordPolicyRequirementsNotMet": { - "message": "รหัสผ่านหลักใหม่ของคุณไม่เป็นไปตามข้อกำหนดของนโยบาย" + "message": "รหัสผ่านหลักใหม่ของคุณไม่ตรงตามข้อกำหนดของนโยบาย" }, "receiveMarketingEmailsV2": { - "message": "Get advice, announcements, and research opportunities from Bitwarden in your inbox." + "message": "รับคำแนะนำ ประกาศ และโอกาสในการทำวิจัยจาก Bitwarden ทางอีเมล" }, "unsubscribe": { - "message": "Unsubscribe" + "message": "ยกเลิกการรับข่าวสาร" }, "atAnyTime": { - "message": "at any time." + "message": "ได้ทุกเมื่อ" }, "byContinuingYouAgreeToThe": { - "message": "By continuing, you agree to the" + "message": "เมื่อดำเนินการต่อ ถือว่าคุณยอมรับ" }, "and": { - "message": "and" + "message": "และ" }, "acceptPolicies": { - "message": "By checking this box you agree to the following:" + "message": "การเลือกช่องนี้หมายความว่าคุณยอมรับสิ่งต่อไปนี้:" }, "acceptPoliciesRequired": { - "message": "Terms of Service and Privacy Policy have not been acknowledged." + "message": "ยังไม่ได้ยอมรับข้อกำหนดในการให้บริการและนโยบายความเป็นส่วนตัว" }, "termsOfService": { - "message": "Terms of Service" + "message": "ข้อกำหนดในการให้บริการ" }, "privacyPolicy": { - "message": "Privacy Policy" + "message": "นโยบายความเป็นส่วนตัว" }, "yourNewPasswordCannotBeTheSameAsYourCurrentPassword": { - "message": "Your new password cannot be the same as your current password." + "message": "รหัสผ่านใหม่ต้องไม่ซ้ำกับรหัสผ่านปัจจุบัน" }, "hintEqualsPassword": { - "message": "Your password hint cannot be the same as your password." + "message": "คำใบ้รหัสผ่านต้องไม่เหมือนกับรหัสผ่าน" }, "ok": { "message": "ตกลง" }, "errorRefreshingAccessToken": { - "message": "Access Token Refresh Error" + "message": "เกิดข้อผิดพลาดในการรีเฟรชโทเค็นการเข้าถึง" }, "errorRefreshingAccessTokenDesc": { - "message": "No refresh token or API keys found. Please try logging out and logging back in." + "message": "ไม่พบโทเค็นรีเฟรชหรือคีย์ API โปรดลองออกจากระบบแล้วเข้าสู่ระบบใหม่" }, "desktopSyncVerificationTitle": { - "message": "Desktop sync verification" + "message": "การยืนยันการซิงค์เดสก์ท็อป" }, "desktopIntegrationVerificationText": { - "message": "Please verify that the desktop application shows this fingerprint: " + "message": "โปรดยืนยันว่าแอปพลิเคชันเดสก์ท็อปแสดงลายนิ้วมือนี้: " }, "desktopIntegrationDisabledTitle": { - "message": "Browser integration is not set up" + "message": "ยังไม่ได้ตั้งค่าการรวมเบราว์เซอร์" }, "desktopIntegrationDisabledDesc": { - "message": "Browser integration is not set up in the Bitwarden desktop application. Please set it up in the settings within the desktop application." + "message": "ยังไม่ได้ตั้งค่าการรวมเบราว์เซอร์ในแอปพลิเคชัน Bitwarden บนเดสก์ท็อป โปรดตั้งค่าในการตั้งค่าภายในแอปพลิเคชันเดสก์ท็อป" }, "startDesktopTitle": { - "message": "Start the Bitwarden desktop application" + "message": "เริ่มแอปพลิเคชัน Bitwarden บนเดสก์ท็อป" }, "startDesktopDesc": { - "message": "The Bitwarden desktop application needs to be started before unlock with biometrics can be used." + "message": "จำเป็นต้องเปิดแอปพลิเคชัน Bitwarden บนเดสก์ท็อปก่อนจึงจะสามารถใช้การปลดล็อกด้วยไบโอเมตริกได้" }, "errorEnableBiometricTitle": { - "message": "Unable to set up biometrics" + "message": "ไม่สามารถตั้งค่าไบโอเมตริกได้" }, "errorEnableBiometricDesc": { - "message": "Action was canceled by the desktop application" + "message": "การดำเนินการถูกยกเลิกโดยแอปพลิเคชันเดสก์ท็อป" }, "nativeMessagingInvalidEncryptionDesc": { - "message": "Desktop application invalidated the secure communication channel. Please retry this operation" + "message": "แอปพลิเคชันเดสก์ท็อปทำให้ช่องทางการสื่อสารที่ปลอดภัยเป็นโมฆะ โปรดลองดำเนินการนี้อีกครั้ง" }, "nativeMessagingInvalidEncryptionTitle": { - "message": "Desktop communication interrupted" + "message": "การสื่อสารกับเดสก์ท็อปถูกขัดจังหวะ" }, "nativeMessagingWrongUserDesc": { - "message": "The desktop application is logged into a different account. Please ensure both applications are logged into the same account." + "message": "แอปพลิเคชันเดสก์ท็อปเข้าสู่ระบบด้วยบัญชีอื่น โปรดตรวจสอบให้แน่ใจว่าทั้งสองแอปพลิเคชันเข้าสู่ระบบด้วยบัญชีเดียวกัน" }, "nativeMessagingWrongUserTitle": { - "message": "Account missmatch" + "message": "บัญชีไม่ตรงกัน" }, "nativeMessagingWrongUserKeyTitle": { - "message": "Biometric key missmatch" + "message": "คีย์ไบโอเมตริกไม่ตรงกัน" }, "nativeMessagingWrongUserKeyDesc": { - "message": "Biometric unlock failed. The biometric secret key failed to unlock the vault. Please try to set up biometrics again." + "message": "การปลดล็อกด้วยไบโอเมตริกล้มเหลว คีย์ลับไบโอเมตริกไม่สามารถปลดล็อกตู้นิรภัยได้ โปรดลองตั้งค่าไบโอเมตริกใหม่อีกครั้ง" }, "biometricsNotEnabledTitle": { - "message": "Biometrics not set up" + "message": "ยังไม่ได้ตั้งค่าไบโอเมตริก" }, "biometricsNotEnabledDesc": { - "message": "Browser biometrics requires desktop biometric to be set up in the settings first." + "message": "ไบโอเมตริกสำหรับเบราว์เซอร์จำเป็นต้องตั้งค่าไบโอเมตริกในแอปเดสก์ท็อปก่อน" }, "biometricsNotSupportedTitle": { - "message": "Biometrics not supported" + "message": "ไม่รองรับไบโอเมตริก" }, "biometricsNotSupportedDesc": { - "message": "Browser biometrics is not supported on this device." + "message": "อุปกรณ์นี้ไม่รองรับไบโอเมตริกสำหรับเบราว์เซอร์" }, "biometricsNotUnlockedTitle": { - "message": "User locked or logged out" + "message": "ผู้ใช้ถูกล็อกหรือออกจากระบบ" }, "biometricsNotUnlockedDesc": { - "message": "Please unlock this user in the desktop application and try again." + "message": "โปรดปลดล็อกผู้ใช้นี้ในแอปพลิเคชันเดสก์ท็อปแล้วลองอีกครั้ง" }, "biometricsNotAvailableTitle": { - "message": "Biometric unlock unavailable" + "message": "การปลดล็อกด้วยไบโอเมตริกไม่พร้อมใช้งาน" }, "biometricsNotAvailableDesc": { - "message": "Biometric unlock is currently unavailable. Please try again later." + "message": "การปลดล็อกด้วยไบโอเมตริกไม่พร้อมใช้งานในขณะนี้ โปรดลองใหม่อีกครั้งในภายหลัง" }, "biometricsFailedTitle": { - "message": "Biometrics failed" + "message": "ไบโอเมตริกล้มเหลว" }, "biometricsFailedDesc": { - "message": "Biometrics cannot be completed, consider using a master password or logging out. If this persists, please contact Bitwarden support." + "message": "ไม่สามารถดำเนินการไบโอเมตริกให้เสร็จสิ้นได้ โปรดพิจารณาใช้รหัสผ่านหลักหรือออกจากระบบ หากปัญหายังคงอยู่ โปรดติดต่อฝ่ายสนับสนุน Bitwarden" }, "nativeMessaginPermissionErrorTitle": { - "message": "Permission not provided" + "message": "ไม่ได้รับอนุญาต" }, "nativeMessaginPermissionErrorDesc": { - "message": "Without permission to communicate with the Bitwarden Desktop Application we cannot provide biometrics in the browser extension. Please try again." + "message": "เราไม่สามารถให้บริการไบโอเมตริกในส่วนขยายเบราว์เซอร์ได้หากไม่ได้รับอนุญาตให้สื่อสารกับแอปพลิเคชัน Bitwarden บนเดสก์ท็อป โปรดลองอีกครั้ง" }, "nativeMessaginPermissionSidebarTitle": { - "message": "Permission request error" + "message": "ข้อผิดพลาดในการขอสิทธิ์" }, "nativeMessaginPermissionSidebarDesc": { - "message": "This action cannot be done in the sidebar, please retry the action in the popup or popout." + "message": "ไม่สามารถดำเนินการนี้ในแถบด้านข้างได้ โปรดลองดำเนินการอีกครั้งในป๊อปอัปหรือหน้าต่างแยก" }, "personalOwnershipSubmitError": { - "message": "Due to an Enterprise Policy, you are restricted from saving items to your personal vault. Change the Ownership option to an organization and choose from available collections." + "message": "เนื่องจากนโยบายองค์กร คุณถูกจำกัดไม่ให้บันทึกรายการลงในตู้นิรภัยส่วนตัว เปลี่ยนตัวเลือกความเป็นเจ้าของเป็นองค์กรและเลือกคอลเลกชันที่มีอยู่" }, "personalOwnershipPolicyInEffect": { - "message": "An organization policy is affecting your ownership options." + "message": "นโยบายองค์กรมีผลต่อตัวเลือกความเป็นเจ้าของของคุณ" }, "personalOwnershipPolicyInEffectImports": { - "message": "An organization policy has blocked importing items into your individual vault." + "message": "นโยบายองค์กรระงับการนำเข้ารายการไปยังตู้นิรภัยส่วนตัวของคุณ" }, "restrictCardTypeImport": { - "message": "Cannot import card item types" + "message": "ไม่สามารถนำเข้ารายการประเภทบัตรได้" }, "restrictCardTypeImportDesc": { - "message": "A policy set by 1 or more organizations prevents you from importing cards to your vaults." + "message": "นโยบายที่กำหนดโดยองค์กรอย่างน้อย 1 แห่งป้องกันไม่ให้คุณนำเข้าบัตรไปยังตู้นิรภัย" }, "domainsTitle": { - "message": "Domains", + "message": "โดเมน", "description": "A category title describing the concept of web domains" }, "blockedDomains": { - "message": "Blocked domains" + "message": "โดเมนที่ถูกบล็อก" }, "learnMoreAboutBlockedDomains": { - "message": "Learn more about blocked domains" + "message": "เรียนรู้เพิ่มเติมเกี่ยวกับโดเมนที่ถูกบล็อก" }, "excludedDomains": { - "message": "Excluded domains" + "message": "โดเมนที่ยกเว้น" }, "excludedDomainsDesc": { - "message": "Bitwarden will not ask to save login details for these domains. You must refresh the page for changes to take effect." + "message": "Bitwarden จะไม่ถามให้บันทึกรายละเอียดการเข้าสู่ระบบสำหรับโดเมนเหล่านี้ คุณต้องรีเฟรชหน้าเว็บเพื่อให้การเปลี่ยนแปลงมีผล" }, "excludedDomainsDescAlt": { - "message": "Bitwarden will not ask to save login details for these domains for all logged in accounts. You must refresh the page for changes to take effect." + "message": "Bitwarden จะไม่ถามให้บันทึกรายละเอียดการเข้าสู่ระบบสำหรับโดเมนเหล่านี้สำหรับทุกบัญชีที่เข้าสู่ระบบ คุณต้องรีเฟรชหน้าเว็บเพื่อให้การเปลี่ยนแปลงมีผล" }, "blockedDomainsDesc": { - "message": "Autofill and other related features will not be offered for these websites. You must refresh the page for changes to take effect." + "message": "การป้อนอัตโนมัติและฟีเจอร์อื่น ๆ ที่เกี่ยวข้องจะไม่พร้อมใช้งานสำหรับเว็บไซต์เหล่านี้ คุณต้องรีเฟรชหน้าเว็บเพื่อให้การเปลี่ยนแปลงมีผล" }, "autofillBlockedNoticeV2": { - "message": "Autofill is blocked for this website." + "message": "การป้อนอัตโนมัติถูกบล็อกสำหรับเว็บไซต์นี้" }, "autofillBlockedNoticeGuidance": { - "message": "Change this in settings" + "message": "เปลี่ยนค่านี้ในการตั้งค่า" }, "change": { - "message": "Change" + "message": "เปลี่ยน" }, "changePassword": { - "message": "Change password", + "message": "เปลี่ยนรหัสผ่าน", "description": "Change password button for browser at risk notification on login." }, "changeButtonTitle": { - "message": "Change password - $ITEMNAME$", + "message": "เปลี่ยนรหัสผ่าน - $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -2730,13 +2740,13 @@ } }, "atRiskPassword": { - "message": "At-risk password" + "message": "รหัสผ่านที่มีความเสี่ยง" }, "atRiskPasswords": { - "message": "At-risk passwords" + "message": "รหัสผ่านที่มีความเสี่ยง" }, "atRiskPasswordDescSingleOrg": { - "message": "$ORGANIZATION$ is requesting you change one password because it is at-risk.", + "message": "$ORGANIZATION$ ร้องขอให้คุณเปลี่ยนรหัสผ่าน 1 รายการเนื่องจากมีความเสี่ยง", "placeholders": { "organization": { "content": "$1", @@ -2745,7 +2755,7 @@ } }, "atRiskPasswordsDescSingleOrgPlural": { - "message": "$ORGANIZATION$ is requesting you change the $COUNT$ passwords because they are at-risk.", + "message": "$ORGANIZATION$ ร้องขอให้คุณเปลี่ยนรหัสผ่าน $COUNT$ รายการเนื่องจากมีความเสี่ยง", "placeholders": { "organization": { "content": "$1", @@ -2758,7 +2768,7 @@ } }, "atRiskPasswordsDescMultiOrgPlural": { - "message": "Your organizations are requesting you change the $COUNT$ passwords because they are at-risk.", + "message": "องค์กรของคุณร้องขอให้คุณเปลี่ยนรหัสผ่าน $COUNT$ รายการเนื่องจากมีความเสี่ยง", "placeholders": { "count": { "content": "$1", @@ -2767,7 +2777,7 @@ } }, "atRiskChangePrompt": { - "message": "Your password for this site is at-risk. $ORGANIZATION$ has requested that you change it.", + "message": "รหัสผ่านของคุณสำหรับเว็บไซต์นี้มีความเสี่ยง $ORGANIZATION$ ได้ร้องขอให้คุณเปลี่ยนรหัสผ่าน", "placeholders": { "organization": { "content": "$1", @@ -2777,7 +2787,7 @@ "description": "Notification body when a login triggers an at-risk password change request and the change password domain is known." }, "atRiskNavigatePrompt": { - "message": "$ORGANIZATION$ wants you to change this password because it is at-risk. Navigate to your account settings to change the password.", + "message": "$ORGANIZATION$ ต้องการให้คุณเปลี่ยนรหัสผ่านนี้เนื่องจากมีความเสี่ยง ไปที่การตั้งค่าบัญชีของคุณเพื่อเปลี่ยนรหัสผ่าน", "placeholders": { "organization": { "content": "$1", @@ -2787,10 +2797,10 @@ "description": "Notification body when a login triggers an at-risk password change request and no change password domain is provided." }, "reviewAndChangeAtRiskPassword": { - "message": "Review and change one at-risk password" + "message": "ตรวจสอบและเปลี่ยนรหัสผ่านที่มีความเสี่ยง 1 รายการ" }, "reviewAndChangeAtRiskPasswordsPlural": { - "message": "Review and change $COUNT$ at-risk passwords", + "message": "ตรวจสอบและเปลี่ยนรหัสผ่านที่มีความเสี่ยง $COUNT$ รายการ", "placeholders": { "count": { "content": "$1", @@ -2799,52 +2809,52 @@ } }, "changeAtRiskPasswordsFaster": { - "message": "Change at-risk passwords faster" + "message": "เปลี่ยนรหัสผ่านที่มีความเสี่ยงได้เร็วยิ่งขึ้น" }, "changeAtRiskPasswordsFasterDesc": { - "message": "Update your settings so you can quickly autofill your passwords and generate new ones" + "message": "อัปเดตการตั้งค่าของคุณเพื่อให้สามารถป้อนรหัสผ่านอัตโนมัติและสร้างรหัสผ่านใหม่ได้อย่างรวดเร็ว" }, "reviewAtRiskLogins": { - "message": "Review at-risk logins" + "message": "ตรวจสอบข้อมูลเข้าสู่ระบบที่มีความเสี่ยง" }, "reviewAtRiskPasswords": { - "message": "Review at-risk passwords" + "message": "ตรวจสอบรหัสผ่านที่มีความเสี่ยง" }, "reviewAtRiskLoginsSlideDesc": { - "message": "Your organization passwords are at-risk because they are weak, reused, and/or exposed.", + "message": "รหัสผ่านองค์กรของคุณมีความเสี่ยงเนื่องจากไม่ปลอดภัย ใช้ซ้ำ และ/หรือรั่วไหล", "description": "Description of the review at-risk login slide on the at-risk password page carousel" }, "reviewAtRiskLoginSlideImgAltPeriod": { - "message": "Illustration of a list of logins that are at-risk." + "message": "ภาพประกอบรายการข้อมูลเข้าสู่ระบบที่มีความเสี่ยง" }, "generatePasswordSlideDesc": { - "message": "Quickly generate a strong, unique password with the Bitwarden autofill menu on the at-risk site.", + "message": "สร้างรหัสผ่านที่รัดกุมและไม่ซ้ำกันอย่างรวดเร็วด้วยเมนูป้อนอัตโนมัติของ Bitwarden บนเว็บไซต์ที่มีความเสี่ยง", "description": "Description of the generate password slide on the at-risk password page carousel" }, "generatePasswordSlideImgAltPeriod": { - "message": "Illustration of the Bitwarden autofill menu displaying a generated password." + "message": "ภาพประกอบเมนูป้อนอัตโนมัติของ Bitwarden แสดงรหัสผ่านที่ถูกสร้าง" }, "updateInBitwarden": { - "message": "Update in Bitwarden" + "message": "อัปเดตใน Bitwarden" }, "updateInBitwardenSlideDesc": { - "message": "Bitwarden will then prompt you to update the password in the password manager.", + "message": "จากนั้น Bitwarden จะแจ้งให้คุณอัปเดตรหัสผ่านในตัวจัดการรหัสผ่าน", "description": "Description of the update in Bitwarden slide on the at-risk password page carousel" }, "updateInBitwardenSlideImgAltPeriod": { - "message": "Illustration of a Bitwarden’s notification prompting the user to update the login." + "message": "ภาพประกอบการแจ้งเตือนของ Bitwarden ที่ให้ผู้ใช้อัปเดตข้อมูลเข้าสู่ระบบ" }, "turnOnAutofill": { - "message": "Turn on autofill" + "message": "เปิดการป้อนอัตโนมัติ" }, "turnedOnAutofill": { - "message": "Turned on autofill" + "message": "เปิดการป้อนอัตโนมัติแล้ว" }, "dismiss": { - "message": "Dismiss" + "message": "ปิด" }, "websiteItemLabel": { - "message": "Website $number$ (URI)", + "message": "เว็บไซต์ $number$ (URI)", "placeholders": { "number": { "content": "$1", @@ -2853,7 +2863,7 @@ } }, "excludedDomainsInvalidDomain": { - "message": "$DOMAIN$ is not a valid domain", + "message": "$DOMAIN$ ไม่ใช่โดเมนที่ถูกต้อง", "placeholders": { "domain": { "content": "$1", @@ -2862,20 +2872,20 @@ } }, "blockedDomainsSavedSuccess": { - "message": "Blocked domain changes saved" + "message": "บันทึกการเปลี่ยนแปลงโดเมนที่ถูกบล็อกแล้ว" }, "excludedDomainsSavedSuccess": { - "message": "Excluded domain changes saved" + "message": "บันทึกการเปลี่ยนแปลงโดเมนที่ยกเว้นแล้ว" }, "limitSendViews": { - "message": "Limit views" + "message": "จำกัดจำนวนการดู" }, "limitSendViewsHint": { - "message": "No one can view this Send after the limit is reached.", + "message": "จะไม่มีใครสามารถดู Send นี้ได้หลังจากครบกำหนด", "description": "Displayed under the limit views field on Send" }, "limitSendViewsCount": { - "message": "$ACCESSCOUNT$ views left", + "message": "ดูได้อีก $ACCESSCOUNT$ ครั้ง", "description": "Displayed under the limit views field on Send", "placeholders": { "accessCount": { @@ -2889,14 +2899,14 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDetails": { - "message": "Send details", + "message": "รายละเอียด Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTypeText": { "message": "ข้อความ" }, "sendTypeTextToShare": { - "message": "Text to share" + "message": "ข้อความที่จะแชร์" }, "sendTypeFile": { "message": "ไฟล์" @@ -2906,36 +2916,36 @@ "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", + "message": "ถึงจำนวนการเข้าถึงสูงสุดแล้ว", "description": "This text will be displayed after a Send has been accessed the maximum amount of times." }, "hideTextByDefault": { - "message": "Hide text by default" + "message": "ซ่อนข้อความโดยค่าเริ่มต้น" }, "expired": { - "message": "Expired" + "message": "หมดอายุ" }, "passwordProtected": { - "message": "Password protected" + "message": "มีการป้องกันด้วยรหัสผ่าน" }, "copyLink": { - "message": "Copy link" + "message": "คัดลอกลิงก์" }, "copySendLink": { - "message": "Copy Send link", + "message": "คัดลอกลิงก์ Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "removePassword": { - "message": "ลบรหัสผ่าน" + "message": "เอารหัสผ่านออก" }, "delete": { "message": "ลบ" }, "removedPassword": { - "message": "รหัสผ่านถูกลบแล้ว" + "message": "เอารหัสผ่านออกแล้ว" }, "deletedSend": { - "message": "Send ถูกลบแล้ว", + "message": "ลบ Send แล้ว", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { @@ -2943,21 +2953,21 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "disabled": { - "message": "Disabled" + "message": "ปิดใช้งาน" }, "removePasswordConfirmation": { - "message": "คุณต้องการลบรหัสผ่านนี้ใช่หรือไม่" + "message": "ยืนยันที่จะเอารหัสผ่านออกหรือไม่" }, "deleteSend": { "message": "ลบ Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendConfirmation": { - "message": "คุณต้องการลบ Send นี้ใช่หรือไม่?", + "message": "ยืนยันที่จะลบ Send นี้หรือไม่", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendPermanentConfirmation": { - "message": "Are you sure you want to permanently delete this Send?", + "message": "ยืนยันที่จะลบ Send นี้ถาวรหรือไม่", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editSend": { @@ -2965,14 +2975,14 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deletionDate": { - "message": "Deletion date" + "message": "วันที่ลบ" }, "deletionDateDescV2": { - "message": "The Send will be permanently deleted on this date.", + "message": "Send จะถูกลบถาวรในวันนี้", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "expirationDate": { - "message": "Expiration date" + "message": "วันที่หมดอายุ" }, "oneDay": { "message": "1 วัน" @@ -2987,41 +2997,41 @@ } }, "custom": { - "message": "Custom" + "message": "กำหนดเอง" }, "sendPasswordDescV3": { - "message": "Add an optional password for recipients to access this Send.", + "message": "เพิ่มรหัสผ่าน (ไม่บังคับ) เพื่อให้ผู้รับใช้เข้าถึง Send นี้", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createSend": { - "message": "New Send", + "message": "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": "รหัสผ่านใหม่" }, "sendDisabled": { - "message": "Send removed", + "message": "เอา Send ออกแล้ว", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { - "message": "Due to an enterprise policy, you are only able to delete an existing Send.", + "message": "เนื่องจากนโยบายองค์กร คุณสามารถลบ Send ที่มีอยู่ได้เท่านั้น", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSend": { - "message": "Send created", + "message": "สร้าง Send แล้ว", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSendSuccessfully": { - "message": "Send created successfully!", + "message": "สร้าง Send สำเร็จแล้ว!", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendExpiresInHoursSingle": { - "message": "The Send will be available to anyone with the link for the next 1 hour.", + "message": "Send จะพร้อมใช้งานสำหรับทุกคนที่มีลิงก์ในอีก 1 ชั่วโมงข้างหน้า", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendExpiresInHours": { - "message": "The Send will be available to anyone with the link for the next $HOURS$ hours.", + "message": "Send จะพร้อมใช้งานสำหรับทุกคนที่มีลิงก์ในอีก $HOURS$ ชั่วโมงข้างหน้า", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.", "placeholders": { "hours": { @@ -3031,11 +3041,11 @@ } }, "sendExpiresInDaysSingle": { - "message": "The Send will be available to anyone with the link for the next 1 day.", + "message": "Send จะพร้อมใช้งานสำหรับทุกคนที่มีลิงก์ในอีก 1 วันข้างหน้า", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendExpiresInDays": { - "message": "The Send will be available to anyone with the link for the next $DAYS$ days.", + "message": "Send จะพร้อมใช้งานสำหรับทุกคนที่มีลิงก์ในอีก $DAYS$ วันข้างหน้า", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.", "placeholders": { "days": { @@ -3045,113 +3055,113 @@ } }, "sendLinkCopied": { - "message": "Send link copied", + "message": "คัดลอกลิงก์ Send แล้ว", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editedSend": { - "message": "Send saved", + "message": "บันทึก Send แล้ว", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFilePopoutDialogText": { - "message": "Pop out extension?", + "message": "แยกหน้าต่างส่วนขยายหรือไม่", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFilePopoutDialogDesc": { - "message": "To create a file Send, you need to pop out the extension to a new window.", + "message": "หากต้องการสร้างไฟล์ Send คุณต้องแยกส่วนขยายออกมาเป็นหน้าต่างใหม่", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLinuxChromiumFileWarning": { - "message": "In order to choose a file, open the extension in the sidebar (if possible) or pop out to a new window by clicking this banner." + "message": "หากต้องการเลือกไฟล์ ให้เปิดส่วนขยายในแถบด้านข้าง (ถ้าทำได้) หรือแยกเป็นหน้าต่างใหม่โดยคลิกที่แบนเนอร์นี้" }, "sendFirefoxFileWarning": { - "message": "In order to choose a file using Firefox, open the extension in the sidebar or pop out to a new window by clicking this banner." + "message": "หากต้องการเลือกไฟล์โดยใช้ Firefox ให้เปิดส่วนขยายในแถบด้านข้างหรือแยกเป็นหน้าต่างใหม่โดยคลิกที่แบนเนอร์นี้" }, "sendSafariFileWarning": { - "message": "In order to choose a file using Safari, pop out to a new window by clicking this banner." + "message": "หากต้องการเลือกไฟล์โดยใช้ Safari ให้แยกเป็นหน้าต่างใหม่โดยคลิกที่แบนเนอร์นี้" }, "popOut": { - "message": "Pop out" + "message": "แยกหน้าต่าง" }, "sendFileCalloutHeader": { - "message": "Before you start" + "message": "ก่อนที่คุณจะเริ่ม" }, "expirationDateIsInvalid": { - "message": "The expiration date provided is not valid." + "message": "วันที่หมดอายุที่ระบุไม่ถูกต้อง" }, "deletionDateIsInvalid": { - "message": "The deletion date provided is not valid." + "message": "วันที่ลบที่ระบุไม่ถูกต้อง" }, "expirationDateAndTimeRequired": { - "message": "An expiration date and time are required." + "message": "จำเป็นต้องระบุวันที่และเวลาหมดอายุ" }, "deletionDateAndTimeRequired": { - "message": "A deletion date and time are required." + "message": "จำเป็นต้องระบุวันที่และเวลาที่จะลบ" }, "dateParsingError": { - "message": "There was an error saving your deletion and expiration dates." + "message": "เกิดข้อผิดพลาดในการบันทึกวันที่ลบและวันที่หมดอายุ" }, "hideYourEmail": { - "message": "Hide your email address from viewers." + "message": "ซ่อนที่อยู่อีเมลของคุณจากผู้ชม" }, "passwordPrompt": { - "message": "การยืนยันให้ป้อนรหัสผ่านหลักอีกครั้ง" + "message": "แจ้งเตือนให้ป้อนรหัสผ่านหลักซ้ำ" }, "passwordConfirmation": { - "message": "Master password confirmation" + "message": "การยืนยันรหัสผ่านหลัก" }, "passwordConfirmationDesc": { - "message": "This action is protected. To continue, please re-enter your master password to verify your identity." + "message": "การดำเนินการนี้ได้รับการป้องกัน หากต้องการดำเนินการต่อ โปรดป้อนรหัสผ่านหลักอีกครั้งเพื่อยืนยันตัวตน" }, "emailVerificationRequired": { - "message": "Email verification required" + "message": "จำเป็นต้องยืนยันอีเมล" }, "emailVerifiedV2": { - "message": "Email verified" + "message": "ยืนยันอีเมลแล้ว" }, "emailVerificationRequiredDesc": { - "message": "You must verify your email to use this feature. You can verify your email in the web vault." + "message": "คุณต้องยืนยันอีเมลเพื่อใช้งานฟีเจอร์นี้ คุณสามารถยืนยันอีเมลได้ในเว็บตู้นิรภัย" }, "masterPasswordSuccessfullySet": { - "message": "Master password successfully set" + "message": "ตั้งรหัสผ่านหลักสำเร็จแล้ว" }, "updatedMasterPassword": { - "message": "Updated master password" + "message": "อัปเดตรหัสผ่านหลักแล้ว" }, "updateMasterPassword": { - "message": "Update master password" + "message": "อัปเดตรหัสผ่านหลัก" }, "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": "รหัสผ่านหลักของคุณเพิ่งถูกเปลี่ยนโดยผู้ดูแลระบบในองค์กร หากต้องการเข้าถึงตู้นิรภัย คุณต้องอัปเดตรหัสผ่านทันที การดำเนินการนี้จะทำให้คุณออกจากระบบเซสชันปัจจุบัน และต้องเข้าสู่ระบบใหม่ เซสชันที่ใช้งานอยู่บนอุปกรณ์อื่นอาจยังคงใช้งานได้นานสูงสุด 1 ชั่วโมง" }, "updateWeakMasterPasswordWarning": { - "message": "Your master password does not meet one or more of your organization policies. In order to access the vault, you must update your master password now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour." + "message": "รหัสผ่านหลักของคุณไม่ตรงตามนโยบายองค์กรอย่างน้อยหนึ่งข้อ หากต้องการเข้าถึงตู้นิรภัย คุณต้องอัปเดตรหัสผ่านหลักทันที การดำเนินการนี้จะทำให้คุณออกจากระบบเซสชันปัจจุบัน และต้องเข้าสู่ระบบใหม่ เซสชันที่ใช้งานอยู่บนอุปกรณ์อื่นอาจยังคงใช้งานได้นานสูงสุด 1 ชั่วโมง" }, "tdeDisabledMasterPasswordRequired": { - "message": "Your organization has disabled trusted device encryption. Please set a master password to access your vault." + "message": "องค์กรของคุณปิดใช้งานการเข้ารหัสอุปกรณ์ที่เชื่อถือได้ โปรดตั้งรหัสผ่านหลักเพื่อเข้าถึงตู้นิรภัยของคุณ" }, "resetPasswordPolicyAutoEnroll": { - "message": "Automatic enrollment" + "message": "การลงทะเบียนอัตโนมัติ" }, "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": "องค์กรนี้มีนโยบายองค์กรที่จะลงทะเบียนคุณในการรีเซ็ตรหัสผ่านโดยอัตโนมัติ การลงทะเบียนจะอนุญาตให้ผู้ดูแลระบบขององค์กรเปลี่ยนรหัสผ่านหลักของคุณได้" }, "selectFolder": { - "message": "Select folder..." + "message": "เลือกโฟลเดอร์..." }, "noFoldersFound": { - "message": "No folders found", + "message": "ไม่พบโฟลเดอร์", "description": "Used as a message within the notification bar when no folders are found" }, "orgPermissionsUpdatedMustSetPassword": { - "message": "Your organization permissions were updated, requiring you to set a master password.", + "message": "สิทธิ์ในองค์กรของคุณได้รับการอัปเดต ซึ่งกำหนดให้คุณต้องตั้งรหัสผ่านหลัก", "description": "Used as a card title description on the set password page to explain why the user is there" }, "orgRequiresYouToSetPassword": { - "message": "Your organization requires you to set a master password.", + "message": "องค์กรของคุณกำหนดให้คุณต้องตั้งรหัสผ่านหลัก", "description": "Used as a card title description on the set password page to explain why the user is there" }, "cardMetrics": { - "message": "out of $TOTAL$", + "message": "จาก $TOTAL$", "placeholders": { "total": { "content": "$1", @@ -3160,20 +3170,20 @@ } }, "verificationRequired": { - "message": "Verification required", + "message": "จำเป็นต้องยืนยันตัวตน", "description": "Default title for the user verification dialog." }, "hours": { - "message": "Hours" + "message": "ชั่วโมง" }, "minutes": { - "message": "Minutes" + "message": "นาที" }, "vaultTimeoutPolicyAffectingOptions": { - "message": "Enterprise policy requirements have been applied to your timeout options" + "message": "ข้อกำหนดนโยบายองค์กรถูกนำไปใช้กับตัวเลือกเวลาหมดเวลาของคุณแล้ว" }, "vaultTimeoutPolicyInEffect": { - "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "message": "นโยบายองค์กรกำหนดระยะเวลาหมดเวลาของตู้นิรภัยสูงสุดไว้ที่ $HOURS$ ชั่วโมง $MINUTES$ นาที", "placeholders": { "hours": { "content": "$1", @@ -3186,7 +3196,7 @@ } }, "vaultTimeoutPolicyInEffect1": { - "message": "$HOURS$ hour(s) and $MINUTES$ minute(s) maximum.", + "message": "สูงสุด $HOURS$ ชั่วโมง $MINUTES$ นาที", "placeholders": { "hours": { "content": "$1", @@ -3199,7 +3209,7 @@ } }, "vaultTimeoutPolicyMaximumError": { - "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "message": "เวลาหมดเวลาเกินข้อจำกัดที่องค์กรกำหนด: สูงสุด $HOURS$ ชั่วโมง $MINUTES$ นาที", "placeholders": { "hours": { "content": "$1", @@ -3212,7 +3222,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "Your organization policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s). Your vault timeout action is set to $ACTION$.", + "message": "นโยบายองค์กรมีผลต่อเวลาหมดเวลาของตู้นิรภัย อนุญาตให้หมดเวลาสูงสุด $HOURS$ ชั่วโมง $MINUTES$ นาที การดำเนินการเมื่อตู้นิรภัยหมดเวลาตั้งค่าไว้ที่ $ACTION$", "placeholders": { "hours": { "content": "$1", @@ -3229,7 +3239,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "Your organization policies have set your vault timeout action to $ACTION$.", + "message": "นโยบายองค์กรกำหนดการดำเนินการเมื่อตู้นิรภัยหมดเวลาเป็น $ACTION$", "placeholders": { "action": { "content": "$1", @@ -3238,52 +3248,52 @@ } }, "vaultTimeoutTooLarge": { - "message": "Your vault timeout exceeds the restrictions set by your organization." + "message": "เวลาหมดเวลาของตู้นิรภัยเกินข้อจำกัดที่องค์กรกำหนด" }, "vaultExportDisabled": { - "message": "Vault export unavailable" + "message": "ไม่สามารถส่งออกตู้นิรภัยได้" }, "personalVaultExportPolicyInEffect": { - "message": "One or more organization policies prevents you from exporting your individual vault." + "message": "นโยบายองค์กรอย่างน้อยหนึ่งรายการป้องกันไม่ให้คุณส่งออกตู้นิรภัยส่วนตัว" }, "copyCustomFieldNameInvalidElement": { - "message": "Unable to identify a valid form element. Try inspecting the HTML instead." + "message": "ไม่สามารถระบุองค์ประกอบแบบฟอร์มที่ถูกต้องได้ ลองตรวจสอบ HTML แทน" }, "copyCustomFieldNameNotUnique": { - "message": "No unique identifier found." + "message": "ไม่พบตัวระบุที่ไม่ซ้ำกัน" }, "organizationName": { - "message": "Organization name" + "message": "ชื่อองค์กร" }, "keyConnectorDomain": { - "message": "Key Connector domain" + "message": "โดเมน Key Connector" }, "leaveOrganization": { - "message": "Leave organization" + "message": "ออกจากองค์กร" }, "removeMasterPassword": { - "message": "Remove master password" + "message": "เอารหัสผ่านหลักออก" }, "removedMasterPassword": { - "message": "Master password removed" + "message": "เอารหัสผ่านหลักออกแล้ว" }, "leaveOrganizationConfirmation": { - "message": "Are you sure you want to leave this organization?" + "message": "ยืนยันที่จะออกจากองค์กรนี้หรือไม่" }, "leftOrganization": { - "message": "You have left the organization." + "message": "คุณออกจากองค์กรแล้ว" }, "toggleCharacterCount": { - "message": "Toggle character count" + "message": "สลับการนับตัวอักษร" }, "sessionTimeout": { - "message": "Your session has timed out. Please go back and try logging in again." + "message": "เซสชันของคุณหมดเวลาแล้ว โปรดย้อนกลับและลองเข้าสู่ระบบอีกครั้ง" }, "exportingPersonalVaultTitle": { - "message": "Exporting individual vault" + "message": "กำลังส่งออกตู้นิรภัยส่วนตัว" }, "exportingIndividualVaultDescription": { - "message": "Only the individual vault items associated with $EMAIL$ will be exported. Organization vault items will not be included. Only vault item information will be exported and will not include associated attachments.", + "message": "เฉพาะรายการในตู้นิรภัยส่วนตัวที่เชื่อมโยงกับ $EMAIL$ เท่านั้นที่จะถูกส่งออก รายการในตู้นิรภัยองค์กรจะไม่รวมอยู่ด้วย ระบบจะส่งออกเฉพาะข้อมูลรายการในตู้นิรภัยและไม่รวมไฟล์แนบที่เกี่ยวข้อง", "placeholders": { "email": { "content": "$1", @@ -3292,7 +3302,7 @@ } }, "exportingIndividualVaultWithAttachmentsDescription": { - "message": "Only the individual vault items including attachments associated with $EMAIL$ will be exported. Organization vault items will not be included", + "message": "เฉพาะรายการในตู้นิรภัยส่วนตัวและไฟล์แนบที่เชื่อมโยงกับ $EMAIL$ เท่านั้นที่จะถูกส่งออก รายการในตู้นิรภัยองค์กรจะไม่รวมอยู่ด้วย", "placeholders": { "email": { "content": "$1", @@ -3301,10 +3311,10 @@ } }, "exportingOrganizationVaultTitle": { - "message": "Exporting organization vault" + "message": "กำลังส่งออกตู้นิรภัยองค์กร" }, "exportingOrganizationVaultDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. Items in individual vaults or other organizations will not be included.", + "message": "เฉพาะตู้นิรภัยองค์กรที่เชื่อมโยงกับ $ORGANIZATION$ เท่านั้นที่จะถูกส่งออก รายการในตู้นิรภัยส่วนตัวหรือองค์กรอื่นจะไม่รวมอยู่ด้วย", "placeholders": { "organization": { "content": "$1", @@ -3313,7 +3323,7 @@ } }, "exportingOrganizationVaultFromPasswordManagerWithDataOwnershipDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported.", + "message": "เฉพาะตู้นิรภัยองค์กรที่เชื่อมโยงกับ $ORGANIZATION$ เท่านั้นที่จะถูกส่งออก", "placeholders": { "organization": { "content": "$1", @@ -3322,7 +3332,7 @@ } }, "exportingOrganizationVaultFromAdminConsoleWithDataOwnershipDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. My items collections will not be included.", + "message": "เฉพาะตู้นิรภัยองค์กรที่เชื่อมโยงกับ $ORGANIZATION$ เท่านั้นที่จะถูกส่งออก คอลเลกชันรายการของฉันจะไม่รวมอยู่ด้วย", "placeholders": { "organization": { "content": "$1", @@ -3331,33 +3341,33 @@ } }, "error": { - "message": "Error" + "message": "ข้อผิดพลาด" }, "decryptionError": { - "message": "Decryption error" + "message": "ข้อผิดพลาดในการถอดรหัส" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "เกิดข้อผิดพลาดในการดึงข้อมูลป้อนอัตโนมัติ" }, "couldNotDecryptVaultItemsBelow": { - "message": "Bitwarden could not decrypt the vault item(s) listed below." + "message": "Bitwarden ไม่สามารถถอดรหัสรายการตู้นิรภัยที่ระบุไว้ด้านล่าง" }, "contactCSToAvoidDataLossPart1": { - "message": "Contact customer success", + "message": "ติดต่อทีม Customer Success", "description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'" }, "contactCSToAvoidDataLossPart2": { - "message": "to avoid additional data loss.", + "message": "เพื่อหลีกเลี่ยงการสูญหายของข้อมูลเพิ่มเติม", "description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'" }, "generateUsername": { - "message": "Generate username" + "message": "สร้างชื่อผู้ใช้" }, "generateEmail": { - "message": "Generate email" + "message": "สร้างอีเมล" }, "spinboxBoundariesHint": { - "message": "Value must be between $MIN$ and $MAX$.", + "message": "ค่าต้องอยู่ระหว่าง $MIN$ ถึง $MAX$", "description": "Explains spin box minimum and maximum values to the user", "placeholders": { "min": { @@ -3371,7 +3381,7 @@ } }, "passwordLengthRecommendationHint": { - "message": " Use $RECOMMENDED$ characters or more to generate a strong password.", + "message": " ใช้ $RECOMMENDED$ ตัวอักษรขึ้นไปเพื่อสร้างรหัสผ่านที่รัดกุม", "description": "Appended to `spinboxBoundariesHint` to recommend a length to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -3381,7 +3391,7 @@ } }, "passphraseNumWordsRecommendationHint": { - "message": " Use $RECOMMENDED$ words or more to generate a strong passphrase.", + "message": " ใช้ $RECOMMENDED$ คำขึ้นไปเพื่อสร้างวลีรหัสผ่านที่รัดกุม", "description": "Appended to `spinboxBoundariesHint` to recommend a number of words to the user. This must include any language-specific 'sentence' separator characters (e.g. a space in english).", "placeholders": { "recommended": { @@ -3391,46 +3401,46 @@ } }, "plusAddressedEmail": { - "message": "Plus addressed email", + "message": "อีเมลแบบ Plus addressing", "description": "Username generator option that appends a random sub-address to the username. For example: address+subaddress@email.com" }, "plusAddressedEmailDesc": { - "message": "Use your email provider's sub-addressing capabilities." + "message": "ใช้ความสามารถ sub-addressing ของผู้ให้บริการอีเมลของคุณ" }, "catchallEmail": { - "message": "Catch-all email" + "message": "อีเมลแบบ Catch-all" }, "catchallEmailDesc": { - "message": "Use your domain's configured catch-all inbox." + "message": "ใช้อินบ็อกซ์แบบ Catch-all ที่กำหนดค่าไว้สำหรับโดเมนของคุณ" }, "random": { - "message": "Random" + "message": "สุ่ม" }, "randomWord": { - "message": "Random word" + "message": "คำสุ่ม" }, "websiteName": { - "message": "Website name" + "message": "ชื่อเว็บไซต์" }, "service": { - "message": "Service" + "message": "บริการ" }, "forwardedEmail": { - "message": "Forwarded email alias" + "message": "นามแฝงอีเมลแบบส่งต่อ" }, "forwardedEmailDesc": { - "message": "Generate an email alias with an external forwarding service." + "message": "สร้างนามแฝงอีเมลด้วยบริการส่งต่อภายนอก" }, "forwarderDomainName": { - "message": "Email domain", + "message": "โดเมนอีเมล", "description": "Labels the domain name email forwarder service option" }, "forwarderDomainNameHint": { - "message": "Choose a domain that is supported by the selected service", + "message": "เลือกโดเมนที่บริการที่เลือกไว้รองรับ", "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { - "message": "$SERVICENAME$ error: $ERRORMESSAGE$", + "message": "ข้อผิดพลาด $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", "placeholders": { "servicename": { @@ -3444,11 +3454,11 @@ } }, "forwarderGeneratedBy": { - "message": "Generated by Bitwarden.", + "message": "สร้างโดย Bitwarden", "description": "Displayed with the address on the forwarding service's configuration screen." }, "forwarderGeneratedByWithWebsite": { - "message": "Website: $WEBSITE$. Generated by Bitwarden.", + "message": "เว็บไซต์: $WEBSITE$ สร้างโดย Bitwarden", "description": "Displayed with the address on the forwarding service's configuration screen.", "placeholders": { "WEBSITE": { @@ -3458,7 +3468,7 @@ } }, "forwaderInvalidToken": { - "message": "Invalid $SERVICENAME$ API token", + "message": "โทเค็น API ของ $SERVICENAME$ ไม่ถูกต้อง", "description": "Displayed when the user's API token is empty or rejected by the forwarding service.", "placeholders": { "servicename": { @@ -3468,7 +3478,7 @@ } }, "forwaderInvalidTokenWithMessage": { - "message": "Invalid $SERVICENAME$ API token: $ERRORMESSAGE$", + "message": "โทเค็น API ของ $SERVICENAME$ ไม่ถูกต้อง: $ERRORMESSAGE$", "description": "Displayed when the user's API token is rejected by the forwarding service with an error message.", "placeholders": { "servicename": { @@ -3482,7 +3492,7 @@ } }, "forwaderInvalidOperation": { - "message": "$SERVICENAME$ refused your request. Please contact your service provider for assistance.", + "message": "$SERVICENAME$ ปฏิเสธคำขอของคุณ โปรดติดต่อผู้ให้บริการเพื่อขอความช่วยเหลือ", "description": "Displayed when the user is forbidden from using the API by the forwarding service.", "placeholders": { "servicename": { @@ -3492,7 +3502,7 @@ } }, "forwaderInvalidOperationWithMessage": { - "message": "$SERVICENAME$ refused your request: $ERRORMESSAGE$", + "message": "$SERVICENAME$ ปฏิเสธคำขอของคุณ: $ERRORMESSAGE$", "description": "Displayed when the user is forbidden from using the API by the forwarding service with an error message.", "placeholders": { "servicename": { @@ -3506,7 +3516,7 @@ } }, "forwarderNoAccountId": { - "message": "Unable to obtain $SERVICENAME$ masked email account ID.", + "message": "ไม่สามารถรับ ID บัญชีอีเมลแบบปิดบังตัวตนของ $SERVICENAME$", "description": "Displayed when the forwarding service fails to return an account ID.", "placeholders": { "servicename": { @@ -3516,7 +3526,7 @@ } }, "forwarderNoDomain": { - "message": "Invalid $SERVICENAME$ domain.", + "message": "โดเมน $SERVICENAME$ ไม่ถูกต้อง", "description": "Displayed when the domain is empty or domain authorization failed at the forwarding service.", "placeholders": { "servicename": { @@ -3526,7 +3536,7 @@ } }, "forwarderNoUrl": { - "message": "Invalid $SERVICENAME$ url.", + "message": "URL ของ $SERVICENAME$ ไม่ถูกต้อง", "description": "Displayed when the url of the forwarding service wasn't supplied.", "placeholders": { "servicename": { @@ -3536,7 +3546,7 @@ } }, "forwarderUnknownError": { - "message": "Unknown $SERVICENAME$ error occurred.", + "message": "เกิดข้อผิดพลาด $SERVICENAME$ ที่ไม่รู้จัก", "description": "Displayed when the forwarding service failed due to an unknown error.", "placeholders": { "servicename": { @@ -3546,7 +3556,7 @@ } }, "forwarderUnknownForwarder": { - "message": "Unknown forwarder: '$SERVICENAME$'.", + "message": "ไม่รู้จักบริการส่งต่อ: '$SERVICENAME$'", "description": "Displayed when the forwarding service is not supported.", "placeholders": { "servicename": { @@ -3556,29 +3566,29 @@ } }, "hostname": { - "message": "Hostname", + "message": "ชื่อโฮสต์", "description": "Part of a URL." }, "apiAccessToken": { - "message": "API Access Token" + "message": "โทเค็นการเข้าถึง API" }, "apiKey": { - "message": "API Key" + "message": "คีย์ API" }, "ssoKeyConnectorError": { - "message": "Key connector error: make sure key connector is available and working correctly." + "message": "ข้อผิดพลาด Key Connector: ตรวจสอบให้แน่ใจว่า Key Connector พร้อมใช้งานและทำงานอย่างถูกต้อง" }, "premiumSubcriptionRequired": { - "message": "Premium subscription required" + "message": "จำเป็นต้องสมัครสมาชิกพรีเมียม" }, "organizationIsDisabled": { - "message": "Organization suspended." + "message": "องค์กรถูกระงับ" }, "disabledOrganizationFilterError": { - "message": "Items in suspended Organizations cannot be accessed. Contact your Organization owner for assistance." + "message": "ไม่สามารถเข้าถึงรายการในองค์กรที่ถูกระงับได้ ติดต่อเจ้าขององค์กรเพื่อขอความช่วยเหลือ" }, "loggingInTo": { - "message": "Logging in to $DOMAIN$", + "message": "กำลังเข้าสู่ระบบ $DOMAIN$", "placeholders": { "domain": { "content": "$1", @@ -3587,16 +3597,16 @@ } }, "serverVersion": { - "message": "Server version" + "message": "เวอร์ชันเซิร์ฟเวอร์" }, "selfHostedServer": { - "message": "self-hosted" + "message": "โฮสต์เอง" }, "thirdParty": { - "message": "Third-party" + "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", @@ -3605,7 +3615,7 @@ } }, "lastSeenOn": { - "message": "last seen on: $DATE$", + "message": "ใช้งานล่าสุดเมื่อ: $DATE$", "placeholders": { "date": { "content": "$1", @@ -3614,58 +3624,58 @@ } }, "loginWithMasterPassword": { - "message": "Log in with master password" + "message": "เข้าสู่ระบบด้วยรหัสผ่านหลัก" }, "newAroundHere": { - "message": "New around here?" + "message": "เพิ่งเคยใช้งานใช่หรือไม่" }, "rememberEmail": { - "message": "Remember email" + "message": "จดจำอีเมล" }, "loginWithDevice": { - "message": "Log in with device" + "message": "เข้าสู่ระบบด้วยอุปกรณ์" }, "fingerprintPhraseHeader": { - "message": "Fingerprint phrase" + "message": "วลีลายนิ้วมือ" }, "fingerprintMatchInfo": { - "message": "โปรดตรวจสอบให้แน่ใจว่าห้องนิรภัยของคุณปลดล็อกอยู่ และลายนิ้วมือตรงกันบนอุปกรณ์อื่น" + "message": "โปรดตรวจสอบให้แน่ใจว่าตู้นิรภัยปลดล็อกอยู่ และวลีลายนิ้วมือตรงกับอุปกรณ์อีกเครื่อง" }, "resendNotification": { - "message": "Resend notification" + "message": "ส่งการแจ้งเตือนอีกครั้ง" }, "viewAllLogInOptions": { - "message": "View all log in options" + "message": "ดูตัวเลือกการเข้าสู่ระบบทั้งหมด" }, "notificationSentDevice": { - "message": "A notification has been sent to your device." + "message": "ส่งการแจ้งเตือนไปยังอุปกรณ์ของคุณแล้ว" }, "notificationSentDevicePart1": { - "message": "Unlock Bitwarden on your device or on the" + "message": "ปลดล็อก Bitwarden บนอุปกรณ์ของคุณหรือบน" }, "notificationSentDeviceAnchor": { - "message": "web app" + "message": "เว็บแอป" }, "notificationSentDevicePart2": { - "message": "Make sure the Fingerprint phrase matches the one below before approving." + "message": "ตรวจสอบให้แน่ใจว่าวลีลายนิ้วมือตรงกับด้านล่างก่อนอนุมัติ" }, "aNotificationWasSentToYourDevice": { - "message": "A notification was sent to your device" + "message": "ส่งการแจ้งเตือนไปยังอุปกรณ์ของคุณแล้ว" }, "youWillBeNotifiedOnceTheRequestIsApproved": { - "message": "You will be notified once the request is approved" + "message": "คุณจะได้รับแจ้งเมื่อคำขอได้รับการอนุมัติ" }, "needAnotherOptionV1": { - "message": "Need another option?" + "message": "ต้องการตัวเลือกอื่นหรือไม่" }, "loginInitiated": { - "message": "Login initiated" + "message": "เริ่มการเข้าสู่ระบบแล้ว" }, "logInRequestSent": { - "message": "Request sent" + "message": "ส่งคำขอแล้ว" }, "loginRequestApprovedForEmailOnDevice": { - "message": "Login request approved for $EMAIL$ on $DEVICE$", + "message": "อนุมัติคำขอเข้าสู่ระบบสำหรับ $EMAIL$ บน $DEVICE$ แล้ว", "placeholders": { "email": { "content": "$1", @@ -3678,40 +3688,40 @@ } }, "youDeniedLoginAttemptFromAnotherDevice": { - "message": "You denied a login attempt from another device. If this was you, try to log in with the device again." + "message": "คุณปฏิเสธความพยายามเข้าสู่ระบบจากอุปกรณ์อื่น หากนี่คือคุณ ให้ลองเข้าสู่ระบบด้วยอุปกรณ์อีกครั้ง" }, "device": { - "message": "Device" + "message": "อุปกรณ์" }, "loginStatus": { - "message": "Login status" + "message": "สถานะการเข้าสู่ระบบ" }, "masterPasswordChanged": { - "message": "Master password saved" + "message": "บันทึกรหัสผ่านหลักแล้ว" }, "exposedMasterPassword": { - "message": "Exposed Master Password" + "message": "รหัสผ่านหลักรั่วไหล" }, "exposedMasterPasswordDesc": { - "message": "Password found in a data breach. Use a unique password to protect your account. Are you sure you want to use an exposed password?" + "message": "พบรหัสผ่านในเหตุการณ์ข้อมูลรั่วไหล ใช้รหัสผ่านที่ไม่ซ้ำกันเพื่อปกป้องบัญชีของคุณ ยืนยันที่จะใช้รหัสผ่านที่รั่วไหลหรือไม่" }, "weakAndExposedMasterPassword": { - "message": "Weak and Exposed Master Password" + "message": "รหัสผ่านหลักไม่ปลอดภัยและรั่วไหล" }, "weakAndBreachedMasterPasswordDesc": { - "message": "Weak password identified and found in a data breach. Use a strong and unique password to protect your account. Are you sure you want to use this password?" + "message": "พบรหัสผ่านที่ไม่ปลอดภัยและอยู่ในเหตุการณ์ข้อมูลรั่วไหล ใช้รหัสผ่านที่รัดกุมและไม่ซ้ำกันเพื่อปกป้องบัญชีของคุณ ยืนยันที่จะใช้รหัสผ่านนี้หรือไม่" }, "checkForBreaches": { - "message": "Check known data breaches for this password" + "message": "ตรวจสอบเหตุการณ์ข้อมูลรั่วไหลสำหรับรหัสผ่านนี้" }, "important": { - "message": "Important:" + "message": "สำคัญ:" }, "masterPasswordHint": { - "message": "Your master password cannot be recovered if you forget it!" + "message": "รหัสผ่านหลักไม่สามารถกู้คืนได้หากคุณลืม!" }, "characterMinimum": { - "message": "$LENGTH$ character minimum", + "message": "ขั้นต่ำ $LENGTH$ ตัวอักษร", "placeholders": { "length": { "content": "$1", @@ -3720,10 +3730,10 @@ } }, "autofillPageLoadPolicyActivated": { - "message": "Your organization policies have turned on autofill on page load." + "message": "นโยบายองค์กรของคุณเปิดใช้งานการป้อนอัตโนมัติเมื่อโหลดหน้าเว็บ" }, "autofillSelectInfoWithCommand": { - "message": "Select an item from this screen, use the shortcut $COMMAND$, or explore other options in settings.", + "message": "เลือกรายการจากหน้านี้ ใช้ทางลัด $COMMAND$ หรือสำรวจตัวเลือกอื่น ๆ ในการตั้งค่า", "placeholders": { "command": { "content": "$1", @@ -3732,31 +3742,31 @@ } }, "autofillSelectInfoWithoutCommand": { - "message": "Select an item from this screen, or explore other options in settings." + "message": "เลือกรายการจากหน้านี้ หรือสำรวจตัวเลือกอื่น ๆ ในการตั้งค่า" }, "gotIt": { - "message": "Got it" + "message": "เข้าใจแล้ว" }, "autofillSettings": { - "message": "Autofill settings" + "message": "การตั้งค่าการป้อนอัตโนมัติ" }, "autofillKeyboardShortcutSectionTitle": { - "message": "Autofill shortcut" + "message": "ทางลัดการป้อนอัตโนมัติ" }, "autofillKeyboardShortcutUpdateLabel": { - "message": "Change shortcut" + "message": "เปลี่ยนทางลัด" }, "autofillKeyboardManagerShortcutsLabel": { - "message": "Manage shortcuts" + "message": "จัดการทางลัด" }, "autofillShortcut": { - "message": "Autofill keyboard shortcut" + "message": "คีย์ลัดการป้อนอัตโนมัติ" }, "autofillLoginShortcutNotSet": { - "message": "The autofill login shortcut is not set. Change this in the browser's settings." + "message": "ยังไม่ได้ตั้งค่าทางลัดการป้อนข้อมูลเข้าสู่ระบบ เปลี่ยนค่านี้ได้ในการตั้งค่าของเบราว์เซอร์" }, "autofillLoginShortcutText": { - "message": "The autofill login shortcut is $COMMAND$. Manage all shortcuts in the browser's settings.", + "message": "ทางลัดการป้อนข้อมูลเข้าสู่ระบบคือ $COMMAND$ จัดการทางลัดทั้งหมดได้ในการตั้งค่าของเบราว์เซอร์", "placeholders": { "command": { "content": "$1", @@ -3765,7 +3775,7 @@ } }, "autofillShortcutTextSafari": { - "message": "Default autofill shortcut: $COMMAND$.", + "message": "ทางลัดการป้อนอัตโนมัติเริ่มต้น: $COMMAND$", "placeholders": { "command": { "content": "$1", @@ -3774,34 +3784,34 @@ } }, "opensInANewWindow": { - "message": "Opens in a new window" + "message": "เปิดในหน้าต่างใหม่" }, "rememberThisDeviceToMakeFutureLoginsSeamless": { - "message": "Remember this device to make future logins seamless" + "message": "จดจำอุปกรณ์นี้เพื่อให้การเข้าสู่ระบบในอนาคตราบรื่นยิ่งขึ้น" }, "manageDevices": { - "message": "Manage devices" + "message": "จัดการอุปกรณ์" }, "currentSession": { - "message": "Current session" + "message": "เซสชันปัจจุบัน" }, "mobile": { - "message": "Mobile", + "message": "มือถือ", "description": "Mobile app" }, "extension": { - "message": "Extension", + "message": "ส่วนขยาย", "description": "Browser extension/addon" }, "desktop": { - "message": "Desktop", + "message": "เดสก์ท็อป", "description": "Desktop app" }, "webVault": { - "message": "Web vault" + "message": "เว็บตู้นิรภัย" }, "webApp": { - "message": "Web app" + "message": "เว็บแอป" }, "cli": { "message": "CLI" @@ -3811,22 +3821,22 @@ "description": "Software Development Kit" }, "requestPending": { - "message": "Request pending" + "message": "คำขอรอดำเนินการ" }, "firstLogin": { - "message": "First login" + "message": "เข้าสู่ระบบครั้งแรก" }, "trusted": { - "message": "Trusted" + "message": "เชื่อถือได้" }, "needsApproval": { - "message": "Needs approval" + "message": "รอการอนุมัติ" }, "devices": { - "message": "Devices" + "message": "อุปกรณ์" }, "accessAttemptBy": { - "message": "Access attempt by $EMAIL$", + "message": "ความพยายามเข้าถึงโดย $EMAIL$", "placeholders": { "email": { "content": "$1", @@ -3835,31 +3845,31 @@ } }, "confirmAccess": { - "message": "Confirm access" + "message": "ยืนยันการเข้าถึง" }, "denyAccess": { - "message": "Deny access" + "message": "ปฏิเสธการเข้าถึง" }, "time": { - "message": "Time" + "message": "เวลา" }, "deviceType": { - "message": "Device Type" + "message": "ประเภทอุปกรณ์" }, "loginRequest": { - "message": "Login request" + "message": "คำขอเข้าสู่ระบบ" }, "thisRequestIsNoLongerValid": { - "message": "This request is no longer valid." + "message": "คำขอนี้ใช้ไม่ได้อีกต่อไป" }, "loginRequestHasAlreadyExpired": { - "message": "Login request has already expired." + "message": "คำขอเข้าสู่ระบบหมดอายุแล้ว" }, "justNow": { - "message": "Just now" + "message": "เมื่อสักครู่" }, "requestedXMinutesAgo": { - "message": "Requested $MINUTES$ minutes ago", + "message": "ร้องขอเมื่อ $MINUTES$ นาทีที่แล้ว", "placeholders": { "minutes": { "content": "$1", @@ -3868,136 +3878,136 @@ } }, "deviceApprovalRequired": { - "message": "Device approval required. Select an approval option below:" + "message": "จำเป็นต้องอนุมัติอุปกรณ์ เลือกตัวเลือกการอนุมัติด้านล่าง:" }, "deviceApprovalRequiredV2": { - "message": "Device approval required" + "message": "จำเป็นต้องอนุมัติอุปกรณ์" }, "selectAnApprovalOptionBelow": { - "message": "Select an approval option below" + "message": "เลือกตัวเลือกการอนุมัติด้านล่าง" }, "rememberThisDevice": { - "message": "Remember this device" + "message": "จดจำอุปกรณ์นี้" }, "uncheckIfPublicDevice": { - "message": "Uncheck if using a public device" + "message": "ไม่ต้องเลือกหากใช้อุปกรณ์สาธารณะ" }, "approveFromYourOtherDevice": { - "message": "Approve from your other device" + "message": "อนุมัติจากอุปกรณ์อื่นของคุณ" }, "requestAdminApproval": { - "message": "Request admin approval" + "message": "ขอการอนุมัติจากผู้ดูแลระบบ" }, "unableToCompleteLogin": { - "message": "Unable to complete login" + "message": "ไม่สามารถเข้าสู่ระบบให้เสร็จสมบูรณ์ได้" }, "loginOnTrustedDeviceOrAskAdminToAssignPassword": { - "message": "You need to log in on a trusted device or ask your administrator to assign you a password." + "message": "คุณต้องเข้าสู่ระบบบนอุปกรณ์ที่เชื่อถือได้ หรือขอให้ผู้ดูแลระบบกำหนดรหัสผ่านให้คุณ" }, "ssoIdentifierRequired": { - "message": "Organization SSO identifier is required." + "message": "จำเป็นต้องระบุตัวระบุ SSO ขององค์กร" }, "creatingAccountOn": { - "message": "Creating account on" + "message": "กำลังสร้างบัญชีบน" }, "checkYourEmail": { - "message": "Check your email" + "message": "ตรวจสอบอีเมลของคุณ" }, "followTheLinkInTheEmailSentTo": { - "message": "Follow the link in the email sent to" + "message": "คลิกลิงก์ในอีเมลที่ส่งไปที่" }, "andContinueCreatingYourAccount": { - "message": "and continue creating your account." + "message": "และดำเนินการสร้างบัญชีต่อ" }, "noEmail": { - "message": "No email?" + "message": "ไม่ได้รับอีเมลใช่ไหม" }, "goBack": { - "message": "Go back" + "message": "ย้อนกลับ" }, "toEditYourEmailAddress": { - "message": "to edit your email address." + "message": "เพื่อแก้ไขที่อยู่อีเมลของคุณ" }, "eu": { "message": "EU", "description": "European Union" }, "accessDenied": { - "message": "Access denied. You do not have permission to view this page." + "message": "การเข้าถึงถูกปฏิเสธ คุณไม่มีสิทธิ์ดูหน้านี้" }, "general": { - "message": "General" + "message": "ทั่วไป" }, "display": { - "message": "Display" + "message": "การแสดงผล" }, "accountSuccessfullyCreated": { - "message": "Account successfully created!" + "message": "สร้างบัญชีสำเร็จแล้ว!" }, "adminApprovalRequested": { - "message": "Admin approval requested" + "message": "ส่งคำขออนุมัติจากผู้ดูแลระบบแล้ว" }, "adminApprovalRequestSentToAdmins": { - "message": "Your request has been sent to your admin." + "message": "ส่งคำขอของคุณไปยังผู้ดูแลระบบแล้ว" }, "troubleLoggingIn": { - "message": "Trouble logging in?" + "message": "มีปัญหาในการเข้าสู่ระบบใช่หรือไม่" }, "loginApproved": { - "message": "Login approved" + "message": "อนุมัติการเข้าสู่ระบบแล้ว" }, "userEmailMissing": { - "message": "User email missing" + "message": "ไม่พบอีเมลผู้ใช้" }, "activeUserEmailNotFoundLoggingYouOut": { - "message": "Active user email not found. Logging you out." + "message": "ไม่พบอีเมลผู้ใช้ที่ใช้งานอยู่ กำลังออกจากระบบ" }, "deviceTrusted": { - "message": "Device trusted" + "message": "อุปกรณ์ที่เชื่อถือได้" }, "trustOrganization": { - "message": "Trust organization" + "message": "เชื่อถือองค์กร" }, "trust": { - "message": "Trust" + "message": "เชื่อถือ" }, "doNotTrust": { - "message": "Do not trust" + "message": "ไม่เชื่อถือ" }, "organizationNotTrusted": { - "message": "Organization is not trusted" + "message": "องค์กรไม่น่าเชื่อถือ" }, "emergencyAccessTrustWarning": { - "message": "เพื่อความปลอดภัยของบัญชีของคุณ โปรดยืนยันว่าคุณได้ให้สิทธิ์การเข้าถึงในกรณีฉุกเฉินแก่ผู้ใช้นี้ และลายนิ้วมือของผู้ใช้ตรงกับที่แสดงในบัญชีของพวกเขาเท่านั้น" + "message": "เพื่อความปลอดภัยของบัญชี โปรดยืนยันเฉพาะเมื่อคุณได้ให้สิทธิ์การเข้าถึงฉุกเฉินแก่ผู้ใช้นี้ และลายนิ้วมือของพวกเขาตรงกับที่แสดงในบัญชีของพวกเขา" }, "orgTrustWarning": { - "message": "เพื่อความปลอดภัยของบัญชีของคุณ ให้ดำเนินการต่อเมื่อคุณเป็นสมาชิกขององค์กรนี้, ได้เปิดใช้งานการกู้คืนบัญชี, และลายนิ้วมือที่แสดงด้านล่างตรงกับลายนิ้วมือขององค์กรเท่านั้น" + "message": "เพื่อความปลอดภัยของบัญชี โปรดดำเนินการต่อเฉพาะเมื่อคุณเป็นสมาชิกขององค์กรนี้ เปิดใช้งานการกู้คืนบัญชี และลายนิ้วมือที่แสดงด้านล่างตรงกับลายนิ้วมือขององค์กร" }, "orgTrustWarning1": { - "message": "This organization has an Enterprise policy that will enroll you in account recovery. Enrollment will allow organization administrators to change your password. Only proceed if you recognize this organization and the fingerprint phrase displayed below matches the organization's fingerprint." + "message": "องค์กรนี้มีนโยบายองค์กรที่จะลงทะเบียนคุณในการกู้คืนบัญชี การลงทะเบียนจะอนุญาตให้ผู้ดูแลระบบองค์กรเปลี่ยนรหัสผ่านของคุณได้ โปรดดำเนินการต่อเฉพาะเมื่อคุณรู้จักองค์กรนี้ และวลีลายนิ้วมือที่แสดงด้านล่างตรงกับลายนิ้วมือขององค์กร" }, "trustUser": { - "message": "Trust user" + "message": "เชื่อถือผู้ใช้" }, "sendsTitleNoItems": { - "message": "Send sensitive information safely", + "message": "ส่งข้อมูลสำคัญอย่างปลอดภัยด้วย Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendsBodyNoItems": { - "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", + "message": "แชร์ไฟล์และข้อมูลอย่างปลอดภัยกับทุกคน บนทุกแพลตฟอร์ม ข้อมูลของคุณจะได้รับการเข้ารหัสแบบ End-to-end และจำกัดการเข้าถึง", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "inputRequired": { - "message": "Input is required." + "message": "จำเป็นต้องระบุข้อมูล" }, "required": { - "message": "required" + "message": "จำเป็น" }, "search": { - "message": "Search" + "message": "ค้นหา" }, "inputMinLength": { - "message": "Input must be at least $COUNT$ characters long.", + "message": "ข้อมูลต้องมีความยาวอย่างน้อย $COUNT$ ตัวอักษร", "placeholders": { "count": { "content": "$1", @@ -4006,7 +4016,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "ข้อมูลต้องมีความยาวไม่เกิน $COUNT$ ตัวอักษร", "placeholders": { "count": { "content": "$1", @@ -4015,7 +4025,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "ไม่อนุญาตให้ใช้อักขระต่อไปนี้: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -4024,7 +4034,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "ค่าที่ป้อนต้องมีค่าอย่างน้อย $MIN$", "placeholders": { "min": { "content": "$1", @@ -4033,7 +4043,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "ค่าที่ป้อนต้องมีค่าไม่เกิน $MAX$", "placeholders": { "max": { "content": "$1", @@ -4042,17 +4052,17 @@ } }, "multipleInputEmails": { - "message": "1 or more emails are invalid" + "message": "อีเมลอย่างน้อย 1 รายการไม่ถูกต้อง" }, "inputTrimValidator": { - "message": "Input must not contain only whitespace.", + "message": "ข้อมูลต้องไม่ประกอบด้วยช่องว่างเพียงอย่างเดียว", "description": "Notification to inform the user that a form's input can't contain only whitespace." }, "inputEmail": { - "message": "Input is not an email address." + "message": "ข้อมูลไม่ใช่ที่อยู่อีเมล" }, "fieldsNeedAttention": { - "message": "$COUNT$ field(s) above need your attention.", + "message": "ฟิลด์ด้านบน $COUNT$ รายการต้องการการตรวจสอบ", "placeholders": { "count": { "content": "$1", @@ -4061,10 +4071,10 @@ } }, "singleFieldNeedsAttention": { - "message": "1 field needs your attention." + "message": "1 ฟิลด์ต้องการการตรวจสอบ" }, "multipleFieldsNeedAttention": { - "message": "$COUNT$ fields need your attention.", + "message": "$COUNT$ ฟิลด์ต้องการการตรวจสอบ", "placeholders": { "count": { "content": "$1", @@ -4073,22 +4083,22 @@ } }, "selectPlaceholder": { - "message": "-- Select --" + "message": "-- เลือก --" }, "multiSelectPlaceholder": { - "message": "-- Type to filter --" + "message": "-- พิมพ์เพื่อกรอง --" }, "multiSelectLoading": { - "message": "Retrieving options..." + "message": "กำลังดึงตัวเลือก..." }, "multiSelectNotFound": { - "message": "No items found" + "message": "ไม่พบรายการ" }, "multiSelectClearAll": { - "message": "Clear all" + "message": "ล้างทั้งหมด" }, "plusNMore": { - "message": "+ $QUANTITY$ more", + "message": "+ อีก $QUANTITY$ รายการ", "placeholders": { "quantity": { "content": "$1", @@ -4097,141 +4107,141 @@ } }, "submenu": { - "message": "Submenu" + "message": "เมนูย่อย" }, "toggleCollapse": { - "message": "Toggle collapse", + "message": "ย่อ/ขยาย", "description": "Toggling an expand/collapse state." }, "aliasDomain": { - "message": "Alias domain" + "message": "โดเมนนามแฝง" }, "autofillOnPageLoadSetToDefault": { - "message": "Autofill on page load set to use default setting.", + "message": "ตั้งค่าการป้อนอัตโนมัติเมื่อโหลดหน้าเว็บเป็นค่าเริ่มต้นแล้ว", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "ไม่สามารถป้อนอัตโนมัติได้" }, "cannotAutofillExactMatch": { - "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + "message": "การจับคู่เริ่มต้นตั้งค่าไว้ที่ 'ตรงกันทุกตัวอักษร' เว็บไซต์ปัจจุบันไม่ตรงกับรายละเอียดข้อมูลเข้าสู่ระบบที่บันทึกไว้สำหรับรายการนี้อย่างถูกต้อง" }, "okay": { - "message": "Okay" + "message": "ตกลง" }, "toggleSideNavigation": { - "message": "Toggle side navigation" + "message": "สลับแถบนำทางด้านข้าง" }, "skipToContent": { - "message": "Skip to content" + "message": "ข้ามไปที่เนื้อหา" }, "bitwardenOverlayButton": { - "message": "Bitwarden autofill menu button", + "message": "ปุ่มเมนูป้อนอัตโนมัติ Bitwarden", "description": "Page title for the iframe containing the overlay button" }, "toggleBitwardenVaultOverlay": { - "message": "Toggle Bitwarden autofill menu", + "message": "สลับเมนูป้อนอัตโนมัติ Bitwarden", "description": "Screen reader and tool tip label for the overlay button" }, "bitwardenVault": { - "message": "Bitwarden autofill menu", + "message": "เมนูป้อนอัตโนมัติ Bitwarden", "description": "Page title in overlay" }, "unlockYourAccountToViewMatchingLogins": { - "message": "Unlock your account to view matching logins", + "message": "ปลดล็อกบัญชีเพื่อดูข้อมูลเข้าสู่ระบบที่ตรงกัน", "description": "Text to display in overlay when the account is locked." }, "unlockYourAccountToViewAutofillSuggestions": { - "message": "Unlock your account to view autofill suggestions", + "message": "ปลดล็อกบัญชีเพื่อดูคำแนะนำการป้อนอัตโนมัติ", "description": "Text to display in overlay when the account is locked." }, "unlockAccount": { - "message": "Unlock account", + "message": "ปลดล็อกบัญชี", "description": "Button text to display in overlay when the account is locked." }, "unlockAccountAria": { - "message": "Unlock your account, opens in a new window", + "message": "ปลดล็อกบัญชี เปิดในหน้าต่างใหม่", "description": "Screen reader text (aria-label) for unlock account button in overlay" }, "totpCodeAria": { - "message": "Time-based One-Time Password Verification Code", + "message": "รหัสยืนยันรหัสผ่านใช้ครั้งเดียวตามเวลา", "description": "Aria label for the totp code displayed in the inline menu for autofill" }, "totpSecondsSpanAria": { - "message": "Time remaining before current TOTP expires", + "message": "เวลาที่เหลือก่อน TOTP ปัจจุบันหมดอายุ", "description": "Aria label for the totp seconds displayed in the inline menu for autofill" }, "fillCredentialsFor": { - "message": "Fill credentials for", + "message": "ป้อนข้อมูลประจำตัวสำหรับ", "description": "Screen reader text for when overlay item is in focused" }, "partialUsername": { - "message": "Partial username", + "message": "ชื่อผู้ใช้บางส่วน", "description": "Screen reader text for when a login item is focused where a partial username is displayed. SR will announce this phrase before reading the text of the partial username" }, "noItemsToShow": { - "message": "No items to show", + "message": "ไม่มีรายการที่จะแสดง", "description": "Text to show in overlay if there are no matching items" }, "newItem": { - "message": "New item", + "message": "รายการใหม่", "description": "Button text to display in overlay when there are no matching items" }, "addNewVaultItem": { - "message": "Add new vault item", + "message": "เพิ่มรายการตู้นิรภัยใหม่", "description": "Screen reader text (aria-label) for new item button in overlay" }, "newLogin": { - "message": "New login", + "message": "ข้อมูลเข้าสู่ระบบใหม่", "description": "Button text to display within inline menu when there are no matching items on a login field" }, "addNewLoginItemAria": { - "message": "Add new vault login item, opens in a new window", + "message": "เพิ่มข้อมูลเข้าสู่ระบบตู้นิรภัยใหม่ เปิดในหน้าต่างใหม่", "description": "Screen reader text (aria-label) for new login button within inline menu" }, "newCard": { - "message": "New card", + "message": "บัตรใหม่", "description": "Button text to display within inline menu when there are no matching items on a credit card field" }, "addNewCardItemAria": { - "message": "Add new vault card item, opens in a new window", + "message": "เพิ่มรายการบัตรตู้นิรภัยใหม่ เปิดในหน้าต่างใหม่", "description": "Screen reader text (aria-label) for new card button within inline menu" }, "newIdentity": { - "message": "New identity", + "message": "ข้อมูลระบุตัวตนใหม่", "description": "Button text to display within inline menu when there are no matching items on an identity field" }, "addNewIdentityItemAria": { - "message": "Add new vault identity item, opens in a new window", + "message": "เพิ่มข้อมูลระบุตัวตนตู้นิรภัยใหม่ เปิดในหน้าต่างใหม่", "description": "Screen reader text (aria-label) for new identity button within inline menu" }, "bitwardenOverlayMenuAvailable": { - "message": "Bitwarden autofill menu available. Press the down arrow key to select.", + "message": "เมนูป้อนอัตโนมัติ Bitwarden พร้อมใช้งาน กดปุ่มลูกศรลงเพื่อเลือก", "description": "Screen reader text for announcing when the overlay opens on the page" }, "turnOn": { - "message": "Turn on" + "message": "เปิด" }, "ignore": { - "message": "Ignore" + "message": "เพิกเฉย" }, "importError": { - "message": "Import error" + "message": "ข้อผิดพลาดในการนำเข้า" }, "importErrorDesc": { - "message": "There was a problem with the data you tried to import. Please resolve the errors listed below in your source file and try again." + "message": "เกิดปัญหากับข้อมูลที่คุณพยายามนำเข้า โปรดแก้ไขข้อผิดพลาดที่ระบุด้านล่างในไฟล์ต้นฉบับแล้วลองอีกครั้ง" }, "resolveTheErrorsBelowAndTryAgain": { - "message": "Resolve the errors below and try again." + "message": "แก้ไขข้อผิดพลาดด้านล่างแล้วลองอีกครั้ง" }, "description": { - "message": "Description" + "message": "คำอธิบาย" }, "importSuccess": { - "message": "Data successfully imported" + "message": "นำเข้าข้อมูลสำเร็จแล้ว" }, "importSuccessNumberOfItems": { - "message": "A total of $AMOUNT$ items were imported.", + "message": "นำเข้าข้อมูลทั้งหมด $AMOUNT$ รายการ", "placeholders": { "amount": { "content": "$1", @@ -4240,46 +4250,46 @@ } }, "tryAgain": { - "message": "Try again" + "message": "ลองอีกครั้ง" }, "verificationRequiredForActionSetPinToContinue": { - "message": "Verification required for this action. Set a PIN to continue." + "message": "จำเป็นต้องยืนยันตัวตนสำหรับการดำเนินการนี้ ตั้งค่า PIN เพื่อดำเนินการต่อ" }, "setPin": { - "message": "ตั้ง PIN" + "message": "ตั้งค่า PIN" }, "verifyWithBiometrics": { - "message": "Verify with biometrics" + "message": "ยืนยันด้วยไบโอเมตริก" }, "awaitingConfirmation": { - "message": "Awaiting confirmation" + "message": "รอการยืนยัน" }, "couldNotCompleteBiometrics": { - "message": "Could not complete biometrics." + "message": "ไม่สามารถดำเนินการไบโอเมตริกให้เสร็จสิ้นได้" }, "needADifferentMethod": { - "message": "Need a different method?" + "message": "ต้องการวิธีอื่นหรือไม่" }, "useMasterPassword": { - "message": "Use master password" + "message": "ใช้รหัสผ่านหลัก" }, "usePin": { - "message": "Use PIN" + "message": "ใช้ PIN" }, "useBiometrics": { - "message": "Use biometrics" + "message": "ใช้ไบโอเมตริก" }, "enterVerificationCodeSentToEmail": { - "message": "Enter the verification code that was sent to your email." + "message": "ป้อนรหัสยืนยันที่ส่งไปที่อีเมลของคุณ" }, "resendCode": { - "message": "Resend code" + "message": "ส่งรหัสอีกครั้ง" }, "total": { - "message": "Total" + "message": "รวม" }, "importWarning": { - "message": "You are importing data to $ORGANIZATION$. Your data may be shared with members of this organization. Do you want to proceed?", + "message": "คุณกำลังนำเข้าข้อมูลไปยัง $ORGANIZATION$ ข้อมูลของคุณอาจถูกแชร์กับสมาชิกขององค์กรนี้ ต้องการดำเนินการต่อหรือไม่", "placeholders": { "organization": { "content": "$1", @@ -4288,67 +4298,67 @@ } }, "duoHealthCheckResultsInNullAuthUrlError": { - "message": "Error connecting with the Duo service. Use a different two-step login method or contact Duo for assistance." + "message": "เกิดข้อผิดพลาดในการเชื่อมต่อกับบริการ Duo ใช้วิธีการเข้าสู่ระบบ 2 ขั้นตอนอื่น หรือติดต่อ Duo เพื่อขอความช่วยเหลือ" }, "duoRequiredForAccount": { - "message": "Duo two-step login is required for your account." + "message": "บัญชีของคุณจำเป็นต้องใช้การเข้าสู่ระบบ 2 ขั้นตอนผ่าน Duo" }, "popoutExtension": { - "message": "Popout extension" + "message": "แยกหน้าต่างส่วนขยาย" }, "launchDuo": { - "message": "Launch Duo" + "message": "เปิดใช้งาน Duo" }, "importFormatError": { - "message": "Data is not formatted correctly. Please check your import file and try again." + "message": "รูปแบบข้อมูลไม่ถูกต้อง โปรดตรวจสอบไฟล์นำเข้าแล้วลองอีกครั้ง" }, "importNothingError": { - "message": "Nothing was imported." + "message": "ไม่มีการนำเข้าข้อมูล" }, "importEncKeyError": { - "message": "Error decrypting the exported file. Your encryption key does not match the encryption key used export the data." + "message": "เกิดข้อผิดพลาดในการถอดรหัสไฟล์ที่ส่งออก กุญแจเข้ารหัสของคุณไม่ตรงกับกุญแจเข้ารหัสที่ใช้ส่งออกข้อมูล" }, "invalidFilePassword": { - "message": "Invalid file password, please use the password you entered when you created the export file." + "message": "รหัสผ่านไฟล์ไม่ถูกต้อง โปรดใช้รหัสผ่านที่คุณป้อนตอนสร้างไฟล์ส่งออก" }, "destination": { - "message": "Destination" + "message": "ปลายทาง" }, "learnAboutImportOptions": { - "message": "Learn about your import options" + "message": "เรียนรู้เกี่ยวกับตัวเลือกการนำเข้า" }, "selectImportFolder": { - "message": "Select a folder" + "message": "เลือกโฟลเดอร์" }, "selectImportCollection": { - "message": "Select a collection" + "message": "เลือกคอลเลกชัน" }, "importTargetHintCollection": { - "message": "Select this option if you want the imported file contents moved to a collection" + "message": "เลือกตัวเลือกนี้หากต้องการย้ายเนื้อหาไฟล์ที่นำเข้าไปยังคอลเลกชัน" }, "importTargetHintFolder": { - "message": "Select this option if you want the imported file contents moved to a folder" + "message": "เลือกตัวเลือกนี้หากต้องการย้ายเนื้อหาไฟล์ที่นำเข้าไปยังโฟลเดอร์" }, "importUnassignedItemsError": { - "message": "File contains unassigned items." + "message": "ไฟล์มีรายการที่ไม่ได้มอบหมาย" }, "selectFormat": { - "message": "Select the format of the import file" + "message": "เลือกรูปแบบของไฟล์นำเข้า" }, "selectImportFile": { - "message": "Select the import file" + "message": "เลือกไฟล์นำเข้า" }, "chooseFile": { "message": "เลือกไฟล์" }, "noFileChosen": { - "message": "ไม่มีไฟล์ที่เลือก" + "message": "ไม่ได้เลือกไฟล์" }, "orCopyPasteFileContents": { - "message": "or copy/paste the import file contents" + "message": "หรือคัดลอก/วางเนื้อหาไฟล์นำเข้า" }, "instructionsFor": { - "message": "$NAME$ Instructions", + "message": "คำแนะนำสำหรับ $NAME$", "description": "The title for the import tool instructions.", "placeholders": { "name": { @@ -4358,200 +4368,200 @@ } }, "confirmVaultImport": { - "message": "Confirm vault import" + "message": "ยืนยันการนำเข้าตู้นิรภัย" }, "confirmVaultImportDesc": { - "message": "This file is password-protected. Please enter the file password to import data." + "message": "ไฟล์นี้มีการป้องกันด้วยรหัสผ่าน โปรดป้อนรหัสผ่านไฟล์เพื่อนำเข้าข้อมูล" }, "confirmFilePassword": { - "message": "Confirm file password" + "message": "ยืนยันรหัสผ่านไฟล์" }, "exportSuccess": { - "message": "Vault data exported" + "message": "ส่งออกข้อมูลตู้นิรภัยแล้ว" }, "typePasskey": { - "message": "Passkey" + "message": "พาสคีย์" }, "accessing": { "message": "กำลังเข้าถึง" }, "loggedInExclamation": { - "message": "Logged in!" + "message": "เข้าสู่ระบบแล้ว!" }, "passkeyNotCopied": { - "message": "Passkey will not be copied" + "message": "จะไม่คัดลอกพาสคีย์" }, "passkeyNotCopiedAlert": { - "message": "The passkey will not be copied to the cloned item. Do you want to continue cloning this item?" + "message": "พาสคีย์จะไม่ถูกคัดลอกไปยังรายการที่โคลน ต้องการโคลนรายการนี้ต่อหรือไม่" }, "logInWithPasskeyQuestion": { - "message": "Log in with passkey?" + "message": "เข้าสู่ระบบด้วยพาสคีย์หรือไม่" }, "passkeyAlreadyExists": { - "message": "A passkey already exists for this application." + "message": "มีพาสคีย์สำหรับแอปพลิเคชันนี้อยู่แล้ว" }, "noPasskeysFoundForThisApplication": { - "message": "No passkeys found for this application." + "message": "ไม่พบพาสคีย์สำหรับแอปพลิเคชันนี้" }, "noMatchingPasskeyLogin": { - "message": "You do not have a matching login for this site." + "message": "คุณไม่มีข้อมูลเข้าสู่ระบบที่ตรงกันสำหรับไซต์นี้" }, "noMatchingLoginsForSite": { - "message": "No matching logins for this site" + "message": "ไม่มีข้อมูลเข้าสู่ระบบที่ตรงกันสำหรับไซต์นี้" }, "searchSavePasskeyNewLogin": { - "message": "Search or save passkey as new login" + "message": "ค้นหาหรือบันทึกพาสคีย์เป็นข้อมูลเข้าสู่ระบบใหม่" }, "confirm": { - "message": "Confirm" + "message": "ยืนยัน" }, "savePasskey": { - "message": "Save passkey" + "message": "บันทึกพาสคีย์" }, "savePasskeyNewLogin": { - "message": "Save passkey as new login" + "message": "บันทึกพาสคีย์เป็นข้อมูลเข้าสู่ระบบใหม่" }, "chooseCipherForPasskeySave": { - "message": "Choose a login to save this passkey to" + "message": "เลือกข้อมูลเข้าสู่ระบบเพื่อบันทึกพาสคีย์นี้" }, "chooseCipherForPasskeyAuth": { - "message": "Choose a passkey to log in with" + "message": "เลือกพาสคีย์เพื่อเข้าสู่ระบบ" }, "passkeyItem": { - "message": "Passkey Item" + "message": "รายการพาสคีย์" }, "overwritePasskey": { - "message": "Overwrite passkey?" + "message": "เขียนทับพาสคีย์หรือไม่" }, "overwritePasskeyAlert": { - "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + "message": "รายการนี้มีพาสคีย์อยู่แล้ว ยืนยันที่จะเขียนทับพาสคีย์ปัจจุบันหรือไม่" }, "featureNotSupported": { - "message": "Feature not yet supported" + "message": "ยังไม่รองรับฟีเจอร์นี้" }, "yourPasskeyIsLocked": { - "message": "Authentication required to use passkey. Verify your identity to continue." + "message": "จำเป็นต้องยืนยันตัวตนเพื่อใช้พาสคีย์ ยืนยันตัวตนของคุณเพื่อดำเนินการต่อ" }, "multifactorAuthenticationCancelled": { - "message": "Multifactor authentication cancelled" + "message": "ยกเลิกการยืนยันตัวตนแบบหลายปัจจัยแล้ว" }, "noLastPassDataFound": { - "message": "No LastPass data found" + "message": "ไม่พบข้อมูล LastPass" }, "incorrectUsernameOrPassword": { - "message": "Incorrect username or password" + "message": "ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง" }, "incorrectPassword": { - "message": "Incorrect password" + "message": "รหัสผ่านไม่ถูกต้อง" }, "incorrectCode": { - "message": "Incorrect code" + "message": "รหัสไม่ถูกต้อง" }, "incorrectPin": { - "message": "Incorrect PIN" + "message": "รหัส PIN ไม่ถูกต้อง" }, "multifactorAuthenticationFailed": { - "message": "Multifactor authentication failed" + "message": "การยืนยันตัวตนแบบหลายปัจจัยล้มเหลว" }, "includeSharedFolders": { - "message": "Include shared folders" + "message": "รวมโฟลเดอร์ที่แชร์" }, "lastPassEmail": { - "message": "LastPass Email" + "message": "อีเมล LastPass" }, "importingYourAccount": { - "message": "Importing your account..." + "message": "กำลังนำเข้าบัญชีของคุณ..." }, "lastPassMFARequired": { - "message": "LastPass multifactor authentication required" + "message": "จำเป็นต้องยืนยันตัวตนแบบหลายปัจจัยของ LastPass" }, "lastPassMFADesc": { - "message": "Enter your one-time passcode from your authentication app" + "message": "ป้อนรหัสผ่านใช้ครั้งเดียวจากแอปยืนยันตัวตนของคุณ" }, "lastPassOOBDesc": { - "message": "Approve the login request in your authentication app or enter a one-time passcode." + "message": "อนุมัติคำขอเข้าสู่ระบบในแอปยืนยันตัวตนของคุณ หรือป้อนรหัสผ่านใช้ครั้งเดียว" }, "passcode": { - "message": "Passcode" + "message": "รหัสผ่าน" }, "lastPassMasterPassword": { - "message": "LastPass master password" + "message": "รหัสผ่านหลัก LastPass" }, "lastPassAuthRequired": { - "message": "LastPass authentication required" + "message": "จำเป็นต้องยืนยันตัวตน LastPass" }, "awaitingSSO": { - "message": "Awaiting SSO authentication" + "message": "กำลังรอการยืนยันตัวตน SSO" }, "awaitingSSODesc": { - "message": "Please continue to log in using your company credentials." + "message": "โปรดดำเนินการเข้าสู่ระบบโดยใช้ข้อมูลประจำตัวของบริษัทของคุณ" }, "seeDetailedInstructions": { - "message": "See detailed instructions on our help site at", + "message": "ดูคำแนะนำโดยละเอียดบนเว็บไซต์ช่วยเหลือของเราที่", "description": "This is followed a by a hyperlink to the help website." }, "importDirectlyFromLastPass": { - "message": "Import directly from LastPass" + "message": "นำเข้าจาก LastPass โดยตรง" }, "importFromCSV": { - "message": "Import from CSV" + "message": "นำเข้าจาก CSV" }, "lastPassTryAgainCheckEmail": { - "message": "Try again or look for an email from LastPass to verify it's you." + "message": "ลองอีกครั้งหรือตรวจสอบอีเมลจาก LastPass เพื่อยืนยันว่าเป็นคุณ" }, "collection": { - "message": "Collection" + "message": "คอลเลกชัน" }, "lastPassYubikeyDesc": { - "message": "Insert the YubiKey associated with your LastPass account into your computer's USB port, then touch its button." + "message": "เสียบ YubiKey ที่เชื่อมโยงกับบัญชี LastPass เข้ากับพอร์ต USB ของคอมพิวเตอร์ แล้วแตะที่ปุ่ม" }, "switchAccount": { - "message": "Switch account" + "message": "สลับบัญชี" }, "switchAccounts": { - "message": "Switch accounts" + "message": "สลับบัญชี" }, "switchToAccount": { - "message": "Switch to account" + "message": "สลับไปที่บัญชี" }, "activeAccount": { - "message": "Active account" + "message": "บัญชีที่ใช้งานอยู่" }, "bitwardenAccount": { - "message": "Bitwarden account" + "message": "บัญชี Bitwarden" }, "availableAccounts": { - "message": "Available accounts" + "message": "บัญชีที่มีอยู่" }, "accountLimitReached": { - "message": "ถึงขีดจำกัดของบัญชีแล้ว กรุณาออกจากระบบบัญชีอื่นเพื่อเพิ่มบัญชีใหม่" + "message": "ถึงขีดจำกัดจำนวนบัญชีแล้ว ออกจากระบบบัญชีหนึ่งเพื่อเพิ่มบัญชีอื่น" }, "active": { - "message": "active" + "message": "ใช้งานอยู่" }, "locked": { - "message": "locked" + "message": "ล็อกอยู่" }, "unlocked": { - "message": "unlocked" + "message": "ปลดล็อกแล้ว" }, "server": { - "message": "server" + "message": "เซิร์ฟเวอร์" }, "hostedAt": { - "message": "hosted at" + "message": "โฮสต์ที่" }, "useDeviceOrHardwareKey": { - "message": "Use your device or hardware key" + "message": "ใช้อุปกรณ์หรือคีย์ฮาร์ดแวร์ของคุณ" }, "justOnce": { - "message": "Just once" + "message": "เพียงครั้งเดียว" }, "alwaysForThisSite": { - "message": "Always for this site" + "message": "เสมอสำหรับไซต์นี้" }, "domainAddedToExcludedDomains": { - "message": "$DOMAIN$ added to excluded domains.", + "message": "เพิ่ม $DOMAIN$ ในโดเมนที่ยกเว้นแล้ว", "placeholders": { "domain": { "content": "$1", @@ -4560,126 +4570,126 @@ } }, "commonImportFormats": { - "message": "Common formats", + "message": "รูปแบบทั่วไป", "description": "Label indicating the most common import formats" }, "uriMatchDefaultStrategyHint": { - "message": "URI match detection is how Bitwarden identifies autofill suggestions.", + "message": "การตรวจสอบการจับคู่ URI คือวิธีที่ Bitwarden ระบุคำแนะนำการป้อนอัตโนมัติ", "description": "Explains to the user that URI match detection determines how Bitwarden suggests autofill options, and clarifies that this default strategy applies when no specific match detection is set for a login item." }, "regExAdvancedOptionWarning": { - "message": "\"Regular expression\" is an advanced option with increased risk of exposing credentials.", + "message": "\"Regular expression\" เป็นตัวเลือกขั้นสูงที่มีความเสี่ยงสูงในการเปิดเผยข้อมูลเข้าสู่ระบบ", "description": "Content for dialog which warns a user when selecting 'regular expression' matching strategy as a cipher match strategy" }, "startsWithAdvancedOptionWarning": { - "message": "\"Starts with\" is an advanced option with increased risk of exposing credentials.", + "message": "\"ขึ้นต้นด้วย\" เป็นตัวเลือกขั้นสูงที่มีความเสี่ยงสูงในการเปิดเผยข้อมูลเข้าสู่ระบบ", "description": "Content for dialog which warns a user when selecting 'starts with' matching strategy as a cipher match strategy" }, "uriMatchWarningDialogLink": { - "message": "More about match detection", + "message": "เพิ่มเติมเกี่ยวกับการตรวจสอบการจับคู่", "description": "Link to match detection docs on warning dialog for advance match strategy" }, "uriAdvancedOption": { - "message": "Advanced options", + "message": "ตัวเลือกขั้นสูง", "description": "Advanced option placeholder for uri option component" }, "confirmContinueToBrowserSettingsTitle": { - "message": "Continue to browser settings?", + "message": "ไปที่การตั้งค่าเบราว์เซอร์หรือไม่", "description": "Title for dialog which asks if the user wants to proceed to a relevant browser settings page" }, "confirmContinueToHelpCenter": { - "message": "Continue to Help Center?", + "message": "ไปที่ศูนย์ช่วยเหลือหรือไม่", "description": "Title for dialog which asks if the user wants to proceed to a relevant Help Center page" }, "confirmContinueToHelpCenterPasswordManagementContent": { - "message": "Change your browser's autofill and password management settings.", + "message": "เปลี่ยนการตั้งค่าการป้อนอัตโนมัติและการจัดการรหัสผ่านของเบราว์เซอร์", "description": "Body content for dialog which asks if the user wants to proceed to the Help Center's page about browser password management settings" }, "confirmContinueToHelpCenterKeyboardShortcutsContent": { - "message": "You can view and set extension shortcuts in your browser's settings.", + "message": "คุณสามารถดูและตั้งค่าทางลัดส่วนขยายได้ในการตั้งค่าของเบราว์เซอร์", "description": "Body content for dialog which asks if the user wants to proceed to the Help Center's page about browser keyboard shortcut settings" }, "confirmContinueToBrowserPasswordManagementSettingsContent": { - "message": "Change your browser's autofill and password management settings.", + "message": "เปลี่ยนการตั้งค่าการป้อนอัตโนมัติและการจัดการรหัสผ่านของเบราว์เซอร์", "description": "Body content for dialog which asks if the user wants to proceed to the browser's password management settings page" }, "confirmContinueToBrowserKeyboardShortcutSettingsContent": { - "message": "You can view and set extension shortcuts in your browser's settings.", + "message": "คุณสามารถดูและตั้งค่าทางลัดส่วนขยายได้ในการตั้งค่าของเบราว์เซอร์", "description": "Body content for dialog which asks if the user wants to proceed to the browser's keyboard shortcut settings page" }, "overrideDefaultBrowserAutofillTitle": { - "message": "Make Bitwarden your default password manager?", + "message": "ตั้งให้ Bitwarden เป็นตัวจัดการรหัสผ่านเริ่มต้นหรือไม่", "description": "Dialog title facilitating the ability to override a chrome browser's default autofill behavior" }, "overrideDefaultBrowserAutofillDescription": { - "message": "Ignoring this option may cause conflicts between Bitwarden autofill suggestions and your browser's.", + "message": "การเพิกเฉยตัวเลือกนี้อาจทำให้เกิดความขัดแย้งระหว่างคำแนะนำการป้อนอัตโนมัติของ Bitwarden กับของเบราว์เซอร์", "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" }, "overrideDefaultBrowserAutoFillSettings": { - "message": "Make Bitwarden your default password manager", + "message": "ตั้งให้ Bitwarden เป็นตัวจัดการรหัสผ่านเริ่มต้น", "description": "Label for the setting that allows overriding the default browser autofill settings" }, "privacyPermissionAdditionNotGrantedTitle": { - "message": "Unable to set Bitwarden as the default password manager", + "message": "ไม่สามารถตั้งค่า Bitwarden เป็นตัวจัดการรหัสผ่านเริ่มต้นได้", "description": "Title for the dialog that appears when the user has not granted the extension permission to set privacy settings" }, "privacyPermissionAdditionNotGrantedDescription": { - "message": "You must grant browser privacy permissions to Bitwarden to set it as the default password manager.", + "message": "คุณต้องอนุญาตสิทธิ์ความเป็นส่วนตัวของเบราว์เซอร์ให้ Bitwarden เพื่อตั้งค่าเป็นตัวจัดการรหัสผ่านเริ่มต้น", "description": "Description for the dialog that appears when the user has not granted the extension permission to set privacy settings" }, "makeDefault": { - "message": "Make default", + "message": "ตั้งเป็นค่าเริ่มต้น", "description": "Button text for the setting that allows overriding the default browser autofill settings" }, "saveCipherAttemptSuccess": { - "message": "Credentials saved successfully!", + "message": "บันทึกข้อมูลเข้าสู่ระบบสำเร็จแล้ว!", "description": "Notification message for when saving credentials has succeeded." }, "passwordSaved": { - "message": "Password saved!", + "message": "บันทึกรหัสผ่านแล้ว!", "description": "Notification message for when saving credentials has succeeded." }, "updateCipherAttemptSuccess": { - "message": "Credentials updated successfully!", + "message": "อัปเดตข้อมูลเข้าสู่ระบบสำเร็จแล้ว!", "description": "Notification message for when updating credentials has succeeded." }, "passwordUpdated": { - "message": "Password updated!", + "message": "อัปเดตรหัสผ่านแล้ว!", "description": "Notification message for when updating credentials has succeeded." }, "saveCipherAttemptFailed": { - "message": "Error saving credentials. Check console for details.", + "message": "เกิดข้อผิดพลาดในการบันทึกข้อมูลเข้าสู่ระบบ ตรวจสอบรายละเอียดในคอนโซล", "description": "Notification message for when saving credentials has failed." }, "success": { - "message": "Success" + "message": "สำเร็จ" }, "removePasskey": { - "message": "Remove passkey" + "message": "เอาพาสคีย์ออก" }, "passkeyRemoved": { - "message": "Passkey removed" + "message": "เอาพาสคีย์ออกแล้ว" }, "autofillSuggestions": { - "message": "Autofill suggestions" + "message": "คำแนะนำการป้อนอัตโนมัติ" }, "itemSuggestions": { - "message": "Suggested items" + "message": "รายการที่แนะนำ" }, "autofillSuggestionsTip": { - "message": "Save a login item for this site to autofill" + "message": "บันทึกข้อมูลเข้าสู่ระบบสำหรับไซต์นี้เพื่อป้อนอัตโนมัติ" }, "yourVaultIsEmpty": { - "message": "Your vault is empty" + "message": "ตู้นิรภัยของคุณว่างเปล่า" }, "noItemsMatchSearch": { - "message": "No items match your search" + "message": "ไม่พบรายการที่ตรงกับการค้นหา" }, "clearFiltersOrTryAnother": { - "message": "Clear filters or try another search term" + "message": "ล้างตัวกรองหรือลองค้นหาด้วยคำอื่น" }, "copyInfoTitle": { - "message": "Copy info - $ITEMNAME$", + "message": "คัดลอกข้อมูล - $ITEMNAME$", "description": "Title for a button that opens a menu with options to copy information from an item.", "placeholders": { "itemname": { @@ -4689,7 +4699,7 @@ } }, "copyNoteTitle": { - "message": "Copy Note - $ITEMNAME$", + "message": "คัดลอกโน้ต - $ITEMNAME$", "description": "Title for a button copies a note to the clipboard.", "placeholders": { "itemname": { @@ -4699,7 +4709,7 @@ } }, "moreOptionsLabel": { - "message": "More options, $ITEMNAME$", + "message": "ตัวเลือกเพิ่มเติม $ITEMNAME$", "description": "Aria label for a button that opens a menu with more options for an item.", "placeholders": { "itemname": { @@ -4709,7 +4719,7 @@ } }, "moreOptionsTitle": { - "message": "More options - $ITEMNAME$", + "message": "ตัวเลือกเพิ่มเติม - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", "placeholders": { "itemname": { @@ -4719,7 +4729,7 @@ } }, "viewItemTitle": { - "message": "View item - $ITEMNAME$", + "message": "ดูรายการ - $ITEMNAME$", "description": "Title for a link that opens a view for an item.", "placeholders": { "itemname": { @@ -4729,7 +4739,7 @@ } }, "viewItemTitleWithField": { - "message": "View item - $ITEMNAME$ - $FIELD$", + "message": "ดูรายการ - $ITEMNAME$ - $FIELD$", "description": "Title for a link that opens a view for an item.", "placeholders": { "itemname": { @@ -4743,7 +4753,7 @@ } }, "autofillTitle": { - "message": "Autofill - $ITEMNAME$", + "message": "ป้อนอัตโนมัติ - $ITEMNAME$", "description": "Title for a button that autofills a login item.", "placeholders": { "itemname": { @@ -4753,7 +4763,7 @@ } }, "autofillTitleWithField": { - "message": "Autofill - $ITEMNAME$ - $FIELD$", + "message": "ป้อนอัตโนมัติ - $ITEMNAME$ - $FIELD$", "description": "Title for a button that autofills a login item.", "placeholders": { "itemname": { @@ -4767,7 +4777,7 @@ } }, "copyFieldCipherName": { - "message": "Copy $FIELD$, $CIPHERNAME$", + "message": "คัดลอก $FIELD$, $CIPHERNAME$", "description": "Title for a button that copies a field value to the clipboard.", "placeholders": { "field": { @@ -4781,49 +4791,49 @@ } }, "noValuesToCopy": { - "message": "No values to copy" + "message": "ไม่มีค่าให้คัดลอก" }, "assignToCollections": { - "message": "Assign to collections" + "message": "มอบหมายให้คอลเลกชัน" }, "copyEmail": { - "message": "Copy email" + "message": "คัดลอกอีเมล" }, "copyPhone": { - "message": "Copy phone" + "message": "คัดลอกเบอร์โทรศัพท์" }, "copyAddress": { - "message": "Copy address" + "message": "คัดลอกที่อยู่" }, "adminConsole": { - "message": "Admin Console" + "message": "คอนโซลผู้ดูแลระบบ" }, "accountSecurity": { "message": "ความปลอดภัยของบัญชี" }, "phishingBlocker": { - "message": "Phishing Blocker" + "message": "ตัวบล็อกฟิชชิง" }, "enablePhishingDetection": { - "message": "Phishing detection" + "message": "การตรวจจับฟิชชิง" }, "enablePhishingDetectionDesc": { - "message": "Display warning before accessing suspected phishing sites" + "message": "แสดงคำเตือนก่อนเข้าถึงไซต์ที่ต้องสงสัยว่าเป็นฟิชชิง" }, "notifications": { - "message": "Notifications" + "message": "การแจ้งเตือน" }, "appearance": { - "message": "Appearance" + "message": "รูปลักษณ์" }, "errorAssigningTargetCollection": { - "message": "Error assigning target collection." + "message": "เกิดข้อผิดพลาดในการมอบหมายคอลเลกชันปลายทาง" }, "errorAssigningTargetFolder": { - "message": "Error assigning target folder." + "message": "เกิดข้อผิดพลาดในการมอบหมายโฟลเดอร์ปลายทาง" }, "viewItemsIn": { - "message": "View items in $NAME$", + "message": "ดูรายการใน $NAME$", "description": "Button to view the contents of a folder or collection", "placeholders": { "name": { @@ -4833,7 +4843,7 @@ } }, "backTo": { - "message": "Back to $NAME$", + "message": "กลับไปที่ $NAME$", "description": "Navigate back to a previous folder or collection", "placeholders": { "name": { @@ -4843,10 +4853,10 @@ } }, "new": { - "message": "New" + "message": "ใหม่" }, "removeItem": { - "message": "Remove $NAME$", + "message": "เอา $NAME$ ออก", "description": "Remove a selected option, such as a folder or collection", "placeholders": { "name": { @@ -4856,44 +4866,44 @@ } }, "itemsWithNoFolder": { - "message": "Items with no folder" + "message": "รายการที่ไม่มีโฟลเดอร์" }, "itemDetails": { - "message": "Item details" + "message": "รายละเอียดรายการ" }, "itemName": { - "message": "Item name" + "message": "ชื่อรายการ" }, "organizationIsDeactivated": { - "message": "Organization is deactivated" + "message": "องค์กรถูกปิดใช้งาน" }, "owner": { - "message": "Owner" + "message": "เจ้าของ" }, "selfOwnershipLabel": { - "message": "You", + "message": "คุณ", "description": "Used as a label to indicate that the user is the owner of an item." }, "contactYourOrgAdmin": { - "message": "Items in deactivated organizations cannot be accessed. Contact your organization owner for assistance." + "message": "ไม่สามารถเข้าถึงรายการในองค์กรที่ถูกปิดใช้งาน ติดต่อเจ้าขององค์กรเพื่อขอความช่วยเหลือ" }, "additionalInformation": { - "message": "Additional information" + "message": "ข้อมูลเพิ่มเติม" }, "itemHistory": { - "message": "ประวัติการแก้ไขรายการ" + "message": "ประวัติรายการ" }, "lastEdited": { - "message": "แก้ไขล่าสุดเมื่อ" + "message": "แก้ไขล่าสุด" }, "ownerYou": { - "message": "Owner: You" + "message": "เจ้าของ: คุณ" }, "linked": { - "message": "Linked" + "message": "เชื่อมโยง" }, "copySuccessful": { - "message": "Copy Successful" + "message": "คัดลอกสำเร็จ" }, "upload": { "message": "อัปโหลด" @@ -4902,10 +4912,10 @@ "message": "เพิ่มไฟล์แนบ" }, "maxFileSizeSansPunctuation": { - "message": "ขนาดไฟล์สูงสุด คือ 500 MB" + "message": "ขนาดไฟล์สูงสุดคือ 500 MB" }, "deleteAttachmentName": { - "message": "Delete attachment $NAME$", + "message": "ลบไฟล์แนบ $NAME$", "placeholders": { "name": { "content": "$1", @@ -4914,7 +4924,7 @@ } }, "downloadAttachmentName": { - "message": "Download $NAME$", + "message": "ดาวน์โหลด $NAME$", "placeholders": { "name": { "content": "$1", @@ -4923,55 +4933,55 @@ } }, "downloadBitwarden": { - "message": "Download Bitwarden" + "message": "ดาวน์โหลด Bitwarden" }, "downloadBitwardenOnAllDevices": { - "message": "Download Bitwarden on all devices" + "message": "ดาวน์โหลด Bitwarden บนทุกอุปกรณ์" }, "getTheMobileApp": { - "message": "Get the mobile app" + "message": "รับแอปมือถือ" }, "getTheMobileAppDesc": { - "message": "Access your passwords on the go with the Bitwarden mobile app." + "message": "เข้าถึงรหัสผ่านของคุณได้ทุกที่ด้วยแอปมือถือ Bitwarden" }, "getTheDesktopApp": { - "message": "Get the desktop app" + "message": "รับแอปเดสก์ท็อป" }, "getTheDesktopAppDesc": { - "message": "Access your vault without a browser, then set up unlock with biometrics to expedite unlocking in both the desktop app and browser extension." + "message": "เข้าถึงตู้นิรภัยโดยไม่ต้องใช้เบราว์เซอร์ จากนั้นตั้งค่าการปลดล็อกด้วยไบโอเมตริกเพื่อเร่งการปลดล็อกทั้งในแอปเดสก์ท็อปและส่วนขยายเบราว์เซอร์" }, "downloadFromBitwardenNow": { - "message": "Download from bitwarden.com now" + "message": "ดาวน์โหลดจาก bitwarden.com เลย" }, "getItOnGooglePlay": { - "message": "Get it on Google Play" + "message": "ดาวน์โหลดได้จาก Google Play" }, "downloadOnTheAppStore": { - "message": "Download on the App Store" + "message": "ดาวน์โหลดได้จาก App Store" }, "permanentlyDeleteAttachmentConfirmation": { - "message": "Are you sure you want to permanently delete this attachment?" + "message": "ยืนยันที่จะลบไฟล์แนบนี้ถาวรหรือไม่" }, "premium": { - "message": "Premium" + "message": "พรีเมียม" }, "unlockFeaturesWithPremium": { - "message": "Unlock reporting, emergency access, and more security features with Premium." + "message": "ปลดล็อกการรายงาน การเข้าถึงฉุกเฉิน และฟีเจอร์ความปลอดภัยเพิ่มเติมด้วยพรีเมียม" }, "freeOrgsCannotUseAttachments": { - "message": "Free organizations cannot use attachments" + "message": "องค์กรฟรีไม่สามารถใช้ไฟล์แนบได้" }, "filters": { - "message": "Filters" + "message": "ตัวกรอง" }, "filterVault": { - "message": "Filter vault" + "message": "กรองตู้นิรภัย" }, "filterApplied": { - "message": "One filter applied" + "message": "ใช้ตัวกรอง 1 รายการ" }, "filterAppliedPlural": { - "message": "$COUNT$ filters applied", + "message": "ใช้ตัวกรอง $COUNT$ รายการ", "placeholders": { "count": { "content": "$1", @@ -4980,16 +4990,16 @@ } }, "personalDetails": { - "message": "Personal details" + "message": "ข้อมูลส่วนตัว" }, "identification": { - "message": "Identification" + "message": "เอกสารระบุตัวตน" }, "contactInfo": { - "message": "Contact info" + "message": "ข้อมูลติดต่อ" }, "downloadAttachment": { - "message": "Download - $ITEMNAME$", + "message": "ดาวน์โหลด - $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -4998,23 +5008,23 @@ } }, "cardNumberEndsWith": { - "message": "card number ends with", + "message": "หมายเลขบัตรลงท้ายด้วย", "description": "Used within the inline menu to provide an aria description when users are attempting to fill a card cipher." }, "loginCredentials": { - "message": "Login credentials" + "message": "ข้อมูลเข้าสู่ระบบ" }, "authenticatorKey": { - "message": "Authenticator key" + "message": "คีย์ยืนยันตัวตน" }, "autofillOptions": { - "message": "ตัวเลือกในการป้อนอัตโนมัติ" + "message": "ตัวเลือกการป้อนอัตโนมัติ" }, "websiteUri": { - "message": "Website (URI)" + "message": "เว็บไซต์ (URI)" }, "websiteUriCount": { - "message": "Website (URI) $COUNT$", + "message": "เว็บไซต์ (URI) $COUNT$", "description": "Label for an input field that contains a website URI. The input field is part of a list of fields, and the count indicates the position of the field in the list.", "placeholders": { "count": { @@ -5024,16 +5034,16 @@ } }, "websiteAdded": { - "message": "Website added" + "message": "เพิ่มเว็บไซต์แล้ว" }, "addWebsite": { - "message": "Add website" + "message": "เพิ่มเว็บไซต์" }, "deleteWebsite": { - "message": "Delete website" + "message": "ลบเว็บไซต์" }, "defaultLabel": { - "message": "Default ($VALUE$)", + "message": "ค่าเริ่มต้น ($VALUE$)", "description": "A label that indicates the default value for a field with the current default value in parentheses.", "placeholders": { "value": { @@ -5043,7 +5053,7 @@ } }, "defaultLabelWithValue": { - "message": "Default ( $VALUE$ )", + "message": "ค่าเริ่มต้น ( $VALUE$ )", "description": "A label that indicates the default value for a field with the current default value in parentheses.", "placeholders": { "value": { @@ -5053,7 +5063,7 @@ } }, "showMatchDetection": { - "message": "Show match detection $WEBSITE$", + "message": "แสดงการตรวจสอบการจับคู่ $WEBSITE$", "placeholders": { "website": { "content": "$1", @@ -5062,7 +5072,7 @@ } }, "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", + "message": "ซ่อนการตรวจสอบการจับคู่ $WEBSITE$", "placeholders": { "website": { "content": "$1", @@ -5071,19 +5081,19 @@ } }, "autoFillOnPageLoad": { - "message": "Autofill on page load?" + "message": "ป้อนอัตโนมัติเมื่อโหลดหน้าเว็บหรือไม่" }, "cardExpiredTitle": { - "message": "Expired card" + "message": "บัตรหมดอายุ" }, "cardExpiredMessage": { - "message": "If you've renewed it, update the card's information" + "message": "หากคุณต่ออายุแล้ว ให้อัปเดตข้อมูลบัตร" }, "cardDetails": { - "message": "Card details" + "message": "รายละเอียดบัตร" }, "cardBrandDetails": { - "message": "$BRAND$ details", + "message": "รายละเอียด $BRAND$", "placeholders": { "brand": { "content": "$1", @@ -5092,40 +5102,40 @@ } }, "showAnimations": { - "message": "Show animations" + "message": "แสดงภาพเคลื่อนไหว" }, "addAccount": { "message": "เพิ่มบัญชี" }, "loading": { - "message": "Loading" + "message": "กำลังโหลด" }, "data": { - "message": "Data" + "message": "ข้อมูล" }, "passkeys": { - "message": "Passkeys", + "message": "พาสคีย์", "description": "A section header for a list of passkeys." }, "passwords": { - "message": "Passwords", + "message": "รหัสผ่าน", "description": "A section header for a list of passwords." }, "logInWithPasskeyAriaLabel": { - "message": "Log in with passkey", + "message": "เข้าสู่ระบบด้วยพาสคีย์", "description": "ARIA label for the inline menu button that logs in with a passkey." }, "assign": { - "message": "Assign" + "message": "มอบหมาย" }, "bulkCollectionAssignmentDialogDescriptionSingular": { - "message": "Only organization members with access to these collections will be able to see the item." + "message": "เฉพาะสมาชิกองค์กรที่มีสิทธิ์เข้าถึงคอลเลกชันเหล่านี้เท่านั้นที่จะเห็นรายการนี้" }, "bulkCollectionAssignmentDialogDescriptionPlural": { - "message": "Only organization members with access to these collections will be able to see the items." + "message": "เฉพาะสมาชิกองค์กรที่มีสิทธิ์เข้าถึงคอลเลกชันเหล่านี้เท่านั้นที่จะเห็นรายการเหล่านี้" }, "bulkCollectionAssignmentWarning": { - "message": "You have selected $TOTAL_COUNT$ items. You cannot update $READONLY_COUNT$ of the items because you do not have edit permissions.", + "message": "คุณเลือก $TOTAL_COUNT$ รายการ คุณไม่สามารถอัปเดต $READONLY_COUNT$ รายการได้เนื่องจากคุณไม่มีสิทธิ์แก้ไข", "placeholders": { "total_count": { "content": "$1", @@ -5140,34 +5150,34 @@ "message": "เพิ่มฟิลด์" }, "add": { - "message": "Add" + "message": "เพิ่ม" }, "fieldType": { - "message": "Field type" + "message": "ประเภทฟิลด์" }, "fieldLabel": { - "message": "Field label" + "message": "ป้ายกำกับฟิลด์" }, "textHelpText": { - "message": "ใช้ช่องข้อความสำหรับเก็บข้อมูล เช่น คำถามเพื่อความปลอดภัย" + "message": "ใช้ฟิลด์ข้อความสำหรับข้อมูลเช่นคำถามความปลอดภัย" }, "hiddenHelpText": { - "message": "Use hidden fields for sensitive data like a password" + "message": "ใช้ฟิลด์ซ่อนสำหรับข้อมูลที่ละเอียดอ่อนเช่นรหัสผ่าน" }, "checkBoxHelpText": { - "message": "Use checkboxes if you'd like to autofill a form's checkbox, like a remember email" + "message": "ใช้ช่องทำเครื่องหมายหากคุณต้องการป้อนข้อมูลลงในช่องทำเครื่องหมายของแบบฟอร์มโดยอัตโนมัติ เช่น การจดจำอีเมล" }, "linkedHelpText": { - "message": "Use a linked field when you are experiencing autofill issues for a specific website." + "message": "ใช้ฟิลด์เชื่อมโยงเมื่อคุณประสบปัญหาการป้อนอัตโนมัติสำหรับเว็บไซต์ใดเว็บไซต์หนึ่งโดยเฉพาะ" }, "linkedLabelHelpText": { - "message": "Enter the the field's html id, name, aria-label, or placeholder." + "message": "ป้อน html id, name, aria-label หรือ placeholder ของฟิลด์" }, "editField": { - "message": "Edit field" + "message": "แก้ไขฟิลด์" }, "editFieldLabel": { - "message": "Edit $LABEL$", + "message": "แก้ไข $LABEL$", "placeholders": { "label": { "content": "$1", @@ -5176,7 +5186,7 @@ } }, "deleteCustomField": { - "message": "Delete $LABEL$", + "message": "ลบ $LABEL$", "placeholders": { "label": { "content": "$1", @@ -5194,7 +5204,7 @@ } }, "reorderToggleButton": { - "message": "Reorder $LABEL$. Use arrow key to move item up or down.", + "message": "จัดลำดับ $LABEL$ ใหม่ ใช้ปุ่มลูกศรเพื่อเลื่อนรายการขึ้นหรือลง", "placeholders": { "label": { "content": "$1", @@ -5203,10 +5213,10 @@ } }, "reorderWebsiteUriButton": { - "message": "Reorder website URI. Use arrow key to move item up or down." + "message": "จัดลำดับ URI เว็บไซต์ใหม่ ใช้ปุ่มลูกศรเพื่อเลื่อนรายการขึ้นหรือลง" }, "reorderFieldUp": { - "message": "$LABEL$ moved up, position $INDEX$ of $LENGTH$", + "message": "เลื่อน $LABEL$ ขึ้น ตำแหน่งที่ $INDEX$ จาก $LENGTH$", "placeholders": { "label": { "content": "$1", @@ -5223,13 +5233,13 @@ } }, "selectCollectionsToAssign": { - "message": "Select collections to assign" + "message": "เลือกคอลเลกชันที่จะมอบหมาย" }, "personalItemTransferWarningSingular": { - "message": "1 item will be permanently transferred to the selected organization. You will no longer own this item." + "message": "1 รายการจะถูกโอนย้ายไปยังองค์กรที่เลือกอย่างถาวร คุณจะไม่ได้เป็นเจ้าของรายการนี้อีกต่อไป" }, "personalItemsTransferWarningPlural": { - "message": "$PERSONAL_ITEMS_COUNT$ items will be permanently transferred to the selected organization. You will no longer own these items.", + "message": "$PERSONAL_ITEMS_COUNT$ รายการจะถูกโอนย้ายไปยังองค์กรที่เลือกอย่างถาวร คุณจะไม่ได้เป็นเจ้าของรายการเหล่านี้อีกต่อไป", "placeholders": { "personal_items_count": { "content": "$1", @@ -5238,7 +5248,7 @@ } }, "personalItemWithOrgTransferWarningSingular": { - "message": "1 item will be permanently transferred to $ORG$. You will no longer own this item.", + "message": "1 รายการจะถูกโอนย้ายไปยัง $ORG$ อย่างถาวร คุณจะไม่ได้เป็นเจ้าของรายการนี้อีกต่อไป", "placeholders": { "org": { "content": "$1", @@ -5247,7 +5257,7 @@ } }, "personalItemsWithOrgTransferWarningPlural": { - "message": "$PERSONAL_ITEMS_COUNT$ items will be permanently transferred to $ORG$. You will no longer own these items.", + "message": "$PERSONAL_ITEMS_COUNT$ รายการจะถูกโอนย้ายไปยัง $ORG$ อย่างถาวร คุณจะไม่ได้เป็นเจ้าของรายการเหล่านี้อีกต่อไป", "placeholders": { "personal_items_count": { "content": "$1", @@ -5260,13 +5270,13 @@ } }, "successfullyAssignedCollections": { - "message": "Successfully assigned collections" + "message": "มอบหมายคอลเลกชันสำเร็จแล้ว" }, "nothingSelected": { - "message": "You have not selected anything." + "message": "คุณไม่ได้เลือกสิ่งใดเลย" }, "itemsMovedToOrg": { - "message": "Items moved to $ORGNAME$", + "message": "ย้ายรายการไปที่ $ORGNAME$ แล้ว", "placeholders": { "orgname": { "content": "$1", @@ -5275,7 +5285,7 @@ } }, "itemMovedToOrg": { - "message": "Item moved to $ORGNAME$", + "message": "ย้ายรายการไปที่ $ORGNAME$ แล้ว", "placeholders": { "orgname": { "content": "$1", @@ -5284,7 +5294,7 @@ } }, "reorderFieldDown": { - "message": "$LABEL$ moved down, position $INDEX$ of $LENGTH$", + "message": "เลื่อน $LABEL$ ลง ตำแหน่งที่ $INDEX$ จาก $LENGTH$", "placeholders": { "label": { "content": "$1", @@ -5301,25 +5311,25 @@ } }, "itemLocation": { - "message": "Item Location" + "message": "ตำแหน่งรายการ" }, "fileSends": { - "message": "File Sends" + "message": "ไฟล์ Send" }, "textSends": { - "message": "Text Sends" + "message": "ข้อความ Send" }, "accountActions": { - "message": "การจัดการบัญชี" + "message": "การดำเนินการกับบัญชี" }, "showNumberOfAutofillSuggestions": { - "message": "Show number of login autofill suggestions on extension icon" + "message": "แสดงจำนวนคำแนะนำการป้อนข้อมูลเข้าสู่ระบบอัตโนมัติบนไอคอนส่วนขยาย" }, "accountAccessRequested": { - "message": "Account access requested" + "message": "ร้องขอการเข้าถึงบัญชีแล้ว" }, "confirmAccessAttempt": { - "message": "Confirm access attempt for $EMAIL$", + "message": "ยืนยันความพยายามเข้าถึงสำหรับ $EMAIL$", "placeholders": { "email": { "content": "$1", @@ -5328,25 +5338,25 @@ } }, "showQuickCopyActions": { - "message": "Show quick copy actions on Vault" + "message": "แสดงการดำเนินการคัดลอกด่วนบนตู้นิรภัย" }, "systemDefault": { - "message": "System default" + "message": "ค่าเริ่มต้นของระบบ" }, "enterprisePolicyRequirementsApplied": { - "message": "Enterprise policy requirements have been applied to this setting" + "message": "ข้อกำหนดนโยบายองค์กรถูกนำมาใช้กับการตั้งค่านี้นี้" }, "sshPrivateKey": { - "message": "Private key" + "message": "กุญแจส่วนตัว" }, "sshPublicKey": { - "message": "Public key" + "message": "กุญแจสาธารณะ" }, "sshFingerprint": { - "message": "Fingerprint" + "message": "ลายนิ้วมือ" }, "sshKeyAlgorithm": { - "message": "Key type" + "message": "ประเภทคีย์" }, "sshKeyAlgorithmED25519": { "message": "ED25519" @@ -5361,58 +5371,58 @@ "message": "RSA 4096-Bit" }, "retry": { - "message": "Retry" + "message": "ลองใหม่" }, "vaultCustomTimeoutMinimum": { - "message": "Minimum custom timeout is 1 minute." + "message": "เวลาหมดเวลาที่กำหนดเองขั้นต่ำคือ 1 นาที" }, "fileSavedToDevice": { - "message": "File saved to device. Manage from your device downloads." + "message": "บันทึกไฟล์ลงในอุปกรณ์แล้ว จัดการได้จากการดาวน์โหลดของอุปกรณ์" }, "showCharacterCount": { - "message": "Show character count" + "message": "แสดงจำนวนตัวอักษร" }, "hideCharacterCount": { - "message": "Hide character count" + "message": "ซ่อนจำนวนตัวอักษร" }, "itemsInTrash": { - "message": "Items in trash" + "message": "รายการในถังขยะ" }, "noItemsInTrash": { - "message": "No items in trash" + "message": "ไม่มีรายการในถังขยะ" }, "noItemsInTrashDesc": { - "message": "Items you delete will appear here and be permanently deleted after 30 days" + "message": "รายการที่คุณลบจะปรากฏที่นี่และจะถูกลบถาวรหลังจาก 30 วัน" }, "trashWarning": { - "message": "Items that have been in trash more than 30 days will automatically be deleted" + "message": "รายการที่อยู่ในถังขยะนานกว่า 30 วันจะถูกลบโดยอัตโนมัติ" }, "restore": { - "message": "Restore" + "message": "กู้คืน" }, "deleteForever": { - "message": "Delete forever" + "message": "ลบถาวร" }, "noEditPermissions": { - "message": "You don't have permission to edit this item" + "message": "คุณไม่มีสิทธิ์แก้ไขรายการนี้" }, "biometricsStatusHelptextUnlockNeeded": { - "message": "Biometric unlock is unavailable because PIN or password unlock is required first." + "message": "การปลดล็อกด้วยไบโอเมตริกไม่พร้อมใช้งาน เนื่องจากต้องปลดล็อกด้วย PIN หรือรหัสผ่านก่อน" }, "biometricsStatusHelptextHardwareUnavailable": { - "message": "Biometric unlock is currently unavailable." + "message": "การปลดล็อกด้วยไบโอเมตริกไม่พร้อมใช้งานในขณะนี้" }, "biometricsStatusHelptextAutoSetupNeeded": { - "message": "Biometric unlock is unavailable due to misconfigured system files." + "message": "การปลดล็อกด้วยไบโอเมตริกไม่พร้อมใช้งานเนื่องจากการกำหนดค่าไฟล์ระบบไม่ถูกต้อง" }, "biometricsStatusHelptextManualSetupNeeded": { - "message": "Biometric unlock is unavailable due to misconfigured system files." + "message": "การปลดล็อกด้วยไบโอเมตริกไม่พร้อมใช้งานเนื่องจากการกำหนดค่าไฟล์ระบบไม่ถูกต้อง" }, "biometricsStatusHelptextDesktopDisconnected": { - "message": "Biometric unlock is unavailable because the Bitwarden desktop app is closed." + "message": "การปลดล็อกด้วยไบโอเมตริกไม่พร้อมใช้งาน เนื่องจากแอป Bitwarden บนเดสก์ท็อปปิดอยู่" }, "biometricsStatusHelptextNotEnabledInDesktop": { - "message": "Biometric unlock is unavailable because it is not enabled for $EMAIL$ in the Bitwarden desktop app.", + "message": "การปลดล็อกด้วยไบโอเมตริกไม่พร้อมใช้งาน เนื่องจากยังไม่ได้เปิดใช้งานสำหรับ $EMAIL$ ในแอป Bitwarden บนเดสก์ท็อป", "placeholders": { "email": { "content": "$1", @@ -5421,41 +5431,41 @@ } }, "biometricsStatusHelptextUnavailableReasonUnknown": { - "message": "Biometric unlock is currently unavailable for an unknown reason." + "message": "การปลดล็อกด้วยไบโอเมตริกไม่พร้อมใช้งานในขณะนี้โดยไม่ทราบสาเหตุ" }, "unlockVault": { - "message": "Unlock your vault in seconds" + "message": "ปลดล็อกตู้นิรภัยของคุณในไม่กี่วินาที" }, "unlockVaultDesc": { - "message": "You can customize your unlock and timeout settings to more quickly access your vault." + "message": "คุณสามารถปรับแต่งการตั้งค่าการปลดล็อกและเวลาหมดเวลาเพื่อเข้าถึงตู้นิรภัยได้รวดเร็วยิ่งขึ้น" }, "unlockPinSet": { - "message": "ตั้งค่า PIN สำหรับปลดล็อกแล้ว" + "message": "ตั้งค่า PIN ปลดล็อกแล้ว" }, "unlockWithBiometricSet": { - "message": "Unlock with biometrics set" + "message": "ตั้งค่าการปลดล็อกด้วยไบโอเมตริกแล้ว" }, "authenticating": { - "message": "Authenticating" + "message": "กำลังยืนยันตัวตน" }, "fillGeneratedPassword": { - "message": "Fill generated password", + "message": "ป้อนรหัสผ่านที่สร้างขึ้น", "description": "Heading for the password generator within the inline menu" }, "passwordRegenerated": { - "message": "Password regenerated", + "message": "สร้างรหัสผ่านใหม่แล้ว", "description": "Notification message for when a password has been regenerated" }, "saveToBitwarden": { - "message": "Save to Bitwarden", + "message": "บันทึกลง Bitwarden", "description": "Confirmation message for saving a login to Bitwarden" }, "spaceCharacterDescriptor": { - "message": "Space", + "message": "เว้นวรรค", "description": "Represents the space key in screen reader content as a readable word" }, "tildeCharacterDescriptor": { - "message": "Tilde", + "message": "ตัวหนอน", "description": "Represents the ~ key in screen reader content as a readable word" }, "backtickCharacterDescriptor": { @@ -5463,23 +5473,23 @@ "description": "Represents the ` key in screen reader content as a readable word" }, "exclamationCharacterDescriptor": { - "message": "Exclamation mark", + "message": "เครื่องหมายตกใจ", "description": "Represents the ! key in screen reader content as a readable word" }, "atSignCharacterDescriptor": { - "message": "At sign", + "message": "เครื่องหมาย @", "description": "Represents the @ key in screen reader content as a readable word" }, "hashSignCharacterDescriptor": { - "message": "Hash sign", + "message": "เครื่องหมาย #", "description": "Represents the # key in screen reader content as a readable word" }, "dollarSignCharacterDescriptor": { - "message": "Dollar sign", + "message": "เครื่องหมาย $", "description": "Represents the $ key in screen reader content as a readable word" }, "percentSignCharacterDescriptor": { - "message": "Percent sign", + "message": "เครื่องหมาย %", "description": "Represents the % key in screen reader content as a readable word" }, "caretCharacterDescriptor": { @@ -5487,154 +5497,154 @@ "description": "Represents the ^ key in screen reader content as a readable word" }, "ampersandCharacterDescriptor": { - "message": "Ampersand", + "message": "เครื่องหมาย &", "description": "Represents the & key in screen reader content as a readable word" }, "asteriskCharacterDescriptor": { - "message": "Asterisk", + "message": "ดอกจัน", "description": "Represents the * key in screen reader content as a readable word" }, "parenLeftCharacterDescriptor": { - "message": "Left parenthesis", + "message": "วงเล็บเปิด", "description": "Represents the ( key in screen reader content as a readable word" }, "parenRightCharacterDescriptor": { - "message": "Right parenthesis", + "message": "วงเล็บปิด", "description": "Represents the ) key in screen reader content as a readable word" }, "hyphenCharacterDescriptor": { - "message": "Underscore", + "message": "ขีดล่าง", "description": "Represents the _ key in screen reader content as a readable word" }, "underscoreCharacterDescriptor": { - "message": "Hyphen", + "message": "ขีดกลาง", "description": "Represents the - key in screen reader content as a readable word" }, "plusCharacterDescriptor": { - "message": "Plus", + "message": "เครื่องหมาย +", "description": "Represents the + key in screen reader content as a readable word" }, "equalsCharacterDescriptor": { - "message": "Equals", + "message": "เครื่องหมาย =", "description": "Represents the = key in screen reader content as a readable word" }, "braceLeftCharacterDescriptor": { - "message": "Left brace", + "message": "ปีกกาเปิด", "description": "Represents the { key in screen reader content as a readable word" }, "braceRightCharacterDescriptor": { - "message": "Right brace", + "message": "ปีกกาปิด", "description": "Represents the } key in screen reader content as a readable word" }, "bracketLeftCharacterDescriptor": { - "message": "Left bracket", + "message": "วงเล็บเหลี่ยมเปิด", "description": "Represents the [ key in screen reader content as a readable word" }, "bracketRightCharacterDescriptor": { - "message": "Right bracket", + "message": "วงเล็บเหลี่ยมปิด", "description": "Represents the ] key in screen reader content as a readable word" }, "pipeCharacterDescriptor": { - "message": "Pipe", + "message": "ขีดตั้ง", "description": "Represents the | key in screen reader content as a readable word" }, "backSlashCharacterDescriptor": { - "message": "Back slash", + "message": "แบคสแลช", "description": "Represents the back slash key in screen reader content as a readable word" }, "colonCharacterDescriptor": { - "message": "Colon", + "message": "โคลอน", "description": "Represents the : key in screen reader content as a readable word" }, "semicolonCharacterDescriptor": { - "message": "Semicolon", + "message": "เซมิโคลอน", "description": "Represents the ; key in screen reader content as a readable word" }, "doubleQuoteCharacterDescriptor": { - "message": "Double quote", + "message": "ฟันหนู", "description": "Represents the double quote key in screen reader content as a readable word" }, "singleQuoteCharacterDescriptor": { - "message": "Single quote", + "message": "ฝนทอง", "description": "Represents the ' key in screen reader content as a readable word" }, "lessThanCharacterDescriptor": { - "message": "Less than", + "message": "น้อยกว่า", "description": "Represents the < key in screen reader content as a readable word" }, "greaterThanCharacterDescriptor": { - "message": "Greater than", + "message": "มากกว่า", "description": "Represents the > key in screen reader content as a readable word" }, "commaCharacterDescriptor": { - "message": "Comma", + "message": "จุลภาค", "description": "Represents the , key in screen reader content as a readable word" }, "periodCharacterDescriptor": { - "message": "Period", + "message": "จุด", "description": "Represents the . key in screen reader content as a readable word" }, "questionCharacterDescriptor": { - "message": "Question mark", + "message": "เครื่องหมายคำถาม", "description": "Represents the ? key in screen reader content as a readable word" }, "forwardSlashCharacterDescriptor": { - "message": "Forward slash", + "message": "ทับ", "description": "Represents the / key in screen reader content as a readable word" }, "lowercaseAriaLabel": { - "message": "Lowercase" + "message": "ตัวพิมพ์เล็ก" }, "uppercaseAriaLabel": { - "message": "Uppercase" + "message": "ตัวพิมพ์ใหญ่" }, "generatedPassword": { - "message": "Generated password" + "message": "รหัสผ่านที่สร้างขึ้น" }, "compactMode": { - "message": "Compact mode" + "message": "โหมดกะทัดรัด" }, "beta": { - "message": "Beta" + "message": "เบต้า" }, "extensionWidth": { - "message": "Extension width" + "message": "ความกว้างส่วนขยาย" }, "wide": { - "message": "Wide" + "message": "กว้าง" }, "extraWide": { - "message": "Extra wide" + "message": "กว้างพิเศษ" }, "sshKeyWrongPassword": { - "message": "The password you entered is incorrect." + "message": "รหัสผ่านที่คุณป้อนไม่ถูกต้อง" }, "importSshKey": { - "message": "Import" + "message": "นำเข้า" }, "confirmSshKeyPassword": { - "message": "Confirm password" + "message": "ยืนยันรหัสผ่าน" }, "enterSshKeyPasswordDesc": { - "message": "Enter the password for the SSH key." + "message": "ป้อนรหัสผ่านสำหรับคีย์ SSH" }, "enterSshKeyPassword": { - "message": "Enter password" + "message": "ป้อนรหัสผ่าน" }, "invalidSshKey": { - "message": "The SSH key is invalid" + "message": "คีย์ SSH ไม่ถูกต้อง" }, "sshKeyTypeUnsupported": { - "message": "The SSH key type is not supported" + "message": "ไม่รองรับประเภทคีย์ SSH" }, "importSshKeyFromClipboard": { - "message": "Import key from clipboard" + "message": "นำเข้าคีย์จากคลิปบอร์ด" }, "sshKeyImported": { - "message": "SSH key imported successfully" + "message": "นำเข้าคีย์ SSH สำเร็จแล้ว" }, "cannotRemoveViewOnlyCollections": { - "message": "You cannot remove collections with View only permissions: $COLLECTIONS$", + "message": "คุณไม่สามารถลบคอลเลกชันที่มีสิทธิ์ดูอย่างเดียวได้: $COLLECTIONS$", "placeholders": { "collections": { "content": "$1", @@ -5643,93 +5653,93 @@ } }, "updateDesktopAppOrDisableFingerprintDialogTitle": { - "message": "Please update your desktop application" + "message": "โปรดอัปเดตแอปพลิเคชันเดสก์ท็อปของคุณ" }, "updateDesktopAppOrDisableFingerprintDialogMessage": { - "message": "To use biometric unlock, please update your desktop application, or disable fingerprint unlock in the desktop settings." + "message": "หากต้องการใช้การปลดล็อกด้วยไบโอเมตริก โปรดอัปเดตแอปพลิเคชันเดสก์ท็อป หรือปิดใช้งานการปลดล็อกด้วยลายนิ้วมือในการตั้งค่าเดสก์ท็อป" }, "changeAtRiskPassword": { - "message": "Change at-risk password" + "message": "เปลี่ยนรหัสผ่านที่มีความเสี่ยง" }, "changeAtRiskPasswordAndAddWebsite": { - "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." + "message": "ข้อมูลเข้าสู่ระบบนี้มีความเสี่ยงและไม่มีเว็บไซต์ เพิ่มเว็บไซต์และเปลี่ยนรหัสผ่านเพื่อความปลอดภัยที่รัดกุมยิ่งขึ้น" }, "missingWebsite": { - "message": "Missing website" + "message": "ไม่มีเว็บไซต์" }, "settingsVaultOptions": { - "message": "Vault options" + "message": "ตัวเลือกตู้นิรภัย" }, "emptyVaultDescription": { - "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + "message": "ตู้นิรภัยปกป้องมากกว่าแค่รหัสผ่าน จัดเก็บข้อมูลเข้าสู่ระบบ เอกสารระบุตัวตน บัตร และโน้ตอย่างปลอดภัยที่นี่" }, "introCarouselLabel": { - "message": "Welcome to Bitwarden" + "message": "ยินดีต้อนรับสู่ Bitwarden" }, "securityPrioritized": { - "message": "Security, prioritized" + "message": "ให้ความสำคัญกับความปลอดภัย" }, "securityPrioritizedBody": { - "message": "Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you." + "message": "บันทึกข้อมูลเข้าสู่ระบบ บัตร และข้อมูลระบุตัวตนลงในตู้นิรภัยที่ปลอดภัย Bitwarden ใช้การเข้ารหัสแบบ End-to-end แบบ Zero-knowledge เพื่อปกป้องสิ่งที่สำคัญสำหรับคุณ" }, "quickLogin": { - "message": "Quick and easy login" + "message": "เข้าสู่ระบบที่รวดเร็วและง่ายดาย" }, "quickLoginBody": { - "message": "ตั้งค่าการปลดล็อกด้วยไบโอเมตริกซ์และการกรอกข้อมูลอัตโนมัติ เพื่อลงชื่อเข้าใช้บัญชีของคุณโดยไม่ต้องพิมพ์แม้แต่ตัวอักษรเดียว" + "message": "ตั้งค่าการปลดล็อกด้วยไบโอเมตริกและการป้อนอัตโนมัติเพื่อเข้าสู่ระบบบัญชีโดยไม่ต้องพิมพ์แม้แต่ตัวอักษรเดียว" }, "secureUser": { - "message": "Level up your logins" + "message": "ยกระดับการเข้าสู่ระบบของคุณ" }, "secureUserBody": { - "message": "Use the generator to create and save strong, unique passwords for all your accounts." + "message": "ใช้ตัวสร้างเพื่อสร้างและบันทึกรหัสผ่านที่รัดกุมและไม่ซ้ำกันสำหรับทุกบัญชีของคุณ" }, "secureDevices": { - "message": "Your data, when and where you need it" + "message": "ข้อมูลของคุณ ทุกที่ทุกเวลาที่คุณต้องการ" }, "secureDevicesBody": { - "message": "Save unlimited passwords across unlimited devices with Bitwarden mobile, browser, and desktop apps." + "message": "บันทึกรหัสผ่านได้ไม่จำกัดบนอุปกรณ์ไม่จำกัดด้วยแอป Bitwarden บนมือถือ เบราว์เซอร์ และเดสก์ท็อป" }, "nudgeBadgeAria": { - "message": "1 notification" + "message": "1 การแจ้งเตือน" }, "emptyVaultNudgeTitle": { - "message": "Import existing passwords" + "message": "นำเข้ารหัสผ่านที่มีอยู่" }, "emptyVaultNudgeBody": { - "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." + "message": "ใช้ตัวนำเข้าเพื่อโอนย้ายข้อมูลเข้าสู่ระบบไปยัง Bitwarden อย่างรวดเร็วโดยไม่ต้องเพิ่มด้วยตนเอง" }, "emptyVaultNudgeButton": { - "message": "Import now" + "message": "นำเข้าเลย" }, "hasItemsVaultNudgeTitle": { - "message": "Welcome to your vault!" + "message": "ยินดีต้อนรับสู่ตู้นิรภัยของคุณ!" }, "phishingPageTitleV2": { - "message": "Phishing attempt detected" + "message": "ตรวจพบความพยายามฟิชชิง" }, "phishingPageSummary": { - "message": "The site you are attempting to visit is a known malicious site and a security risk." + "message": "ไซต์ที่คุณพยายามเข้าชมเป็นไซต์อันตรายที่รู้จักและมีความเสี่ยงด้านความปลอดภัย" }, "phishingPageCloseTabV2": { - "message": "Close this tab" + "message": "ปิดแท็บนี้" }, "phishingPageContinueV2": { - "message": "Continue to this site (not recommended)" + "message": "ไปที่ไซต์นี้ต่อ (ไม่แนะนำ)" }, "phishingPageExplanation1": { - "message": "This site was found in ", + "message": "พบไซต์นี้ใน ", "description": "This is in multiple parts to allow for bold text in the middle of the sentence. A proper name follows this." }, "phishingPageExplanation2": { - "message": ", an open-source list of known phishing sites used for stealing personal and sensitive information.", + "message": " ซึ่งเป็นรายการโอเพนซอร์สของไซต์ฟิชชิงที่รู้จักที่ใช้ขโมยข้อมูลส่วนบุคคลและข้อมูลสำคัญ", "description": "This is in multiple parts to allow for bold text in the middle of the sentence. A proper name precedes this." }, "phishingPageLearnMore": { - "message": "Learn more about phishing detection" + "message": "เรียนรู้เพิ่มเติมเกี่ยวกับการตรวจจับฟิชชิง" }, "protectedBy": { - "message": "Protected by $PRODUCT$", + "message": "ปกป้องโดย $PRODUCT$", "placeholders": { "product": { "content": "$1", @@ -5738,208 +5748,208 @@ } }, "hasItemsVaultNudgeBodyOne": { - "message": "Autofill items for the current page" + "message": "ป้อนข้อมูลรายการอัตโนมัติสำหรับหน้าปัจจุบัน" }, "hasItemsVaultNudgeBodyTwo": { - "message": "Favorite items for easy access" + "message": "ตั้งรายการโปรดเพื่อการเข้าถึงที่ง่ายดาย" }, "hasItemsVaultNudgeBodyThree": { - "message": "Search your vault for something else" + "message": "ค้นหาสิ่งอื่นในตู้นิรภัยของคุณ" }, "newLoginNudgeTitle": { - "message": "Save time with autofill" + "message": "ประหยัดเวลาด้วยการป้อนอัตโนมัติ" }, "newLoginNudgeBodyOne": { - "message": "Include a", + "message": "ระบุ", "description": "This is in multiple parts to allow for bold text in the middle of the sentence.", "example": "Include a Website so this login appears as an autofill suggestion." }, "newLoginNudgeBodyBold": { - "message": "Website", + "message": "เว็บไซต์", "description": "This is in multiple parts to allow for bold text in the middle of the sentence.", "example": "Include a Website so this login appears as an autofill suggestion." }, "newLoginNudgeBodyTwo": { - "message": "so this login appears as an autofill suggestion.", + "message": "เพื่อให้ข้อมูลเข้าสู่ระบบนี้ปรากฏเป็นคำแนะนำการป้อนอัตโนมัติ", "description": "This is in multiple parts to allow for bold text in the middle of the sentence.", "example": "Include a Website so this login appears as an autofill suggestion." }, "newCardNudgeTitle": { - "message": "Seamless online checkout" + "message": "ชำระเงินออนไลน์ได้อย่างราบรื่น" }, "newCardNudgeBody": { - "message": "With cards, easily autofill payment forms securely and accurately." + "message": "ด้วยบัตร คุณสามารถป้อนแบบฟอร์มการชำระเงินอัตโนมัติได้อย่างปลอดภัยและแม่นยำ" }, "newIdentityNudgeTitle": { - "message": "Simplify creating accounts" + "message": "สร้างบัญชีได้ง่ายขึ้น" }, "newIdentityNudgeBody": { - "message": "With identities, quickly autofill long registration or contact forms." + "message": "ด้วยข้อมูลระบุตัวตน คุณสามารถป้อนแบบฟอร์มลงทะเบียนหรือข้อมูลติดต่อยาว ๆ ได้อย่างรวดเร็ว" }, "newNoteNudgeTitle": { - "message": "Keep your sensitive data safe" + "message": "เก็บรักษาข้อมูลสำคัญของคุณให้ปลอดภัย" }, "newNoteNudgeBody": { - "message": "With notes, securely store sensitive data like banking or insurance details." + "message": "ด้วยโน้ต คุณสามารถจัดเก็บข้อมูลสำคัญ เช่น รายละเอียดธนาคารหรือประกันภัยได้อย่างปลอดภัย" }, "newSshNudgeTitle": { - "message": "Developer-friendly SSH access" + "message": "การเข้าถึง SSH ที่เป็นมิตรกับนักพัฒนา" }, "newSshNudgeBodyOne": { - "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication.", + "message": "จัดเก็บคีย์ของคุณและเชื่อมต่อกับ SSH Agent เพื่อการยืนยันตัวตนที่รวดเร็วและเข้ารหัส", "description": "Two part message", "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" }, "newSshNudgeBodyTwo": { - "message": "Learn more about SSH agent", + "message": "เรียนรู้เพิ่มเติมเกี่ยวกับ SSH Agent", "description": "Two part message", "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" }, "generatorNudgeTitle": { - "message": "Quickly create passwords" + "message": "สร้างรหัสผ่านอย่างรวดเร็ว" }, "generatorNudgeBodyOne": { - "message": "Easily create strong and unique passwords by clicking on", + "message": "สร้างรหัสผ่านที่รัดกุมและไม่ซ้ำกันได้ง่าย ๆ โดยคลิกที่", "description": "Two part message", "example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure." }, "generatorNudgeBodyTwo": { - "message": "to help you keep your logins secure.", + "message": "เพื่อช่วยรักษาความปลอดภัยข้อมูลเข้าสู่ระบบของคุณ", "description": "Two part message", "example": "Easily create strong and unique passwords by clicking on {icon} to help you keep your logins secure." }, "generatorNudgeBodyAria": { - "message": "Easily create strong and unique passwords by clicking on the Generate password button to help you keep your logins secure.", + "message": "สร้างรหัสผ่านที่รัดกุมและไม่ซ้ำกันได้ง่าย ๆ โดยคลิกที่ปุ่มสร้างรหัสผ่าน เพื่อช่วยรักษาความปลอดภัยข้อมูลเข้าสู่ระบบของคุณ", "description": "Aria label for the body content of the generator nudge" }, "aboutThisSetting": { - "message": "About this setting" + "message": "เกี่ยวกับการตั้งค่านี้" }, "permitCipherDetailsDescription": { - "message": "Bitwarden will use saved login URIs to identify which icon or change password URL should be used to improve your experience. No information is collected or saved when you use this service." + "message": "Bitwarden จะใช้ URI ข้อมูลเข้าสู่ระบบที่บันทึกไว้เพื่อระบุว่าควรใช้ไอคอนหรือ URL เปลี่ยนรหัสผ่านใดเพื่อปรับปรุงประสบการณ์ของคุณ ไม่มีการรวบรวมหรือบันทึกข้อมูลเมื่อคุณใช้บริการนี้" }, "noPermissionsViewPage": { - "message": "You do not have permissions to view this page. Try logging in with a different account." + "message": "คุณไม่มีสิทธิ์ดูหน้านี้ ลองเข้าสู่ระบบด้วยบัญชีอื่น" }, "wasmNotSupported": { - "message": "WebAssembly is not supported on your browser or is not enabled. WebAssembly is required to use the Bitwarden app.", + "message": "เบราว์เซอร์ของคุณไม่รองรับ WebAssembly หรือไม่ได้เปิดใช้งาน จำเป็นต้องมี WebAssembly เพื่อใช้งานแอป Bitwarden", "description": "'WebAssembly' is a technical term and should not be translated." }, "showMore": { - "message": "Show more" + "message": "แสดงเพิ่มเติม" }, "showLess": { - "message": "Show less" + "message": "แสดงน้อยลง" }, "next": { - "message": "Next" + "message": "ถัดไป" }, "moreBreadcrumbs": { - "message": "More breadcrumbs", + "message": "Breadcrumbs เพิ่มเติม", "description": "This is used in the context of a breadcrumb navigation, indicating that there are more items in the breadcrumb trail that are not currently displayed." }, "confirmKeyConnectorDomain": { - "message": "Confirm Key Connector domain" + "message": "ยืนยันโดเมน Key Connector" }, "atRiskLoginsSecured": { - "message": "Great job securing your at-risk logins!" + "message": "เยี่ยมมาก คุณจัดการความปลอดภัยข้อมูลเข้าสู่ระบบที่มีความเสี่ยงแล้ว!" }, "upgradeNow": { - "message": "Upgrade now" + "message": "อัปเกรดตอนนี้" }, "builtInAuthenticator": { - "message": "Built-in authenticator" + "message": "ตัวยืนยันตัวตนในตัว" }, "secureFileStorage": { - "message": "Secure file storage" + "message": "พื้นที่จัดเก็บไฟล์ที่ปลอดภัย" }, "emergencyAccess": { - "message": "Emergency access" + "message": "การเข้าถึงฉุกเฉิน" }, "breachMonitoring": { - "message": "Breach monitoring" + "message": "การตรวจสอบข้อมูลรั่วไหล" }, "andMoreFeatures": { - "message": "And more!" + "message": "และอื่น ๆ!" }, "advancedOnlineSecurity": { - "message": "Advanced online security" + "message": "ความปลอดภัยออนไลน์ขั้นสูง" }, "upgradeToPremium": { - "message": "Upgrade to Premium" + "message": "อัปเกรดเป็นพรีเมียม" }, "unlockAdvancedSecurity": { - "message": "Unlock advanced security features" + "message": "ปลดล็อกฟีเจอร์ความปลอดภัยขั้นสูง" }, "unlockAdvancedSecurityDesc": { - "message": "A Premium subscription gives you more tools to stay secure and in control" + "message": "การสมัครสมาชิกพรีเมียมมอบเครื่องมือเพิ่มเติมเพื่อให้คุณปลอดภัยและควบคุมได้" }, "explorePremium": { - "message": "Explore Premium" + "message": "สำรวจพรีเมียม" }, "loadingVault": { - "message": "Loading vault" + "message": "กำลังโหลดตู้นิรภัย" }, "vaultLoaded": { - "message": "Vault loaded" + "message": "โหลดตู้นิรภัยแล้ว" }, "settingDisabledByPolicy": { - "message": "This setting is disabled by your organization's policy.", + "message": "การตั้งค่านี้ถูกปิดใช้งานโดยนโยบายองค์กรของคุณ", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." }, "zipPostalCodeLabel": { - "message": "ZIP / Postal code" + "message": "รหัสไปรษณีย์" }, "cardNumberLabel": { - "message": "Card number" + "message": "หมายเลขบัตร" }, "removeMasterPasswordForOrgUserKeyConnector": { - "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + "message": "องค์กรของคุณไม่ใช้รหัสผ่านหลักในการเข้าสู่ระบบ Bitwarden อีกต่อไป หากต้องการดำเนินการต่อ ให้ยืนยันองค์กรและโดเมน" }, "continueWithLogIn": { - "message": "Continue with log in" + "message": "ดำเนินการเข้าสู่ระบบต่อ" }, "doNotContinue": { - "message": "Do not continue" + "message": "ไม่ดำเนินการต่อ" }, "domain": { - "message": "Domain" + "message": "โดเมน" }, "keyConnectorDomainTooltip": { - "message": "This domain will store your account encryption keys, so make sure you trust it. If you're not sure, check with your admin." + "message": "โดเมนนี้จะจัดเก็บกุญแจเข้ารหัสบัญชีของคุณ ดังนั้นโปรดตรวจสอบให้แน่ใจว่าคุณเชื่อถือ หากไม่แน่ใจ ให้ตรวจสอบกับผู้ดูแลระบบ" }, "verifyYourOrganization": { - "message": "Verify your organization to log in" + "message": "ยืนยันองค์กรของคุณเพื่อเข้าสู่ระบบ" }, "organizationVerified": { - "message": "Organization verified" + "message": "ยืนยันองค์กรแล้ว" }, "domainVerified": { - "message": "Domain verified" + "message": "ยืนยันโดเมนแล้ว" }, "leaveOrganizationContent": { - "message": "If you don't verify your organization, your access to the organization will be revoked." + "message": "หากคุณไม่ยืนยันองค์กร สิทธิ์การเข้าถึงองค์กรของคุณจะถูกเพิกถอน" }, "leaveNow": { - "message": "Leave now" + "message": "ออกตอนนี้" }, "verifyYourDomainToLogin": { - "message": "Verify your domain to log in" + "message": "ยืนยันโดเมนของคุณเพื่อเข้าสู่ระบบ" }, "verifyYourDomainDescription": { - "message": "To continue with log in, verify this domain." + "message": "หากต้องการดำเนินการเข้าสู่ระบบต่อ ให้ยืนยันโดเมนนี้" }, "confirmKeyConnectorOrganizationUserDescription": { - "message": "To continue with log in, verify the organization and domain." + "message": "หากต้องการดำเนินการเข้าสู่ระบบต่อ ให้ยืนยันองค์กรและโดเมน" }, "sessionTimeoutSettingsAction": { - "message": "Timeout action" + "message": "การดำเนินการเมื่อหมดเวลา" }, "sessionTimeoutSettingsManagedByOrganization": { - "message": "This setting is managed by your organization." + "message": "การตั้งค่านี้ได้รับการจัดการโดยองค์กรของคุณ" }, "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { - "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "message": "องค์กรของคุณกำหนดเวลาหมดเวลาเซสชันสูงสุดไว้ที่ $HOURS$ ชั่วโมง $MINUTES$ นาที", "placeholders": { "hours": { "content": "$1", @@ -5952,16 +5962,16 @@ } }, "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { - "message": "Your organization has set the default session timeout to Immediately." + "message": "องค์กรของคุณกำหนดเวลาหมดเวลาเซสชันเริ่มต้นเป็น ทันที" }, "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { - "message": "Your organization has set the default session timeout to On system lock." + "message": "องค์กรของคุณกำหนดเวลาหมดเวลาเซสชันเริ่มต้นเป็น เมื่อล็อกระบบ" }, "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { - "message": "Your organization has set the default session timeout to On browser restart." + "message": "องค์กรของคุณกำหนดเวลาหมดเวลาเซสชันเริ่มต้นเป็น เมื่อรีสตาร์ตเบราว์เซอร์" }, "sessionTimeoutSettingsPolicyMaximumError": { - "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "message": "เวลาหมดเวลาสูงสุดต้องไม่เกิน $HOURS$ ชั่วโมง $MINUTES$ นาที", "placeholders": { "hours": { "content": "$1", @@ -5974,25 +5984,25 @@ } }, "sessionTimeoutOnRestart": { - "message": "On browser restart" + "message": "เมื่อรีสตาร์ตเบราว์เซอร์" }, "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { - "message": "Set an unlock method to change your timeout action" + "message": "ตั้งค่าวิธีการปลดล็อกเพื่อเปลี่ยนการดำเนินการเมื่อหมดเวลา" }, "upgrade": { - "message": "Upgrade" + "message": "อัปเกรด" }, "leaveConfirmationDialogTitle": { - "message": "Are you sure you want to leave?" + "message": "ยืนยันที่จะออกหรือไม่" }, "leaveConfirmationDialogContentOne": { - "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + "message": "หากปฏิเสธ รายการส่วนตัวจะยังคงอยู่ในบัญชีของคุณ แต่คุณจะเสียสิทธิ์เข้าถึงรายการที่แชร์และฟีเจอร์ขององค์กร" }, "leaveConfirmationDialogContentTwo": { - "message": "Contact your admin to regain access." + "message": "ติดต่อผู้ดูแลระบบเพื่อขอรับสิทธิ์เข้าถึงอีกครั้ง" }, "leaveConfirmationDialogConfirmButton": { - "message": "Leave $ORGANIZATION$", + "message": "ออกจาก $ORGANIZATION$", "placeholders": { "organization": { "content": "$1", @@ -6001,10 +6011,10 @@ } }, "howToManageMyVault": { - "message": "How do I manage my vault?" + "message": "ฉันจะจัดการตู้นิรภัยได้อย่างไร" }, "transferItemsToOrganizationTitle": { - "message": "Transfer items to $ORGANIZATION$", + "message": "โอนย้ายรายการไปยัง $ORGANIZATION$", "placeholders": { "organization": { "content": "$1", @@ -6013,7 +6023,7 @@ } }, "transferItemsToOrganizationContent": { - "message": "$ORGANIZATION$ is requiring all items to be owned by the organization for security and compliance. Click accept to transfer ownership of your items.", + "message": "$ORGANIZATION$ กำหนดให้รายการทั้งหมดต้องเป็นขององค์กรเพื่อความปลอดภัยและการปฏิบัติตามข้อกำหนด คลิกยอมรับเพื่อโอนกรรมสิทธิ์รายการของคุณ", "placeholders": { "organization": { "content": "$1", @@ -6022,12 +6032,12 @@ } }, "acceptTransfer": { - "message": "Accept transfer" + "message": "ยอมรับการโอนย้าย" }, "declineAndLeave": { - "message": "Decline and leave" + "message": "ปฏิเสธและออก" }, "whyAmISeeingThis": { - "message": "Why am I seeing this?" + "message": "ทำไมฉันจึงเห็นสิ่งนี้" } } diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index 70e9a5c0afe..e6a787716a4 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Dışa aktarılacak konum" }, - "export": { - "message": "Dışa aktar" + "exportVerb": { + "message": "Dışa aktar", + "description": "The verb form of the word Export" }, - "import": { - "message": "İçe aktar" + "exportNoun": { + "message": "Dışa aktar", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "İçe aktar", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "İçe aktar", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Dosya biçimi" diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index 8d040513d67..d777dee1472 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -437,7 +437,7 @@ "message": "Синхронізація" }, "syncNow": { - "message": "Sync now" + "message": "Синхронізувати" }, "lastSync": { "message": "Остання синхронізація:" @@ -583,7 +583,7 @@ "message": "Архівовані записи виключаються з результатів звичайного пошуку та пропозицій автозаповнення. Ви дійсно хочете архівувати цей запис?" }, "upgradeToUseArchive": { - "message": "A premium membership is required to use Archive." + "message": "Для використання архіву необхідна передплата Premium." }, "edit": { "message": "Змінити" @@ -595,10 +595,10 @@ "message": "Переглянути все" }, "showAll": { - "message": "Show all" + "message": "Показати все" }, "viewLess": { - "message": "View less" + "message": "Показати менше" }, "viewLogin": { "message": "Переглянути запис" @@ -803,10 +803,10 @@ "message": "З блокуванням системи" }, "onIdle": { - "message": "On system idle" + "message": "Бездіяльність системи" }, "onSleep": { - "message": "On system sleep" + "message": "Режим сну" }, "onRestart": { "message": "З перезапуском браузера" @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Експортувати з" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Формат файлу" @@ -1407,25 +1417,25 @@ "message": "Докладніше" }, "migrationsFailed": { - "message": "An error occurred updating the encryption settings." + "message": "Сталася помилка під час оновлення налаштувань шифрування." }, "updateEncryptionSettingsTitle": { - "message": "Update your encryption settings" + "message": "Оновіть свої налаштування шифрування" }, "updateEncryptionSettingsDesc": { - "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + "message": "Нові рекомендовані налаштування шифрування покращать безпеку вашого облікового запису. Щоб оновити їх, введіть свій головний пароль." }, "confirmIdentityToContinue": { - "message": "Confirm your identity to continue" + "message": "Щоб продовжити, підтвердьте свої облікові дані" }, "enterYourMasterPassword": { - "message": "Enter your master password" + "message": "Введіть головний пароль" }, "updateSettings": { - "message": "Update settings" + "message": "Оновити налаштування" }, "later": { - "message": "Later" + "message": "Пізніше" }, "authenticatorKeyTotp": { "message": "Ключ автентифікації (TOTP)" @@ -1458,13 +1468,13 @@ "message": "Вкладення збережено" }, "fixEncryption": { - "message": "Fix encryption" + "message": "Виправити шифрування" }, "fixEncryptionTooltip": { - "message": "This file is using an outdated encryption method." + "message": "Для цього файлу використовується застарілий метод шифрування." }, "attachmentUpdated": { - "message": "Attachment updated" + "message": "Вкладення оновлено" }, "file": { "message": "Файл" @@ -1476,7 +1486,7 @@ "message": "Оберіть файл" }, "itemsTransferred": { - "message": "Items transferred" + "message": "Записи переміщено" }, "maxFileSize": { "message": "Максимальний розмір файлу 500 МБ." @@ -1509,7 +1519,7 @@ "message": "1 ГБ зашифрованого сховища для файлів." }, "premiumSignUpStorageV2": { - "message": "$SIZE$ encrypted storage for file attachments.", + "message": "$SIZE$ зашифрованого сховища для вкладених файлів.", "placeholders": { "size": { "content": "$1", @@ -1916,7 +1926,7 @@ "message": "Рік завершення" }, "monthly": { - "message": "month" + "message": "місяць" }, "expiration": { "message": "Термін дії" @@ -2488,7 +2498,7 @@ } }, "topLayerHijackWarning": { - "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + "message": "Ця сторінка заважає роботі Bitwarden. Задля безпеки вбудоване меню Bitwarden тимчасово вимкнено." }, "setMasterPassword": { "message": "Встановити головний пароль" @@ -4802,13 +4812,13 @@ "message": "Безпека облікового запису" }, "phishingBlocker": { - "message": "Phishing Blocker" + "message": "Блокувальник шахрайства" }, "enablePhishingDetection": { - "message": "Phishing detection" + "message": "Виявлення шахрайства" }, "enablePhishingDetectionDesc": { - "message": "Display warning before accessing suspected phishing sites" + "message": "Показувати попередження перед відвідуванням підозрюваних шахрайських сайтів" }, "notifications": { "message": "Сповіщення" @@ -4956,7 +4966,7 @@ "message": "Premium" }, "unlockFeaturesWithPremium": { - "message": "Unlock reporting, emergency access, and more security features with Premium." + "message": "Розблокуйте звіти, екстрений доступ та інші функції безпеки, передплативши Premium." }, "freeOrgsCannotUseAttachments": { "message": "Організації без передплати не можуть використовувати вкладення" @@ -5043,7 +5053,7 @@ } }, "defaultLabelWithValue": { - "message": "Default ( $VALUE$ )", + "message": "Типово ( $VALUE$ )", "description": "A label that indicates the default value for a field with the current default value in parentheses.", "placeholders": { "value": { @@ -5863,25 +5873,25 @@ "message": "Інші можливості!" }, "advancedOnlineSecurity": { - "message": "Advanced online security" + "message": "Розширена онлайн-безпека" }, "upgradeToPremium": { "message": "Покращити до Premium" }, "unlockAdvancedSecurity": { - "message": "Unlock advanced security features" + "message": "Розблокуйте розширені функції безпеки" }, "unlockAdvancedSecurityDesc": { - "message": "A Premium subscription gives you more tools to stay secure and in control" + "message": "З передплатою Premium ви матимете більше інструментів безпеки та контролю" }, "explorePremium": { - "message": "Explore Premium" + "message": "Ознайомитися з Premium" }, "loadingVault": { - "message": "Loading vault" + "message": "Завантаження сховища" }, "vaultLoaded": { - "message": "Vault loaded" + "message": "Сховище завантажено" }, "settingDisabledByPolicy": { "message": "Цей параметр вимкнено політикою вашої організації.", @@ -5894,52 +5904,52 @@ "message": "Номер картки" }, "removeMasterPasswordForOrgUserKeyConnector": { - "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + "message": "Ваша організація більше не використовує головні паролі для входу в Bitwarden. Щоб продовжити, підтвердіть організацію та домен." }, "continueWithLogIn": { - "message": "Continue with log in" + "message": "Перейти до входу" }, "doNotContinue": { - "message": "Do not continue" + "message": "Не продовжувати" }, "domain": { - "message": "Domain" + "message": "Домен" }, "keyConnectorDomainTooltip": { - "message": "This domain will store your account encryption keys, so make sure you trust it. If you're not sure, check with your admin." + "message": "Цей домен зберігатиме ключі шифрування вашого облікового запису, тому переконайтеся в його надійності. Якщо ви не впевнені, зверніться до свого адміністратора." }, "verifyYourOrganization": { - "message": "Verify your organization to log in" + "message": "Підтвердьте свою організацію, щоб увійти" }, "organizationVerified": { - "message": "Organization verified" + "message": "Організацію підтверджено" }, "domainVerified": { - "message": "Domain verified" + "message": "Домен підтверджено" }, "leaveOrganizationContent": { - "message": "If you don't verify your organization, your access to the organization will be revoked." + "message": "Якщо ви не підтвердите свою організацію, ваш доступ до неї буде відкликаний." }, "leaveNow": { - "message": "Leave now" + "message": "Покинути" }, "verifyYourDomainToLogin": { - "message": "Verify your domain to log in" + "message": "Підтвердьте свій домен, щоб увійти" }, "verifyYourDomainDescription": { - "message": "To continue with log in, verify this domain." + "message": "Щоб перейти до входу, підтвердьте цей домен." }, "confirmKeyConnectorOrganizationUserDescription": { - "message": "To continue with log in, verify the organization and domain." + "message": "Щоб перейти до входу, підтвердьте організацію і домен." }, "sessionTimeoutSettingsAction": { - "message": "Timeout action" + "message": "Дія після часу очікування" }, "sessionTimeoutSettingsManagedByOrganization": { - "message": "This setting is managed by your organization." + "message": "Цим параметром керує ваша організація." }, "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { - "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "message": "Ваша організація встановила максимальний час очікування сеансу $HOURS$ год і $MINUTES$ хв.", "placeholders": { "hours": { "content": "$1", @@ -5952,16 +5962,16 @@ } }, "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { - "message": "Your organization has set the default session timeout to Immediately." + "message": "Ваша організація встановила максимальний час очікування сеансу \"Негайно\"." }, "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { - "message": "Your organization has set the default session timeout to On system lock." + "message": "Ваша організація встановила максимальний час очікування сеансу \"Блокування системи\"." }, "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { - "message": "Your organization has set the default session timeout to On browser restart." + "message": "Ваша організація встановила максимальний час очікування сеансу \"З перезапуском браузера\"." }, "sessionTimeoutSettingsPolicyMaximumError": { - "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "message": "Максимальний час очікування не може перевищувати $HOURS$ год і $MINUTES$ хв", "placeholders": { "hours": { "content": "$1", @@ -5974,25 +5984,25 @@ } }, "sessionTimeoutOnRestart": { - "message": "On browser restart" + "message": "З перезапуском браузера" }, "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { - "message": "Set an unlock method to change your timeout action" + "message": "Встановіть спосіб розблокування, щоб змінити дію під час очікування" }, "upgrade": { - "message": "Upgrade" + "message": "Оновити" }, "leaveConfirmationDialogTitle": { - "message": "Are you sure you want to leave?" + "message": "Ви дійсно хочете покинути?" }, "leaveConfirmationDialogContentOne": { - "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + "message": "Відхиливши, ваші особисті записи залишаться у вашому обліковому записі, але ви втратите доступ до спільних записів та функцій організації." }, "leaveConfirmationDialogContentTwo": { - "message": "Contact your admin to regain access." + "message": "Зверніться до свого адміністратора, щоб відновити доступ." }, "leaveConfirmationDialogConfirmButton": { - "message": "Leave $ORGANIZATION$", + "message": "Покинути $ORGANIZATION$", "placeholders": { "organization": { "content": "$1", @@ -6001,10 +6011,10 @@ } }, "howToManageMyVault": { - "message": "How do I manage my vault?" + "message": "Як керувати своїм сховищем?" }, "transferItemsToOrganizationTitle": { - "message": "Transfer items to $ORGANIZATION$", + "message": "Перемістити записи до $ORGANIZATION$", "placeholders": { "organization": { "content": "$1", @@ -6013,7 +6023,7 @@ } }, "transferItemsToOrganizationContent": { - "message": "$ORGANIZATION$ is requiring all items to be owned by the organization for security and compliance. Click accept to transfer ownership of your items.", + "message": "З міркувань безпеки та для забезпечення відповідності $ORGANIZATION$ вимагає, щоб власником всіх елементів була організація. Натисніть кнопку \"прийняти\", щоб передати права власності на ваші елементи.", "placeholders": { "organization": { "content": "$1", @@ -6022,12 +6032,12 @@ } }, "acceptTransfer": { - "message": "Accept transfer" + "message": "Схвалити переміщення" }, "declineAndLeave": { - "message": "Decline and leave" + "message": "Відхилити і покинути" }, "whyAmISeeingThis": { - "message": "Why am I seeing this?" + "message": "Чому я це бачу?" } } diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 4049af21e06..cd8ad7cbc91 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "Xuất từ" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Import", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Import", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Định dạng tập tin" diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index 4ebfaca7ef8..c7f7fbcd618 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "导出自" }, - "export": { - "message": "导出" + "exportVerb": { + "message": "导出", + "description": "The verb form of the word Export" }, - "import": { - "message": "导入" + "exportNoun": { + "message": "导出", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "导入", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "导入", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "文件格式" @@ -1476,7 +1486,7 @@ "message": "选择一个文件" }, "itemsTransferred": { - "message": "项目已传输" + "message": "项目已转移" }, "maxFileSize": { "message": "文件最大为 500 MB。" @@ -2355,7 +2365,7 @@ "message": "无效 PIN 码。" }, "tooManyInvalidPinEntryAttemptsLoggingOut": { - "message": "无效的 PIN 输入尝试次数过多,正在注销。" + "message": "无效的 PIN 输入尝试次数过多。正在注销。" }, "unlockWithBiometrics": { "message": "使用生物识别解锁" @@ -3794,7 +3804,7 @@ "description": "Browser extension/addon" }, "desktop": { - "message": "桌面", + "message": "桌面端", "description": "Desktop app" }, "webVault": { @@ -4802,13 +4812,13 @@ "message": "账户安全" }, "phishingBlocker": { - "message": "Phishing Blocker" + "message": "网络钓鱼拦截器" }, "enablePhishingDetection": { - "message": "Phishing detection" + "message": "网络钓鱼检测" }, "enablePhishingDetectionDesc": { - "message": "Display warning before accessing suspected phishing sites" + "message": "在访问疑似钓鱼网站前显示警告" }, "notifications": { "message": "通知" @@ -5697,7 +5707,7 @@ "message": "导入现有密码" }, "emptyVaultNudgeBody": { - "message": "使用导入器快速将登录传输到 Bitwarden 而无需手动添加。" + "message": "使用导入器快速将登录转移到 Bitwarden 而无需手动添加。" }, "emptyVaultNudgeButton": { "message": "立即导入" @@ -5726,7 +5736,7 @@ "description": "This is in multiple parts to allow for bold text in the middle of the sentence. A proper name precedes this." }, "phishingPageLearnMore": { - "message": "进一步了解钓鱼检测" + "message": "进一步了解网络钓鱼检测" }, "protectedBy": { "message": "受 $PRODUCT$ 保护", @@ -6004,7 +6014,7 @@ "message": "我该如何管理我的密码库?" }, "transferItemsToOrganizationTitle": { - "message": "传输项目到 $ORGANIZATION$", + "message": "转移项目到 $ORGANIZATION$", "placeholders": { "organization": { "content": "$1", @@ -6013,7 +6023,7 @@ } }, "transferItemsToOrganizationContent": { - "message": "出于安全和合规考虑,$ORGANIZATION$ 要求所有项目归组织所有。点击「接受」以传输您的项目的所有权。", + "message": "出于安全和合规考虑,$ORGANIZATION$ 要求所有项目归组织所有。点击「接受」以转移您的项目的所有权。", "placeholders": { "organization": { "content": "$1", @@ -6022,7 +6032,7 @@ } }, "acceptTransfer": { - "message": "接受传输" + "message": "接受转移" }, "declineAndLeave": { "message": "拒绝并退出" diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index e0b69c212b5..abb25c48b43 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -437,7 +437,7 @@ "message": "同步" }, "syncNow": { - "message": "Sync now" + "message": "立即同步" }, "lastSync": { "message": "上次同步於:" @@ -1322,11 +1322,21 @@ "exportFrom": { "message": "匯出自" }, - "export": { - "message": "Export" + "exportVerb": { + "message": "匯出", + "description": "The verb form of the word Export" }, - "import": { - "message": "Import" + "exportNoun": { + "message": "匯出", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "匯入", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "匯入", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "檔案格式" @@ -1476,7 +1486,7 @@ "message": "選取檔案" }, "itemsTransferred": { - "message": "Items transferred" + "message": "項目已轉移" }, "maxFileSize": { "message": "檔案最大為 500MB。" @@ -4802,13 +4812,13 @@ "message": "帳戶安全性" }, "phishingBlocker": { - "message": "Phishing Blocker" + "message": "釣魚封鎖器" }, "enablePhishingDetection": { - "message": "Phishing detection" + "message": "釣魚偵測" }, "enablePhishingDetectionDesc": { - "message": "Display warning before accessing suspected phishing sites" + "message": "在存取疑似釣魚網站前顯示警告" }, "notifications": { "message": "通知" @@ -5894,43 +5904,43 @@ "message": "支付卡號碼" }, "removeMasterPasswordForOrgUserKeyConnector": { - "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + "message": "您的組織已不再使用主密碼登入 Bitwarden。若要繼續,請驗證組織與網域。" }, "continueWithLogIn": { - "message": "Continue with log in" + "message": "繼續登入" }, "doNotContinue": { - "message": "Do not continue" + "message": "不要繼續" }, "domain": { - "message": "Domain" + "message": "網域" }, "keyConnectorDomainTooltip": { - "message": "This domain will store your account encryption keys, so make sure you trust it. If you're not sure, check with your admin." + "message": "此網域將儲存您帳號的加密金鑰,請確認您信任它。若不確定,請洽詢您的管理員。" }, "verifyYourOrganization": { - "message": "Verify your organization to log in" + "message": "驗證您的組織以登入" }, "organizationVerified": { - "message": "Organization verified" + "message": "組織已驗證" }, "domainVerified": { - "message": "Domain verified" + "message": "已驗證網域" }, "leaveOrganizationContent": { - "message": "If you don't verify your organization, your access to the organization will be revoked." + "message": "若您未驗證組織,將會被撤銷對該組織的存取權限。" }, "leaveNow": { - "message": "Leave now" + "message": "立即離開" }, "verifyYourDomainToLogin": { - "message": "Verify your domain to log in" + "message": "驗證您的網域以登入" }, "verifyYourDomainDescription": { - "message": "To continue with log in, verify this domain." + "message": "若要繼續登入,請驗證此網域。" }, "confirmKeyConnectorOrganizationUserDescription": { - "message": "To continue with log in, verify the organization and domain." + "message": "若要繼續登入,請驗證組織與網域。" }, "sessionTimeoutSettingsAction": { "message": "逾時後動作" @@ -5980,19 +5990,19 @@ "message": "設定一個解鎖方式來變更您的密碼庫逾時動作。" }, "upgrade": { - "message": "Upgrade" + "message": "升級" }, "leaveConfirmationDialogTitle": { - "message": "Are you sure you want to leave?" + "message": "確定要離開嗎?" }, "leaveConfirmationDialogContentOne": { - "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + "message": "若選擇拒絕,您的個人項目將保留在帳號中,但您將失去對共用項目與組織功能的存取權。" }, "leaveConfirmationDialogContentTwo": { - "message": "Contact your admin to regain access." + "message": "請聯絡您的管理員以重新取得存取權限。" }, "leaveConfirmationDialogConfirmButton": { - "message": "Leave $ORGANIZATION$", + "message": "離開 $ORGANIZATION$", "placeholders": { "organization": { "content": "$1", @@ -6001,10 +6011,10 @@ } }, "howToManageMyVault": { - "message": "How do I manage my vault?" + "message": "我要如何管理我的密碼庫?" }, "transferItemsToOrganizationTitle": { - "message": "Transfer items to $ORGANIZATION$", + "message": "將項目轉移至 $ORGANIZATION$", "placeholders": { "organization": { "content": "$1", @@ -6013,7 +6023,7 @@ } }, "transferItemsToOrganizationContent": { - "message": "$ORGANIZATION$ is requiring all items to be owned by the organization for security and compliance. Click accept to transfer ownership of your items.", + "message": "$ORGANIZATION$ 為了安全性與合規性,要求所有項目皆由組織擁有。點擊接受即可轉移您項目的擁有權。", "placeholders": { "organization": { "content": "$1", @@ -6022,12 +6032,12 @@ } }, "acceptTransfer": { - "message": "Accept transfer" + "message": "同意轉移" }, "declineAndLeave": { - "message": "Decline and leave" + "message": "拒絕並離開" }, "whyAmISeeingThis": { - "message": "Why am I seeing this?" + "message": "為什麼我會看到此訊息?" } } diff --git a/apps/browser/src/auth/popup/settings/account-security.component.spec.ts b/apps/browser/src/auth/popup/settings/account-security.component.spec.ts index 0f799fe7d4d..ebabbadf71c 100644 --- a/apps/browser/src/auth/popup/settings/account-security.component.spec.ts +++ b/apps/browser/src/auth/popup/settings/account-security.component.spec.ts @@ -1,5 +1,5 @@ import { Component } from "@angular/core"; -import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; import { ActivatedRoute } from "@angular/router"; import { mock } from "jest-mock-extended"; @@ -37,7 +37,12 @@ import { UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { DialogService, ToastService } from "@bitwarden/components"; import { newGuid } from "@bitwarden/guid"; -import { BiometricStateService, BiometricsService, KeyService } from "@bitwarden/key-management"; +import { + BiometricStateService, + BiometricsService, + BiometricsStatus, + KeyService, +} from "@bitwarden/key-management"; import { BrowserApi } from "../../../platform/browser/browser-api"; import BrowserPopupUtils from "../../../platform/browser/browser-popup-utils"; @@ -64,6 +69,7 @@ describe("AccountSecurityComponent", () => { const apiService = mock(); const billingService = mock(); const biometricStateService = mock(); + const biometricsService = mock(); const configService = mock(); const dialogService = mock(); const keyService = mock(); @@ -75,6 +81,7 @@ describe("AccountSecurityComponent", () => { const validationService = mock(); const vaultNudgesService = mock(); const vaultTimeoutSettingsService = mock(); + const mockI18nService = mock(); // Mock subjects to control the phishing detection observables let phishingAvailableSubject: BehaviorSubject; @@ -91,14 +98,14 @@ describe("AccountSecurityComponent", () => { provide: BillingAccountProfileStateService, useValue: billingService, }, - { provide: BiometricsService, useValue: mock() }, + { provide: BiometricsService, useValue: biometricsService }, { provide: BiometricStateService, useValue: biometricStateService }, { provide: CipherService, useValue: mock() }, { provide: CollectionService, useValue: mock() }, { provide: ConfigService, useValue: configService }, { provide: DialogService, useValue: dialogService }, { provide: EnvironmentService, useValue: mock() }, - { provide: I18nService, useValue: mock() }, + { provide: I18nService, useValue: mockI18nService }, { provide: KeyService, useValue: keyService }, { provide: LockService, useValue: lockService }, { provide: LogService, useValue: mock() }, @@ -153,6 +160,7 @@ describe("AccountSecurityComponent", () => { pinServiceAbstraction.isPinSet.mockResolvedValue(false); configService.getFeatureFlag$.mockReturnValue(of(false)); billingService.hasPremiumPersonally$.mockReturnValue(of(true)); + mockI18nService.t.mockImplementation((key) => `${key}-used-i18n`); policyService.policiesByType$.mockReturnValue(of([null])); @@ -459,4 +467,118 @@ describe("AccountSecurityComponent", () => { }); }); }); + + describe("biometrics polling timer", () => { + let browserApiSpy: jest.SpyInstance; + + beforeEach(() => { + browserApiSpy = jest.spyOn(BrowserApi, "permissionsGranted"); + }); + + afterEach(() => { + component.ngOnDestroy(); + }); + + it("disables biometric control when canEnableBiometricUnlock is false", fakeAsync(async () => { + biometricsService.canEnableBiometricUnlock.mockResolvedValue(false); + + await component.ngOnInit(); + tick(); + + expect(component.form.controls.biometric.disabled).toBe(true); + })); + + it("enables biometric control when canEnableBiometricUnlock is true", fakeAsync(async () => { + biometricsService.canEnableBiometricUnlock.mockResolvedValue(true); + + await component.ngOnInit(); + tick(); + + expect(component.form.controls.biometric.disabled).toBe(false); + })); + + it("skips status check when nativeMessaging permission is not granted and not Safari", fakeAsync(async () => { + biometricsService.canEnableBiometricUnlock.mockResolvedValue(true); + browserApiSpy.mockResolvedValue(false); + platformUtilsService.isSafari.mockReturnValue(false); + + await component.ngOnInit(); + tick(); + + expect(biometricsService.getBiometricsStatusForUser).not.toHaveBeenCalled(); + expect(component.biometricUnavailabilityReason).toBeUndefined(); + })); + + it("checks biometrics status when nativeMessaging permission is granted", fakeAsync(async () => { + biometricsService.canEnableBiometricUnlock.mockResolvedValue(true); + browserApiSpy.mockResolvedValue(true); + platformUtilsService.isSafari.mockReturnValue(false); + biometricsService.getBiometricsStatusForUser.mockResolvedValue( + BiometricsStatus.DesktopDisconnected, + ); + + await component.ngOnInit(); + tick(); + + expect(biometricsService.getBiometricsStatusForUser).toHaveBeenCalledWith(mockUserId); + })); + + it("should check status on Safari", fakeAsync(async () => { + biometricsService.canEnableBiometricUnlock.mockResolvedValue(true); + browserApiSpy.mockResolvedValue(false); + platformUtilsService.isSafari.mockReturnValue(true); + biometricsService.getBiometricsStatusForUser.mockResolvedValue( + BiometricsStatus.DesktopDisconnected, + ); + + await component.ngOnInit(); + tick(); + + expect(biometricsService.getBiometricsStatusForUser).toHaveBeenCalledWith(mockUserId); + })); + + test.each([ + [ + BiometricsStatus.DesktopDisconnected, + "biometricsStatusHelptextDesktopDisconnected-used-i18n", + ], + [ + BiometricsStatus.NotEnabledInConnectedDesktopApp, + "biometricsStatusHelptextNotEnabledInDesktop-used-i18n", + ], + [ + BiometricsStatus.HardwareUnavailable, + "biometricsStatusHelptextHardwareUnavailable-used-i18n", + ], + ])( + "sets expected unavailability reason for %s status when biometric not available", + fakeAsync(async (biometricStatus: BiometricsStatus, expected: string) => { + biometricsService.canEnableBiometricUnlock.mockResolvedValue(false); + browserApiSpy.mockResolvedValue(true); + platformUtilsService.isSafari.mockReturnValue(false); + biometricsService.getBiometricsStatusForUser.mockResolvedValue(biometricStatus); + + await component.ngOnInit(); + tick(); + + expect(component.biometricUnavailabilityReason).toBe(expected); + }), + ); + + it("should not set unavailability reason for error statuses when biometric is available", fakeAsync(async () => { + biometricsService.canEnableBiometricUnlock.mockResolvedValue(true); + browserApiSpy.mockResolvedValue(true); + platformUtilsService.isSafari.mockReturnValue(false); + biometricsService.getBiometricsStatusForUser.mockResolvedValue( + BiometricsStatus.DesktopDisconnected, + ); + + await component.ngOnInit(); + tick(); + + // Status is DesktopDisconnected but biometric IS available, so don't show error + expect(component.biometricUnavailabilityReason).toBe(""); + component.ngOnDestroy(); + })); + }); }); diff --git a/apps/browser/src/auth/popup/settings/account-security.component.ts b/apps/browser/src/auth/popup/settings/account-security.component.ts index 7c36754c894..6a3378670bf 100644 --- a/apps/browser/src/auth/popup/settings/account-security.component.ts +++ b/apps/browser/src/auth/popup/settings/account-security.component.ts @@ -149,6 +149,7 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { protected refreshTimeoutSettings$ = new BehaviorSubject(undefined); private destroy$ = new Subject(); + private readonly BIOMETRICS_POLLING_INTERVAL = 2000; constructor( private accountService: AccountService, @@ -264,10 +265,9 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { }; this.form.patchValue(initialValues, { emitEvent: false }); - timer(0, 1000) + timer(0, this.BIOMETRICS_POLLING_INTERVAL) .pipe( switchMap(async () => { - const status = await this.biometricsService.getBiometricsStatusForUser(activeAccount.id); const biometricSettingAvailable = await this.biometricsService.canEnableBiometricUnlock(); if (!biometricSettingAvailable) { this.form.controls.biometric.disable({ emitEvent: false }); @@ -275,6 +275,15 @@ export class AccountSecurityComponent implements OnInit, OnDestroy { this.form.controls.biometric.enable({ emitEvent: false }); } + // Biometrics status shouldn't be checked if permissions are needed. + const needsPermissionPrompt = + !(await BrowserApi.permissionsGranted(["nativeMessaging"])) && + !this.platformUtilsService.isSafari(); + if (needsPermissionPrompt) { + return; + } + + const status = await this.biometricsService.getBiometricsStatusForUser(activeAccount.id); if (status === BiometricsStatus.DesktopDisconnected && !biometricSettingAvailable) { this.biometricUnavailabilityReason = this.i18nService.t( "biometricsStatusHelptextDesktopDisconnected", diff --git a/apps/browser/src/autofill/fido2/content/messaging/messenger.ts b/apps/browser/src/autofill/fido2/content/messaging/messenger.ts index 257f7e9efd5..61ed7a8ed08 100644 --- a/apps/browser/src/autofill/fido2/content/messaging/messenger.ts +++ b/apps/browser/src/autofill/fido2/content/messaging/messenger.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Message, MessageTypes } from "./message"; const SENDER = "bitwarden-webauthn"; @@ -25,7 +23,7 @@ type Handler = ( * handling aborts and exceptions across separate execution contexts. */ export class Messenger { - private messageEventListener: (event: MessageEvent) => void | null = null; + private messageEventListener: ((event: MessageEvent) => void) | null = null; private onDestroy = new EventTarget(); /** @@ -60,6 +58,12 @@ export class Messenger { this.broadcastChannel.addEventListener(this.messageEventListener); } + private stripMetadata({ SENDER, senderId, ...message }: MessageWithMetadata): Message { + void SENDER; + void senderId; + return message; + } + /** * Sends a request to the content script and returns the response. * AbortController signals will be forwarded to the content script. @@ -74,7 +78,9 @@ export class Messenger { try { const promise = new Promise((resolve) => { - localPort.onmessage = (event: MessageEvent) => resolve(event.data); + localPort.onmessage = (event: MessageEvent) => { + resolve(this.stripMetadata(event.data)); + }; }); const abortListener = () => @@ -129,7 +135,9 @@ export class Messenger { try { const handlerResponse = await this.handler(message, abortController); - port.postMessage({ ...handlerResponse, SENDER }); + if (handlerResponse !== undefined) { + port.postMessage({ ...handlerResponse, SENDER }); + } } catch (error) { port.postMessage({ SENDER, diff --git a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts index 64ef7d180ed..ad1241e98d2 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { EVENTS } from "@bitwarden/common/autofill/constants"; import { ThemeTypes } from "@bitwarden/common/platform/enums"; @@ -15,14 +13,17 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe private readonly setElementStyles = setElementStyles; private readonly sendExtensionMessage = sendExtensionMessage; private port: chrome.runtime.Port | null = null; - private portKey: string; + private portKey?: string; private readonly extensionOrigin: string; private iframeMutationObserver: MutationObserver; - private iframe: HTMLIFrameElement; - private ariaAlertElement: HTMLDivElement; - private ariaAlertTimeout: number | NodeJS.Timeout; - private delayedCloseTimeout: number | NodeJS.Timeout; - private fadeInTimeout: number | NodeJS.Timeout; + /** + * Initialized in initMenuIframe which makes it safe to assert non null by lifecycle. + */ + private iframe!: HTMLIFrameElement; + private ariaAlertElement?: HTMLDivElement; + private ariaAlertTimeout: number | NodeJS.Timeout | null = null; + private delayedCloseTimeout: number | NodeJS.Timeout | null = null; + private fadeInTimeout: number | NodeJS.Timeout | null = null; private readonly fadeInOpacityTransition = "opacity 125ms ease-out 0s"; private readonly fadeOutOpacityTransition = "opacity 65ms ease-out 0s"; private iframeStyles: Partial = { @@ -50,7 +51,7 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe }; private foreignMutationsCount = 0; private mutationObserverIterations = 0; - private mutationObserverIterationsResetTimeout: number | NodeJS.Timeout; + private mutationObserverIterationsResetTimeout: number | NodeJS.Timeout | null = null; private readonly backgroundPortMessageHandlers: BackgroundPortMessageHandlers = { initAutofillInlineMenuButton: ({ message }) => this.initAutofillInlineMenu(message), initAutofillInlineMenuList: ({ message }) => this.initAutofillInlineMenu(message), @@ -134,7 +135,9 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe this.port.onDisconnect.addListener(this.handlePortDisconnect); this.port.onMessage.addListener(this.handlePortMessage); - this.announceAriaAlert(this.ariaAlert, 2000); + if (this.ariaAlert) { + this.announceAriaAlert(this.ariaAlert, 2000); + } }; /** @@ -155,7 +158,7 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe this.ariaAlertTimeout = globalThis.setTimeout(async () => { const isFieldFocused = await this.sendExtensionMessage("checkIsFieldCurrentlyFocused"); - if (isFieldFocused || triggeredByUser) { + if ((isFieldFocused || triggeredByUser) && this.ariaAlertElement) { this.shadow.appendChild(this.ariaAlertElement); } this.ariaAlertTimeout = null; @@ -242,7 +245,7 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe */ private initAutofillInlineMenuList(message: AutofillInlineMenuIframeExtensionMessage) { const { theme } = message; - let borderColor: string; + let borderColor: string | undefined; let verifiedTheme = theme; if (verifiedTheme === ThemeTypes.System) { verifiedTheme = globalThis.matchMedia("(prefers-color-scheme: dark)").matches @@ -274,8 +277,8 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe * * @param position - The position styles to apply to the iframe */ - private updateIframePosition(position: Partial) { - if (!globalThis.document.hasFocus()) { + private updateIframePosition(position?: Partial) { + if (!position || !globalThis.document.hasFocus()) { return; } @@ -295,7 +298,9 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe this.handleFadeInInlineMenuIframe(); } - this.announceAriaAlert(this.ariaAlert, 2000); + if (this.ariaAlert) { + this.announceAriaAlert(this.ariaAlert, 2000); + } } /** @@ -359,8 +364,8 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe * @param customElement - The element to update the styles for * @param styles - The styles to apply to the element */ - private updateElementStyles(customElement: HTMLElement, styles: Partial) { - if (!customElement) { + private updateElementStyles(customElement: HTMLElement, styles?: Partial) { + if (!customElement || !styles) { return; } diff --git a/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts b/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts index ab3b8144426..5784fd7a73a 100644 --- a/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts +++ b/apps/browser/src/autofill/overlay/notifications/content/overlay-notifications-content.service.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { EVENTS } from "@bitwarden/common/autofill/constants"; import { BrowserApi } from "../../../../platform/browser/browser-api"; @@ -84,11 +82,15 @@ export class OverlayNotificationsContentService implements OverlayNotificationsC } const { type, typeData, params } = message.data; + if (!typeData) { + return; + } + if (this.currentNotificationBarType && type !== this.currentNotificationBarType) { this.closeNotificationBar(); } - const initData = { + const initData: NotificationBarIframeInitData = { type: type as NotificationType, isVaultLocked: typeData.isVaultLocked, theme: typeData.theme, @@ -116,7 +118,9 @@ export class OverlayNotificationsContentService implements OverlayNotificationsC const closedByUser = typeof message.data?.closedByUser === "boolean" ? message.data.closedByUser : true; if (message.data?.fadeOutNotification) { - setElementStyles(this.notificationBarIframeElement, { opacity: "0" }, true); + if (this.notificationBarIframeElement) { + setElementStyles(this.notificationBarIframeElement, { opacity: "0" }, true); + } globalThis.setTimeout(() => this.closeNotificationBar(closedByUser), 150); return; } @@ -166,7 +170,9 @@ export class OverlayNotificationsContentService implements OverlayNotificationsC this.createNotificationBarElement(); this.setupInitNotificationBarMessageListener(initData); - globalThis.document.body.appendChild(this.notificationBarRootElement); + if (this.notificationBarRootElement) { + globalThis.document.body.appendChild(this.notificationBarRootElement); + } } } @@ -179,7 +185,7 @@ export class OverlayNotificationsContentService implements OverlayNotificationsC const isNotificationFresh = initData.launchTimestamp && Date.now() - initData.launchTimestamp < 250; - this.currentNotificationBarType = initData.type; + this.currentNotificationBarType = initData.type ?? null; this.notificationBarIframeElement = globalThis.document.createElement("iframe"); this.notificationBarIframeElement.id = "bit-notification-bar-iframe"; const parentOrigin = globalThis.location.origin; @@ -206,11 +212,13 @@ export class OverlayNotificationsContentService implements OverlayNotificationsC * This will animate the notification bar into view. */ private handleNotificationBarIframeOnLoad = () => { - setElementStyles( - this.notificationBarIframeElement, - { transform: "translateX(0)", opacity: "1" }, - true, - ); + if (this.notificationBarIframeElement) { + setElementStyles( + this.notificationBarIframeElement, + { transform: "translateX(0)", opacity: "1" }, + true, + ); + } this.notificationBarIframeElement?.removeEventListener( EVENTS.LOAD, @@ -252,6 +260,7 @@ export class OverlayNotificationsContentService implements OverlayNotificationsC const handleInitNotificationBarMessage = (event: MessageEvent) => { const { source, data } = event; if ( + !this.notificationBarIframeElement?.contentWindow || source !== this.notificationBarIframeElement.contentWindow || data?.command !== "initNotificationBar" ) { @@ -282,13 +291,14 @@ export class OverlayNotificationsContentService implements OverlayNotificationsC return; } - this.notificationBarIframeElement.remove(); + this.notificationBarIframeElement?.remove(); this.notificationBarIframeElement = null; - this.notificationBarElement.remove(); + this.notificationBarElement?.remove(); this.notificationBarElement = null; this.notificationBarShadowRoot = null; - this.notificationBarRootElement.remove(); + + this.notificationBarRootElement?.remove(); this.notificationBarRootElement = null; const removableNotificationTypes = new Set([ @@ -297,7 +307,11 @@ export class OverlayNotificationsContentService implements OverlayNotificationsC NotificationTypes.AtRiskPassword, ] as NotificationType[]); - if (closedByUserAction && removableNotificationTypes.has(this.currentNotificationBarType)) { + if ( + closedByUserAction && + this.currentNotificationBarType && + removableNotificationTypes.has(this.currentNotificationBarType) + ) { void sendExtensionMessage("bgRemoveTabFromNotificationQueue"); } @@ -310,7 +324,7 @@ export class OverlayNotificationsContentService implements OverlayNotificationsC * @param message - The message to send to the notification bar iframe. */ private sendMessageToNotificationBarIframe(message: Record) { - if (this.notificationBarIframeElement) { + if (this.notificationBarIframeElement?.contentWindow) { this.notificationBarIframeElement.contentWindow.postMessage(message, this.extensionOrigin); } } diff --git a/apps/browser/src/autofill/popup/settings/autofill.component.html b/apps/browser/src/autofill/popup/settings/autofill.component.html index 1153ad58719..085145adb19 100644 --- a/apps/browser/src/autofill/popup/settings/autofill.component.html +++ b/apps/browser/src/autofill/popup/settings/autofill.component.html @@ -6,16 +6,18 @@
-
- -
+ @if (showSpotlightNudge$ | async) { +
+ +
+ }

{{ "autofillSuggestionsSectionTitle" | i18n }}

diff --git a/apps/browser/src/autofill/popup/settings/autofill.component.ts b/apps/browser/src/autofill/popup/settings/autofill.component.ts index 49be3104dc1..acb2aa7a970 100644 --- a/apps/browser/src/autofill/popup/settings/autofill.component.ts +++ b/apps/browser/src/autofill/popup/settings/autofill.component.ts @@ -611,6 +611,10 @@ export class AutofillComponent implements OnInit { if (this.canOverrideBrowserAutofillSetting) { this.defaultBrowserAutofillDisabled = true; await this.updateDefaultBrowserAutofillDisabled(); + await this.nudgesService.dismissNudge( + NudgeType.AutofillNudge, + await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)), + ); } else { await this.openURI(event, this.disablePasswordManagerURI); } diff --git a/apps/browser/src/autofill/services/collect-autofill-content.service.ts b/apps/browser/src/autofill/services/collect-autofill-content.service.ts index 367599f7ad0..18eb8e2baf8 100644 --- a/apps/browser/src/autofill/services/collect-autofill-content.service.ts +++ b/apps/browser/src/autofill/services/collect-autofill-content.service.ts @@ -60,6 +60,15 @@ export class CollectAutofillContentService implements CollectAutofillContentServ "button", "image", "file", + "search", + "url", + "date", + "time", + "datetime", // Note: datetime is deprecated in HTML5; keeping here for backwards compatibility + "datetime-local", + "week", + "color", + "range", ]); constructor( diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 7b509380f6d..3cd8b59aabc 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -87,6 +87,8 @@ import { HibpApiService } from "@bitwarden/common/dirt/services/hibp-api.service import { PhishingDetectionSettingsService } from "@bitwarden/common/dirt/services/phishing-detection/phishing-detection-settings.service"; import { ClientType } from "@bitwarden/common/enums"; import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service"; +import { AccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/account-cryptographic-state.service"; +import { DefaultAccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/default-account-cryptographic-state.service"; import { DefaultKeyGenerationService, KeyGenerationService, @@ -124,6 +126,7 @@ import { FileUploadService as FileUploadServiceAbstraction } from "@bitwarden/co import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { RegisterSdkService } from "@bitwarden/common/platform/abstractions/sdk/register-sdk.service"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; @@ -162,6 +165,7 @@ import { MigrationRunner } from "@bitwarden/common/platform/services/migration-r import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory"; import { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/default-sdk.service"; import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory"; +import { DefaultRegisterSdkService } from "@bitwarden/common/platform/services/sdk/register-sdk.service"; import { SystemService } from "@bitwarden/common/platform/services/system.service"; import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service"; import { PrimarySecondaryStorageService } from "@bitwarden/common/platform/storage/primary-secondary-storage.service"; @@ -455,11 +459,13 @@ export default class MainBackground { syncServiceListener: SyncServiceListener; browserInitialInstallService: BrowserInitialInstallService; backgroundSyncService: BackgroundSyncService; + accountCryptographicStateService: AccountCryptographicStateService; webPushConnectionService: WorkerWebPushConnectionService | UnsupportedWebPushConnectionService; themeStateService: DefaultThemeStateService; autoSubmitLoginBackground: AutoSubmitLoginBackground; sdkService: SdkService; + registerSdkService: RegisterSdkService; sdkLoadService: SdkLoadService; cipherAuthorizationService: CipherAuthorizationService; endUserNotificationService: EndUserNotificationService; @@ -575,7 +581,7 @@ export default class MainBackground { "ephemeral", "bitwarden-ephemeral", ); - await sessionStorage.save("session-key", derivedKey); + await sessionStorage.save("session-key", derivedKey.toJSON()); return derivedKey; }); @@ -794,18 +800,6 @@ export default class MainBackground { this.apiService, this.accountService, ); - this.keyConnectorService = new KeyConnectorService( - this.accountService, - this.masterPasswordService, - this.keyService, - this.apiService, - this.tokenService, - this.logService, - this.organizationService, - this.keyGenerationService, - logoutCallback, - this.stateProvider, - ); this.authService = new AuthService( this.accountService, @@ -843,6 +837,37 @@ export default class MainBackground { this.configService, ); + this.registerSdkService = new DefaultRegisterSdkService( + sdkClientFactory, + this.environmentService, + this.platformUtilsService, + this.accountService, + this.apiService, + this.stateProvider, + this.configService, + ); + + this.accountCryptographicStateService = new DefaultAccountCryptographicStateService( + this.stateProvider, + ); + + this.keyConnectorService = new KeyConnectorService( + this.accountService, + this.masterPasswordService, + this.keyService, + this.apiService, + this.tokenService, + this.logService, + this.organizationService, + this.keyGenerationService, + logoutCallback, + this.stateProvider, + this.configService, + this.registerSdkService, + this.securityStateService, + this.accountCryptographicStateService, + ); + this.pinService = new PinService( this.encryptService, this.logService, @@ -1010,6 +1035,7 @@ export default class MainBackground { this.avatarService = new AvatarService(this.apiService, this.stateProvider); this.providerService = new ProviderService(this.stateProvider); + this.syncService = new DefaultSyncService( this.masterPasswordService, this.accountService, @@ -1037,6 +1063,7 @@ export default class MainBackground { this.stateProvider, this.securityStateService, this.kdfConfigService, + this.accountCryptographicStateService, ); this.syncServiceListener = new SyncServiceListener( @@ -1483,6 +1510,7 @@ export default class MainBackground { this.billingAccountProfileStateService, this.configService, this.organizationService, + this.platformUtilsService, this.stateProvider, ); diff --git a/apps/browser/src/dirt/phishing-detection/phishing-resources.ts b/apps/browser/src/dirt/phishing-detection/phishing-resources.ts new file mode 100644 index 00000000000..262d6cf833b --- /dev/null +++ b/apps/browser/src/dirt/phishing-detection/phishing-resources.ts @@ -0,0 +1,98 @@ +export type PhishingResource = { + name?: string; + remoteUrl: string; + checksumUrl: string; + todayUrl: string; + /** Matcher used to decide whether a given URL matches an entry from this resource */ + match: (url: URL, entry: string) => boolean; +}; + +export const PhishingResourceType = Object.freeze({ + Domains: "domains", + Links: "links", +} as const); + +export type PhishingResourceType = (typeof PhishingResourceType)[keyof typeof PhishingResourceType]; + +export const PHISHING_RESOURCES: Record = { + [PhishingResourceType.Domains]: [ + { + name: "Phishing.Database Domains", + remoteUrl: + "https://raw.githubusercontent.com/Phishing-Database/Phishing.Database/master/phishing-domains-ACTIVE.txt", + checksumUrl: + "https://raw.githubusercontent.com/Phishing-Database/checksums/refs/heads/master/phishing-domains-ACTIVE.txt.md5", + todayUrl: + "https://raw.githubusercontent.com/Phishing-Database/Phishing.Database/refs/heads/master/phishing-domains-NEW-today.txt", + match: (url: URL, entry: string) => { + if (!entry) { + return false; + } + const candidate = entry.trim().toLowerCase().replace(/\/$/, ""); + // If entry contains a scheme, strip it for comparison + const e = candidate.replace(/^https?:\/\//, ""); + // Compare against hostname or host+path + if (e === url.hostname.toLowerCase()) { + return true; + } + const urlNoProto = url.href + .toLowerCase() + .replace(/https?:\/\//, "") + .replace(/\/$/, ""); + return urlNoProto === e || urlNoProto.startsWith(e + "/"); + }, + }, + ], + [PhishingResourceType.Links]: [ + { + name: "Phishing.Database Links", + remoteUrl: + "https://raw.githubusercontent.com/Phishing-Database/Phishing.Database/master/phishing-links-ACTIVE.txt", + checksumUrl: + "https://raw.githubusercontent.com/Phishing-Database/checksums/refs/heads/master/phishing-links-ACTIVE.txt.md5", + todayUrl: + "https://raw.githubusercontent.com/Phishing-Database/Phishing.Database/refs/heads/master/phishing-links-NEW-today.txt", + match: (url: URL, entry: string) => { + if (!entry) { + return false; + } + // Basic HTML entity decode for common cases (the lists sometimes contain &) + const decodeHtml = (s: string) => s.replace(/&/g, "&"); + + const normalizedEntry = decodeHtml(entry.trim()).toLowerCase().replace(/\/$/, ""); + + // Normalize URL for comparison - always strip protocol for consistent matching + const normalizedUrl = decodeHtml(url.href).toLowerCase().replace(/\/$/, ""); + const urlNoProto = normalizedUrl.replace(/^https?:\/\//, ""); + + // Strip protocol from entry if present (http:// and https:// should be treated as equivalent) + const entryNoProto = normalizedEntry.replace(/^https?:\/\//, ""); + + // Compare full path (without protocol) - exact match + if (urlNoProto === entryNoProto) { + return true; + } + + // Check if URL starts with entry (prefix match for subpaths/query/hash) + // e.g., entry "site.com/phish" matches "site.com/phish/subpage" or "site.com/phish?id=1" + if ( + urlNoProto.startsWith(entryNoProto + "/") || + urlNoProto.startsWith(entryNoProto + "?") || + urlNoProto.startsWith(entryNoProto + "#") + ) { + return true; + } + + return false; + }, + }, + ], +}; + +export function getPhishingResources( + type: PhishingResourceType, + index = 0, +): PhishingResource | undefined { + const list = PHISHING_RESOURCES[type] ?? []; + return list[index]; +} diff --git a/apps/browser/src/dirt/phishing-detection/services/phishing-data.service.spec.ts b/apps/browser/src/dirt/phishing-detection/services/phishing-data.service.spec.ts index 94f3e99f8be..30aa947092d 100644 --- a/apps/browser/src/dirt/phishing-detection/services/phishing-data.service.spec.ts +++ b/apps/browser/src/dirt/phishing-detection/services/phishing-data.service.spec.ts @@ -25,7 +25,7 @@ describe("PhishingDataService", () => { }; let fetchChecksumSpy: jest.SpyInstance; - let fetchDomainsSpy: jest.SpyInstance; + let fetchWebAddressesSpy: jest.SpyInstance; beforeEach(() => { jest.useFakeTimers(); @@ -45,113 +45,113 @@ describe("PhishingDataService", () => { platformUtilsService, ); - fetchChecksumSpy = jest.spyOn(service as any, "fetchPhishingDomainsChecksum"); - fetchDomainsSpy = jest.spyOn(service as any, "fetchPhishingDomains"); + fetchChecksumSpy = jest.spyOn(service as any, "fetchPhishingChecksum"); + fetchWebAddressesSpy = jest.spyOn(service as any, "fetchPhishingWebAddresses"); }); - describe("isPhishingDomains", () => { - it("should detect a phishing domain", async () => { + describe("isPhishingWebAddress", () => { + it("should detect a phishing web address", async () => { setMockState({ - domains: ["phish.com", "badguy.net"], + webAddresses: ["phish.com", "badguy.net"], timestamp: Date.now(), checksum: "abc123", applicationVersion: "1.0.0", }); const url = new URL("http://phish.com"); - const result = await service.isPhishingDomain(url); + const result = await service.isPhishingWebAddress(url); expect(result).toBe(true); }); - it("should not detect a safe domain", async () => { + it("should not detect a safe web address", async () => { setMockState({ - domains: ["phish.com", "badguy.net"], + webAddresses: ["phish.com", "badguy.net"], timestamp: Date.now(), checksum: "abc123", applicationVersion: "1.0.0", }); const url = new URL("http://safe.com"); - const result = await service.isPhishingDomain(url); + const result = await service.isPhishingWebAddress(url); expect(result).toBe(false); }); - it("should match against root domain", async () => { + it("should match against root web address", async () => { setMockState({ - domains: ["phish.com", "badguy.net"], + webAddresses: ["phish.com", "badguy.net"], timestamp: Date.now(), checksum: "abc123", applicationVersion: "1.0.0", }); const url = new URL("http://phish.com/about"); - const result = await service.isPhishingDomain(url); + const result = await service.isPhishingWebAddress(url); expect(result).toBe(true); }); it("should not error on empty state", async () => { setMockState(undefined as any); const url = new URL("http://phish.com/about"); - const result = await service.isPhishingDomain(url); + const result = await service.isPhishingWebAddress(url); expect(result).toBe(false); }); }); - describe("getNextDomains", () => { - it("refetches all domains if applicationVersion has changed", async () => { + describe("getNextWebAddresses", () => { + it("refetches all web addresses if applicationVersion has changed", async () => { const prev: PhishingData = { - domains: ["a.com"], + webAddresses: ["a.com"], timestamp: Date.now() - 60000, checksum: "old", applicationVersion: "1.0.0", }; fetchChecksumSpy.mockResolvedValue("new"); - fetchDomainsSpy.mockResolvedValue(["d.com", "e.com"]); + fetchWebAddressesSpy.mockResolvedValue(["d.com", "e.com"]); platformUtilsService.getApplicationVersion.mockResolvedValue("2.0.0"); - const result = await service.getNextDomains(prev); + const result = await service.getNextWebAddresses(prev); - expect(result!.domains).toEqual(["d.com", "e.com"]); + expect(result!.webAddresses).toEqual(["d.com", "e.com"]); expect(result!.checksum).toBe("new"); expect(result!.applicationVersion).toBe("2.0.0"); }); it("only updates timestamp if checksum matches", async () => { const prev: PhishingData = { - domains: ["a.com"], + webAddresses: ["a.com"], timestamp: Date.now() - 60000, checksum: "abc", applicationVersion: "1.0.0", }; fetchChecksumSpy.mockResolvedValue("abc"); - const result = await service.getNextDomains(prev); - expect(result!.domains).toEqual(prev.domains); + const result = await service.getNextWebAddresses(prev); + expect(result!.webAddresses).toEqual(prev.webAddresses); expect(result!.checksum).toBe("abc"); expect(result!.timestamp).not.toBe(prev.timestamp); }); it("patches daily domains if cache is fresh", async () => { const prev: PhishingData = { - domains: ["a.com"], + webAddresses: ["a.com"], timestamp: Date.now() - 60000, checksum: "old", applicationVersion: "1.0.0", }; fetchChecksumSpy.mockResolvedValue("new"); - fetchDomainsSpy.mockResolvedValue(["b.com", "c.com"]); - const result = await service.getNextDomains(prev); - expect(result!.domains).toEqual(["a.com", "b.com", "c.com"]); + fetchWebAddressesSpy.mockResolvedValue(["b.com", "c.com"]); + const result = await service.getNextWebAddresses(prev); + expect(result!.webAddresses).toEqual(["a.com", "b.com", "c.com"]); expect(result!.checksum).toBe("new"); }); it("fetches all domains if cache is old", async () => { const prev: PhishingData = { - domains: ["a.com"], + webAddresses: ["a.com"], timestamp: Date.now() - 2 * 24 * 60 * 60 * 1000, checksum: "old", applicationVersion: "1.0.0", }; fetchChecksumSpy.mockResolvedValue("new"); - fetchDomainsSpy.mockResolvedValue(["d.com", "e.com"]); - const result = await service.getNextDomains(prev); - expect(result!.domains).toEqual(["d.com", "e.com"]); + fetchWebAddressesSpy.mockResolvedValue(["d.com", "e.com"]); + const result = await service.getNextWebAddresses(prev); + expect(result!.webAddresses).toEqual(["d.com", "e.com"]); expect(result!.checksum).toBe("new"); }); }); diff --git a/apps/browser/src/dirt/phishing-detection/services/phishing-data.service.ts b/apps/browser/src/dirt/phishing-detection/services/phishing-data.service.ts index 6e1bf07c647..21fe74f1873 100644 --- a/apps/browser/src/dirt/phishing-detection/services/phishing-data.service.ts +++ b/apps/browser/src/dirt/phishing-detection/services/phishing-data.service.ts @@ -20,14 +20,16 @@ import { ScheduledTaskNames, TaskSchedulerService } from "@bitwarden/common/plat import { LogService } from "@bitwarden/logging"; import { GlobalStateProvider, KeyDefinition, PHISHING_DETECTION_DISK } from "@bitwarden/state"; +import { getPhishingResources, PhishingResourceType } from "../phishing-resources"; + export type PhishingData = { - domains: string[]; + webAddresses: string[]; timestamp: number; checksum: string; /** * We store the application version to refetch the entire dataset on a new client release. - * This counteracts daily appends updates not removing inactive or false positive domains. + * This counteracts daily appends updates not removing inactive or false positive web addresses. */ applicationVersion: string; }; @@ -37,34 +39,27 @@ export const PHISHING_DOMAINS_KEY = new KeyDefinition( "phishingDomains", { deserializer: (value: PhishingData) => - value ?? { domains: [], timestamp: 0, checksum: "", applicationVersion: "" }, + value ?? { webAddresses: [], timestamp: 0, checksum: "", applicationVersion: "" }, }, ); -/** Coordinates fetching, caching, and patching of known phishing domains */ +/** Coordinates fetching, caching, and patching of known phishing web addresses */ export class PhishingDataService { - private static readonly RemotePhishingDatabaseUrl = - "https://raw.githubusercontent.com/Phishing-Database/Phishing.Database/master/phishing-domains-ACTIVE.txt"; - private static readonly RemotePhishingDatabaseChecksumUrl = - "https://raw.githubusercontent.com/Phishing-Database/checksums/refs/heads/master/phishing-domains-ACTIVE.txt.md5"; - private static readonly RemotePhishingDatabaseTodayUrl = - "https://raw.githubusercontent.com/Phishing-Database/Phishing.Database/refs/heads/master/phishing-domains-NEW-today.txt"; - - private _testDomains = this.getTestDomains(); + private _testWebAddresses = this.getTestWebAddresses(); private _cachedState = this.globalStateProvider.get(PHISHING_DOMAINS_KEY); - private _domains$ = this._cachedState.state$.pipe( + private _webAddresses$ = this._cachedState.state$.pipe( map( (state) => new Set( - (state?.domains?.filter((line) => line.trim().length > 0) ?? []).concat( - this._testDomains, + (state?.webAddresses?.filter((line) => line.trim().length > 0) ?? []).concat( + this._testWebAddresses, "phishing.testcategory.com", // Included for QA to test in prod ), ), ), ); - // How often are new domains added to the remote? + // How often are new web addresses added to the remote? readonly UPDATE_INTERVAL_DURATION = 24 * 60 * 60 * 1000; // 24 hours private _triggerUpdate$ = new Subject(); @@ -75,7 +70,7 @@ export class PhishingDataService { this._cachedState.state$.pipe( first(), // Only take the first value to avoid an infinite loop when updating the cache below switchMap(async (cachedState) => { - const next = await this.getNextDomains(cachedState); + const next = await this.getNextWebAddresses(cachedState); if (next) { await this._cachedState.update(() => next); this.logService.info(`[PhishingDataService] cache updated`); @@ -85,7 +80,7 @@ export class PhishingDataService { count: 3, delay: (err, count) => { this.logService.error( - `[PhishingDataService] Unable to update domains. Attempt ${count}.`, + `[PhishingDataService] Unable to update web addresses. Attempt ${count}.`, err, ); return timer(5 * 60 * 1000); // 5 minutes @@ -97,7 +92,7 @@ export class PhishingDataService { err: unknown /** Eslint actually crashed if you remove this type: https://github.com/cartant/eslint-plugin-rxjs/issues/122 */, ) => { this.logService.error( - "[PhishingDataService] Retries unsuccessful. Unable to update domains.", + "[PhishingDataService] Retries unsuccessful. Unable to update web addresses.", err, ); return EMPTY; @@ -114,6 +109,7 @@ export class PhishingDataService { private globalStateProvider: GlobalStateProvider, private logService: LogService, private platformUtilsService: PlatformUtilsService, + private resourceType: PhishingResourceType = PhishingResourceType.Links, ) { this.taskSchedulerService.registerTaskHandler(ScheduledTaskNames.phishingDomainUpdate, () => { this._triggerUpdate$.next(); @@ -125,22 +121,31 @@ export class PhishingDataService { } /** - * Checks if the given URL is a known phishing domain + * Checks if the given URL is a known phishing web address * * @param url The URL to check - * @returns True if the URL is a known phishing domain, false otherwise + * @returns True if the URL is a known phishing web address, false otherwise */ - async isPhishingDomain(url: URL): Promise { - const domains = await firstValueFrom(this._domains$); - const result = domains.has(url.hostname); - if (result) { - return true; + async isPhishingWebAddress(url: URL): Promise { + // Use domain (hostname) matching for domain resources, and link matching for links resources + const entries = await firstValueFrom(this._webAddresses$); + + const resource = getPhishingResources(this.resourceType); + if (resource && resource.match) { + for (const entry of entries) { + if (resource.match(url, entry)) { + return true; + } + } + return false; } - return false; + + // Default/domain behavior: exact hostname match as a fallback + return entries.has(url.hostname); } - async getNextDomains(prev: PhishingData | null): Promise { - prev = prev ?? { domains: [], timestamp: 0, checksum: "", applicationVersion: "" }; + async getNextWebAddresses(prev: PhishingData | null): Promise { + prev = prev ?? { webAddresses: [], timestamp: 0, checksum: "", applicationVersion: "" }; const timestamp = Date.now(); const prevAge = timestamp - prev.timestamp; this.logService.info(`[PhishingDataService] Cache age: ${prevAge}`); @@ -148,7 +153,7 @@ export class PhishingDataService { const applicationVersion = await this.platformUtilsService.getApplicationVersion(); // If checksum matches, return existing data with new timestamp & version - const remoteChecksum = await this.fetchPhishingDomainsChecksum(); + const remoteChecksum = await this.fetchPhishingChecksum(this.resourceType); if (remoteChecksum && prev.checksum === remoteChecksum) { this.logService.info( `[PhishingDataService] Remote checksum matches local checksum, updating timestamp only.`, @@ -157,66 +162,66 @@ export class PhishingDataService { } // Checksum is different, data needs to be updated. - // Approach 1: Fetch only new domains and append + // Approach 1: Fetch only new web addresses and append const isOneDayOldMax = prevAge <= this.UPDATE_INTERVAL_DURATION; if (isOneDayOldMax && applicationVersion === prev.applicationVersion) { - const dailyDomains: string[] = await this.fetchPhishingDomains( - PhishingDataService.RemotePhishingDatabaseTodayUrl, - ); + const webAddressesTodayUrl = getPhishingResources(this.resourceType)!.todayUrl; + const dailyWebAddresses: string[] = + await this.fetchPhishingWebAddresses(webAddressesTodayUrl); this.logService.info( - `[PhishingDataService] ${dailyDomains.length} new phishing domains added`, + `[PhishingDataService] ${dailyWebAddresses.length} new phishing web addresses added`, ); return { - domains: prev.domains.concat(dailyDomains), + webAddresses: prev.webAddresses.concat(dailyWebAddresses), checksum: remoteChecksum, timestamp, applicationVersion, }; } - // Approach 2: Fetch all domains - const domains = await this.fetchPhishingDomains(PhishingDataService.RemotePhishingDatabaseUrl); + // Approach 2: Fetch all web addresses + const remoteUrl = getPhishingResources(this.resourceType)!.remoteUrl; + const remoteWebAddresses = await this.fetchPhishingWebAddresses(remoteUrl); return { - domains, + webAddresses: remoteWebAddresses, timestamp, checksum: remoteChecksum, applicationVersion, }; } - private async fetchPhishingDomainsChecksum() { - const response = await this.apiService.nativeFetch( - new Request(PhishingDataService.RemotePhishingDatabaseChecksumUrl), - ); + private async fetchPhishingChecksum(type: PhishingResourceType = PhishingResourceType.Domains) { + const checksumUrl = getPhishingResources(type)!.checksumUrl; + const response = await this.apiService.nativeFetch(new Request(checksumUrl)); if (!response.ok) { throw new Error(`[PhishingDataService] Failed to fetch checksum: ${response.status}`); } return response.text(); } - private async fetchPhishingDomains(url: string) { + private async fetchPhishingWebAddresses(url: string) { const response = await this.apiService.nativeFetch(new Request(url)); if (!response.ok) { - throw new Error(`[PhishingDataService] Failed to fetch domains: ${response.status}`); + throw new Error(`[PhishingDataService] Failed to fetch web addresses: ${response.status}`); } return response.text().then((text) => text.split("\n")); } - private getTestDomains() { + private getTestWebAddresses() { const flag = devFlagEnabled("testPhishingUrls"); if (!flag) { return []; } - const domains = devFlagValue("testPhishingUrls") as unknown[]; - if (domains && domains instanceof Array) { + const webAddresses = devFlagValue("testPhishingUrls") as unknown[]; + if (webAddresses && webAddresses instanceof Array) { this.logService.debug( - "[PhishingDetectionService] Dev flag enabled for testing phishing detection. Adding test phishing domains:", - domains, + "[PhishingDetectionService] Dev flag enabled for testing phishing detection. Adding test phishing web addresses:", + webAddresses, ); - return domains as string[]; + return webAddresses as string[]; } return []; } diff --git a/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.spec.ts b/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.spec.ts index ceb18bd1573..06a37f12faa 100644 --- a/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.spec.ts +++ b/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.spec.ts @@ -79,4 +79,9 @@ describe("PhishingDetectionService", () => { // phishingDetectionSettingsService, // ); // }); + + // TODO + // it("should not enable phishing detection for safari", () => { + // + // }); }); diff --git a/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.ts b/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.ts index e04d08559ab..d90e872eef8 100644 --- a/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.ts +++ b/apps/browser/src/dirt/phishing-detection/services/phishing-detection.service.ts @@ -94,7 +94,7 @@ export class PhishingDetectionService { this._ignoredHostnames.delete(url.hostname); return; } - const isPhishing = await phishingDataService.isPhishingDomain(url); + const isPhishing = await phishingDataService.isPhishingWebAddress(url); if (!isPhishing) { return; } diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index 1651f616e03..26add57d1ae 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": "Bitwarden", - "version": "2025.12.0", + "version": "2025.12.1", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index 67399192b64..64d182ebd3d 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": "Bitwarden", - "version": "2025.12.0", + "version": "2025.12.1", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/models/browserComponentState.ts b/apps/browser/src/models/browserComponentState.ts deleted file mode 100644 index 50ee9fe34d4..00000000000 --- a/apps/browser/src/models/browserComponentState.ts +++ /dev/null @@ -1,16 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -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 deleted file mode 100644 index 364f4beb6bf..00000000000 --- a/apps/browser/src/models/browserGroupingsComponentState.ts +++ /dev/null @@ -1,46 +0,0 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { CollectionView } from "@bitwarden/admin-console/common"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { DeepJsonify } from "@bitwarden/common/types/deep-jsonify"; -import { CipherType } from "@bitwarden/common/vault/enums"; -import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; - -import { BrowserComponentState } from "./browserComponentState"; - -export class BrowserGroupingsComponentState extends BrowserComponentState { - favoriteCiphers: CipherView[]; - noFolderCiphers: CipherView[]; - ciphers: CipherView[]; - collectionCounts: Map; - folderCounts: Map; - typeCounts: Map; - 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/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index eb64c076192..12e1288e806 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -68,6 +68,7 @@ import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router import { RouteCacheOptions } from "../platform/services/popup-view-cache-background.service"; import { CredentialGeneratorHistoryComponent } from "../tools/popup/generator/credential-generator-history.component"; import { CredentialGeneratorComponent } from "../tools/popup/generator/credential-generator.component"; +import { firefoxPopoutGuard } from "../tools/popup/guards/firefox-popout.guard"; import { SendAddEditComponent as SendAddEditV2Component } from "../tools/popup/send-v2/add-edit/send-add-edit.component"; import { SendCreatedComponent } from "../tools/popup/send-v2/send-created/send-created.component"; import { SendV2Component } from "../tools/popup/send-v2/send-v2.component"; @@ -262,7 +263,7 @@ const routes: Routes = [ { path: "import", component: ImportBrowserV2Component, - canActivate: [authGuard], + canActivate: [authGuard, firefoxPopoutGuard()], data: { elevation: 1 } satisfies RouteDataProperties, }, { @@ -340,13 +341,13 @@ const routes: Routes = [ { path: "add-send", component: SendAddEditV2Component, - canActivate: [authGuard], + canActivate: [authGuard, firefoxPopoutGuard()], data: { elevation: 1 } satisfies RouteDataProperties, }, { path: "edit-send", component: SendAddEditV2Component, - canActivate: [authGuard], + canActivate: [authGuard, firefoxPopoutGuard()], data: { elevation: 1 } satisfies RouteDataProperties, }, { diff --git a/apps/browser/src/popup/components/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.html b/apps/browser/src/popup/components/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.html index 7a1815b86ed..484f9680519 100644 --- a/apps/browser/src/popup/components/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.html +++ b/apps/browser/src/popup/components/extension-anon-layout-wrapper/extension-anon-layout-wrapper.component.html @@ -1,21 +1,30 @@ + +
+ +
+ + + + + +
-
- - @if (showAcctSwitcher && hasLoggedInAccount) { - - } -
diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 39c53b7da56..7a2ded5bb83 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -23,6 +23,8 @@ import { WINDOW, } from "@bitwarden/angular/services/injection-tokens"; import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module"; +import { AUTOFILL_NUDGE_SERVICE } from "@bitwarden/angular/vault"; +import { SingleNudgeService } from "@bitwarden/angular/vault/services/default-single-nudge.service"; import { LoginComponentService, TwoFactorAuthComponentService, @@ -208,6 +210,7 @@ import { } from "../../platform/system-notifications/browser-system-notification.service"; import { fromChromeRuntimeMessaging } from "../../platform/utils/from-chrome-runtime-messaging"; import { FilePopoutUtilsService } from "../../tools/popup/services/file-popout-utils.service"; +import { BrowserAutofillNudgeService } from "../../vault/popup/services/browser-autofill-nudge.service"; import { Fido2UserVerificationService } from "../../vault/services/fido2-user-verification.service"; import { ExtensionAnonLayoutWrapperDataService } from "../components/extension-anon-layout-wrapper/extension-anon-layout-wrapper-data.service"; @@ -523,6 +526,7 @@ const safeProviders: SafeProvider[] = [ BillingAccountProfileStateService, ConfigService, OrganizationService, + PlatformUtilsService, StateProvider, ], }), @@ -755,6 +759,11 @@ const safeProviders: SafeProvider[] = [ MessagingServiceAbstraction, ], }), + safeProvider({ + provide: AUTOFILL_NUDGE_SERVICE as SafeInjectionToken, + useClass: BrowserAutofillNudgeService, + deps: [], + }), ]; @NgModule({ diff --git a/apps/browser/src/safari/desktop/Info.plist b/apps/browser/src/safari/desktop/Info.plist index b687d9d2f3a..94542609351 100644 --- a/apps/browser/src/safari/desktop/Info.plist +++ b/apps/browser/src/safari/desktop/Info.plist @@ -25,7 +25,7 @@ LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright - Copyright © 2015-2025 Bitwarden Inc. All rights reserved. + Copyright © 2015-2026 Bitwarden Inc. All rights reserved. NSMainStoryboardFile Main NSPrincipalClass diff --git a/apps/browser/src/safari/safari/Info.plist b/apps/browser/src/safari/safari/Info.plist index 95172846758..68b872610e9 100644 --- a/apps/browser/src/safari/safari/Info.plist +++ b/apps/browser/src/safari/safari/Info.plist @@ -30,7 +30,7 @@ $(PRODUCT_MODULE_NAME).SafariWebExtensionHandler NSHumanReadableCopyright - Copyright © 2015-2025 Bitwarden Inc. All rights reserved. + Copyright © 2015-2026 Bitwarden Inc. All rights reserved. NSHumanReadableDescription A secure and free password manager for all of your devices. SFSafariAppExtensionBundleIdentifiersToReplace diff --git a/apps/browser/src/tools/popup/guards/firefox-popout.guard.spec.ts b/apps/browser/src/tools/popup/guards/firefox-popout.guard.spec.ts new file mode 100644 index 00000000000..df04b965d4c --- /dev/null +++ b/apps/browser/src/tools/popup/guards/firefox-popout.guard.spec.ts @@ -0,0 +1,194 @@ +import { TestBed } from "@angular/core/testing"; +import { ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router"; + +import { DeviceType } from "@bitwarden/common/enums"; + +import { BrowserApi } from "../../../platform/browser/browser-api"; +import BrowserPopupUtils from "../../../platform/browser/browser-popup-utils"; +import { BrowserPlatformUtilsService } from "../../../platform/services/platform-utils/browser-platform-utils.service"; + +import { firefoxPopoutGuard } from "./firefox-popout.guard"; + +describe("firefoxPopoutGuard", () => { + let getDeviceSpy: jest.SpyInstance; + let inPopoutSpy: jest.SpyInstance; + let inSidebarSpy: jest.SpyInstance; + let openPopoutSpy: jest.SpyInstance; + let closePopupSpy: jest.SpyInstance; + + const mockRoute = {} as ActivatedRouteSnapshot; + const mockState: RouterStateSnapshot = { + url: "/import?param=value", + } as RouterStateSnapshot; + + beforeEach(() => { + getDeviceSpy = jest.spyOn(BrowserPlatformUtilsService, "getDevice"); + inPopoutSpy = jest.spyOn(BrowserPopupUtils, "inPopout"); + inSidebarSpy = jest.spyOn(BrowserPopupUtils, "inSidebar"); + openPopoutSpy = jest.spyOn(BrowserPopupUtils, "openPopout").mockImplementation(); + closePopupSpy = jest.spyOn(BrowserApi, "closePopup").mockImplementation(); + + TestBed.configureTestingModule({}); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe("when browser is Firefox", () => { + beforeEach(() => { + getDeviceSpy.mockReturnValue(DeviceType.FirefoxExtension); + inPopoutSpy.mockReturnValue(false); + inSidebarSpy.mockReturnValue(false); + }); + + it("should open popout and block navigation when not already in popout or sidebar", async () => { + const guard = firefoxPopoutGuard(); + const result = await TestBed.runInInjectionContext(() => guard(mockRoute, mockState)); + + expect(getDeviceSpy).toHaveBeenCalledWith(window); + expect(inPopoutSpy).toHaveBeenCalledWith(window); + expect(inSidebarSpy).toHaveBeenCalledWith(window); + expect(openPopoutSpy).toHaveBeenCalledWith("popup/index.html#/import?param=value"); + expect(closePopupSpy).toHaveBeenCalledWith(window); + expect(result).toBe(false); + }); + + it("should not add autoClosePopout parameter to the url", async () => { + const guard = firefoxPopoutGuard(); + await TestBed.runInInjectionContext(() => guard(mockRoute, mockState)); + + expect(openPopoutSpy).toHaveBeenCalledWith("popup/index.html#/import?param=value"); + expect(openPopoutSpy).not.toHaveBeenCalledWith(expect.stringContaining("autoClosePopout")); + }); + + it("should allow navigation when already in popout", async () => { + inPopoutSpy.mockReturnValue(true); + + const guard = firefoxPopoutGuard(); + const result = await TestBed.runInInjectionContext(() => guard(mockRoute, mockState)); + + expect(openPopoutSpy).not.toHaveBeenCalled(); + expect(closePopupSpy).not.toHaveBeenCalled(); + expect(result).toBe(true); + }); + + it("should allow navigation when already in sidebar", async () => { + inSidebarSpy.mockReturnValue(true); + + const guard = firefoxPopoutGuard(); + const result = await TestBed.runInInjectionContext(() => guard(mockRoute, mockState)); + + expect(openPopoutSpy).not.toHaveBeenCalled(); + expect(closePopupSpy).not.toHaveBeenCalled(); + expect(result).toBe(true); + }); + }); + + describe("when browser is not Firefox", () => { + beforeEach(() => { + inPopoutSpy.mockReturnValue(false); + inSidebarSpy.mockReturnValue(false); + }); + + it.each([ + { deviceType: DeviceType.ChromeExtension, name: "ChromeExtension" }, + { deviceType: DeviceType.EdgeExtension, name: "EdgeExtension" }, + { deviceType: DeviceType.OperaExtension, name: "OperaExtension" }, + { deviceType: DeviceType.SafariExtension, name: "SafariExtension" }, + { deviceType: DeviceType.VivaldiExtension, name: "VivaldiExtension" }, + ])( + "should allow navigation without opening popout when device is $name", + async ({ deviceType }) => { + getDeviceSpy.mockReturnValue(deviceType); + + const guard = firefoxPopoutGuard(); + const result = await TestBed.runInInjectionContext(() => guard(mockRoute, mockState)); + + expect(getDeviceSpy).toHaveBeenCalledWith(window); + expect(openPopoutSpy).not.toHaveBeenCalled(); + expect(closePopupSpy).not.toHaveBeenCalled(); + expect(result).toBe(true); + }, + ); + }); + + describe("file picker routes", () => { + beforeEach(() => { + getDeviceSpy.mockReturnValue(DeviceType.FirefoxExtension); + inPopoutSpy.mockReturnValue(false); + inSidebarSpy.mockReturnValue(false); + }); + + it("should open popout for /import route", async () => { + const importState: RouterStateSnapshot = { + url: "/import", + } as RouterStateSnapshot; + + const guard = firefoxPopoutGuard(); + const result = await TestBed.runInInjectionContext(() => guard(mockRoute, importState)); + + expect(openPopoutSpy).toHaveBeenCalledWith("popup/index.html#/import"); + expect(closePopupSpy).toHaveBeenCalledWith(window); + expect(result).toBe(false); + }); + + it("should open popout for /add-send route", async () => { + const addSendState: RouterStateSnapshot = { + url: "/add-send", + } as RouterStateSnapshot; + + const guard = firefoxPopoutGuard(); + const result = await TestBed.runInInjectionContext(() => guard(mockRoute, addSendState)); + + expect(openPopoutSpy).toHaveBeenCalledWith("popup/index.html#/add-send"); + expect(closePopupSpy).toHaveBeenCalledWith(window); + expect(result).toBe(false); + }); + + it("should open popout for /edit-send route", async () => { + const editSendState: RouterStateSnapshot = { + url: "/edit-send", + } as RouterStateSnapshot; + + const guard = firefoxPopoutGuard(); + const result = await TestBed.runInInjectionContext(() => guard(mockRoute, editSendState)); + + expect(openPopoutSpy).toHaveBeenCalledWith("popup/index.html#/edit-send"); + expect(closePopupSpy).toHaveBeenCalledWith(window); + expect(result).toBe(false); + }); + }); + + describe("url handling", () => { + beforeEach(() => { + getDeviceSpy.mockReturnValue(DeviceType.FirefoxExtension); + inPopoutSpy.mockReturnValue(false); + inSidebarSpy.mockReturnValue(false); + }); + + it("should preserve query parameters in the popout url", async () => { + const stateWithQuery: RouterStateSnapshot = { + url: "/import?foo=bar&baz=qux", + } as RouterStateSnapshot; + + const guard = firefoxPopoutGuard(); + await TestBed.runInInjectionContext(() => guard(mockRoute, stateWithQuery)); + + expect(openPopoutSpy).toHaveBeenCalledWith("popup/index.html#/import?foo=bar&baz=qux"); + expect(closePopupSpy).toHaveBeenCalledWith(window); + }); + + it("should handle urls without query parameters", async () => { + const stateWithoutQuery: RouterStateSnapshot = { + url: "/simple-path", + } as RouterStateSnapshot; + + const guard = firefoxPopoutGuard(); + await TestBed.runInInjectionContext(() => guard(mockRoute, stateWithoutQuery)); + + expect(openPopoutSpy).toHaveBeenCalledWith("popup/index.html#/simple-path"); + expect(closePopupSpy).toHaveBeenCalledWith(window); + }); + }); +}); diff --git a/apps/browser/src/tools/popup/guards/firefox-popout.guard.ts b/apps/browser/src/tools/popup/guards/firefox-popout.guard.ts new file mode 100644 index 00000000000..821f1b7a5bc --- /dev/null +++ b/apps/browser/src/tools/popup/guards/firefox-popout.guard.ts @@ -0,0 +1,38 @@ +import { ActivatedRouteSnapshot, CanActivateFn, RouterStateSnapshot } from "@angular/router"; + +import { BrowserApi } from "@bitwarden/browser/platform/browser/browser-api"; +import BrowserPopupUtils from "@bitwarden/browser/platform/browser/browser-popup-utils"; +import { BrowserPlatformUtilsService } from "@bitwarden/browser/platform/services/platform-utils/browser-platform-utils.service"; +import { DeviceType } from "@bitwarden/common/enums"; + +/** + * Guard that forces a popout window on Firefox browser when a file picker could be exposed. + * Necessary to avoid a crash: https://bugzilla.mozilla.org/show_bug.cgi?id=1292701 + * Also disallows the user from closing a popout and re-opening the view exposing the file picker. + * + * @returns CanActivateFn that opens popout and blocks navigation on Firefox + */ +export function firefoxPopoutGuard(): CanActivateFn { + return async (_route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { + // Check if browser is Firefox using the platform utils service + const deviceType = BrowserPlatformUtilsService.getDevice(window); + const isFirefox = deviceType === DeviceType.FirefoxExtension; + + // Check if already in popout/sidebar + const inPopout = BrowserPopupUtils.inPopout(window); + const inSidebar = BrowserPopupUtils.inSidebar(window); + + // Open popout if on Firefox and not already in popout/sidebar + if (isFirefox && !inPopout && !inSidebar) { + // Don't add autoClosePopout for file picker scenarios - user should manually close + await BrowserPopupUtils.openPopout(`popup/index.html#${state.url}`); + + // Close the original popup window + BrowserApi.closePopup(window); + + return false; // Block navigation - popout will reload + } + + return true; // Allow navigation + }; +} diff --git a/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.html b/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.html index 16711fabbf4..828c1667c57 100644 --- a/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.html +++ b/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.html @@ -1,39 +1,37 @@ -
- - - - - - + + + + + + -
-
- -
-

- {{ "createdSendSuccessfully" | i18n }} -

-

- {{ formatExpirationDate() }} -

- +
+
+
- - - - - -
+

+ {{ "createdSendSuccessfully" | i18n }} +

+

+ {{ formatExpirationDate() }} +

+ +
+ + + + + diff --git a/apps/browser/src/tools/popup/settings/export/export-browser-v2.component.html b/apps/browser/src/tools/popup/settings/export/export-browser-v2.component.html index 5473bbe620e..36c84d9b788 100644 --- a/apps/browser/src/tools/popup/settings/export/export-browser-v2.component.html +++ b/apps/browser/src/tools/popup/settings/export/export-browser-v2.component.html @@ -1,5 +1,5 @@ - + @@ -21,7 +21,7 @@ bitFormButton buttonType="primary" > - {{ "export" | i18n }} + {{ "exportVerb" | i18n }} diff --git a/apps/browser/src/tools/popup/settings/settings-v2.component.html b/apps/browser/src/tools/popup/settings/settings-v2.component.html index 683b7d70ed6..06c89e15f59 100644 --- a/apps/browser/src/tools/popup/settings/settings-v2.component.html +++ b/apps/browser/src/tools/popup/settings/settings-v2.component.html @@ -34,13 +34,11 @@

{{ "autofill" | i18n }}

- 1 + @if (showAutofillBadge$ | async) { + 1 + }
diff --git a/apps/browser/src/tools/popup/settings/settings-v2.component.spec.ts b/apps/browser/src/tools/popup/settings/settings-v2.component.spec.ts index f51d514289e..4cc3ed0149c 100644 --- a/apps/browser/src/tools/popup/settings/settings-v2.component.spec.ts +++ b/apps/browser/src/tools/popup/settings/settings-v2.component.spec.ts @@ -148,31 +148,7 @@ describe("SettingsV2Component", () => { expect(openSpy).toHaveBeenCalledWith(dialogService); }); - it("isBrowserAutofillSettingOverridden$ emits the value from the AutofillBrowserSettingsService", async () => { - pushActiveAccount(); - - mockAutofillSettings.isBrowserAutofillSettingOverridden.mockResolvedValue(true); - - const fixture = TestBed.createComponent(SettingsV2Component); - const component = fixture.componentInstance; - fixture.detectChanges(); - await fixture.whenStable(); - - const value = await firstValueFrom(component["isBrowserAutofillSettingOverridden$"]); - expect(value).toBe(true); - - mockAutofillSettings.isBrowserAutofillSettingOverridden.mockResolvedValue(false); - - const fixture2 = TestBed.createComponent(SettingsV2Component); - const component2 = fixture2.componentInstance; - fixture2.detectChanges(); - await fixture2.whenStable(); - - const value2 = await firstValueFrom(component2["isBrowserAutofillSettingOverridden$"]); - expect(value2).toBe(false); - }); - - it("showAutofillBadge$ emits true when default autofill is NOT disabled and nudge is true", async () => { + it("showAutofillBadge$ emits true when showNudgeBadge is true", async () => { pushActiveAccount(); mockNudges.showNudgeBadge$.mockImplementation((type: NudgeType) => @@ -184,30 +160,10 @@ describe("SettingsV2Component", () => { fixture.detectChanges(); await fixture.whenStable(); - mockAutofillSettings.defaultBrowserAutofillDisabled$.next(false); - const value = await firstValueFrom(component.showAutofillBadge$); expect(value).toBe(true); }); - it("showAutofillBadge$ emits false when default autofill IS disabled even if nudge is true", async () => { - pushActiveAccount(); - - mockNudges.showNudgeBadge$.mockImplementation((type: NudgeType) => - of(type === NudgeType.AutofillNudge), - ); - - const fixture = TestBed.createComponent(SettingsV2Component); - const component = fixture.componentInstance; - fixture.detectChanges(); - await fixture.whenStable(); - - mockAutofillSettings.defaultBrowserAutofillDisabled$.next(true); - - const value = await firstValueFrom(component.showAutofillBadge$); - expect(value).toBe(false); - }); - it("dismissBadge dismisses when showVaultBadge$ emits true", async () => { const acct = pushActiveAccount(); diff --git a/apps/browser/src/tools/popup/settings/settings-v2.component.ts b/apps/browser/src/tools/popup/settings/settings-v2.component.ts index 95aeeb2f480..e10d41b9445 100644 --- a/apps/browser/src/tools/popup/settings/settings-v2.component.ts +++ b/apps/browser/src/tools/popup/settings/settings-v2.component.ts @@ -1,16 +1,7 @@ import { CommonModule } from "@angular/common"; import { ChangeDetectionStrategy, Component } from "@angular/core"; import { RouterModule } from "@angular/router"; -import { - combineLatest, - filter, - firstValueFrom, - from, - map, - Observable, - shareReplay, - switchMap, -} from "rxjs"; +import { filter, firstValueFrom, Observable, shareReplay, switchMap } from "rxjs"; import { PremiumUpgradeDialogComponent } from "@bitwarden/angular/billing/components"; import { JslibModule } from "@bitwarden/angular/jslib.module"; @@ -28,8 +19,6 @@ import { } from "@bitwarden/components"; import { CurrentAccountComponent } from "../../../auth/popup/account-switching/current-account.component"; -import { AutofillBrowserSettingsService } from "../../../autofill/services/autofill-browser-settings.service"; -import { BrowserApi } from "../../../platform/browser/browser-api"; import { PopOutComponent } from "../../../platform/popup/components/pop-out.component"; import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component"; @@ -55,12 +44,6 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co export class SettingsV2Component { NudgeType = NudgeType; - protected isBrowserAutofillSettingOverridden$ = from( - this.autofillBrowserSettingsService.isBrowserAutofillSettingOverridden( - BrowserApi.getBrowserClientVendor(window), - ), - ); - private authenticatedAccount$: Observable = this.accountService.activeAccount$.pipe( filter((account): account is Account => account !== null), shareReplay({ bufferSize: 1, refCount: true }), @@ -82,23 +65,13 @@ export class SettingsV2Component { ), ); - showAutofillBadge$: Observable = combineLatest([ - this.autofillBrowserSettingsService.defaultBrowserAutofillDisabled$, - this.authenticatedAccount$, - ]).pipe( - switchMap(([defaultBrowserAutofillDisabled, account]) => - this.nudgesService.showNudgeBadge$(NudgeType.AutofillNudge, account.id).pipe( - map((badgeStatus) => { - return !defaultBrowserAutofillDisabled && badgeStatus; - }), - ), - ), + showAutofillBadge$: Observable = this.authenticatedAccount$.pipe( + switchMap((account) => this.nudgesService.showNudgeBadge$(NudgeType.AutofillNudge, account.id)), ); constructor( private readonly nudgesService: NudgesService, private readonly accountService: AccountService, - private readonly autofillBrowserSettingsService: AutofillBrowserSettingsService, private readonly accountProfileStateService: BillingAccountProfileStateService, private readonly dialogService: DialogService, ) {} diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html index 88bff47191a..d8c12122120 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html @@ -15,8 +15,8 @@ } @if (savedUrls().length > 1) {
-

- {{ "savedWebsites" | i18n: savedUrls().length }} +

+ {{ "savedWebsites" | i18n: savedUrls().length.toString() }}

- - @if (!(autofillConfirmationFlagEnabled$ | async)) { - - } diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.spec.ts index b9f48b7407b..bd9ce108522 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.spec.ts @@ -13,7 +13,6 @@ import { UriMatchStrategy, UriMatchStrategySetting, } from "@bitwarden/common/models/domain/domain-service"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -40,10 +39,6 @@ describe("ItemMoreOptionsComponent", () => { openSimpleDialog: jest.fn().mockResolvedValue(true), open: jest.fn(), }; - const featureFlag$ = new BehaviorSubject(false); - const configService = { - getFeatureFlag$: jest.fn().mockImplementation(() => featureFlag$.asObservable()), - }; const cipherService = { getFullCipherView: jest.fn(), encrypt: jest.fn(), @@ -93,7 +88,6 @@ describe("ItemMoreOptionsComponent", () => { TestBed.configureTestingModule({ imports: [ItemMoreOptionsComponent, NoopAnimationsModule], providers: [ - { provide: ConfigService, useValue: configService }, { provide: CipherService, useValue: cipherService }, { provide: VaultPopupAutofillService, useValue: autofillSvc }, @@ -152,22 +146,6 @@ describe("ItemMoreOptionsComponent", () => { expect(passwordRepromptService.passwordRepromptCheck).toHaveBeenCalledWith(baseCipher); }); - it("calls the autofill service to autofill without showing the confirmation dialog when the feature flag is disabled", async () => { - autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); - - await component.doAutofill(); - - expect(cipherService.getFullCipherView).toHaveBeenCalled(); - expect(autofillSvc.doAutofill).toHaveBeenCalledTimes(1); - expect(autofillSvc.doAutofill).toHaveBeenCalledWith( - expect.objectContaining({ id: "cipher-1" }), - true, - true, - ); - expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); - expect(dialogService.openSimpleDialog).not.toHaveBeenCalled(); - }); - it("does nothing if the user fails master password reprompt", async () => { baseCipher.reprompt = 2; // Master Password reprompt enabled autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); @@ -181,7 +159,6 @@ describe("ItemMoreOptionsComponent", () => { }); it("does not show the exact match dialog when the default match strategy is Exact and autofill confirmation is not to be shown", async () => { - // autofill confirmation dialog is not shown when either the feature flag is disabled uriMatchStrategy$.next(UriMatchStrategy.Exact); autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com/path" }); await component.doAutofill(); @@ -191,8 +168,6 @@ describe("ItemMoreOptionsComponent", () => { describe("autofill confirmation dialog", () => { beforeEach(() => { - // autofill confirmation dialog is shown when feature flag is enabled - featureFlag$.next(true); uriMatchStrategy$.next(UriMatchStrategy.Domain); passwordRepromptService.passwordRepromptCheck.mockResolvedValue(true); }); @@ -206,7 +181,7 @@ describe("ItemMoreOptionsComponent", () => { expect(passwordRepromptService.passwordRepromptCheck).toHaveBeenCalledWith(baseCipher); }); - it("opens the autofill confirmation dialog with filtered saved URLs when the feature flag is enabled", async () => { + it("opens the autofill confirmation dialog with filtered saved URLs", async () => { autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com/path" }); const openSpy = mockConfirmDialogResult(AutofillConfirmationDialogResult.Canceled); diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts index b65acc6ca8e..c4353e17bef 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.ts @@ -11,9 +11,7 @@ import { OrganizationService } from "@bitwarden/common/admin-console/abstraction import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { CipherId, UserId } from "@bitwarden/common/types/guid"; import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; @@ -37,7 +35,6 @@ import { PasswordRepromptService } from "@bitwarden/vault"; import { BrowserPremiumUpgradePromptService } from "../../../services/browser-premium-upgrade-prompt.service"; import { VaultPopupAutofillService } from "../../../services/vault-popup-autofill.service"; -import { VaultPopupItemsService } from "../../../services/vault-popup-items.service"; import { AddEditQueryParams } from "../add-edit/add-edit-v2.component"; import { AutofillConfirmationDialogComponent, @@ -98,10 +95,6 @@ export class ItemMoreOptionsComponent { protected autofillAllowed$ = this.vaultPopupAutofillService.autofillAllowed$; - protected autofillConfirmationFlagEnabled$ = this.configService - .getFeatureFlag$(FeatureFlag.AutofillConfirmation) - .pipe(map((isFeatureFlagEnabled) => isFeatureFlagEnabled)); - protected uriMatchStrategy$ = this.domainSettingsService.resolvedDefaultUriMatchStrategy$; /** @@ -166,8 +159,6 @@ export class ItemMoreOptionsComponent { private collectionService: CollectionService, private restrictedItemTypesService: RestrictedItemTypesService, private cipherArchiveService: CipherArchiveService, - private configService: ConfigService, - private vaultPopupItemsService: VaultPopupItemsService, private domainSettingsService: DomainSettingsService, ) {} @@ -216,13 +207,9 @@ export class ItemMoreOptionsComponent { const cipherHasAllExactMatchLoginUris = uris.length > 0 && uris.every((u) => u.uri && u.match === UriMatchStrategy.Exact); - const showAutofillConfirmation = await firstValueFrom(this.autofillConfirmationFlagEnabled$); const uriMatchStrategy = await firstValueFrom(this.uriMatchStrategy$); - if ( - showAutofillConfirmation && - (cipherHasAllExactMatchLoginUris || uriMatchStrategy === UriMatchStrategy.Exact) - ) { + if (cipherHasAllExactMatchLoginUris || uriMatchStrategy === UriMatchStrategy.Exact) { await this.dialogService.openSimpleDialog({ title: { key: "cannotAutofill" }, content: { key: "cannotAutofillExactMatch" }, @@ -233,11 +220,6 @@ export class ItemMoreOptionsComponent { return; } - if (!showAutofillConfirmation) { - await this.vaultPopupAutofillService.doAutofill(cipher, true, true); - return; - } - const currentTab = await firstValueFrom(this.vaultPopupAutofillService.currentAutofillTab$); if (!currentTab?.url) { diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html index 347c5fe6286..6382b5fee0e 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html @@ -108,7 +108,7 @@
- + { stop: jest.fn(), } as Partial; + const vaultItemsTransferSvc = { + transferInProgress$: new BehaviorSubject(false), + enforceOrganizationDataOwnership: jest.fn().mockResolvedValue(undefined), + } as Partial; + function getObs(cmp: any, key: string): Observable { return cmp[key] as Observable; } @@ -283,6 +292,9 @@ describe("VaultV2Component", () => { AutofillVaultListItemsComponent, VaultListItemsContainerComponent, ], + providers: [ + { provide: VaultItemsTransferService, useValue: DefaultVaultItemsTransferService }, + ], }, add: { imports: [ @@ -296,6 +308,7 @@ describe("VaultV2Component", () => { AutofillVaultListItemsStubComponent, VaultListItemsContainerStubComponent, ], + providers: [{ provide: VaultItemsTransferService, useValue: vaultItemsTransferSvc }], }, }); @@ -344,6 +357,7 @@ describe("VaultV2Component", () => { it("loading$ is true when items loading or filters missing; false when both ready", () => { const itemsLoading$ = itemsSvc.loading$ as unknown as BehaviorSubject; const allFilters$ = filtersSvc.allFilters$ as unknown as Subject; + const readySubject$ = component["readySubject"] as unknown as BehaviorSubject; const values: boolean[] = []; getObs(component, "loading$").subscribe((v) => values.push(!!v)); @@ -354,6 +368,8 @@ describe("VaultV2Component", () => { itemsLoading$.next(false); + readySubject$.next(true); + expect(values[values.length - 1]).toBe(false); }); diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts index 63d971081df..30d1d21abfb 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts @@ -16,6 +16,7 @@ import { switchMap, take, tap, + BehaviorSubject, } from "rxjs"; import { PremiumUpgradeDialogComponent } from "@bitwarden/angular/billing/components"; @@ -42,7 +43,11 @@ import { NoItemsModule, TypographyModule, } from "@bitwarden/components"; -import { DecryptionFailureDialogComponent } from "@bitwarden/vault"; +import { + DecryptionFailureDialogComponent, + VaultItemsTransferService, + DefaultVaultItemsTransferService, +} from "@bitwarden/vault"; import { CurrentAccountComponent } from "../../../../auth/popup/account-switching/current-account.component"; import { BrowserApi } from "../../../../platform/browser/browser-api"; @@ -105,6 +110,7 @@ type VaultState = UnionOfValues; VaultFadeInOutSkeletonComponent, VaultFadeInOutComponent, ], + providers: [{ provide: VaultItemsTransferService, useClass: DefaultVaultItemsTransferService }], }) export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals @@ -125,7 +131,22 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { activeUserId: UserId | null = null; - private loading$ = this.vaultPopupLoadingService.loading$.pipe( + /** + * Subject that indicates whether the vault is ready to render + * and that all initialization tasks have been completed (ngOnInit). + * @private + */ + private readySubject = new BehaviorSubject(false); + + /** + * Indicates whether the vault is loading and not yet ready to be displayed. + * @protected + */ + protected loading$ = combineLatest([ + this.vaultPopupLoadingService.loading$, + this.readySubject.asObservable(), + ]).pipe( + map(([loading, ready]) => loading || !ready), distinctUntilChanged(), tap((loading) => { const key = loading ? "loadingVault" : "vaultLoaded"; @@ -200,14 +221,15 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { protected showSkeletonsLoaders$ = combineLatest([ this.loading$, this.searchService.isCipherSearching$, + this.vaultItemsTransferService.transferInProgress$, this.skeletonFeatureFlag$, ]).pipe( - map( - ([loading, cipherSearching, skeletonsEnabled]) => - (loading || cipherSearching) && skeletonsEnabled, - ), + map(([loading, cipherSearching, transferInProgress, skeletonsEnabled]) => { + return (loading || cipherSearching || transferInProgress) && skeletonsEnabled; + }), distinctUntilChanged(), skeletonLoadingDelay(), + shareReplay({ bufferSize: 1, refCount: true }), ); protected newItemItemValues$: Observable = @@ -251,6 +273,7 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { private i18nService: I18nService, private configService: ConfigService, private searchService: SearchService, + private vaultItemsTransferService: VaultItemsTransferService, ) { combineLatest([ this.vaultPopupItemsService.emptyVault$, @@ -305,6 +328,10 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { cipherIds: ciphers.map((c) => c.id as CipherId), }); }); + + await this.vaultItemsTransferService.enforceOrganizationDataOwnership(this.activeUserId); + + this.readySubject.next(true); } ngOnDestroy() { diff --git a/apps/browser/src/vault/popup/services/browser-autofill-nudge.service.spec.ts b/apps/browser/src/vault/popup/services/browser-autofill-nudge.service.spec.ts new file mode 100644 index 00000000000..40782760283 --- /dev/null +++ b/apps/browser/src/vault/popup/services/browser-autofill-nudge.service.spec.ts @@ -0,0 +1,157 @@ +import { TestBed } from "@angular/core/testing"; +import { mock, MockProxy } from "jest-mock-extended"; +import { firstValueFrom } from "rxjs"; + +import { NudgeStatus, NudgeType } from "@bitwarden/angular/vault"; +import { VaultProfileService } from "@bitwarden/angular/vault/services/vault-profile.service"; +import { BrowserClientVendors } from "@bitwarden/common/autofill/constants"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { StateProvider } from "@bitwarden/common/platform/state"; +import { UserId } from "@bitwarden/common/types/guid"; + +import { FakeStateProvider, mockAccountServiceWith } from "../../../../../../libs/common/spec"; +import { BrowserApi } from "../../../platform/browser/browser-api"; + +import { BrowserAutofillNudgeService } from "./browser-autofill-nudge.service"; + +describe("BrowserAutofillNudgeService", () => { + let service: BrowserAutofillNudgeService; + let vaultProfileService: MockProxy; + let fakeStateProvider: FakeStateProvider; + + const userId = "test-user-id" as UserId; + const nudgeType = NudgeType.AutofillNudge; + + const notDismissedStatus: NudgeStatus = { + hasBadgeDismissed: false, + hasSpotlightDismissed: false, + }; + + const dismissedStatus: NudgeStatus = { + hasBadgeDismissed: true, + hasSpotlightDismissed: true, + }; + + // Set profile creation date to now (new account, within 30 days) + const recentProfileDate = new Date(); + + beforeEach(() => { + vaultProfileService = mock(); + vaultProfileService.getProfileCreationDate.mockResolvedValue(recentProfileDate); + + fakeStateProvider = new FakeStateProvider(mockAccountServiceWith(userId)); + + TestBed.configureTestingModule({ + providers: [ + BrowserAutofillNudgeService, + { + provide: VaultProfileService, + useValue: vaultProfileService, + }, + { + provide: StateProvider, + useValue: fakeStateProvider, + }, + { + provide: LogService, + useValue: mock(), + }, + ], + }); + + service = TestBed.inject(BrowserAutofillNudgeService); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + describe("nudgeStatus$", () => { + it("returns parent status when browser client is Unknown", async () => { + jest + .spyOn(BrowserApi, "getBrowserClientVendor") + .mockReturnValue(BrowserClientVendors.Unknown); + jest.spyOn(BrowserApi, "browserAutofillSettingsOverridden").mockResolvedValue(true); + + const result = await firstValueFrom(service.nudgeStatus$(nudgeType, userId)); + + expect(result).toEqual(notDismissedStatus); + }); + + it("returns parent status when browser autofill is not overridden", async () => { + jest.spyOn(BrowserApi, "getBrowserClientVendor").mockReturnValue(BrowserClientVendors.Chrome); + jest.spyOn(BrowserApi, "browserAutofillSettingsOverridden").mockResolvedValue(false); + + const result = await firstValueFrom(service.nudgeStatus$(nudgeType, userId)); + + expect(result).toEqual(notDismissedStatus); + }); + + it("returns dismissed status when browser autofill is overridden", async () => { + jest.spyOn(BrowserApi, "getBrowserClientVendor").mockReturnValue(BrowserClientVendors.Chrome); + jest.spyOn(BrowserApi, "browserAutofillSettingsOverridden").mockResolvedValue(true); + + const result = await firstValueFrom(service.nudgeStatus$(nudgeType, userId)); + + expect(result).toEqual(dismissedStatus); + }); + + it("preserves parent dismissed status when account is older than 30 days", async () => { + // Set profile creation date to more than 30 days ago + const oldProfileDate = new Date(Date.now() - 31 * 24 * 60 * 60 * 1000); + vaultProfileService.getProfileCreationDate.mockResolvedValue(oldProfileDate); + + jest.spyOn(BrowserApi, "getBrowserClientVendor").mockReturnValue(BrowserClientVendors.Chrome); + jest.spyOn(BrowserApi, "browserAutofillSettingsOverridden").mockResolvedValue(false); + + const result = await firstValueFrom(service.nudgeStatus$(nudgeType, userId)); + + expect(result).toEqual(dismissedStatus); + }); + + it("combines parent dismissed and browser autofill overridden status", async () => { + // Set profile creation date to more than 30 days ago (parent dismisses) + const oldProfileDate = new Date(Date.now() - 31 * 24 * 60 * 60 * 1000); + vaultProfileService.getProfileCreationDate.mockResolvedValue(oldProfileDate); + + jest.spyOn(BrowserApi, "getBrowserClientVendor").mockReturnValue(BrowserClientVendors.Chrome); + jest.spyOn(BrowserApi, "browserAutofillSettingsOverridden").mockResolvedValue(true); + + const result = await firstValueFrom(service.nudgeStatus$(nudgeType, userId)); + + expect(result).toEqual(dismissedStatus); + }); + + it.each([ + BrowserClientVendors.Chrome, + BrowserClientVendors.Edge, + BrowserClientVendors.Opera, + BrowserClientVendors.Vivaldi, + ])("checks browser autofill settings for %s browser", async (browserVendor) => { + const getBrowserClientVendorSpy = jest + .spyOn(BrowserApi, "getBrowserClientVendor") + .mockReturnValue(browserVendor); + const browserAutofillSettingsOverriddenSpy = jest + .spyOn(BrowserApi, "browserAutofillSettingsOverridden") + .mockResolvedValue(true); + + await firstValueFrom(service.nudgeStatus$(nudgeType, userId)); + + expect(getBrowserClientVendorSpy).toHaveBeenCalledWith(window); + expect(browserAutofillSettingsOverriddenSpy).toHaveBeenCalled(); + }); + + it("does not check browser autofill settings for Unknown browser", async () => { + jest + .spyOn(BrowserApi, "getBrowserClientVendor") + .mockReturnValue(BrowserClientVendors.Unknown); + const browserAutofillSettingsOverriddenSpy = jest + .spyOn(BrowserApi, "browserAutofillSettingsOverridden") + .mockResolvedValue(true); + + await firstValueFrom(service.nudgeStatus$(nudgeType, userId)); + + expect(browserAutofillSettingsOverriddenSpy).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/browser/src/vault/popup/services/browser-autofill-nudge.service.ts b/apps/browser/src/vault/popup/services/browser-autofill-nudge.service.ts new file mode 100644 index 00000000000..7fe5f527bcb --- /dev/null +++ b/apps/browser/src/vault/popup/services/browser-autofill-nudge.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from "@angular/core"; +import { Observable, switchMap } from "rxjs"; + +import { NudgeStatus, NudgeType } from "@bitwarden/angular/vault"; +import { NewAccountNudgeService } from "@bitwarden/angular/vault/services/custom-nudges-services/new-account-nudge.service"; +import { BrowserClientVendors } from "@bitwarden/common/autofill/constants"; +import { UserId } from "@bitwarden/common/types/guid"; + +import { BrowserApi } from "../../../platform/browser/browser-api"; + +/** + * Browser-specific autofill nudge service. + * Extends NewAccountNudgeService (30-day account age check) and adds + * browser autofill setting detection. + * + * Nudge is dismissed if: + * - Account is older than 30 days (inherited from NewAccountNudgeService) + * - Browser's built-in password manager is already disabled via privacy settings + */ +@Injectable() +export class BrowserAutofillNudgeService extends NewAccountNudgeService { + override nudgeStatus$(nudgeType: NudgeType, userId: UserId): Observable { + return super.nudgeStatus$(nudgeType, userId).pipe( + switchMap(async (status) => { + const browserClient = BrowserApi.getBrowserClientVendor(window); + const browserAutofillOverridden = + browserClient !== BrowserClientVendors.Unknown && + (await BrowserApi.browserAutofillSettingsOverridden()); + + return { + hasBadgeDismissed: status.hasBadgeDismissed || browserAutofillOverridden, + hasSpotlightDismissed: status.hasSpotlightDismissed || browserAutofillOverridden, + }; + }), + ); + } +} diff --git a/apps/browser/src/vault/popup/settings/vault-settings-v2.component.html b/apps/browser/src/vault/popup/settings/vault-settings-v2.component.html index 407015d3a06..d5b94df5008 100644 --- a/apps/browser/src/vault/popup/settings/vault-settings-v2.component.html +++ b/apps/browser/src/vault/popup/settings/vault-settings-v2.component.html @@ -15,7 +15,7 @@