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/.claude/skills/angular-modernization/SKILL.md b/.claude/skills/angular-modernization/SKILL.md new file mode 100644 index 00000000000..187f715bde2 --- /dev/null +++ b/.claude/skills/angular-modernization/SKILL.md @@ -0,0 +1,156 @@ +--- +name: angular-modernization +description: Modernizes Angular code such as components and directives to follow best practices using both automatic CLI migrations and Bitwarden-specific patterns. YOU must use this skill when someone requests modernizing Angular code. DO NOT invoke for general Angular discussions unrelated to modernization. +allowed-tools: Read, Write, Glob, Bash(npx ng generate:*) +--- + +# Angular Modernization + +Transforms legacy Angular components to modern architecture using a two-step approach: + +1. **Automated migrations** - Angular CLI schematics for standalone, control flow, and signals +2. **Bitwarden patterns** - ADR compliance, OnPush change detection, proper visibility, thin components + +## Workflow + +### Step 1: Run Angular CLI Migrations + +**âš ī¸ CRITICAL: ALWAYS use Angular CLI migrations when available. DO NOT manually migrate features that have CLI schematics.** + +Angular provides automated schematics that handle edge cases, update tests, and ensure correctness. Manual migration should ONLY be used for patterns not covered by CLI tools. + +**IMPORTANT:** + +- Always run the commands using `npx ng`. +- All the commands must be run on directories and NOT files. Use the `--path` option to target directories. +- Run migrations in order (some depend on others) + +#### 1. Standalone Components + +```bash +npx ng generate @angular/core:standalone --path= --mode=convert-to-standalone +``` + +NgModule-based → standalone architecture + +#### 2. Control Flow Syntax + +```bash +npx ng generate @angular/core:control-flow +``` + +`*ngIf`, `*ngFor`, `*ngSwitch` → `@if`, `@for`, `@switch` + +#### 3. Signal Inputs + +```bash +npx ng generate @angular/core:signal-input-migration +``` + +`@Input()` → signal inputs + +#### 4. Signal Outputs + +```bash +npx ng generate @angular/core:output-migration +``` + +`@Output()` → signal outputs + +#### 5. Signal Queries + +```bash +npx ng generate @angular/core:signal-queries-migration +``` + +`@ViewChild`, `@ContentChild`, etc. → signal queries + +#### 6. inject() Function + +```bash +npx ng generate @angular/core:inject-migration +``` + +Constructor injection → `inject()` function + +#### 7. Self-Closing Tag + +```bash +npx ng generate @angular/core:self-closing-tag +``` + +Updates templates to self-closing syntax + +#### 8. Unused Imports + +```bash +npx ng generate @angular/core:unused-imports +``` + +Removes unused imports + +### Step 2: Apply Bitwarden Patterns + +See [migration-patterns.md](migration-patterns.md) for detailed examples. + +1. Add OnPush change detection +2. Apply visibility modifiers (`protected` for template access, `private` for internal) +3. Convert local component state to signals +4. Keep service observables (don't convert to signals) +5. Extract business logic to services +6. Organize class members correctly +7. Update tests for standalone + +### Step 3: Validate + +- Fix linting and formatting using `npm run lint:fix` +- Run tests using `npm run test` + +If any errors occur, fix them accordingly. + +## Key Decisions + +### Signals vs Observables + +- **Signals** - Component-local state only (ADR-0027) +- **Observables** - Service state and cross-component communication (ADR-0003) +- Use `toSignal()` to bridge observables into signal-based components + +### Visibility + +- `protected` - Template-accessible members +- `private` - Internal implementation + +### Other Rules + +- Always add OnPush change detection +- No TypeScript enums (use const objects with type aliases per ADR-0025) +- No code regions (refactor instead) +- Thin components (business logic in services) + +## Validation Checklist + +Before completing migration: + +- [ ] OnPush change detection added +- [ ] Visibility modifiers applied (`protected`/`private`) +- [ ] Signals for component state, observables for service state +- [ ] Class members organized (see [migration-patterns.md](migration-patterns.md#class-member-organization)) +- [ ] Tests updated and passing +- [ ] No new TypeScript enums +- [ ] No code regions + +## References + +### Bitwarden ADRs + +- [ADR-0003: Observable Data Services](https://contributing.bitwarden.com/architecture/adr/observable-data-services) +- [ADR-0025: No TypeScript Enums](https://contributing.bitwarden.com/architecture/adr/no-enums) +- [ADR-0027: Angular Signals](https://contributing.bitwarden.com/architecture/adr/angular-signals) +- [Bitwarden Angular Style Guide](https://contributing.bitwarden.com/contributing/code-style/web/angular) + +### Angular Resources + +- [Angular Style Guide](https://angular.dev/style-guide) +- [Angular Migrations](https://angular.dev/reference/migrations) +- [Angular CLI Schematics](https://angular.dev/tools/cli/schematics) diff --git a/.claude/skills/angular-modernization/migration-patterns.md b/.claude/skills/angular-modernization/migration-patterns.md new file mode 100644 index 00000000000..284f90a410f --- /dev/null +++ b/.claude/skills/angular-modernization/migration-patterns.md @@ -0,0 +1,253 @@ +# Angular Migration Patterns Reference + +## Table of Contents + +- [Component Architecture](#component-architecture) +- [Dependency Injection](#dependency-injection) +- [Reactivity Patterns](#reactivity-patterns) +- [Template Syntax](#template-syntax) +- [Type Safety](#type-safety) + +## Component Architecture + +### Standalone Components + +Angular defaults to standalone components. Components should omit `standalone: true`, and any component specifying `standalone: false` SHALL be migrated to standalone. + +```typescript +@Component({ + selector: "app-user-profile", + imports: [CommonModule, ReactiveFormsModule, AsyncPipe], + templateUrl: "./user-profile.component.html", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class UserProfileComponent {} +``` + +### Class Member Organization + +```typescript +@Component({...}) +export class MyComponent { + // 1. Inputs (public) + @Input() data: string; + + // 2. Outputs (public) + @Output() valueChange = new EventEmitter(); + + // 3. ViewChild/ContentChild + @ViewChild('template') template: TemplateRef; + + // 4. Injected dependencies (private/protected) + private userService = inject(UserService); + protected dialogService = inject(DialogService); + + // 5. Public properties + public formGroup: FormGroup; + + // 6. Protected properties (template-accessible) + protected isLoading = signal(false); + protected items$ = this.itemService.items$; + + // 7. Private properties + private cache = new Map(); + + // 8. Lifecycle hooks + ngOnInit() {} + + // 9. Public methods + public save() {} + + // 10. Protected methods (template-accessible) + protected handleClick() {} + + // 11. Private methods + private processData() {} +} +``` + +## Dependency Injection + +### Modern inject() Function + +**Before:** + +```typescript +constructor( + private userService: UserService, + private route: ActivatedRoute +) {} +``` + +**After:** + +```typescript +private userService = inject(UserService); +private route = inject(ActivatedRoute); +``` + +## Reactivity Patterns + +### Signals for Component State (ADR-0027) + +```typescript +// Local state +protected selectedFolder = signal(null); +protected isLoading = signal(false); + +// Derived state +protected hasSelection = computed(() => this.selectedFolder() !== null); +``` + +### Prefer computed() Over effect() + +Use `computed()` for derived values. Use `effect()` only for side effects (logging, analytics, DOM sync). + +**❌ Bad:** + +```typescript +constructor() { + effect(() => { + const id = this.selectedId(); + this.selectedItem.set(this.items().find(i => i.id === id) ?? null); + }); +} +``` + +**✅ Good:** + +```typescript +selectedItem = computed(() => this.items().find((i) => i.id === this.selectedId()) ?? null); +``` + +### Observables for Service Communication (ADR-0003) + +```typescript +// In component +protected folders$ = this.folderService.folders$; + +// Template +//
+ +// For explicit subscriptions +constructor() { + this.userService.user$ + .pipe(takeUntilDestroyed()) + .subscribe(user => this.handleUser(user)); +} +``` + +### Bridging Observables to Signals + +Use `toSignal()` to convert service observables to signals in components. Keep service state as observables (ADR-0003). + +**Before:** + +```typescript +private destroy$ = new Subject(); +users: User[] = []; + +ngOnInit() { + this.userService.users$.pipe(takeUntil(this.destroy$)) + .subscribe(users => this.users = users); +} + +ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); +} +``` + +**After:** + +```typescript +protected users = toSignal(this.userService.users$, { initialValue: [] }); +``` + +## Template Syntax + +### New Control Flow + +**Before:** + +```html +
+

{{ item.name }}

+
+Loading... +``` + +**After:** + +```html +@if (user$ | async; as user) { @for (item of user.items; track item.id) { +

{{ item.name }}

+} } @else { +

Loading...

+} +``` + +### Prefer Class/Style Bindings Over ngClass/ngStyle + +Use `[class.*]` and `[style.*]` bindings instead of `ngClass`/`ngStyle`. + +**❌ Bad:** + +```html +
+
+
+``` + +**✅ Good:** + +```html +
+
+
+``` + +## Type Safety + +### No TypeScript Enums (ADR-0025) + +**Before:** + +```typescript +enum CipherType { + Login = 1, + SecureNote = 2, +} +``` + +**After:** + +```typescript +export const CipherType = Object.freeze({ + Login: 1, + SecureNote: 2, +} as const); +export type CipherType = (typeof CipherType)[keyof typeof CipherType]; +``` + +### Reactive Forms + +```typescript +protected formGroup = new FormGroup({ + name: new FormControl('', { nonNullable: true }), + email: new FormControl('', { validators: [Validators.email] }), +}); +``` + +## Anti-Patterns to Avoid + +- ❌ Manually refactoring when CLI migrations exist +- ❌ Manual subscriptions without `takeUntilDestroyed()` +- ❌ TypeScript enums (use const objects per ADR-0025) +- ❌ Mixing constructor injection with `inject()` +- ❌ Signals in services shared with non-Angular code (ADR-0003) +- ❌ Business logic in components +- ❌ Code regions +- ❌ Converting service observables to signals (ADR-0003) +- ❌ Using `effect()` for derived state (use `computed()`) +- ❌ Using `ngClass`/`ngStyle` (use `[class.*]`/`[style.*]`) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b58d1511dca..c77fa959047 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,7 +8,9 @@ apps/desktop/desktop_native @bitwarden/team-platform-dev apps/desktop/desktop_native/objc/src/native/autofill @bitwarden/team-autofill-desktop-dev apps/desktop/desktop_native/core/src/autofill @bitwarden/team-autofill-desktop-dev +apps/desktop/desktop_native/macos_provider @bitwarden/team-autofill-desktop-dev apps/desktop/desktop_native/core/src/secure_memory @bitwarden/team-key-management-dev + ## No ownership for Cargo.lock and Cargo.toml to allow dependency updates apps/desktop/desktop_native/Cargo.lock apps/desktop/desktop_native/Cargo.toml @@ -73,6 +75,7 @@ bitwarden_license/bit-cli/src/admin-console @bitwarden/team-admin-console-dev libs/angular/src/admin-console @bitwarden/team-admin-console-dev libs/common/src/admin-console @bitwarden/team-admin-console-dev libs/admin-console @bitwarden/team-admin-console-dev +libs/auto-confirm @bitwarden/team-admin-console-dev ## Billing team files ## apps/browser/src/billing @bitwarden/team-billing-dev @@ -233,3 +236,4 @@ libs/pricing @bitwarden/team-billing-dev .claude/ @bitwarden/team-ai-sme .github/workflows/respond.yml @bitwarden/team-ai-sme .github/workflows/review-code.yml @bitwarden/team-ai-sme +libs/subscription @bitwarden/team-billing-dev diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index edbc9d98cc9..224020991d1 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -9,27 +9,3 @@ ## 📸 Screenshots - -## ⏰ Reminders before review - -- Contributor guidelines followed -- All formatters and local linters executed and passed -- Written new unit and / or integration tests where applicable -- Protected functional changes with optionality (feature flags) -- Used internationalization (i18n) for all UI strings -- CI builds passed -- Communicated to DevOps any deployment requirements -- Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team - -## đŸĻŽ Reviewer guidelines - - - -- 👍 (`:+1:`) or similar for great changes -- 📝 (`:memo:`) or â„šī¸ (`:information_source:`) for notes or general info -- ❓ (`:question:`) for questions -- 🤔 (`:thinking:`) or 💭 (`:thought_balloon:`) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion -- 🎨 (`:art:`) for suggestions / improvements -- ❌ (`:x:`) or âš ī¸ (`:warning:`) for more significant problems or concerns needing attention -- 🌱 (`:seedling:`) or â™ģī¸ (`:recycle:`) for future improvements or indications of technical debt -- ⛏ (`:pick:`) for minor or nitpick changes diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 6e142edf8a7..1b6522c94dd 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -3,6 +3,7 @@ extends: ["github>bitwarden/renovate-config"], // Extends our default configuration for pinned dependencies enabledManagers: ["cargo", "github-actions", "npm"], packageRules: [ + // ==================== Repo-Wide Update Behavior Rules ==================== { // Group all Github Action minor updates together to reduce PR noise. groupName: "Minor github-actions updates", @@ -16,13 +17,6 @@ matchDepNames: ["rust"], commitMessageTopic: "Rust", }, - { - // By default, we send patch updates to the Dependency Dashboard and do not generate a PR. - // We want to generate PRs for a select number of dependencies to ensure we stay up to date on these. - matchPackageNames: ["browserslist", "electron", "rxjs", "typescript", "webpack", "zone.js"], - matchUpdateTypes: ["patch"], - dependencyDashboardApproval: false, - }, { // Disable major and minor updates for TypeScript and Zone.js because they are managed by Angular. matchPackageNames: ["typescript", "zone.js"], @@ -44,6 +38,8 @@ description: "Manually updated using ng update", enabled: false, }, + + // ==================== Team Ownership Rules ==================== { matchPackageNames: ["buffer", "bufferutil", "core-js", "process", "url", "util"], description: "Admin Console owned dependencies", @@ -79,28 +75,6 @@ commitMessagePrefix: "[deps] Architecture:", reviewers: ["team:dept-architecture"], }, - { - matchPackageNames: [ - "@angular-eslint/schematics", - "@eslint/compat", - "@typescript-eslint/rule-tester", - "@typescript-eslint/utils", - "angular-eslint", - "eslint-config-prettier", - "eslint-import-resolver-typescript", - "eslint-plugin-import", - "eslint-plugin-rxjs-angular", - "eslint-plugin-rxjs", - "eslint-plugin-storybook", - "eslint-plugin-tailwindcss", - "eslint", - "husky", - "lint-staged", - "typescript-eslint", - ], - groupName: "Minor and patch linting updates", - matchUpdateTypes: ["minor", "patch"], - }, { matchPackageNames: [ "@emotion/css", @@ -119,7 +93,7 @@ "rimraf", "ssh-encoding", "ssh-key", - "@storybook/web-components-webpack5", + "@storybook/web-components-vite", "tabbable", "tldts", "wait-on", @@ -154,11 +128,11 @@ "@types/glob", "@types/lowdb", "@types/node", - "@types/node-forge", "@types/node-ipc", "@yao-pkg/pkg", "anyhow", "arboard", + "ashpd", "babel-loader", "base64-loader", "base64", @@ -169,6 +143,7 @@ "core-foundation", "copy-webpack-plugin", "css-loader", + "ctor", "dirs", "electron", "electron-builder", @@ -184,6 +159,7 @@ "html-webpack-injector", "html-webpack-plugin", "interprocess", + "itertools", "json5", "keytar", "libc", @@ -192,12 +168,10 @@ "napi", "napi-build", "napi-derive", - "node-forge", "node-ipc", "nx", "oo7", "oslog", - "parse5", "pin-project", "pkg", "postcss", @@ -207,6 +181,7 @@ "sass", "sass-loader", "scopeguard", + "secmem-proc", "security-framework", "security-framework-sys", "semver", @@ -215,6 +190,9 @@ "simplelog", "style-loader", "sysinfo", + "thiserror", + "tokio", + "tokio-util", "tracing", "tracing-subscriber", "ts-node", @@ -236,61 +214,17 @@ "windows-registry", "zbus", "zbus_polkit", + "zeroizing-alloc", ], description: "Platform owned dependencies", commitMessagePrefix: "[deps] Platform:", reviewers: ["team:team-platform-dev"], }, { - // We need to group all napi-related packages together to avoid build errors caused by version incompatibilities. - groupName: "napi", - matchPackageNames: ["napi", "napi-build", "napi-derive"], - }, - { - // We need to group all macOS/iOS binding-related packages together to avoid build errors caused by version incompatibilities. - groupName: "macOS/iOS bindings", - matchPackageNames: ["core-foundation", "security-framework", "security-framework-sys"], - }, - { - // We need to group all zbus-related packages together to avoid build errors caused by version incompatibilities. - groupName: "zbus", - matchPackageNames: ["zbus", "zbus_polkit"], - }, - { - // We need to group all windows-related packages together to avoid build errors caused by version incompatibilities. - groupName: "windows", - matchPackageNames: ["windows", "windows-core", "windows-future", "windows-registry"], - }, - { - // We group all webpack build-related minor and patch updates together to reduce PR noise. - // We include patch updates here because we want PRs for webpack patch updates and it's in this group. - matchPackageNames: [ - "@babel/core", - "@babel/preset-env", - "babel-loader", - "base64-loader", - "browserslist", - "copy-webpack-plugin", - "css-loader", - "html-loader", - "html-webpack-injector", - "html-webpack-plugin", - "mini-css-extract-plugin", - "postcss-loader", - "postcss", - "sass-loader", - "sass", - "style-loader", - "ts-loader", - "tsconfig-paths-webpack-plugin", - "webpack-cli", - "webpack-dev-server", - "webpack-node-externals", - "webpack", - ], - description: "webpack-related build dependencies", - groupName: "Minor and patch webpack updates", - matchUpdateTypes: ["minor", "patch"], + matchUpdateTypes: ["lockFileMaintenance"], + description: "Platform owns lock file maintenance", + commitMessagePrefix: "[deps] Platform:", + reviewers: ["team:team-platform-dev"], }, { matchPackageNames: [ @@ -311,26 +245,24 @@ "@compodoc/compodoc", "@ng-select/ng-select", "@storybook/addon-a11y", - "@storybook/addon-actions", "@storybook/addon-designs", - "@storybook/addon-essentials", - "@storybook/addon-interactions", + "@storybook/addon-docs", "@storybook/addon-links", "@storybook/test-runner", "@storybook/addon-themes", "@storybook/angular", - "@storybook/manager-api", - "@storybook/theming", "@types/react", "autoprefixer", "bootstrap", "chromatic", "ngx-toastr", + "path-browserify", "react", "react-dom", "remark-gfm", "storybook", "tailwindcss", + "vite-tsconfig-paths", "zone.js", "@tailwindcss/container-queries", ], @@ -351,11 +283,6 @@ commitMessagePrefix: "[deps] SM:", reviewers: ["team:team-secrets-manager-dev"], }, - { - // We need to update several Jest-related packages together, for version compatibility. - groupName: "jest", - matchPackageNames: ["@types/jest", "jest", "ts-jest", "jest-preset-angular"], - }, { matchPackageNames: [ "@microsoft/signalr-protocol-msgpack", @@ -363,6 +290,7 @@ "@types/jsdom", "@types/papaparse", "@types/zxcvbn", + "aes-gcm", "async-trait", "clap", "jsdom", @@ -370,6 +298,7 @@ "oidc-client-ts", "papaparse", "utf-8-validate", + "verifysign", "zxcvbn", ], description: "Tools owned dependencies", @@ -411,19 +340,209 @@ }, { matchPackageNames: [ + "@types/node-forge", "aes", "big-integer", "cbc", + "chacha20poly1305", + "linux-keyutils", + "memsec", + "node-forge", "rsa", "russh-cryptovec", "sha2", - "memsec", - "linux-keyutils", ], description: "Key Management owned dependencies", commitMessagePrefix: "[deps] KM:", reviewers: ["team:team-key-management-dev"], }, + + // ==================== Grouping Rules ==================== + // These come after any specific team assignment rules to ensure + // that grouping is not overridden by subsequent rule definitions. + { + matchPackageNames: [ + "@angular-eslint/schematics", + "@eslint/compat", + "@typescript-eslint/rule-tester", + "@typescript-eslint/utils", + "angular-eslint", + "eslint-config-prettier", + "eslint-import-resolver-typescript", + "eslint-plugin-import", + "eslint-plugin-rxjs-angular", + "eslint-plugin-rxjs", + "eslint-plugin-storybook", + "eslint-plugin-tailwindcss", + "eslint", + "husky", + "lint-staged", + "typescript-eslint", + ], + groupName: "Minor and patch linting updates", + matchUpdateTypes: ["minor", "patch"], + }, + { + // We need to group all napi-related packages together to avoid build errors caused by version incompatibilities. + groupName: "napi", + matchPackageNames: ["napi", "napi-build", "napi-derive"], + }, + { + // We need to group all macOS/iOS binding-related packages together to avoid build errors caused by version incompatibilities. + groupName: "macOS/iOS bindings", + matchPackageNames: ["core-foundation", "security-framework", "security-framework-sys"], + }, + { + // We need to group all zbus-related packages together to avoid build errors caused by version incompatibilities. + groupName: "zbus", + matchPackageNames: ["zbus", "zbus_polkit"], + }, + { + // We need to group all windows-related packages together to avoid build errors caused by version incompatibilities. + groupName: "windows", + matchPackageNames: ["windows", "windows-core", "windows-future", "windows-registry"], + }, + { + // We need to group all tokio-related packages together to avoid build errors caused by version incompatibilities. + groupName: "tokio", + matchPackageNames: ["bytes", "tokio", "tokio-util"], + }, + { + // We group all webpack build-related minor and patch updates together to reduce PR noise. + // We include patch updates here because we want PRs for webpack patch updates and it's in this group. + matchPackageNames: [ + "@babel/core", + "@babel/preset-env", + "babel-loader", + "base64-loader", + "browserslist", + "copy-webpack-plugin", + "css-loader", + "html-loader", + "html-webpack-injector", + "html-webpack-plugin", + "mini-css-extract-plugin", + "postcss-loader", + "postcss", + "sass-loader", + "sass", + "style-loader", + "ts-loader", + "tsconfig-paths-webpack-plugin", + "webpack-cli", + "webpack-dev-server", + "webpack-node-externals", + "webpack", + ], + description: "webpack-related build dependencies", + groupName: "Minor and patch webpack updates", + matchUpdateTypes: ["minor", "patch"], + }, + { + // We need to update several Jest-related packages together, for version compatibility. + groupName: "jest", + matchPackageNames: ["@types/jest", "jest", "ts-jest", "jest-preset-angular"], + }, + + // ==================== Dashboard Rules ==================== + { + // For the packages below, we have decided we will only be creating PRs + // for major updates, and sending minor (as well as patch) to the dashboard. + // This rule comes AFTER grouping rules so that groups are respected while still + // sending minor/patch updates to the dependency dashboard for approval. + matchPackageNames: [ + "anyhow", + "arboard", + "ashpd", + "babel-loader", + "base64-loader", + "base64", + "bindgen", + "byteorder", + "bytes", + "core-foundation", + "copy-webpack-plugin", + "css-loader", + "ctor", + "dirs", + "electron-builder", + "electron-log", + "electron-reload", + "electron-store", + "electron-updater", + "embed_plist", + "futures", + "hex", + "homedir", + "html-loader", + "html-webpack-injector", + "html-webpack-plugin", + "interprocess", + "json5", + "keytar", + "libc", + "lowdb", + "mini-css-extract-plugin", + "napi", + "napi-build", + "napi-derive", + "node-ipc", + "nx", + "oo7", + "oslog", + "pin-project", + "pkg", + "postcss", + "postcss-loader", + "rand", + "sass", + "sass-loader", + "scopeguard", + "secmem-proc", + "security-framework", + "security-framework-sys", + "semver", + "serde", + "serde_json", + "simplelog", + "style-loader", + "sysinfo", + "thiserror", + "tokio", + "tokio-util", + "tracing", + "tracing-subscriber", + "ts-node", + "ts-loader", + "tsconfig-paths-webpack-plugin", + "type-fest", + "typenum", + "typescript-strict-plugin", + "uniffi", + "webpack-cli", + "webpack-dev-server", + "webpack-node-externals", + "widestring", + "windows", + "windows-core", + "windows-future", + "windows-registry", + "zbus", + "zbus_polkit", + "zeroizing-alloc", + ], + matchUpdateTypes: ["minor", "patch"], + dependencyDashboardApproval: true, + }, + { + // By default, we send patch updates to the Dependency Dashboard and do not generate a PR. + // We want to generate PRs for a select number of dependencies to ensure we stay up to date on these. + matchPackageNames: ["browserslist", "electron", "rxjs", "typescript", "webpack", "zone.js"], + matchUpdateTypes: ["patch"], + dependencyDashboardApproval: false, + }, + + // ==================== Special Version Constraints ==================== { // Any versions of lowdb above 1.0.0 are not compatible with CommonJS. matchPackageNames: ["lowdb"], 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..7b35baf01e2 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 @@ -193,7 +193,7 @@ jobs: zip -r browser-source.zip browser-source - name: Upload browser source - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: ${{matrix.license_type.archive_name_prefix}}browser-source-${{ env._BUILD_NUMBER }}.zip path: browser-source.zip @@ -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 @@ -272,7 +272,7 @@ jobs: npm --version - name: Download browser source - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: ${{matrix.license_type.source_archive_name_prefix}}browser-source-${{ env._BUILD_NUMBER }}.zip @@ -336,7 +336,7 @@ jobs: working-directory: browser-source/apps/browser - name: Upload extension artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: ${{ matrix.license_type.artifact_prefix }}${{ matrix.browser.artifact_name }}-${{ env._BUILD_NUMBER }}.zip path: browser-source/apps/browser/dist/${{matrix.license_type.archive_name_prefix}}${{ matrix.browser.archive_name }} @@ -349,7 +349,7 @@ jobs: - name: Upload dev extension artifact if: ${{ matrix.browser.archive_name_dev != '' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: ${{ matrix.license_type.artifact_prefix }}${{ matrix.browser.artifact_name_dev }}-${{ env._BUILD_NUMBER }}.zip path: browser-source/apps/browser/dist/${{matrix.license_type.archive_name_prefix}}${{ matrix.browser.archive_name_dev }} @@ -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 @@ -523,7 +523,7 @@ jobs: ls -la - name: Upload Safari artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: ${{matrix.license_type.archive_name_prefix}}dist-safari-${{ env._BUILD_NUMBER }}.zip path: apps/browser/dist/${{matrix.license_type.archive_name_prefix}}dist-safari.zip @@ -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 @@ -565,7 +565,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Upload Sources - uses: crowdin/github-action@08713f00a50548bfe39b37e8f44afb53e7a802d4 # v2.12.0 + uses: crowdin/github-action@60debf382ee245b21794321190ad0501db89d8c1 # v2.13.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index 964cbc834c5..d0abe8e12e7 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 @@ -268,7 +268,7 @@ jobs: fi - name: Upload unix zip asset - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}${{ matrix.os.target_suffix }}-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}${{ matrix.os.target_suffix }}-${{ env._PACKAGE_VERSION }}.zip @@ -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 @@ -482,7 +482,7 @@ jobs: } - name: Upload windows zip asset - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bw${{ matrix.license_type.artifact_prefix }}-windows-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-windows-${{ env._PACKAGE_VERSION }}.zip @@ -490,7 +490,7 @@ jobs: - name: Upload Chocolatey asset if: matrix.license_type.build_prefix == 'bit' - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg path: apps/cli/dist/chocolatey/bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg @@ -503,7 +503,7 @@ jobs: - name: Upload NPM Build Directory asset if: matrix.license_type.build_prefix == 'bit' - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip path: apps/cli/bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip @@ -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 @@ -535,7 +535,7 @@ jobs: echo "BW Package Version: $_PACKAGE_VERSION" - name: Get bw linux cli - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: bw-linux-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/snap @@ -572,7 +572,7 @@ jobs: run: sudo snap remove bw - name: Upload snap asset - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bw_${{ env._PACKAGE_VERSION }}_amd64.snap path: apps/cli/dist/snap/bw_${{ env._PACKAGE_VERSION }}_amd64.snap diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 03a09ac8c48..0d4009e54f9 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -55,9 +55,9 @@ 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 }} + ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - name: Verify @@ -88,9 +88,9 @@ 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 }} + ref: ${{ github.event.pull_request.head.sha }} persist-credentials: true - name: Get Package Version @@ -173,11 +173,15 @@ 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 }} + fetch-depth: 1 + ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false + - name: Free disk space + uses: bitwarden/gh-actions/free-disk-space@main + - name: Set up Node uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: @@ -186,7 +190,7 @@ jobs: node-version: ${{ env._NODE_VERSION }} - name: Cache Rust dependencies - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: | apps/desktop/desktop_native -> target @@ -195,7 +199,7 @@ jobs: - name: Set up environment run: | sudo apt-get update - sudo apt-get -y install pkg-config libxss-dev rpm musl-dev musl-tools flatpak flatpak-builder + sudo apt-get -y install pkg-config libxss-dev rpm flatpak flatpak-builder - name: Set up Snap run: sudo snap install snapcraft --classic @@ -232,7 +236,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 id: cache with: path: | @@ -248,51 +252,51 @@ jobs: env: PKG_CONFIG_ALLOW_CROSS: true PKG_CONFIG_ALL_STATIC: true - TARGET: musl + # 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: | - rustup target add x86_64-unknown-linux-musl - node build.js --target=x86_64-unknown-linux-musl + node build.js --target=x86_64-unknown-linux-gnu --release - name: Build application run: npm run dist:lin - name: Upload tar.gz artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bitwarden_${{ env._PACKAGE_VERSION }}_x64.tar.gz path: apps/desktop/dist/bitwarden_desktop_x64.tar.gz if-no-files-found: error - name: Upload .deb artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb if-no-files-found: error - name: Upload .rpm artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm if-no-files-found: error - name: Upload .snap artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap path: apps/desktop/dist/bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap if-no-files-found: error - name: Upload .AppImage artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: ${{ needs.setup.outputs.release_channel }}-linux.yml path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-linux.yml @@ -305,7 +309,7 @@ jobs: sudo npm run pack:lin:flatpak - name: Upload flatpak artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: com.bitwarden.desktop.flatpak path: apps/desktop/dist/com.bitwarden.desktop.flatpak @@ -329,9 +333,9 @@ 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 }} + ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - name: Set up Node @@ -342,7 +346,7 @@ jobs: node-version: ${{ env._NODE_VERSION }} - name: Cache Rust dependencies - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: | apps/desktop/desktop_native -> target @@ -351,7 +355,7 @@ jobs: - name: Set up environment run: | sudo apt-get update - sudo apt-get -y install pkg-config libxss-dev rpm musl-dev musl-tools flatpak flatpak-builder squashfs-tools ruby ruby-dev rubygems build-essential + sudo apt-get -y install pkg-config libxss-dev rpm flatpak flatpak-builder squashfs-tools ruby ruby-dev rubygems build-essential sudo gem install --no-document fpm - name: Set up Snap @@ -395,7 +399,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 id: cache with: path: | @@ -411,10 +415,10 @@ jobs: env: PKG_CONFIG_ALLOW_CROSS: true PKG_CONFIG_ALL_STATIC: true - TARGET: musl + # 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: | - rustup target add aarch64-unknown-linux-musl - node build.js --target=aarch64-unknown-linux-musl + 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' @@ -433,14 +437,14 @@ jobs: run: npm run dist:lin:arm64 - name: Upload .snap artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bitwarden_${{ env._PACKAGE_VERSION }}_arm64.snap path: apps/desktop/dist/bitwarden_${{ env._PACKAGE_VERSION }}_arm64.snap if-no-files-found: error - name: Upload tar.gz artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bitwarden_${{ env._PACKAGE_VERSION }}_arm64.tar.gz path: apps/desktop/dist/bitwarden_desktop_arm64.tar.gz @@ -453,7 +457,7 @@ jobs: sudo npm run pack:lin:flatpak - name: Upload flatpak artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: com.bitwarden.desktop-arm64.flatpak path: apps/desktop/dist/com.bitwarden.desktop.flatpak @@ -477,9 +481,9 @@ 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 }} + ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - name: Set up Node @@ -490,7 +494,7 @@ jobs: node-version: ${{ env._NODE_VERSION }} - name: Cache Rust dependencies - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: | apps/desktop/desktop_native -> target @@ -558,7 +562,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 id: cache with: path: | @@ -569,7 +573,9 @@ jobs: - name: Build Native Module if: steps.cache.outputs.cache-hit != 'true' working-directory: apps/desktop/desktop_native - run: node build.js cross-platform + env: + MODE: ${{ github.event_name == 'workflow_call' && '--release' || '' }} + run: node build.js cross-platform "$env:MODE" - name: Build run: npm run build @@ -624,7 +630,7 @@ jobs: -NewName bitwarden-$env:_PACKAGE_VERSION-arm64.nsis.7z - name: Upload portable exe artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe @@ -632,7 +638,7 @@ jobs: - name: Upload installer exe artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/nsis-web/Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe @@ -640,7 +646,7 @@ jobs: - name: Upload appx ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx @@ -648,7 +654,7 @@ jobs: - name: Upload store appx ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx @@ -656,7 +662,7 @@ jobs: - name: Upload NSIS ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z @@ -664,7 +670,7 @@ jobs: - name: Upload appx x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx @@ -672,7 +678,7 @@ jobs: - name: Upload store appx x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx @@ -680,7 +686,7 @@ jobs: - name: Upload NSIS x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z @@ -688,7 +694,7 @@ jobs: - name: Upload appx ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx @@ -696,7 +702,7 @@ jobs: - name: Upload store appx ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx @@ -704,7 +710,7 @@ jobs: - name: Upload NSIS ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z @@ -712,7 +718,7 @@ jobs: - name: Upload nupkg artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bitwarden.${{ env._PACKAGE_VERSION }}.nupkg path: apps/desktop/dist/chocolatey/bitwarden.${{ env._PACKAGE_VERSION }}.nupkg @@ -720,7 +726,7 @@ jobs: - name: Upload auto-update artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: ${{ needs.setup.outputs.release_channel }}.yml path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release_channel }}.yml @@ -743,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 @@ -756,7 +762,7 @@ jobs: node-version: ${{ env._NODE_VERSION }} - name: Cache Rust dependencies - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: | apps/desktop/desktop_native -> target @@ -821,7 +827,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 id: cache with: path: | @@ -832,7 +838,9 @@ jobs: - name: Build Native Module if: steps.cache.outputs.cache-hit != 'true' working-directory: apps/desktop/desktop_native - run: node build.js cross-platform + env: + MODE: ${{ github.event_name == 'workflow_call' && '--release' || '' }} + run: node build.js cross-platform "$env:MODE" - name: Build run: npm run build @@ -875,7 +883,7 @@ jobs: -NewName latest-beta.yml - name: Upload portable exe artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Beta-Portable-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/Bitwarden-Beta-Portable-${{ env._PACKAGE_VERSION }}.exe @@ -883,7 +891,7 @@ jobs: - name: Upload installer exe artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Beta-Installer-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/nsis-web/Bitwarden-Beta-Installer-${{ env._PACKAGE_VERSION }}.exe @@ -891,7 +899,7 @@ jobs: - name: Upload appx ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32.appx @@ -899,7 +907,7 @@ jobs: - name: Upload store appx ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32-store.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32-store.appx @@ -907,7 +915,7 @@ jobs: - name: Upload NSIS ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bitwarden-beta-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-beta-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z @@ -915,7 +923,7 @@ jobs: - name: Upload appx x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64.appx @@ -923,7 +931,7 @@ jobs: - name: Upload store appx x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64-store.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64-store.appx @@ -931,7 +939,7 @@ jobs: - name: Upload NSIS x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bitwarden-beta-${{ env._PACKAGE_VERSION }}-x64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-beta-${{ env._PACKAGE_VERSION }}-x64.nsis.7z @@ -939,7 +947,7 @@ jobs: - name: Upload appx ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64.appx @@ -947,7 +955,7 @@ jobs: - name: Upload store appx ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64-store.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64-store.appx @@ -955,7 +963,7 @@ jobs: - name: Upload NSIS ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bitwarden-beta-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-beta-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z @@ -963,7 +971,7 @@ jobs: - name: Upload auto-update artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: latest-beta.yml path: apps/desktop/dist/nsis-web/latest-beta.yml @@ -986,9 +994,9 @@ 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 }} + ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - name: Set up Node @@ -999,15 +1007,15 @@ jobs: node-version: ${{ env._NODE_VERSION }} - name: Set up Python - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: - python-version: '3.14' + python-version: '3.14.2' - name: Set up Node-gyp - run: python3 -m pip install setuptools + run: python -m pip install setuptools - name: Cache Rust dependencies - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: | apps/desktop/desktop_native -> target @@ -1020,17 +1028,18 @@ jobs: rustup show echo "GitHub ref: $GITHUB_REF" echo "GitHub event: $GITHUB_EVENT" + xcodebuild -showsdks - name: Cache Build id: build-cache - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Cache Safari id: safari-cache - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -1176,7 +1185,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 id: cache with: path: | @@ -1187,7 +1196,9 @@ jobs: - name: Build Native Module if: steps.cache.outputs.cache-hit != 'true' working-directory: apps/desktop/desktop_native - run: node build.js cross-platform + env: + MODE: ${{ github.event_name == 'workflow_call' && '--release' || '' }} + run: node build.js cross-platform "$MODE" - name: Build application (dev) run: npm run build @@ -1223,9 +1234,9 @@ 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 }} + ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - name: Set up Node @@ -1236,15 +1247,15 @@ jobs: node-version: ${{ env._NODE_VERSION }} - name: Set up Python - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: - python-version: '3.14' + python-version: '3.14.2' - name: Set up Node-gyp - run: python3 -m pip install setuptools + run: python -m pip install setuptools - name: Cache Rust dependencies - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: | apps/desktop/desktop_native -> target @@ -1257,17 +1268,18 @@ jobs: rustup show echo "GitHub ref: $GITHUB_REF" echo "GitHub event: $GITHUB_EVENT" + xcodebuild -showsdks - name: Get Build Cache id: build-cache - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -1397,7 +1409,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 id: cache with: path: | @@ -1408,14 +1420,16 @@ jobs: - name: Build Native Module if: steps.cache.outputs.cache-hit != 'true' working-directory: apps/desktop/desktop_native - run: node build.js cross-platform + env: + MODE: ${{ github.event_name == 'workflow_call' && '--release' || '' }} + run: node build.js cross-platform "$MODE" - name: Build if: steps.build-cache.outputs.cache-hit != 'true' run: npm run build - name: Download Browser artifact - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: path: ${{ github.workspace }}/browser-build-artifacts @@ -1448,28 +1462,28 @@ jobs: run: npm run pack:mac - name: Upload .zip artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip if-no-files-found: error - name: Upload .dmg artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg if-no-files-found: error - name: Upload .dmg blockmap artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: ${{ needs.setup.outputs.release_channel }}-mac.yml path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-mac.yml @@ -1495,9 +1509,9 @@ 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 }} + ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - name: Set up Node @@ -1508,15 +1522,15 @@ jobs: node-version: ${{ env._NODE_VERSION }} - name: Set up Python - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: - python-version: '3.14' + python-version: '3.14.2' - name: Set up Node-gyp - run: python3 -m pip install setuptools + run: python -m pip install setuptools - name: Cache Rust dependencies - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: | apps/desktop/desktop_native -> target @@ -1529,17 +1543,18 @@ jobs: rustup show echo "GitHub ref: $GITHUB_REF" echo "GitHub event: $GITHUB_EVENT" + xcodebuild -showsdks - name: Get Build Cache id: build-cache - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: apps/desktop/build key: ${{ runner.os }}-${{ github.run_id }}-build - name: Setup Safari Cache id: safari-cache - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: apps/browser/dist/Safari key: ${{ runner.os }}-${{ github.run_id }}-safari-extension @@ -1677,7 +1692,7 @@ jobs: npm link ../sdk-internal - name: Cache Native Module - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 id: cache with: path: | @@ -1688,14 +1703,16 @@ jobs: - name: Build Native Module if: steps.cache.outputs.cache-hit != 'true' working-directory: apps/desktop/desktop_native - run: node build.js cross-platform + env: + MODE: ${{ github.event_name == 'workflow_call' && '--release' || '' }} + run: node build.js cross-platform "$MODE" - name: Build if: steps.build-cache.outputs.cache-hit != 'true' run: npm run build - name: Download Browser artifact - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: path: ${{ github.workspace }}/browser-build-artifacts @@ -1738,14 +1755,14 @@ jobs: $buildInfo | ConvertTo-Json | Set-Content -Path dist/macos-build-number.json - name: Upload MacOS App Store build number artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: macos-build-number.json path: apps/desktop/dist/macos-build-number.json if-no-files-found: error - name: Upload .pkg artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg path: apps/desktop/dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg @@ -1833,9 +1850,9 @@ 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 }} + ref: ${{ github.event.pull_request.head.sha }} persist-credentials: false - name: Log in to Azure @@ -1856,7 +1873,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Upload Sources - uses: crowdin/github-action@08713f00a50548bfe39b37e8f44afb53e7a802d4 # v2.12.0 + uses: crowdin/github-action@60debf382ee245b21794321190ad0501db89d8c1 # v2.13.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} @@ -1867,6 +1884,328 @@ jobs: upload_sources: true upload_translations: false + validate-linux-x64-deb: + name: Validate Linux x64 .deb + runs-on: ubuntu-22.04 + needs: + - setup + - linux + env: + _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} + steps: + - name: Check out repo + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 1 + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + + - name: Download deb artifact + uses: bitwarden/gh-actions/download-artifacts@main + with: + run_id: ${{ github.run_id }} + path: apps/desktop/artifacts/linux/deb + artifacts: Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb + + - name: Install deps + run: | + sudo apt-get update + sudo apt-get install -y libasound2 xvfb + + - name: Install .deb + working-directory: apps/desktop/artifacts/linux/deb + run: sudo apt-get install -y ./Bitwarden-${_PACKAGE_VERSION}-amd64.deb + + - name: Run .deb + run: | + xvfb-run -a bitwarden & + sleep 30 + if pgrep bitwarden > /dev/null; then + pkill -9 bitwarden + echo "Bitwarden is running." + else + echo "Bitwarden is not running." + exit 1 + fi + + validate-linux-x64-appimage: + name: Validate Linux x64 appimage + runs-on: ubuntu-22.04 + needs: + - setup + - linux + env: + _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} + steps: + - name: Check out repo + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 1 + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + + - name: Download appimage artifact + uses: bitwarden/gh-actions/download-artifacts@main + with: + run_id: ${{ github.run_id }} + path: apps/desktop/artifacts/linux/appimage + artifacts: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage + + - name: Install deps + run: | + sudo apt-get update + sudo apt-get install -y libasound2 libfuse2 xvfb + + - name: Run AppImage + working-directory: apps/desktop/artifacts/linux/appimage + run: | + chmod a+x ./Bitwarden-${_PACKAGE_VERSION}-x86_64.AppImage + xvfb-run -a ./Bitwarden-${_PACKAGE_VERSION}-x86_64.AppImage --no-sandbox & + sleep 30 + if pgrep bitwarden > /dev/null; then + pkill -9 bitwarden + echo "Bitwarden is running." + else + echo "Bitwarden is not running." + exit 1 + fi + + validate-linux-wayland: + name: Validate Linux Wayland + runs-on: ubuntu-22.04 + needs: + - setup + - linux + env: + _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} + steps: + - name: Check out repo + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 1 + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + + - name: Download appimage artifact + uses: bitwarden/gh-actions/download-artifacts@main + with: + run_id: ${{ github.run_id }} + path: apps/desktop/artifacts/linux/appimage + artifacts: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage + + - name: Install deps + run: | + sudo apt-get update + sudo apt-get install -y libasound2 libfuse2 xvfb + sudo apt-get install -y weston libwayland-client0 libwayland-server0 libwayland-dev + + - name: Run headless Wayland compositor + run: | + # Start Weston in a virtual terminal in headless mode + weston --headless --socket=wayland-0 & + # Let the compositor start + sleep 5 + + - name: Run AppImage + working-directory: apps/desktop/artifacts/linux/appimage + env: + WAYLAND_DISPLAY: wayland-0 + run: | + chmod a+x ./Bitwarden-${_PACKAGE_VERSION}-x86_64.AppImage + xvfb-run -a ./Bitwarden-${_PACKAGE_VERSION}-x86_64.AppImage --no-sandbox & + sleep 30 + if pgrep bitwarden > /dev/null; then + pkill -9 bitwarden + echo "Bitwarden is running." + else + echo "Bitwarden is not running." + exit 1 + fi + + validate-linux-flatpak: + name: Validate Linux ${{ matrix.os }} Flatpak + runs-on: ${{ matrix.os || 'ubuntu-22.04' }} + strategy: + matrix: + os: + - ubuntu-22.04 + - ubuntu-22.04-arm + needs: + - setup + - linux + - linux-arm64 + steps: + - name: Check out repo + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 1 + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + + - name: Download flatpak artifact + uses: bitwarden/gh-actions/download-artifacts@main + with: + run_id: ${{ github.run_id }} + path: apps/desktop/artifacts/linux/flatpak/ + artifacts: com.bitwarden.${{ matrix.os == 'ubuntu-22.04' && 'desktop' || 'desktop-arm64' }}.flatpak + + - name: Install deps + run: | + sudo apt-get update + sudo apt-get install -y libasound2 flatpak xvfb dbus-x11 + flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo + flatpak install -y --user flathub + + - name: Install flatpak + working-directory: apps/desktop/artifacts/linux/flatpak + run: flatpak install -y --user --bundle com.bitwarden.desktop.flatpak + + - name: Run Flatpak + run: | + export $(dbus-launch) + xvfb-run -a flatpak run com.bitwarden.desktop & + sleep 30 + if pgrep bitwarden > /dev/null; then + pkill -9 bitwarden + echo "Bitwarden is running." + else + echo "Bitwarden is not running." + exit 1 + fi + + validate-linux-snap: + name: Validate Linux ${{ matrix.os }} Snap + runs-on: ${{ matrix.os || 'ubuntu-22.04' }} + strategy: + matrix: + os: + - ubuntu-22.04 + - ubuntu-22.04-arm + needs: + - setup + - linux + - linux-arm64 + env: + _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} + _CPU_ARCH: ${{ matrix.os == 'ubuntu-22.04' && 'amd64' || 'arm64' }} + steps: + - name: Check out repo + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 1 + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + + - name: Download snap artifact + uses: bitwarden/gh-actions/download-artifacts@main + with: + run_id: ${{ github.run_id }} + path: apps/desktop/artifacts/linux/snap + artifacts: bitwarden_${{ env._PACKAGE_VERSION }}_${{ env._CPU_ARCH }}.snap + + - name: Install deps + run: | + sudo apt-get update + sudo apt-get install -y libasound2 snapd xvfb + + - name: Install snap + working-directory: apps/desktop/artifacts/linux/snap + run: | + sudo snap install --dangerous ./bitwarden_${_PACKAGE_VERSION}_${_CPU_ARCH}.snap + + - name: Run Snap + run: | + xvfb-run -a snap run bitwarden & + sleep 30 + if pgrep bitwarden > /dev/null; then + pkill -9 bitwarden + echo "Bitwarden is running." + else + echo "Bitwarden is not running." + exit 1 + fi + + validate-macos-dmg: + name: Validate MacOS dmg + runs-on: macos-15 + needs: + - setup + - macos-package-github + env: + _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} + steps: + - name: Check out repo + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 1 + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + + - name: Download dmg artifact + uses: bitwarden/gh-actions/download-artifacts@main + with: + run_id: ${{ github.run_id }} + path: apps/desktop/artifacts/macos/dmg + artifacts: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg + + - name: Install dmg + working-directory: apps/desktop/artifacts/macos/dmg + run: | + # mount + hdiutil attach Bitwarden-${_PACKAGE_VERSION}-universal.dmg + # install + cp -r /Volumes/Bitwarden\ ${_PACKAGE_VERSION}-universal/Bitwarden.app /Applications/ + # unmount + hdiutil detach /Volumes/Bitwarden\ ${_PACKAGE_VERSION}-universal + + - name: Run dmg + run: | + open /Applications/Bitwarden.app/Contents/MacOS/Bitwarden & + sleep 30 + if pgrep Bitwarden > /dev/null; then + pkill -9 Bitwarden + echo "Bitwarden is running." + else + echo "Bitwarden is not running." + exit 1 + fi + + validate-windows-portable: + name: Validate Windows portable + runs-on: windows-2022 + needs: + - setup + - windows + env: + _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} + steps: + - name: Check out repo + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 1 + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + + - name: Download portable artifact + uses: bitwarden/gh-actions/download-artifacts@main + with: + run_id: ${{ github.run_id }} + path: apps/desktop/artifacts/windows/portable + artifacts: Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe + + - name: Run Portable exe + working-directory: apps/desktop/artifacts/windows/portable + run: | + "./Bitwarden-Portable-$_PACKAGE_VERSION.exe" --no-sandbox & + sleep 30 + if tasklist | grep Bitwarden ; then + taskkill //F //IM "Bitwarden.exe" + echo "Bitwarden is running." + else + echo "Bitwarden is not running." + exit 1 + fi + check-failures: name: Check for failures if: always() @@ -1881,6 +2220,13 @@ jobs: - macos-package-github - macos-package-mas - crowdin-push + - validate-linux-x64-deb + - validate-linux-x64-appimage + - validate-linux-flatpak + - validate-linux-snap + - validate-linux-wayland + - validate-macos-dmg + - validate-windows-portable permissions: contents: read id-token: write diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 02ab7727c24..7b92de0f22a 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 @@ -112,7 +112,7 @@ jobs: npm_command: dist:bit:selfhost - artifact_name: selfhosted-DEV license_type: "commercial" - image_name: web + image_name: web-dev npm_command: build:bit:selfhost:dev git_metadata: true - artifact_name: cloud-QA @@ -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 @@ -204,7 +204,7 @@ jobs: ########## Set up Docker ########## - name: Set up Docker - uses: docker/setup-docker-action@efe9e3891a4f7307e689f2100b33a155b900a608 # v4.5.0 + uses: docker/setup-docker-action@e43656e248c0bd0647d3f5c195d116aacf6fcaf4 # v4.7.0 with: daemon-config: | { @@ -218,7 +218,7 @@ jobs: uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 ########## ACRs ########## - name: Log in to Azure @@ -307,7 +307,7 @@ jobs: zip -r web-$_VERSION-${{ matrix.artifact_name }}.zip build - name: Upload ${{ matrix.artifact_name }} artifact - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip path: apps/web/web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip @@ -334,7 +334,7 @@ jobs: - name: Scan Docker image if: ${{ needs.setup.outputs.has_secrets == 'true' }} id: container-scan - uses: anchore/scan-action@568b89d27fc18c60e56937bff480c91c772cd993 # v7.1.0 + uses: anchore/scan-action@62b74fb7bb810d2c45b1865f47a77655621862a5 # v7.2.3 with: image: ${{ steps.image-name.outputs.name }} fail-build: false @@ -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 @@ -390,7 +390,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Upload Sources - uses: crowdin/github-action@08713f00a50548bfe39b37e8f44afb53e7a802d4 # v2.12.0 + uses: crowdin/github-action@60debf382ee245b21794321190ad0501db89d8c1 # v2.13.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} diff --git a/.github/workflows/chromatic.yml b/.github/workflows/chromatic.yml index 677d3dfc1df..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 @@ -65,7 +65,7 @@ jobs: - name: Cache NPM id: npm-cache - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1 with: path: "~/.npm" key: ${{ runner.os }}-npm-chromatic-${{ hashFiles('**/package-lock.json') }} diff --git a/.github/workflows/crowdin-pull.yml b/.github/workflows/crowdin-pull.yml index 5475c4dd692..a707fef0889 100644 --- a/.github/workflows/crowdin-pull.yml +++ b/.github/workflows/crowdin-pull.yml @@ -49,7 +49,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Generate GH App token - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 id: app-token with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} @@ -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..61e2b3631e6 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 @@ -45,7 +45,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Lint ${{ matrix.app.name }} config - uses: crowdin/github-action@08713f00a50548bfe39b37e8f44afb53e7a802d4 # v2.12.0 + uses: crowdin/github-action@60debf382ee245b21794321190ad0501db89d8c1 # v2.13.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_PROJECT_ID: ${{ matrix.app.project_id }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 48d3eca2f4e..81d79df569c 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 @@ -52,6 +52,7 @@ jobs: ! -path "*/Cargo.lock" \ ! -path "./apps/desktop/macos/*" \ ! -path "*/CLAUDE.md" \ + ! -path "*/SKILL.md" \ > tmp.txt diff <(sort .github/whitelist-capital-letters.txt) <(sort tmp.txt) @@ -94,18 +95,18 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Install Rust - uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # stable + uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # stable with: toolchain: stable components: rustfmt, clippy - name: Install Rust nightly - uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # stable + uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # stable with: toolchain: nightly components: rustfmt @@ -114,7 +115,7 @@ jobs: run: rustup --version - name: Cache cargo registry - uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2.7.7 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 - name: Run cargo fmt working-directory: ./apps/desktop/desktop_native @@ -127,7 +128,7 @@ jobs: RUSTFLAGS: "-D warnings" - name: Install cargo-sort - run: cargo install cargo-sort --locked --git https://github.com/DevinR528/cargo-sort.git --rev f5047967021cbb1f822faddc355b3b07674305a1 + run: cargo install cargo-sort --locked --git https://github.com/DevinR528/cargo-sort.git --rev ac6e328faf467a39e38ab48dc60dcf4f6a46d7a5 # v2.0.2 - name: Cargo sort working-directory: ./apps/desktop/desktop_native @@ -141,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@2e9d707ef49c9b094d45955b60c7e5c0dfedeb14 # v2.66.5 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..3a7431c07f0 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 @@ -36,7 +36,7 @@ jobs: run: npm ci - name: Set Nx SHAs for affected detection - uses: nrwl/nx-set-shas@826660b82addbef3abff5fa871492ebad618c9e1 # v4.3.3 + uses: nrwl/nx-set-shas@3e9ad7370203c1e93d109be57f3b72eb0eb511b1 # v4.4.0 - name: Run Nx affected tasks continue-on-error: true 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..c5db7ea9295 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 @@ -331,7 +331,7 @@ jobs: run: wget "https://github.com/bitwarden/clients/releases/download/${_RELEASE_TAG}/macos-build-number.json" - name: Setup Ruby and Install Fastlane - uses: ruby/setup-ruby@d5126b9b3579e429dd52e51e68624dda2e05be25 # v1.267.0 + uses: ruby/setup-ruby@708024e6c902387ab41de36e1669e43b5ee7085e # v1.283.0 with: ruby-version: '3.4.7' bundler-cache: false diff --git a/.github/workflows/publish-web.yml b/.github/workflows/publish-web.yml index 62d9342cf61..c45e249d083 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 @@ -182,11 +182,13 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Generate GH App token - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 id: app-token with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} + owner: ${{ github.repository_owner }} + repositories: self-host - name: Trigger Bitwarden lite build uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 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 7f87a1e5628..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 @@ -98,6 +98,14 @@ jobs: working-directory: apps/desktop/artifacts run: mv "Bitwarden-${PKG_VERSION}-universal.pkg" "Bitwarden-${PKG_VERSION}-universal.pkg.archive" + - name: Rename .tar.gz to include version + env: + PKG_VERSION: ${{ steps.version.outputs.version }} + working-directory: apps/desktop/artifacts + run: | + mv "bitwarden_desktop_x64.tar.gz" "bitwarden_${PKG_VERSION}_x64.tar.gz" + mv "bitwarden_desktop_arm64.tar.gz" "bitwarden_${PKG_VERSION}_arm64.tar.gz" + - name: Create Release uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0 if: ${{ steps.release_channel.outputs.channel == 'latest' && github.event.inputs.release_type != 'Dry Run' }} 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 faf119cce2b..33b4df24d7a 100644 --- a/.github/workflows/repository-management.yml +++ b/.github/workflows/repository-management.yml @@ -29,7 +29,7 @@ on: default: false target_ref: default: "main" - description: "Branch/Tag to target for cut" + description: "Branch/Tag to target for cut (ignored if not cutting rc)" required: true type: string version_number_override: @@ -71,6 +71,7 @@ jobs: version_web: ${{ steps.set-final-version-output.outputs.version_web }} permissions: id-token: write + contents: write steps: - name: Validate version input format @@ -93,27 +94,47 @@ jobs: keyvault: gh-org-bitwarden secrets: "BW-GHAPP-ID,BW-GHAPP-KEY" + - name: Retrieve GPG secrets + id: retrieve-gpg-secrets + uses: bitwarden/gh-actions/get-keyvault-secrets@main + with: + keyvault: "bitwarden-ci" + secrets: "github-gpg-private-key, github-gpg-private-key-passphrase" + - name: Log out from Azure uses: bitwarden/gh-actions/azure-logout@main - name: Generate GH App token - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 id: app-token with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} + permission-contents: write # for committing and pushing to main (bypasses rulesets) - name: Check out branch - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: - ref: main + ref: ${{ github.ref }} token: ${{ steps.app-token.outputs.token }} persist-credentials: true - name: Configure Git run: | - git config --local user.email "actions@github.com" - git config --local user.name "Github Actions" + git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com" + git config --local user.name "bitwarden-devops-bot" + + - name: Setup GPG signing + env: + GPG_PRIVATE_KEY: ${{ steps.retrieve-gpg-secrets.outputs.github-gpg-private-key }} + GPG_PASSPHRASE: ${{ steps.retrieve-gpg-secrets.outputs.github-gpg-private-key-passphrase }} + run: | + echo "$GPG_PRIVATE_KEY" | gpg --import --batch + GPG_KEY_ID=$(gpg --list-secret-keys --keyid-format=long | grep -o "rsa[0-9]\+/[A-F0-9]\+" | head -n1 | cut -d'/' -f2) + git config --local user.signingkey "$GPG_KEY_ID" + git config --local commit.gpgsign true + export GPG_TTY=$(tty) + echo "test" | gpg --clearsign --pinentry-mode=loopback --passphrase "$GPG_PASSPHRASE" > /dev/null 2>&1 ######################## # VERSION BUMP SECTION # @@ -425,13 +446,15 @@ jobs: echo "No changes to commit!"; fi - - name: Commit files + - name: Commit version bumps with GPG signature if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }} - run: git commit -m "Bumped client version(s)" -a + run: | + git commit -m "Bumped client version(s)" -a - - name: Push changes + - name: Push changes to main if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }} - run: git push + run: | + git push cut_branch: name: Cut branch @@ -462,14 +485,15 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Generate GH App token - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 id: app-token with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} + 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..765e900af5c 100644 --- a/.github/workflows/sdk-breaking-change-check.yml +++ b/.github/workflows/sdk-breaking-change-check.yml @@ -53,7 +53,7 @@ jobs: secrets: "BW-GHAPP-ID,BW-GHAPP-KEY" - name: Generate GH App token - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 id: app-token with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} @@ -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/stale-bot.yml b/.github/workflows/stale-bot.yml index 246e0d48c5d..2a27a9b3101 100644 --- a/.github/workflows/stale-bot.yml +++ b/.github/workflows/stale-bot.yml @@ -15,7 +15,7 @@ jobs: pull-requests: write steps: - name: 'Run stale action' - uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0 + uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1 with: stale-issue-label: 'needs-reply' stale-pr-label: 'needs-changes' diff --git a/.github/workflows/test-browser-interactions.yml b/.github/workflows/test-browser-interactions.yml index dfc0f28b9c6..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 @@ -75,7 +75,7 @@ jobs: - name: Trigger test-all workflow in browser-interactions-testing if: steps.changed-files.outputs.monitored == 'true' - uses: peter-evans/repository-dispatch@5fc4efd1a4797ddb68ffd0714a238564e4cc0e6f # v4.0.0 + uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1 with: token: ${{ steps.app-token.outputs.token }} repository: "bitwarden/browser-interactions-testing" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f53bfc39d36..eedf991d826 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 @@ -62,7 +62,7 @@ jobs: run: npm test -- --coverage --maxWorkers=3 - name: Report test results - uses: dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3 # v2.1.1 + uses: dorny/test-reporter@b082adf0eced0765477756c2a610396589b8c637 # v2.5.0 if: ${{ github.event.pull_request.head.repo.full_name == github.repository && !cancelled() }} with: name: Test Results @@ -71,10 +71,12 @@ jobs: fail-on-error: true - name: Upload results to codecov.io - uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1 + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 + with: + report_type: test_results - name: Upload test coverage - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: jest-coverage path: ./coverage/lcov.info @@ -103,7 +105,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 @@ -111,7 +113,7 @@ jobs: working-directory: ./apps/desktop/desktop_native run: cargo build - - name: Test Ubuntu + - name: Linux unit tests if: ${{ matrix.os=='ubuntu-22.04' }} working-directory: ./apps/desktop/desktop_native run: | @@ -120,35 +122,39 @@ jobs: mkdir -p ~/.local/share/keyrings eval "$(printf '\n' | gnome-keyring-daemon --unlock)" eval "$(printf '\n' | /usr/bin/gnome-keyring-daemon --start)" - cargo test -- --test-threads=1 + cargo test --lib -- --test-threads=1 - - name: Test macOS + - name: MacOS unit tests if: ${{ matrix.os=='macos-14' }} working-directory: ./apps/desktop/desktop_native - run: cargo test -- --test-threads=1 + run: cargo test --lib -- --test-threads=1 - - name: Test Windows + - name: Windows unit tests if: ${{ matrix.os=='windows-2022'}} working-directory: ./apps/desktop/desktop_native - run: cargo test --workspace --exclude=desktop_napi -- --test-threads=1 + run: cargo test --lib --workspace --exclude=desktop_napi -- --test-threads=1 + + - name: Doc tests + working-directory: ./apps/desktop/desktop_native + run: cargo test --doc rust-coverage: name: Rust Coverage 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 - name: Install rust - uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b # stable + uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # stable with: toolchain: stable components: llvm-tools - name: Cache cargo registry - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: "apps/desktop/desktop_native -> target" @@ -160,7 +166,7 @@ jobs: run: cargo llvm-cov --all-features --lcov --output-path lcov.info --workspace --no-cfg-coverage - name: Upload test coverage - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: rust-coverage path: ./apps/desktop/desktop_native/lcov.info @@ -173,24 +179,24 @@ 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 - name: Download jest coverage - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: jest-coverage path: ./ - name: Download rust coverage - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: rust-coverage path: ./apps/desktop/desktop_native - name: Upload coverage to codecov.io - uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 with: files: | ./lcov.info diff --git a/.github/workflows/version-auto-bump.yml b/.github/workflows/version-auto-bump.yml index 65f004149de..2aba68c45a9 100644 --- a/.github/workflows/version-auto-bump.yml +++ b/.github/workflows/version-auto-bump.yml @@ -31,7 +31,7 @@ jobs: uses: bitwarden/gh-actions/azure-logout@main - name: Generate GH App token - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 + uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 id: app-token with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} @@ -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/.storybook/main.ts b/.storybook/main.ts index d3811bb178d..353d959a6b9 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -12,6 +12,8 @@ const config: StorybookConfig = { "../libs/dirt/card/src/**/*.stories.@(js|jsx|ts|tsx)", "../libs/pricing/src/**/*.mdx", "../libs/pricing/src/**/*.stories.@(js|jsx|ts|tsx)", + "../libs/subscription/src/**/*.mdx", + "../libs/subscription/src/**/*.stories.@(js|jsx|ts|tsx)", "../libs/tools/send/send-ui/src/**/*.mdx", "../libs/tools/send/send-ui/src/**/*.stories.@(js|jsx|ts|tsx)", "../libs/vault/src/**/*.mdx", @@ -28,15 +30,13 @@ const config: StorybookConfig = { ], addons: [ getAbsolutePath("@storybook/addon-links"), - getAbsolutePath("@storybook/addon-essentials"), getAbsolutePath("@storybook/addon-a11y"), getAbsolutePath("@storybook/addon-designs"), - getAbsolutePath("@storybook/addon-interactions"), getAbsolutePath("@storybook/addon-themes"), { // @storybook/addon-docs is part of @storybook/addon-essentials - // eslint-disable-next-line storybook/no-uninstalled-addons - name: "@storybook/addon-docs", + + name: getAbsolutePath("@storybook/addon-docs"), options: { mdxPluginOptions: { mdxCompileOptions: { @@ -60,6 +60,10 @@ const config: StorybookConfig = { webpackFinal: async (config, { configType }) => { if (config.resolve) { config.resolve.plugins = [new TsconfigPathsPlugin()] as any; + config.resolve.fallback = { + ...config.resolve.fallback, + path: require.resolve("path-browserify"), + }; } return config; }, diff --git a/.storybook/manager.js b/.storybook/manager.js index e0ec04fd375..7ba923a0b2d 100644 --- a/.storybook/manager.js +++ b/.storybook/manager.js @@ -1,5 +1,5 @@ -import { addons } from "@storybook/manager-api"; -import { create } from "@storybook/theming/create"; +import { addons } from "storybook/manager-api"; +import { create } from "storybook/theming"; const lightTheme = create({ base: "light", diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 0b14f9d7444..266cf79d8b1 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -28,7 +28,7 @@ const preview: Preview = { ], parameters: { a11y: { - element: "#storybook-root", + context: "#storybook-root", }, controls: { matchers: { @@ -49,7 +49,7 @@ const preview: Preview = { }, }, backgrounds: { - disable: true, + disabled: true, }, }, tags: ["autodocs"], diff --git a/angular.json b/angular.json index 87ee7dc57b2..e1cc2aad82a 100644 --- a/angular.json +++ b/angular.json @@ -220,5 +220,31 @@ } } } + }, + "schematics": { + "@schematics/angular:component": { + "type": "component" + }, + "@schematics/angular:directive": { + "type": "directive" + }, + "@schematics/angular:service": { + "type": "service" + }, + "@schematics/angular:guard": { + "typeSeparator": "." + }, + "@schematics/angular:interceptor": { + "typeSeparator": "." + }, + "@schematics/angular:module": { + "typeSeparator": "." + }, + "@schematics/angular:pipe": { + "typeSeparator": "." + }, + "@schematics/angular:resolver": { + "typeSeparator": "." + } } } diff --git a/apps/browser/package.json b/apps/browser/package.json index cf2be624a22..745c9d6f3e3 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/browser", - "version": "2025.12.0", + "version": "2026.1.0", "scripts": { "build": "npm run build:chrome", "build:bit": "npm run build:bit:chrome", diff --git a/apps/browser/spec/mock-port.spec-util.ts b/apps/browser/spec/mock-port.spec-util.ts index 39239ba8817..427a0b3aa9c 100644 --- a/apps/browser/spec/mock-port.spec-util.ts +++ b/apps/browser/spec/mock-port.spec-util.ts @@ -12,7 +12,10 @@ export function mockPorts() { (chrome.runtime.connect as jest.Mock).mockImplementation((portInfo) => { const port = mockDeep(); port.name = portInfo.name; - port.sender = { url: chrome.runtime.getURL("") }; + port.sender = { + url: chrome.runtime.getURL(""), + origin: chrome.runtime.getURL(""), + }; // convert to internal port delete (port as any).tab; diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 03d5eb0a9f6..937672bfd60 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… ؅؁ØĒاح Ø§Ų„Ų…ØąŲˆØą" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Ø§ØŗØĒØŽØ¯Ø§Ų… ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø§Ų„ØŖØ­Ø§Ø¯ŲŠ" }, @@ -436,8 +439,8 @@ "sync": { "message": "Ø§Ų„Ų…Ø˛Ø§Ų…Ų†ØŠ" }, - "syncVaultNow": { - "message": "Ų…Ø˛Ø§Ų…Ų†ØŠ Ø§Ų„ØŽØ˛Ø§Ų†ØŠ Ø§Ų„ØĸŲ†" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "ØĸØŽØą Ų…Ø˛Ø§Ų…Ų†ØŠ:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "ØĒØˇØ¨ŲŠŲ‚ ŲˆŲŠØ¨ Bitwarden" }, - "importItems": { - "message": "Ø§ØŗØĒŲŠØąØ§Ø¯ Ø§Ų„ØšŲ†Ø§ØĩØą" - }, "select": { "message": "ØĒØ­Ø¯ŲŠØ¯" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "ØĒØšØ¯ŲŠŲ„" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Ø§Ų„ØĒØĩØ¯ŲŠØą Ų…Ų†" }, - "exportVault": { - "message": "ØĒØĩØ¯ŲŠØą Ø§Ų„ØŽØ˛Ø§Ų†ØŠ" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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": "Øĩ؊ØēØŠ Ø§Ų„Ų…Ų„Ų" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Ų…ØšØąŲØŠ Ø§Ų„Ų…Ø˛ŲŠØ¯" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "؅؁ØĒاح Ø§Ų„Ų…ØĩØ§Ø¯Ų‚ØŠ (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "ØĒŲ… Ø­ŲØ¸ Ø§Ų„Ų…ØąŲŲ‚Ø§ØĒ" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Ø§Ų„Ų…Ų„Ų" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "حدد Ų…Ų„ŲŲ‹Ø§" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Ø§Ų„Ø­ØŦŲ… Ø§Ų„ØŖŲ‚ØĩŲ‰ ؄؄؅؄؁ Ų‡Ųˆ 500 Ų…ŲŠØŦØ§Ø¨Ø§ŲŠØĒ." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 ØŦ؊ØēØ§Ø¨Ø§ŲŠØĒ ŲˆØ­Ø¯ØŠ ØĒØŽØ˛ŲŠŲ† Ų…Ø´ŲØąØŠ Ų„Ų…ØąŲŲ‚Ø§ØĒ Ø§Ų„Ų…Ų„ŲØ§ØĒ." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Ø§Ų„ŲˆØĩŲˆŲ„ Ø§Ų„ØˇØ§ØąØĻ." }, "premiumSignUpTwoStepOptions": { "message": "ØŽŲŠØ§ØąØ§ØĒ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨ØŽØˇŲˆØĒŲŠŲ† Ø§Ų„Ų…Ų…Ų„ŲˆŲƒØŠ Ų„ØŦŲ‡Ø§ØĒ Ø§ØŽØąŲ‰ Ų…ØĢŲ„ YubiKey ؈ Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Ų†Ø¸Ø§ŲØŠ ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØąØŒ Øĩح؊ Ø§Ų„Ø­ØŗØ§Ø¨ØŒ ؈ØĒŲ‚Ø§ØąŲŠØą ØĒØŗØąŲŠØ¨Ø§ØĒ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ Ų„Ų„Ø­ŲØ§Ø¸ ØšŲ„Ų‰ ØŗŲ„Ø§Ų…ØŠ ØŽØ˛Ø§Ų†ØĒ؃." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "ØĒŲ… Ø­Ø°Ų Ø§Ų„ØšŲ†ØĩØą Ø¨Ø´ŲƒŲ„ داØĻŲ…" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Ø§ØŗØĒؚاد؊ Ø§Ų„ØšŲ†ØĩØą" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Ų„Ų… ؊ØĒŲ… Ø§Ų„ØšØĢŲˆØą ØšŲ„Ų‰ Ų…ØšØąŲ ŲØąŲŠØ¯." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "ØŽØˇØŖ" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "ØŽØˇØŖ ؁؃ Ø§Ų„ØĒØ´ŲŲŠØą" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "ØĒØŦØ§Ų‡Ų„" }, - "importData": { - "message": "Ø§ØŗØĒŲŠØąØ§Ø¯ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "ØŽØˇØŖ ؁؊ Ø§Ų„Ø§ØŗØĒŲŠØąØ§Ø¯" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index 7dbd1ba3e7c..58c9b5a0cb8 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Keçid açarÄą ilə giriş et" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Vahid daxil olma Ãŧsulunu istifadə et" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sinxr" }, - "syncVaultNow": { - "message": "Seyfi indi sinxronlaşdÄąr" + "syncNow": { + "message": "İndi sinxr." }, "lastSync": { "message": "Son sinxr:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden veb tətbiqi" }, - "importItems": { - "message": "Elementləri daxilə kÃļçÃŧr" - }, "select": { "message": "Seçin" }, @@ -576,17 +576,29 @@ "itemWasSentToArchive": { "message": "Element arxivə gÃļndərildi" }, + "itemWasUnarchived": { + "message": "Element arxivdən Ã§ÄąxarÄąldÄą" + }, "itemUnarchived": { "message": "Element arxivdən Ã§ÄąxarÄąldÄą" }, "archiveItem": { "message": "Elementi arxivlə" }, - "archiveItemConfirmDesc": { - "message": "Arxivlənmiş elementlər Ãŧmumi axtarÄąÅŸ nəticələrindən və avto-doldurma təkliflərindən xaric ediləcək. Bu elementi arxivləmək istədiyinizə əminsiniz?" + "archiveItemDialogContent": { + "message": "Arxivləndikdən sonra, bu element axtarÄąÅŸ nəticələrindən və avto-doldurma təkliflərindən xaric ediləcək." + }, + "archived": { + "message": "Arxivləndi" + }, + "unarchiveAndSave": { + "message": "Arxivdən Ã§Äąxart və saxla" }, "upgradeToUseArchive": { - "message": "A premium membership is required to use Archive." + "message": "Arxivi istifadə etmək ÃŧçÃŧn premium ÃŧzvlÃŧk tələb olunur." + }, + "itemRestored": { + "message": "Element bərpa edildi" }, "edit": { "message": "DÃŧzəliş et" @@ -598,7 +610,7 @@ "message": "HamÄąsÄąna bax" }, "showAll": { - "message": "Show all" + "message": "HamÄąsÄąnÄą gÃļstər" }, "viewLess": { "message": "Daha azÄąna bax" @@ -1254,7 +1266,7 @@ "description": "Detailed error message shown when saving login details fails." }, "changePasswordWarning": { - "message": "Parolunuzu dəyişdirdikdən sonra yeni parolunuzla giriş etməli olacaqsÄąnÄąz. Digər cihazlardakÄą aktiv sessiyalar bir saat ərzində Ã§ÄąxÄąÅŸ sonlandÄąrÄąlacaq." + "message": "Parolunuzu dəyişdirdikdən sonra yeni parolunuzla giriş etməli olacaqsÄąnÄąz. Digər cihazlardakÄą aktiv sessiyalar bir saat ərzində sonlandÄąrÄąlacaq." }, "accountRecoveryUpdateMasterPasswordSubtitle": { "message": "HesabÄąn geri qaytarÄąlmasÄą prosesini tamamlamaq ÃŧçÃŧn ana parolunuzu dəyişdirin." @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Buradan xaricə kÃļçÃŧr" }, - "exportVault": { - "message": "Seyfi xaricə kÃļçÃŧr" + "exportVerb": { + "message": "Xaricə kÃļçÃŧr", + "description": "The verb form of the word Export" + }, + "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Äą" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Daha ətraflÄą" }, + "migrationsFailed": { + "message": "Şifrələmə ayarlarÄąnÄą gÃŧncəlləyərkən bir xəta baş verdi." + }, + "updateEncryptionSettingsTitle": { + "message": "Şifrələmə ayarlarÄąnÄązÄą gÃŧncəlləyin" + }, + "updateEncryptionSettingsDesc": { + "message": "TÃļvsiyə edilən yeni şifrələmə ayarlarÄą, hesabÄąnÄązÄąn təhlÃŧkəsizliyini artÄąracaq. İndi gÃŧncəlləmək ÃŧçÃŧn ana parolunuzu daxil edin." + }, + "confirmIdentityToContinue": { + "message": "Davam etmək ÃŧçÃŧn kimliyinizi təsdiqləyin" + }, + "enterYourMasterPassword": { + "message": "Ana parolunuzu daxil edin" + }, + "updateSettings": { + "message": "AyarlarÄą gÃŧncəllə" + }, + "later": { + "message": "Sonra" + }, "authenticatorKeyTotp": { "message": "Kimlik doğrulayÄącÄą açarÄą (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Qoşma saxlanÄąldÄą." }, + "fixEncryption": { + "message": "Şifrələməni dÃŧzəlt" + }, + "fixEncryptionTooltip": { + "message": "Bu fayl, kÃļhnə bir şifrələmə Ãŧsulunu istifadə edir." + }, + "attachmentUpdated": { + "message": "Qoşma gÃŧncəllənib" + }, "file": { "message": "Fayl" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Bir fayl seçin" }, + "itemsTransferred": { + "message": "Elementlər kÃļçÃŧrÃŧldÃŧ" + }, "maxFileSize": { "message": "Maksimal fayl həcmi 500 MB-dÄąr" }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "Fayl qoşmalarÄą ÃŧçÃŧn 1 GB şifrələnmiş anbar sahəsi" }, + "premiumSignUpStorageV2": { + "message": "Fayl qoşmalarÄą ÃŧçÃŧn $SIZE$ şifrələnmiş anbar sahəsi.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "FÃļvqəladə hal erişimi." }, "premiumSignUpTwoStepOptions": { "message": "YubiKey və Duo kimi mÃŧlkiyyətçi iki addÄąmlÄą giriş seçimləri." }, + "premiumSubscriptionEnded": { + "message": "Premium abunəliyiniz bitdi" + }, + "archivePremiumRestart": { + "message": "Arxivinizə təkrar erişmək ÃŧçÃŧn Premium abunəliyinizi yenidən başladÄąn. Təkrar başlatmazdan əvvəl arxivlənmiş elementin detallarÄąna dÃŧzəliş etsəniz, həmin element seyfinizə daÅŸÄąnacaq." + }, + "restartPremium": { + "message": "\"Premium\"u yenidən başlat" + }, "ppremiumSignUpReports": { "message": "Seyfinizi gÃŧvəndə saxlamaq ÃŧçÃŧn parol gigiyenasÄą, hesab sağlamlığı və veri pozuntusu hesabatlarÄą." }, @@ -1874,7 +1950,7 @@ "message": "Son istifadə ili" }, "monthly": { - "message": "month" + "message": "ay" }, "expiration": { "message": "Bitmə vaxtÄą" @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Element birdəfəlik silindi" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Elementi bərpa et" }, @@ -2446,7 +2525,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" @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Unikal identifikator tapÄąlmadÄą." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "AşağıdakÄą təşkilatlarÄąn Ãŧzvləri ÃŧçÃŧn artÄąq ana parol tələb olunmur. LÃŧtfən aşağıdakÄą domeni təşkilatÄąnÄązÄąn inzibatÃ§ÄąsÄą ilə təsdiqləyin." - }, "organizationName": { "message": "Təşkilat adÄą" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Xəta" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Şifrə açma xətasÄą" }, @@ -4075,7 +4157,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" @@ -4176,10 +4258,6 @@ "ignore": { "message": "Yox say" }, - "importData": { - "message": "Veriləri daxilə kÃļçÃŧr", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Daxilə kÃļçÃŧrmə xətasÄą" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "Daha çox seçim" + }, "moreOptionsTitle": { "message": "Daha çox seçim - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Konsolu" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Avtomatik istifadəçi təsdiqi" + }, + "automaticUserConfirmationHint": { + "message": "Bu cihazÄąn kilidi aÃ§Äąq olduqda gÃļzləyən istifadəçiləri avtomatik təsdiqlə" + }, + "autoConfirmOnboardingCallout": { + "message": "Avtomatik istifadəçi təsdiqi ilə vaxta qənaət edin" + }, + "autoConfirmWarning": { + "message": "Bu, təşkilatÄąnÄązÄąn veri təhlÃŧkəsizliyinə təsir edə bilər. " + }, + "autoConfirmWarningLink": { + "message": "Risklər barədə Ãļyrən" + }, + "autoConfirmSetup": { + "message": "Yeni istifadəçiləri avtomatik təsdiqlə" + }, + "autoConfirmSetupDesc": { + "message": "Bu cihazÄąn kilidi aÃ§Äąq olduqda yeni istifadəçilər avtomatik təsdiqlənəcək." + }, + "autoConfirmSetupHint": { + "message": "Potensial təhlÃŧkəsizlik riskləri nələrdir?" + }, + "autoConfirmEnabled": { + "message": "Avtomatik təsdiq işə salÄąndÄą" + }, + "availableNow": { + "message": "İndi mmÃļvcuddur" + }, "accountSecurity": { "message": "Hesab gÃŧvənliyi" }, + "phishingBlocker": { + "message": "Fişinq əngəlləyici" + }, + "enablePhishingDetection": { + "message": "Fişinq aşkarlama" + }, + "enablePhishingDetectionDesc": { + "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" }, @@ -4912,7 +5035,7 @@ "message": "Premium" }, "unlockFeaturesWithPremium": { - "message": "Unlock reporting, emergency access, and more security features with Premium." + "message": "Premium ilə şikayət gÃļndərmə, fÃļvqəladə hal erişimi və daha çox təhlÃŧkəsizlik Ãļzəlliyinin kilidini aÃ§Äąn." }, "freeOrgsCannotUseAttachments": { "message": "Ödənişsiz təşkilatlar qoşmalarÄą istifadə edə bilməz" @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "$WEBSITE$ ilə uyuşma aşkarlamasÄąnÄą gizlət", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Uyuşma aşkarlamasÄąnÄą gÃļstər" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Uyuşma aşkarlamasÄąnÄą gizlət" }, "autoFillOnPageLoad": { "message": "Səhifə yÃŧklənəndə avto-doldurulsun?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Ekstra enli" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "Daxil etdiyiniz parol yanlÄąÅŸdÄąr." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Bu giriş risk altÄąndadÄąr və bir veb sayt əskikdir. Daha gÃŧclÃŧ təhlÃŧkəsizlik ÃŧçÃŧn bir veb sayt əlavə edin və parolu dəyişdirin." }, + "vulnerablePassword": { + "message": "Zəifliyi olan parol." + }, + "changeNow": { + "message": "İndi dəyişdir" + }, "missingWebsite": { "message": "Əskik veb sayt" }, @@ -5818,17 +5947,17 @@ "andMoreFeatures": { "message": "Və daha çoxu!" }, - "planDescPremium": { - "message": "Tam onlayn təhlÃŧkəsizlik" + "advancedOnlineSecurity": { + "message": "QabaqcÄąl onlayn təhlÃŧkəsizlik" }, "upgradeToPremium": { "message": "\"Premium\"a yÃŧksəlt" }, "unlockAdvancedSecurity": { - "message": "Unlock advanced security features" + "message": "QabaqcÄąl təhlÃŧkəsizlik Ãļzəlliklərinin kilidini aç" }, "unlockAdvancedSecurityDesc": { - "message": "A Premium subscription gives you more tools to stay secure and in control" + "message": "Premium abunəlik, gÃŧvəndə qalmağınÄąz və nəzarəti əlinizdə saxlamağınÄąz ÃŧçÃŧn sizə daha çox alət verir" }, "explorePremium": { "message": "Premium-u kəşf et" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Kart nÃļmrəsi" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "TəşkilatÄąnÄąz, artÄąq Bitwarden-ə giriş etmək ÃŧçÃŧn ana parol istifadə etmir. Davam etmək ÃŧçÃŧn təşkilatÄą və domeni doğrulayÄąn." + }, + "continueWithLogIn": { + "message": "Giriş etməyə davam" + }, + "doNotContinue": { + "message": "Davam etmə" + }, + "domain": { + "message": "Domen" + }, + "keyConnectorDomainTooltip": { + "message": "Bu domen, hesabÄąnÄązÄąn şifrələmə açarlarÄąnÄą saxlayacaq, ona gÃļrə də, bu domenə gÃŧvəndiyinizə əmin olun. Əmin deyilsinizsə, adminizlə əlaqə saxlayÄąn." + }, + "verifyYourOrganization": { + "message": "Giriş etmək ÃŧçÃŧn təşkilatÄąnÄązÄą doğrulayÄąn" + }, + "organizationVerified": { + "message": "Təşkilat doğrulandÄą" + }, + "domainVerified": { + "message": "Domen doğrulandÄą" + }, + "leaveOrganizationContent": { + "message": "TəşkilatÄąnÄązÄą doğrulamasanÄąz, təşkilata erişiminiz ləğv ediləcək." + }, + "leaveNow": { + "message": "İndi tərk et" + }, + "verifyYourDomainToLogin": { + "message": "Giriş etmək ÃŧçÃŧn domeninizi doğrulayÄąn" + }, + "verifyYourDomainDescription": { + "message": "Giriş prosesini davam etdirmək ÃŧçÃŧn bu domeni doğrulayÄąn." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Giriş prosesini davam etdirmək ÃŧçÃŧn bu təşkilatÄą və domeni doğrulayÄąn." + }, "sessionTimeoutSettingsAction": { "message": "Vaxt bitmə əməliyyatÄą" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Bu ayar, təşkilatÄąnÄąz tərəfindən idarə olunur." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "TəşkilatÄąnÄąz, maksimum seyf bitmə vaxtÄąnÄą $HOURS$ saat $MINUTES$ dəqiqə olaraq ayarladÄą.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "TəşkilatÄąnÄąz, seansÄąn ilkin bitmə vaxtÄąnÄą dərhal olaraq təyin etdi." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "TəşkilatÄąnÄąz, seansÄąn ilkin bitmə vaxtÄąnÄą Sistem kilidi aÃ§Äąlanda olaraq təyin etdi." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "TəşkilatÄąnÄąz, seansÄąn ilkin bitmə vaxtÄąnÄą Brauzer yenidən başladÄąlanda olaraq təyin etdi." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maksimum bitmə vaxtÄą $HOURS$ saat $MINUTES$ dəqiqə dəyərini aşa bilməz", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Brauzer yenidən başladÄąlanda" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Vaxt bitmə əməliyyatÄąnÄązÄą dəyişdirmək ÃŧçÃŧn bir kilid açma Ãŧsulu qurun." + }, + "upgrade": { + "message": "YÃŧksəlt" + }, + "leaveConfirmationDialogTitle": { + "message": "Tərk etmək istədiyinizə əminsiniz?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Rədd cavabÄą versəniz, fərdi elementləriniz hesabÄąnÄązda qalacaq, paylaÅŸÄąlan elementlərə və təşkilat Ãļzəlliklərinə erişimi itirəcəksiniz." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Erişimi təkrar qazanmaq ÃŧçÃŧn admininizlə əlaqə saxlayÄąn." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Tərk et: $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Seyfimi necə idarə edim?" + }, + "transferItemsToOrganizationTitle": { + "message": "Elementləri bura kÃļçÃŧr: $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$, təhlÃŧkəsizlik və riayətlilik ÃŧçÃŧn bÃŧtÃŧn elementlərin təşkilata aid olmasÄąnÄą tələb edir. Elementlərinizin sahibliyini transfer etmək ÃŧçÃŧn qəbul edin.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Transferi qəbul et" + }, + "declineAndLeave": { + "message": "Rədd et və tərk et" + }, + "whyAmISeeingThis": { + "message": "Bunu niyə gÃļrÃŧrəm?" + }, + "resizeSideNavigation": { + "message": "Yan naviqasiyanÄą yeni. ÃļlçÃŧləndir" } } diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 89651f0038e..68277cfeb00 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "ĐŖĐ˛Đ°ĐšŅŅ†Ņ– С ĐēĐģŅŽŅ‡ĐžĐŧ Đ´ĐžŅŅ‚ŅƒĐŋ҃" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Đ’Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°Ņ†ŅŒ адĐŊĐ°Ņ€Đ°ĐˇĐžĐ˛Ņ‹ ŅžĐ˛Đ°Ņ…ĐžĐ´" }, @@ -436,8 +439,8 @@ "sync": { "message": "ĐĄŅ–ĐŊŅ…Ņ€Đ°ĐŊŅ–ĐˇĐ°Đ˛Đ°Ņ†ŅŒ" }, - "syncVaultNow": { - "message": "ĐĄŅ–ĐŊŅ…Ņ€Đ°ĐŊŅ–ĐˇĐ°Đ˛Đ°Ņ†ŅŒ ŅŅ…ĐžĐ˛Ņ–ŅˆŅ‡Đ° ĐˇĐ°Ņ€Đ°Đˇ" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "АĐŋĐžŅˆĐŊŅŅ ҁҖĐŊŅ…Ņ€Đ°ĐŊŅ–ĐˇĐ°Ņ†Ņ‹Ņ:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Đ’ŅĐą-ĐŋŅ€Đ°ĐŗŅ€Đ°Đŧа Bitwarden" }, - "importItems": { - "message": "ІĐŧĐŋĐ°Ņ€Ņ‚Đ°Đ˛Đ°ĐŊĐŊĐĩ ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°Ņž" - }, "select": { "message": "Đ’Ņ‹ĐąŅ€Đ°Ņ†ŅŒ" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Đ ŅĐ´Đ°ĐŗĐ°Đ˛Đ°Ņ†ŅŒ" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Đ­ĐēҁĐŋĐ°Ņ€Ņ‚Đ°Đ˛Đ°ĐŊĐŊĐĩ С" }, - "exportVault": { - "message": "Đ­ĐēҁĐŋĐ°Ņ€Ņ‚Đ°Đ˛Đ°Ņ†ŅŒ ŅŅ…ĐžĐ˛Ņ–ŅˆŅ‡Đ°" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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": "Đ¤Đ°Ņ€ĐŧĐ°Ņ‚ Ņ„Đ°ĐšĐģа" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "ДавĐĩĐ´Đ°Ņ†Ņ†Đ° йОĐģҌ҈" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "КĐģŅŽŅ‡ Đ°ŅžŅ‚ŅĐŊ҂ҋ҄ҖĐēĐ°Ņ†Ņ‹Ņ– (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "ДаĐģŅƒŅ‡ŅĐŊĐŊĐĩ ĐˇĐ°Ņ…Đ°Đ˛Đ°ĐŊа." }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "ФаКĐģ" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Đ’Ņ‹ĐąĐĩҀҋ҆Đĩ Ņ„Đ°ĐšĐģ." }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "МаĐēҁҖĐŧаĐģҌĐŊŅ‹ ĐŋаĐŧĐĩŅ€ Ņ„Đ°ĐšĐģа 500 МБ." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 ГБ ĐˇĐ°ŅˆŅ‹Ņ„Ņ€Đ°Đ˛Đ°ĐŊĐ°ĐŗĐ° ŅŅ…ĐžĐ˛Ņ–ŅˆŅ‡Đ° Đ´ĐģŅ даĐģŅƒŅ‡Đ°ĐŊҋ҅ Ņ„Đ°ĐšĐģĐ°Ņž." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Đ­ĐēŅŅ‚Ņ€Đ°ĐŊĐŊŅ‹ Đ´ĐžŅŅ‚ŅƒĐŋ." }, "premiumSignUpTwoStepOptions": { "message": "ĐŸŅ€Đ°ĐŋҀҋĐĩŅ‚Đ°Ņ€ĐŊŅ‹Ņ Đ˛Đ°Ņ€Ņ‹ŅĐŊ҂ҋ Đ´Đ˛ŅƒŅ…ŅŅ‚Đ°ĐŋĐŊĐ°ĐŗĐ° ŅžĐ˛Đ°Ņ…ĐžĐ´Ņƒ, Ņ‚Đ°ĐēŅ–Ņ ŅĐē YubiKey Ņ– Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Đ“Ņ–ĐŗŅ–ĐĩĐŊа ĐŋĐ°Ņ€ĐžĐģŅŅž, ĐˇĐ´Đ°Ņ€ĐžŅžĐĩ ŅžĐģŅ–ĐēĐžĐ˛Đ°ĐŗĐ° СаĐŋŅ–ŅŅƒ Ņ– ҁĐŋŅ€Đ°Đ˛Đ°ĐˇĐ´Đ°Ņ‡Ņ‹ ай ŅƒŅ†Đĩ҇ĐēĐ°Ņ… даĐŊҋ҅ Đ´ĐģŅ СайĐĩҁĐŋŅŅ‡ŅĐŊĐŊŅ ĐąŅŅĐŋĐĩĐēŅ– Đ˛Đ°ŅˆĐ°ĐŗĐ° ŅŅ…ĐžĐ˛Ņ–ŅˆŅ‡Đ°." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Đ­ĐģĐĩĐŧĐĩĐŊŅ‚ Đ˛Ņ‹Đ´Đ°ĐģĐĩĐŊŅ‹ ĐŊĐ°ĐˇĐ°ŅžŅŅ‘Đ´Ņ‹" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "АдĐŊĐ°Đ˛Ņ–Ņ†ŅŒ ŅĐģĐĩĐŧĐĩĐŊŅ‚" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "НĐĩ СĐŊОКдСĐĩĐŊŅ‹ ŅžĐŊŅ–ĐēаĐģҌĐŊŅ‹ Ņ–Đ´ŅĐŊ҂ҋ҄ҖĐēĐ°Ņ‚Đ°Ņ€." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "ПаĐŧŅ‹ĐģĐēа" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "IĐŗĐŊĐ°Ņ€Đ°Đ˛Đ°Ņ†ŅŒ" }, - "importData": { - "message": "ІĐŧĐŋĐ°Ņ€Ņ‚Đ°Đ˛Đ°ĐŊĐŊĐĩ даĐŊҋ҅", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "ПаĐŧŅ‹ĐģĐēа Ņ–ĐŧĐŋĐ°Ņ€Ņ‚Đ°Đ˛Đ°ĐŊĐŊŅ" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "КаĐŊŅĐžĐģҌ адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚Đ°Ņ€Đ°" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Đ‘ŅŅĐŋĐĩĐēĐĩ аĐēĐ°ŅžĐŊŅ‚Đ°" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "АĐŋĐ°Đ˛ŅŅˆŅ‡ŅĐŊĐŊŅ–" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index b40f4b91cb4..05ee1fc5765 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "ВĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ ҁҊҁ ҁĐĩĐēŅ€ĐĩŅ‚ĐĩĐŊ ĐēĐģŅŽŅ‡" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "ИСĐŋĐžĐģСваĐŊĐĩ ĐŊа ĐĩĐ´ĐŊĐžĐēŅ€Đ°Ņ‚ĐŊа идĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Ņ" }, @@ -436,7 +439,7 @@ "sync": { "message": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩ" }, - "syncVaultNow": { + "syncNow": { "message": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩ ҁĐĩĐŗĐ°" }, "lastSync": { @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "ĐŖĐĩĐą ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ" }, - "importItems": { - "message": "ВĐŊĐ°ŅŅĐŊĐĩ ĐŊа ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸" - }, "select": { "message": "Đ˜ĐˇĐąĐžŅ€" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "ЕĐģĐĩĐŧĐĩĐŊŅ‚ŅŠŅ‚ ĐąĐĩ҈Đĩ ĐŋŅ€ĐĩĐŧĐĩҁ҂ĐĩĐŊ в Đ°Ņ€Ņ…Đ¸Đ˛Đ°" }, + "itemWasUnarchived": { + "message": "ЕĐģĐĩĐŧĐĩĐŊŅ‚ŅŠŅ‚ ĐąĐĩ҈Đĩ иСвадĐĩĐŊ ĐžŅ‚ Đ°Ņ€Ņ…Đ¸Đ˛Đ°" + }, "itemUnarchived": { "message": "ЕĐģĐĩĐŧĐĩĐŊŅ‚ŅŠŅ‚ ĐąĐĩ҈Đĩ иСвадĐĩĐŊ ĐžŅ‚ Đ°Ņ€Ņ…Đ¸Đ˛Đ°" }, "archiveItem": { "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°" }, - "archiveItemConfirmDesc": { - "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐ¸Ņ‚Đĩ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ŅĐ° иСĐēĐģŅŽŅ‡ĐĩĐŊи ĐžŅ‚ ĐžĐąŅ‰Đ¸Ņ‚Đĩ Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸ ĐŋŅ€Đ¸ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ и ĐžŅ‚ ĐŋŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊĐ¸ŅŅ‚Đ° Са Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋĐžĐŋҊĐģваĐŊĐĩ. ĐĐ°Đ¸ŅŅ‚Đ¸ĐŊа Đģи Đ¸ŅĐēĐ°Ņ‚Đĩ да Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°Ņ‚Đĩ Ņ‚ĐžĐˇĐ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚?" + "archiveItemDialogContent": { + "message": "ĐĄĐģĐĩĐ´ ĐēĐ°Ņ‚Đž ĐąŅŠĐ´Đĩ Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊ, Ņ‚ĐžĐˇĐ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ ĐŊŅĐŧа да ҁĐĩ ĐŋĐžĐēаСва в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐŋŅ€Đ¸ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ, ĐŊĐ¸Ņ‚Đž в ĐŋŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊĐ¸ŅŅ‚Đ° Са Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋĐžĐŋҊĐģваĐŊĐĩ." + }, + "archived": { + "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐž" + }, + "unarchiveAndSave": { + "message": "Đ Đ°ĐˇĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ и СаĐŋаСваĐŊĐĩ" }, "upgradeToUseArchive": { "message": "За да ҁĐĩ Đ˛ŅŠĐˇĐŋĐžĐģĐˇĐ˛Đ°Ņ‚Đĩ ĐžŅ‚ Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩŅ‚Đž, Ņ‚Ņ€ŅĐąĐ˛Đ° да ĐŋĐžĐģĐˇĐ˛Đ°Ņ‚Đĩ ĐŋĐģĐ°Ņ‚ĐĩĐŊ айОĐŊаĐŧĐĩĐŊŅ‚." }, + "itemRestored": { + "message": "ЗаĐŋĐ¸ŅŅŠŅ‚ ĐąĐĩ Đ˛ŅŠĐˇŅŅ‚Đ°ĐŊОвĐĩĐŊ" + }, "edit": { "message": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "ИСĐŊĐ°ŅŅĐŊĐĩ ĐžŅ‚" }, - "exportVault": { - "message": "ИСĐŊĐ°ŅŅĐŊĐĩ ĐŊа ҂ҀĐĩĐˇĐžŅ€Đ°" + "exportVerb": { + "message": "ИСĐŊĐ°ŅŅĐŊĐĩ", + "description": "The verb form of the word Export" + }, + "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": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚ ĐŊа Ņ„Đ°ĐšĐģа" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "ĐĐ°ŅƒŅ‡ĐĩŅ‚Đĩ ĐŋОвĐĩ҇Đĩ" }, + "migrationsFailed": { + "message": "Đ’ŅŠĐˇĐŊиĐēĐŊа ĐŗŅ€Đĩ҈Đēа ĐŋŅ€Đ¸ ОйĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩŅ‚Đž ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐĩ." + }, + "updateEncryptionSettingsTitle": { + "message": "ОбĐŊОвĐĩŅ‚Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ ŅĐ¸ Са ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐĩ" + }, + "updateEncryptionSettingsDesc": { + "message": "ĐĐžĐ˛Đ¸Ņ‚Đĩ ĐŋŅ€ĐĩĐŋĐžŅ€ŅŠŅ‡Đ°ĐŊи ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи Са ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐĩ ҉Đĩ ĐŋĐžĐ´ĐžĐąŅ€ŅŅ‚ ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚Ņ‚Đ° ĐŊа аĐēĐ°ŅƒĐŊŅ‚Đ° Ви. Đ’ŅŠĐ˛ĐĩĐ´ĐĩŅ‚Đĩ ĐŗĐģавĐŊĐ°Ņ‚Đ° ŅĐ¸ ĐŋĐ°Ņ€ĐžĐģа, Са да ĐŗĐ¸ ОйĐŊĐžĐ˛Đ¸Ņ‚Đĩ ҁĐĩĐŗĐ°." + }, + "confirmIdentityToContinue": { + "message": "ĐŸĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩŅ‚Đĩ ŅĐ°ĐŧĐžĐģĐ¸Ņ‡ĐŊĐžŅŅ‚Ņ‚Đ° ŅĐ¸, Са да ĐŋŅ€ĐžĐ´ŅŠĐģĐļĐ¸Ņ‚Đĩ" + }, + "enterYourMasterPassword": { + "message": "Đ’ŅŠĐ˛ĐĩĐ´ĐĩŅ‚Đĩ ĐŗĐģавĐŊĐ°Ņ‚Đ° ŅĐ¸ ĐŋĐ°Ņ€ĐžĐģа" + }, + "updateSettings": { + "message": "ОбĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ" + }, + "later": { + "message": "По-ĐēҊҁĐŊĐž" + }, "authenticatorKeyTotp": { "message": "ĐŖĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€Đ¸Ņ‚ĐĩĐģĐĩĐŊ ĐēĐģŅŽŅ‡ (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "ĐŸŅ€Đ¸ĐēĐ°Ņ‡ĐĩĐŊĐ¸ŅŅ‚ Ņ„Đ°ĐšĐģ Đĩ СаĐŋаСĐĩĐŊ." }, + "fixEncryption": { + "message": "ПоĐŋŅ€Đ°Đ˛ŅĐŊĐĩ ĐŊа ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐĩŅ‚Đž" + }, + "fixEncryptionTooltip": { + "message": "ĐĸОСи Ņ„Đ°ĐšĐģ иСĐŋĐžĐģСва ĐžŅŅ‚Đ°Ņ€ŅĐģ ĐŧĐĩŅ‚ĐžĐ´ ĐŊа ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐĩ." + }, + "attachmentUpdated": { + "message": "ĐŸŅ€Đ¸ĐēĐ°Ņ‡ĐĩĐŊĐ¸ŅŅ‚ Ņ„Đ°ĐšĐģ Đĩ аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊ" + }, "file": { "message": "ФаКĐģ" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "ИСйĐĩŅ€ĐĩŅ‚Đĩ Ņ„Đ°ĐšĐģ." }, + "itemsTransferred": { + "message": "ЕĐģĐĩĐŧĐĩĐŊŅ‚Đ¸Ņ‚Đĩ ŅĐ° ĐŋŅ€ĐĩŅ…Đ˛ŅŠŅ€ĐģĐĩĐŊи" + }, "maxFileSize": { "message": "ГоĐģĐĩĐŧиĐŊĐ°Ņ‚Đ° ĐŊа Ņ„Đ°ĐšĐģа Đĩ ĐŊаК-ĐŧĐŊĐžĐŗĐž 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB ĐŋŅ€ĐžŅŅ‚Ņ€Đ°ĐŊŅŅ‚Đ˛Đž Са Ņ„Đ°ĐšĐģОвĐĩ, ĐēĐžĐ¸Ņ‚Đž ҁĐĩ ŅˆĐ¸Ņ„Ņ€Đ¸Ņ€Đ°Ņ‚." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ ĐŋŅ€ĐžŅŅ‚Ņ€Đ°ĐŊŅŅ‚Đ˛Đž Са Ņ„Đ°ĐšĐģОвĐĩ, ĐēĐžĐ¸Ņ‚Đž ҁĐĩ ŅˆĐ¸Ņ„Ņ€Đ¸Ņ€Đ°Ņ‚.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "ĐĐ˛Đ°Ņ€Đ¸ĐĩĐŊ Đ´ĐžŅŅ‚ŅŠĐŋ" }, "premiumSignUpTwoStepOptions": { "message": "Đ§Đ°ŅŅ‚ĐŊĐž Đ´Đ˛ŅƒŅŅ‚ĐĩĐŋĐĩĐŊĐŊĐž ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ ҇ҀĐĩС YubiKey и Duo." }, + "premiumSubscriptionEnded": { + "message": "Đ’Đ°ŅˆĐ¸ŅŅ‚ айОĐŊаĐŧĐĩĐŊŅ‚ Са ĐŋĐģĐ°Ņ‚ĐĩĐŊĐ¸Ņ ĐŋĐģаĐŊ Đĩ ĐŋŅ€Đ¸ĐēĐģŅŽŅ‡Đ¸Đģ" + }, + "archivePremiumRestart": { + "message": "АĐēĐž Đ¸ŅĐēĐ°Ņ‚Đĩ ĐžŅ‚ĐŊОвО да ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚Đĩ Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž Đ°Ņ€Ņ…Đ¸Đ˛Đ° ŅĐ¸, Ņ‚Ņ€ŅĐąĐ˛Đ° да ĐŋОдĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐŋĐģĐ°Ņ‚ĐĩĐŊĐ¸Ņ ŅĐ¸ айОĐŊаĐŧĐĩĐŊŅ‚. АĐēĐž Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€Đ°Ņ‚Đĩ даĐŊĐŊĐ¸Ņ‚Đĩ Са Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ ĐŋŅ€Đĩди ĐŋОдĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩŅ‚Đž, Ņ‚ĐžĐš ҉Đĩ ĐąŅŠĐ´Đĩ Đ˛ŅŠŅ€ĐŊĐ°Ņ‚ в ҂ҀĐĩĐˇĐžŅ€Đ°." + }, + "restartPremium": { + "message": "ПодĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐŋĐģĐ°Ņ‚ĐĩĐŊĐ¸Ņ айОĐŊаĐŧĐĩĐŊŅ‚" + }, "ppremiumSignUpReports": { "message": "ĐŸŅ€ĐžĐ˛ĐĩŅ€Đēи в ҁĐŋĐ¸ŅŅŠŅ†Đ¸Ņ‚Đĩ ҁ ĐŋŅƒĐąĐģиĐēŅƒĐ˛Đ°ĐŊи ĐŋĐ°Ņ€ĐžĐģи, ĐŋŅ€ĐžĐ˛ĐĩŅ€Đēа ĐŊа Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸Đ¸Ņ‚Đĩ и Đ´ĐžĐēĐģади Са ĐŋŅ€ĐžĐąĐ¸Đ˛Đ¸Ņ‚Đĩ в ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚Ņ‚Đ°, ĐēĐžĐĩŅ‚Đž ҁĐŋĐžĐŧĐ°ĐŗĐ° ҂ҀĐĩĐˇĐžŅ€ŅŠŅ‚ ви да Đĩ Đ´ĐžĐŋҊĐģĐŊĐ¸Ņ‚ĐĩĐģĐŊĐž ĐˇĐ°Ņ‰Đ¸Ņ‚ĐĩĐŊ." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "ЗаĐŋĐ¸ŅŅŠŅ‚ Đĩ Đ¸ĐˇŅ‚Ņ€Đ¸Ņ‚ ĐžĐēĐžĐŊŅ‡Đ°Ņ‚ĐĩĐģĐŊĐž" }, + "archivedItemRestored": { + "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐ¸ŅŅ‚ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ Đĩ Đ˛ŅŠĐˇŅŅ‚Đ°ĐŊОвĐĩĐŊ" + }, "restoreItem": { "message": "Đ’ŅŠĐˇŅŅ‚Đ°ĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа СаĐŋĐ¸Ņ" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "ĐŅĐŧа ĐŊаĐŧĐĩŅ€ĐĩĐŊ ҃ĐŊиĐēаĐģĐĩĐŊ идĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžŅ€." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "За ҇ĐģĐĩĐŊОвĐĩŅ‚Đĩ ĐŊа ҁĐģĐĩĐ´ĐŊĐ°Ņ‚Đ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ вĐĩ҇Đĩ ĐŊĐĩ ҁĐĩ Đ¸ĐˇĐ¸ŅĐēва ĐŗĐģавĐŊа ĐŋĐ°Ņ€ĐžĐģа. ĐŸĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩŅ‚Đĩ Đ´ĐžĐŧĐĩĐšĐŊа ĐŋĐž-Đ´ĐžĐģ҃ ҁ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° ĐŊа ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° ŅĐ¸." - }, "organizationName": { "message": "ИĐŧĐĩ ĐŊа ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ°" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Đ“Ņ€Đĩ҈Đēа" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ Đ´ĐĩŅˆĐ¸Ņ„Ņ€Đ¸Ņ€Đ°ĐŊĐĩ" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "ĐŸŅ€ĐĩĐŊĐĩĐąŅ€ĐĩĐŗĐ˛Đ°ĐŊĐĩ" }, - "importData": { - "message": "ВĐŊĐ°ŅŅĐŊĐĩ ĐŊа даĐŊĐŊи", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ вĐŊĐ°ŅŅĐŊĐĩŅ‚Đž" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "ĐžŅ‰Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи" + }, "moreOptionsTitle": { "message": "ĐžŅ‰Đĩ ĐžĐŋŅ†Đ¸Đ¸ – $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚Đ¸Đ˛ĐŊа ĐēĐžĐŊСОĐģа" }, + "admin": { + "message": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€" + }, + "automaticUserConfirmation": { + "message": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋĐžŅ‚Đ˛ŅŠŅ€ĐļĐ´ĐĩĐŊиĐĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģĐ¸Ņ‚Đĩ" + }, + "automaticUserConfirmationHint": { + "message": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋĐžŅ‚Đ˛ŅŠŅ€ĐļĐ´ĐĩĐŊиĐĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģĐ¸Ņ‚Đĩ, ĐēĐžĐŗĐ°Ņ‚Đž Ņ‚ĐžĐ˛Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž Đĩ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊĐž" + }, + "autoConfirmOnboardingCallout": { + "message": "ĐĄĐŋĐĩҁ҂ĐĩŅ‚Đĩ Đ˛Ņ€ĐĩĐŧĐĩ ҁ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐžŅ‚Đž ĐŋĐžŅ‚Đ˛ŅŠŅ€ĐļĐ´ĐĩĐŊиĐĩ ĐŊа ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģĐ¸Ņ‚Đĩ" + }, + "autoConfirmWarning": { + "message": "ĐĸОва ĐŧĐžĐļĐĩ да ҁĐĩ ĐžŅ‚Ņ€Đ°ĐˇĐ¸ ĐŊа ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚Ņ‚Đ° ĐŊа даĐŊĐŊĐ¸Ņ‚Đĩ в ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° Ви. " + }, + "autoConfirmWarningLink": { + "message": "ĐĐ°ŅƒŅ‡ĐĩŅ‚Đĩ ĐŋОвĐĩ҇Đĩ Са Ņ€Đ¸ŅĐēОвĐĩŅ‚Đĩ" + }, + "autoConfirmSetup": { + "message": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋĐžŅ‚Đ˛ŅŠŅ€ĐļдаваĐŊĐĩ ĐŊа ĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи" + }, + "autoConfirmSetupDesc": { + "message": "ĐĐžĐ˛Đ¸Ņ‚Đĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģи ҉Đĩ ĐąŅŠĐ´Đ°Ņ‚ ĐŋĐžŅ‚Đ˛ŅŠŅ€ĐļдаваĐŊи Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž, Đ´ĐžĐēĐ°Ņ‚Đž Ņ‚ĐžĐ˛Đ° ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž Đĩ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊĐž." + }, + "autoConfirmSetupHint": { + "message": "КаĐēви ŅĐ° Đ˛ŅŠĐˇĐŧĐžĐļĐŊĐ¸Ņ‚Đĩ Ņ€Đ¸ŅĐēОвĐĩ Са ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚Ņ‚Đ°?" + }, + "autoConfirmEnabled": { + "message": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐžŅ‚Đž ĐŋĐžŅ‚Đ˛ŅŠŅ€ĐļдаваĐŊĐĩ Đĩ вĐēĐģŅŽŅ‡ĐĩĐŊĐž" + }, + "availableNow": { + "message": "НаĐģĐ¸Ņ‡ĐŊĐž ҁĐĩĐŗĐ°" + }, "accountSecurity": { "message": "Đ—Đ°Ņ‰Đ¸Ņ‚Đ° ĐŊа Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸ŅŅ‚Đ°" }, + "phishingBlocker": { + "message": "БĐģĐžĐēĐ°Ņ‚ĐžŅ€ ĐŊа иСĐŧаĐŧи" + }, + "enablePhishingDetection": { + "message": "РаСĐŋОСĐŊаваĐŊĐĩ ĐŊа иСĐŧаĐŧи" + }, + "enablePhishingDetectionDesc": { + "message": "ПоĐēаСваĐŊĐĩ ĐŊа ĐŋŅ€ĐĩĐ´ŅƒĐŋŅ€ĐĩĐļĐ´ĐĩĐŊиĐĩ ĐŋŅ€Đĩди ĐˇĐ°Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа ҃ĐĩĐą ŅĐ°ĐšŅ‚ĐžĐ˛Đĩ ĐŋĐžĐ´ĐžĐˇĐ¸Ņ€Đ°ĐŊи Са иСĐŧаĐŧĐŊи" + }, "notifications": { "message": "ИСвĐĩŅŅ‚Đ¸Ņ" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ĐžŅ‚ĐēŅ€Đ¸Ņ‚ĐžŅ‚Đž ŅŅŠĐ˛ĐŋадĐĩĐŊиĐĩ $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "ПоĐēаСваĐŊĐĩ ĐŊа ĐžŅ‚ĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩŅ‚Đž ĐŊа ŅŅŠĐ˛ĐŋадĐĩĐŊĐ¸Ņ" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ĐžŅ‚ĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩŅ‚Đž ĐŊа ŅŅŠĐ˛ĐŋадĐĩĐŊĐ¸Ņ" }, "autoFillOnPageLoad": { "message": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋĐžĐŋҊĐģваĐŊĐĩ ĐŋŅ€Đ¸ ĐˇĐ°Ņ€ĐĩĐļдаĐŊĐĩ ĐŊа ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ°Ņ‚Đ°?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "МĐŊĐžĐŗĐž ŅˆĐ¸Ņ€ĐžĐēĐž" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "Đ’ŅŠĐ˛ĐĩĐ´ĐĩĐŊĐ°Ņ‚Đ° ĐŋĐ°Ņ€ĐžĐģа Đĩ ĐŊĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģĐŊа." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "ĐĸОСи ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ Са вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ Đĩ в Ņ€Đ¸ŅĐē и в ĐŊĐĩĐŗĐž ĐģиĐŋŅĐ˛Đ° ҃ĐĩĐą ŅĐ°ĐšŅ‚. ДобавĐĩŅ‚Đĩ ҃ĐĩĐą ŅĐ°ĐšŅ‚ и ҁĐŧĐĩĐŊĐĩŅ‚Đĩ ĐŋĐ°Ņ€ĐžĐģĐ°Ņ‚Đ°, Са ĐŋĐž-Đ´ĐžĐąŅ€Đ° ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚." }, + "vulnerablePassword": { + "message": "ĐŖŅĐˇĐ˛Đ¸Đŧа ĐŋĐ°Ņ€ĐžĐģа." + }, + "changeNow": { + "message": "ĐŸŅ€ĐžĐŧŅĐŊа ҁĐĩĐŗĐ°" + }, "missingWebsite": { "message": "ЛиĐŋŅĐ˛Đ°Ņ‰ ҃ĐĩĐą ŅĐ°ĐšŅ‚" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "И ĐžŅ‰Đĩ!" }, - "planDescPremium": { - "message": "ĐŸŅŠĐģĐŊа ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚ в ИĐŊŅ‚ĐĩŅ€ĐŊĐĩŅ‚" + "advancedOnlineSecurity": { + "message": "Đ Đ°ĐˇŅˆĐ¸Ņ€ĐĩĐŊа ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚ в ИĐŊŅ‚ĐĩŅ€ĐŊĐĩŅ‚" }, "upgradeToPremium": { "message": "ĐĐ°Đ´ĐŗŅ€Đ°Đ´ĐĩŅ‚Đĩ Đ´Đž ПĐģĐ°Ņ‚ĐĩĐŊĐ¸Ņ ĐŋĐģаĐŊ" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "НоĐŧĐĩŅ€ ĐŊа ĐēĐ°Ņ€Ņ‚Đ°Ņ‚Đ°" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Đ’Đ°ŅˆĐ°Ņ‚Đ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ вĐĩ҇Đĩ ĐŊĐĩ иСĐŋĐžĐģСва ĐŗĐģавĐŊи ĐŋĐ°Ņ€ĐžĐģи Са вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ в Đ‘Đ¸Ņ‚ŅƒĐžŅ€Đ´ĐĩĐŊ. За да ĐŋŅ€ĐžĐ´ŅŠĐģĐļĐ¸Ņ‚Đĩ, ĐŋĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩŅ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° и Đ´ĐžĐŧĐĩĐšĐŊа." + }, + "continueWithLogIn": { + "message": "ĐŸŅ€ĐžĐ´ŅŠĐģĐļаваĐŊĐĩ ҁ вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩŅ‚Đž" + }, + "doNotContinue": { + "message": "НĐĩ ĐŋŅ€ĐžĐ´ŅŠĐģĐļаваĐŧ" + }, + "domain": { + "message": "ДоĐŧĐĩĐšĐŊ" + }, + "keyConnectorDomainTooltip": { + "message": "ĐĸОСи Đ´ĐžĐŧĐĩĐšĐŊ ҉Đĩ ŅŅŠŅ…Ņ€Đ°ĐŊŅĐ˛Đ° ĐēĐģŅŽŅ‡ĐžĐ˛ĐĩŅ‚Đĩ Са ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐĩ ĐŊа аĐēĐ°ŅƒĐŊŅ‚Đ° Ви, Ņ‚Đ°Đēа ҇Đĩ ҁĐĩ ŅƒĐ˛ĐĩŅ€ĐĩŅ‚Đĩ, ҇Đĩ Đŧ҃ иĐŧĐ°Ņ‚Đĩ дОвĐĩŅ€Đ¸Đĩ. АĐēĐž иĐŧĐ°Ņ‚Đĩ ҁҊĐŧĐŊĐĩĐŊĐ¸Ņ, ŅĐ˛ŅŠŅ€ĐļĐĩŅ‚Đĩ ҁĐĩ ҁ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° ŅĐ¸." + }, + "verifyYourOrganization": { + "message": "ĐŸĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩŅ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° ŅĐ¸, Са да ҁĐĩ вĐŋĐ¸ŅˆĐĩŅ‚Đĩ" + }, + "organizationVerified": { + "message": "ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° Đĩ ĐŋĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩĐŊа" + }, + "domainVerified": { + "message": "ДоĐŧĐĩĐšĐŊŅŠŅ‚ Đĩ ĐŋĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩĐŊ" + }, + "leaveOrganizationContent": { + "message": "АĐēĐž ĐŊĐĩ ĐŋĐžŅ‚Đ˛ŅŠŅ€Đ´Đ¸Ņ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ°, Đ´ĐžŅŅ‚ŅŠĐŋŅŠŅ‚ Ви Đ´Đž ĐŊĐĩŅ ҉Đĩ ĐąŅŠĐ´Đĩ ĐŋŅ€ĐĩŅƒŅŅ‚Đ°ĐŊОвĐĩĐŊ." + }, + "leaveNow": { + "message": "НаĐŋ҃ҁĐēаĐŊĐĩ ҁĐĩĐŗĐ°" + }, + "verifyYourDomainToLogin": { + "message": "ĐŸĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩŅ‚Đĩ Đ´ĐžĐŧĐĩĐšĐŊа ŅĐ¸, Са да ҁĐĩ вĐŋĐ¸ŅˆĐĩŅ‚Đĩ" + }, + "verifyYourDomainDescription": { + "message": "За да ĐŋŅ€ĐžĐ´ŅŠĐģĐļĐ¸Ņ‚Đĩ ҁ вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩŅ‚Đž, ĐŋĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩŅ‚Đĩ Ņ‚ĐžĐˇĐ¸ Đ´ĐžĐŧĐĩĐšĐŊ." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "За да ĐŋŅ€ĐžĐ´ŅŠĐģĐļĐ¸Ņ‚Đĩ ҁ вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩŅ‚Đž, ĐŋĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩŅ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° и Đ´ĐžĐŧĐĩĐšĐŊа." + }, "sessionTimeoutSettingsAction": { "message": "ДĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŋŅ€Đ¸ Đ¸ĐˇŅ‚Đ¸Ņ‡Đ°ĐŊĐĩŅ‚Đž ĐŊа Đ˛Ņ€ĐĩĐŧĐĩŅ‚Đž Са Đ´ĐžŅŅ‚ŅŠĐŋ" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "ĐĸаСи ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēа ҁĐĩ ҃ĐŋŅ€Đ°Đ˛ĐģŅĐ˛Đ° ĐžŅ‚ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° Ви." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° Ви Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐ¸Đģа ĐŧаĐēŅĐ¸ĐŧаĐģĐŊĐžŅ‚Đž Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩ Са Đ´ĐžŅŅ‚ŅŠĐŋ ĐŊа [%1$i] Ņ‡Đ°Ņ(а) и [%2$i] ĐŧиĐŊŅƒŅ‚Đ¸.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° Ви Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐ¸Đģа ŅŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐŊĐžŅ‚Đž Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩ Са Đ´ĐžŅŅ‚ŅŠĐŋ да ĐąŅŠĐ´Đĩ ĐŊ҃Đģа." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° Ви Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐ¸Đģа ŅŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐŊĐžŅ‚Đž Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩ Са Đ´ĐžŅŅ‚ŅŠĐŋ да ĐąŅŠĐ´Đĩ Đ´Đž СаĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŊа ŅĐ¸ŅŅ‚ĐĩĐŧĐ°Ņ‚Đ°." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° Ви Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐ¸Đģа ŅŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐŊĐžŅ‚Đž Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩ Са Đ´ĐžŅŅ‚ŅŠĐŋ да ĐąŅŠĐ´Đĩ Đ´Đž Ņ€ĐĩŅŅ‚Đ°Ņ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐąŅ€Đ°ŅƒĐˇŅŠŅ€Đ°." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "МаĐēŅĐ¸ĐŧаĐģĐŊĐžŅ‚Đž Đ˛Ņ€ĐĩĐŧĐĩ ĐŊа Đ´ĐžŅŅ‚ŅŠĐŋ ĐŊĐĩ ĐŧĐžĐļĐĩ да ĐŋŅ€ĐĩĐ˛Đ¸ŅˆĐ°Đ˛Đ° $HOURS$ Ņ‡Đ°Ņ(а) и $MINUTES$ ĐŧиĐŊŅƒŅ‚Đ¸", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "ĐŸŅ€Đ¸ Ņ€ĐĩŅŅ‚Đ°Ņ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐąŅ€Đ°ŅƒĐˇŅŠŅ€Đ°" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Đ—Đ°Đ´Đ°ĐšŅ‚Đĩ ĐŧĐĩŅ‚ĐžĐ´ Са ĐžŅ‚ĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ, Са да ĐŧĐžĐļĐĩ да ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸ĐĩŅ‚Đž ĐŋŅ€Đ¸ Đ¸ĐˇŅ‚Đ¸Ņ‡Đ°ĐŊĐĩ ĐŊа Đ˛Ņ€ĐĩĐŧĐĩŅ‚Đž Са Đ´ĐžŅŅ‚ŅŠĐŋ" + }, + "upgrade": { + "message": "ĐĐ°Đ´ĐŗŅ€Đ°ĐļдаĐŊĐĩ" + }, + "leaveConfirmationDialogTitle": { + "message": "ĐĐ°Đ¸ŅŅ‚Đ¸ĐŊа Đģи Đ¸ŅĐēĐ°Ņ‚Đĩ да ĐŊаĐŋ҃ҁĐŊĐĩŅ‚Đĩ?" + }, + "leaveConfirmationDialogContentOne": { + "message": "АĐēĐž ĐžŅ‚ĐēаĐļĐĩŅ‚Đĩ, Đ’Đ°ŅˆĐ¸Ņ‚Đĩ ŅĐžĐąŅŅ‚Đ˛ĐĩĐŊи ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ҉Đĩ ĐžŅŅ‚Đ°ĐŊĐ°Ņ‚ в аĐēĐ°ŅƒĐŊŅ‚Đ° Ви, ĐŊĐž ҉Đĩ ĐˇĐ°ĐŗŅƒĐąĐ¸Ņ‚Đĩ Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž ҁĐŋОдĐĩĐģĐĩĐŊĐ¸Ņ‚Đĩ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ и Ņ„ŅƒĐŊĐēŅ†Đ¸ĐžĐŊаĐģĐŊĐžŅŅ‚Đ¸Ņ‚Đĩ ĐŊа ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ°." + }, + "leaveConfirmationDialogContentTwo": { + "message": "ĐĄĐ˛ŅŠŅ€ĐļĐĩŅ‚Đĩ ҁĐĩ ҁ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€, Са да ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚Đĩ Đ´ĐžŅŅ‚ŅŠĐŋ ĐžŅ‚ĐŊОвО." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "НаĐŋ҃ҁĐēаĐŊĐĩ ĐŊа $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "КаĐē да ҃ĐŋŅ€Đ°Đ˛ĐģŅĐ˛Đ°Đŧ ҂ҀĐĩĐˇĐžŅ€Đ° ŅĐ¸?" + }, + "transferItemsToOrganizationTitle": { + "message": "ĐŸŅ€ĐĩŅ…Đ˛ŅŠŅ€ĐģŅĐŊĐĩ ĐŊа ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐēҊĐŧ $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ Đ¸ĐˇĐ¸ŅĐēва Đ˛ŅĐ¸Ņ‡Đēи ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ да ŅŅ‚Đ°ĐŊĐ°Ņ‚ ĐŋŅ€Đ¸Ņ‚ĐĩĐļаĐŊиĐĩ ĐŊа ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ°, Са ĐŋĐž-Đ´ĐžĐąŅ€Đ° ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚ и ŅŅŠĐ˛ĐŧĐĩŅŅ‚Đ¸ĐŧĐžŅŅ‚. ИСйĐĩŅ€ĐĩŅ‚Đĩ, ҇Đĩ ĐŋŅ€Đ¸ĐĩĐŧĐ°Ņ‚Đĩ, Са да ĐŋŅ€ĐĩŅ…Đ˛ŅŠŅ€ĐģĐ¸Ņ‚Đĩ ŅĐžĐąŅŅ‚Đ˛ĐĩĐŊĐžŅŅ‚Ņ‚Đ° ĐŊа ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸Ņ‚Đĩ ŅĐ¸ ĐēҊĐŧ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ°.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "ĐŸŅ€Đ¸ĐĩĐŧаĐŊĐĩ ĐŊа ĐŋŅ€ĐĩŅ…Đ˛ŅŠŅ€ĐģŅĐŊĐĩŅ‚Đž" + }, + "declineAndLeave": { + "message": "ĐžŅ‚ĐēаСваĐŊĐĩ и ĐŊаĐŋ҃ҁĐēаĐŊĐĩ" + }, + "whyAmISeeingThis": { + "message": "Đ—Đ°Ņ‰Đž виĐļдаĐŧ Ņ‚ĐžĐ˛Đ°?" + }, + "resizeSideNavigation": { + "message": "ĐŸŅ€ĐĩĐžŅ€Đ°ĐˇĐŧĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ ĐŊа ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ‡ĐŊĐ°Ņ‚Đ° ĐŊĐ°Đ˛Đ¸ĐŗĐ°Ņ†Đ¸Ņ" } } diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 4f8e7054305..fa4d93fa9ee 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "āϏāĻŋāĻ™ā§āĻ•" }, - "syncVaultNow": { - "message": "āĻāĻ–āύāχ āĻ­āĻ˛ā§āϟ āϏāĻŋāĻ™ā§āĻ• āĻ•āϰ⧁āύ" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "āĻļ⧇āώ āϏāĻŋāĻ™ā§āĻ•:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "āĻŦāĻ¸ā§āϤ⧁ āφāĻŽāĻĻāĻžāύāĻŋ" - }, "select": { "message": "āύāĻŋāĻ°ā§āĻŦāĻžāϚāύ āĻ•āϰ⧁āύ" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "āϏāĻŽā§āĻĒāĻžāĻĻāύāĻž" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "āĻ­āĻ˛ā§āϟ āϰāĻĢāϤāĻžāύāĻŋ" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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": "āĻĢāĻžāχāϞ⧇āϰ āϧāϰāĻŖ" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "āφāϰāĻ“ āϜāĻžāύ⧁āύ" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "āĻĒā§āϰāĻŽāĻžāĻŖā§€āĻ•āϰāĻŖāĻ•āĻžāϰ⧀ āϕ⧀ (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "āϏāĻ‚āϝ⧁āĻ•ā§āϤāĻŋāϟāĻŋ āϏāĻ‚āϰāĻ•ā§āώāĻŖ āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇āĨ¤" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "āĻĢāĻžāχāϞ" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "āĻāĻ•āϟāĻŋ āĻĢāĻžāχāϞ āύāĻŋāĻ°ā§āĻŦāĻžāϚāύ āĻ•āϰ⧁āύāĨ¤" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "āϏāĻ°ā§āĻŦā§‹āĻšā§āϚ āĻĢāĻžāχāϞ⧇āϰ āφāĻ•āĻžāϰ ā§§ā§Ļā§Ļ āĻāĻŽāĻŦāĻŋāĨ¤" }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "āĻĢāĻžāχāϞ āϏāĻ‚āϝ⧁āĻ•ā§āϤāĻŋāϰ āϜāĻ¨ā§āϝ ā§§ āϜāĻŋāĻŦāĻŋ āĻāύāĻ•ā§āϰāĻŋāĻĒā§āĻŸā§‡āĻĄ āĻ¸ā§āĻĨāĻžāύāĨ¤" }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "āφāĻĒāύāĻžāϰ āĻ­āĻ˛ā§āϟāϟāĻŋ āϏ⧁āϰāĻ•ā§āώāĻŋāϤ āϰāĻžāĻ–āϤ⧇ āĻĒāĻžāϏāĻ“āϝāĻŧāĻžāĻ°ā§āĻĄ āĻ¸ā§āĻŦāĻžāĻ¸ā§āĻĨā§āϝāĻ•āϰāύ, āĻ…ā§āϝāĻžāĻ•āĻžāωāĻ¨ā§āϟ āĻ¸ā§āĻŦāĻžāĻ¸ā§āĻĨā§āϝ āĻāĻŦāĻ‚ āĻĄā§‡āϟāĻž āϞāĻ™ā§āϘāύ⧇āϰ āĻĒā§āϰāϤāĻŋāĻŦ⧇āĻĻāύāĨ¤" }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "āĻŦāĻ¸ā§āϤ⧁āϟāĻŋ āĻ¸ā§āĻĨāĻžāϝāĻŧā§€āĻ­āĻžāĻŦ⧇ āĻŽā§āϛ⧇ āĻĢ⧇āϞāĻž āĻšā§Ÿā§‡āϛ⧇" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "āĻŦāĻ¸ā§āϤ⧁ āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰ" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index 9d5631c47e2..7eb327b034a 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sync" }, - "syncVaultNow": { - "message": "Sync vault now" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Last sync:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Import items" - }, "select": { "message": "Select" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Edit" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Authenticator key (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Attachment saved" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "File" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Select a file" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maximum file size is 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restore item" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 255263a6da7..3a9333b5471 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Inicieu sessiÃŗ amb la clau de pas" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Inici de sessiÃŗ Ãēnic" }, @@ -436,8 +439,8 @@ "sync": { "message": "SincronitzaciÃŗ" }, - "syncVaultNow": { - "message": "Sincronitza la caixa forta ara" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Última sincronitzaciÃŗ:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "AplicaciÃŗ web Bitwarden" }, - "importItems": { - "message": "Importa elements" - }, "select": { "message": "Selecciona" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Edita" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Exporta des de" }, - "exportVault": { - "message": "Exporta caixa forta" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "MÊs informaciÃŗ" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Clau d'autenticaciÃŗ (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "S'ha guardat el fitxer adjunt" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Fitxer" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Seleccioneu un fitxer" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "La mida màxima del fitxer Ês de 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB d'emmagatzematge xifrat per als fitxers adjunts." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "AccÊs d’emergència." }, "premiumSignUpTwoStepOptions": { "message": "Opcions propietàries de doble factor com ara YubiKey i Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Requisits d'higiene de la contrasenya, salut del compte i informe d'infraccions de dades per mantenir la seguretat de la vostra caixa forta." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Element suprimit definitivament" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restaura l'element" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No s'ha trobat cap identificador Ãēnic." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Error de desxifrat" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignora" }, - "importData": { - "message": "Importa dades", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Error d'importaciÃŗ" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "MÊs opcions - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Consola d'administraciÃŗ" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Seguretat del compte" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notificacions" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Habilita l'emplenament automàtic en carregar la pàgina?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra ample" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index eff2c6c0ea7..46618df6257 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "PřihlÃĄsit se pomocí přístupovÊho klíče" }, + "unlockWithPasskey": { + "message": "Odemknout pomocí přístupovÊho klíče" + }, "useSingleSignOn": { "message": "PouŞít jednotnÊ přihlÃĄÅĄení" }, @@ -436,8 +439,8 @@ "sync": { "message": "Synchronizace" }, - "syncVaultNow": { - "message": "Synchronizovat trezor nyní" + "syncNow": { + "message": "Synchronizovat nyní" }, "lastSync": { "message": "Poslední synchronizace:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "WebovÃĄ aplikace Bitwardenu" }, - "importItems": { - "message": "Importovat poloÅžky" - }, "select": { "message": "Vybrat" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "PoloÅžka byla přesunuta do archivu" }, + "itemWasUnarchived": { + "message": "PoloÅžka byla odebrÃĄna z archivu" + }, "itemUnarchived": { "message": "PoloÅžka byla odebrÃĄna z archivu" }, "archiveItem": { "message": "Archivovat poloÅžku" }, - "archiveItemConfirmDesc": { - "message": "ArchivovanÊ poloÅžky jsou vyloučeny z obecnÃŊch vÃŊsledků vyhledÃĄvÃĄní a z nÃĄvrhů automatickÊho vyplňovÃĄní. Jste si jisti, Åže chcete tuto poloÅžku archivovat?" + "archiveItemDialogContent": { + "message": "Jakmile bude tato poloÅžka archivovÃĄna, bude vyloučena z vÃŊsledků vyhledÃĄvÃĄní a z nÃĄvrhů automatickÊho vyplňovÃĄní." + }, + "archived": { + "message": "ArchivovÃĄno" + }, + "unarchiveAndSave": { + "message": "Odebrat z archivu a uloÅžit" }, "upgradeToUseArchive": { "message": "Pro pouÅžití funkce Archiv je potřebnÊ prÊmiovÊ členství." }, + "itemRestored": { + "message": "PoloÅžka byla obnovena" + }, "edit": { "message": "Upravit" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Exportovat z" }, - "exportVault": { - "message": "Exportovat trezor" + "exportVerb": { + "message": "Exportovat", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Dozvědět se více" }, + "migrationsFailed": { + "message": "DoÅĄlo k chybě při aktualizaci nastavení ÅĄifrovÃĄní." + }, + "updateEncryptionSettingsTitle": { + "message": "Aktualizovat nastavení ÅĄifrovÃĄní" + }, + "updateEncryptionSettingsDesc": { + "message": "NovÊ doporučenÊ nastavení ÅĄifrovÃĄní zlepÅĄÃ­ bezpečnost VaÅĄeho Ãēčtu. Pokud chcete aktualizovat nyní, zadejte hlavní heslo." + }, + "confirmIdentityToContinue": { + "message": "Pro pokračovÃĄní potvrďte svou identitu" + }, + "enterYourMasterPassword": { + "message": "Zadejte svÊ hlavní heslo" + }, + "updateSettings": { + "message": "Aktualizovat nastavení" + }, + "later": { + "message": "Později" + }, "authenticatorKeyTotp": { "message": "Autentizační klíč (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Příloha byla uloÅžena" }, + "fixEncryption": { + "message": "Opravit ÅĄifrovÃĄní" + }, + "fixEncryptionTooltip": { + "message": "Tento soubor pouŞívÃĄ zastaralou ÅĄifrovací metodu." + }, + "attachmentUpdated": { + "message": "Příloha byla aktualizovÃĄna" + }, "file": { "message": "Soubor" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Zvolte soubor" }, + "itemsTransferred": { + "message": "PřevedenÊ poloÅžky" + }, "maxFileSize": { "message": "MaximÃĄlní velikost souboru je 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB ÅĄifrovanÊho ÃēloÅžiÅĄtě pro přílohy." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ ÅĄifrovanÊho ÃēloÅžiÅĄtě pro přílohy.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "NouzovÃŊ přístup" }, "premiumSignUpTwoStepOptions": { "message": "Volby proprietÃĄlních dvoufÃĄzovÃŊch přihlÃĄÅĄení jako je YubiKey a Duo." }, + "premiumSubscriptionEnded": { + "message": "VaÅĄe předplatnÊ Premium skončilo" + }, + "archivePremiumRestart": { + "message": "Chcete-li získat přístup k VaÅĄemu archivu, restartujte předplatnÊ Premium. Pokud upravíte detaily archivovanÊ poloÅžky před restartovÃĄním, bude přesunuta zpět do VaÅĄeho trezoru." + }, + "restartPremium": { + "message": "Restartovat Premium" + }, "ppremiumSignUpReports": { "message": "Reporty o hygieně VaÅĄich hesel, zdraví Ãēčtu a naruÅĄeních bezpečnosti." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "PoloÅžka byla trvale smazÃĄna" }, + "archivedItemRestored": { + "message": "ArchivovanÃĄ poloÅžka byla obnovena" + }, "restoreItem": { "message": "Obnovit poloÅžku" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Nenalezen ÅžÃĄdnÃŊ jedinečnÃŊ identifikÃĄtor." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Hlavní heslo jiÅž není vyÅžadovÃĄno pro členy nÃĄsledující organizace. Potvrďte níŞe uvedenou domÊnu u sprÃĄvce VaÅĄÃ­ organizace." - }, "organizationName": { "message": "NÃĄzev organizace" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Chyba" }, + "prfUnlockFailed": { + "message": "Nepodařilo se odemknout pomocí přístupovÊho klíče. Zkuste to znovu nebo pouÅžijte jinou metodu odemknutí." + }, + "noPrfCredentialsAvailable": { + "message": "K odemknutí nejsou k dispozici ÅžÃĄdnÊ přístupovÊ klíče s podporou PRF. Nejprve se přihlaste pomocí hesla." + }, "decryptionError": { "message": "Chyba deÅĄifrovÃĄní" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignorovat" }, - "importData": { - "message": "Importovat data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Chyba importu" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "Více voleb" + }, "moreOptionsTitle": { "message": "Více voleb - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Konzole sprÃĄvce" }, + "admin": { + "message": "AdministrÃĄtor" + }, + "automaticUserConfirmation": { + "message": "AutomatickÊ potvrzení uÅživatele" + }, + "automaticUserConfirmationHint": { + "message": "Automaticky potvrdit čekající uÅživatele, kdyÅž je toto zařízení odemčeno" + }, + "autoConfirmOnboardingCallout": { + "message": "UÅĄetřete čas s automatickÃŊm potvrzením uÅživatele" + }, + "autoConfirmWarning": { + "message": "To by mohlo ovlivnit bezpečnost dat VaÅĄÃ­ organizace. " + }, + "autoConfirmWarningLink": { + "message": "Více o rizicích" + }, + "autoConfirmSetup": { + "message": "Automaticky potvrdit novÊ uÅživatele" + }, + "autoConfirmSetupDesc": { + "message": "Noví uÅživatelÊ budou automaticky potvrzeni, kdyÅž bude toto zařízení odemčeno." + }, + "autoConfirmSetupHint": { + "message": "JakÃĄ jsou moÅžnÃĄ bezpečnostní rizika?" + }, + "autoConfirmEnabled": { + "message": "Zapnuto automatickÊ potvrzení" + }, + "availableNow": { + "message": "Nyní k dispozici" + }, "accountSecurity": { "message": "Zabezpečení Ãēčtu" }, + "phishingBlocker": { + "message": "BlokovÃĄní phishingu" + }, + "enablePhishingDetection": { + "message": "Detekce phishingu" + }, + "enablePhishingDetectionDesc": { + "message": "Zobrazí varovÃĄní před přístupem k podezřelÃŊm phishingovÃŊm strÃĄnkÃĄm." + }, "notifications": { "message": "OznÃĄmení" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "SkrÃŊt detekci shody $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Zobrazit detekci shody" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "SkrÃŊt detekci shody" }, "autoFillOnPageLoad": { "message": "Automaticky vyplnit při načtení strÃĄnky?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra ÅĄirokÊ" }, + "narrow": { + "message": "ÚzkÊ" + }, "sshKeyWrongPassword": { "message": "ZadanÊ heslo není sprÃĄvnÊ." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Tyto přihlaÅĄovací Ãēdaje jsou ohroÅženÊ a chybí jim webovÃĄ strÃĄnka. Přidejte webovou strÃĄnku a změňte heslo pro větÅĄÃ­ bezpečnost." }, + "vulnerablePassword": { + "message": "ZranitelnÊ heslo." + }, + "changeNow": { + "message": "Změnit nyní" + }, "missingWebsite": { "message": "Chybějící webovÃĄ strÃĄnka" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "A jeÅĄtě více!" }, - "planDescPremium": { - "message": "Dokončit online zabezpečení" + "advancedOnlineSecurity": { + "message": "PokročilÊ zabezpečení online" }, "upgradeToPremium": { "message": "Aktualizovat na Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Číslo karty" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "VaÅĄe organizace jiÅž k přihlÃĄÅĄení do Bitwardenu nepouŞívÃĄ hlavní hesla. Chcete-li pokračovat, ověřte organizaci a domÊnu." + }, + "continueWithLogIn": { + "message": "Pokračovat s přihlÃĄÅĄením" + }, + "doNotContinue": { + "message": "Nepokračovat" + }, + "domain": { + "message": "DomÊna" + }, + "keyConnectorDomainTooltip": { + "message": "Tato domÊna uloŞí ÅĄifrovací klíče VaÅĄeho Ãēčtu, takÅže se ujistěte, Åže jí věříte. Pokud si nejste jisti, kontaktujte VaÅĄeho sprÃĄvce." + }, + "verifyYourOrganization": { + "message": "Ověřte svou organizaci pro přihlÃĄÅĄení" + }, + "organizationVerified": { + "message": "Organizace byla ověřena" + }, + "domainVerified": { + "message": "DomÊna byla ověřena" + }, + "leaveOrganizationContent": { + "message": "Pokud neověříte svou organizaci, VÃĄÅĄ přístup k organizaci bude zruÅĄen." + }, + "leaveNow": { + "message": "Opustit hned" + }, + "verifyYourDomainToLogin": { + "message": "Ověřte svou domÊnu pro přihlÃĄÅĄení" + }, + "verifyYourDomainDescription": { + "message": "Chcete-li pokračovat v přihlÃĄÅĄení, ověřte tuto domÊnu." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Chcete-li pokračovat v přihlÃĄÅĄení, ověřte organizaci a domÊnu." + }, "sessionTimeoutSettingsAction": { "message": "Akce vyprÅĄení časovÊho limitu" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Tato nastavení je spravovÃĄno VaÅĄÃ­ organizací." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "VaÅĄe organizace nastavila maximÃĄlní časovÃŊ limit relace na $HOURS$ hodin a $MINUTES$ minut.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "VaÅĄe organizace nastavila vÃŊchozí časovÃŊ limit relace na OkamÅžitě." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "VaÅĄe organizace nastavila vÃŊchozí časovÃŊ limit relace na Při uzamknutí systÊmu." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "VaÅĄe organizace nastavila vÃŊchozí časovÃŊ limit relace na Při restartu prohlíŞeče." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "MaximÃĄlní časovÃŊ limit nesmí překročit $HOURS$ hodin a $MINUTES$ minut", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Při restartu prohlíŞeče" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Nastavte metodu odemknutí, abyste změnili akci při vyprÅĄení časovÊho limitu" + }, + "upgrade": { + "message": "Aktualizovat" + }, + "leaveConfirmationDialogTitle": { + "message": "Opravdu chcete odejít?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Odmítnutím zůstanou VaÅĄe osobní poloÅžky ve VaÅĄem Ãēčtu, ale ztratíte přístup ke sdílenÃŊm poloÅžkÃĄm a funkcím organizace." + }, + "leaveConfirmationDialogContentTwo": { + "message": "ObraÅĨte se na svÊho sprÃĄvce, abyste znovu získali přístup." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Opustit $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Jak mohu spravovat svůj trezor?" + }, + "transferItemsToOrganizationTitle": { + "message": "PřenÊst poloÅžky do $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ vyÅžaduje, aby byly vÅĄechny poloÅžky vlastněny organizací z důvodu bezpečnosti a shody. Klepnutím na tlačítko pro převod vlastnictví VaÅĄich poloÅžek.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Přijmout převod" + }, + "declineAndLeave": { + "message": "Odmítnout a opustit" + }, + "whyAmISeeingThis": { + "message": "Proč se mi toto zobrazuje?" + }, + "resizeSideNavigation": { + "message": "Změnit velikost boční navigace" } } diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 99fcdffcc97..d765b7d8a10 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -344,16 +347,16 @@ "message": "Bitwarden for Business" }, "bitwardenAuthenticator": { - "message": "Dilyswr Bitwarden" + "message": "Dilysydd Bitwarden" }, "continueToAuthenticatorPageDesc": { "message": "Bitwarden Authenticator allows you to store authenticator keys and generate TOTP codes for 2-step verification flows. Learn more on the bitwarden.com website" }, "bitwardenSecretsManager": { - "message": "Bitwarden Secrets Manager" + "message": "Rheolydd Cyfrinachau Bitwarden" }, "continueToSecretsManagerPageDesc": { - "message": "Securely store, manage, and share developer secrets with Bitwarden Secrets Manager. Learn more on the bitwarden.com website." + "message": "Gallwch storio, rheoli a rhannu cyfrinachau datblygwyr yn ddiogel gyda Rheolydd Cyfrinachau Bitwarden. Dysgwch fwy ar wefan bitwarden.com." }, "passwordlessDotDev": { "message": "Passwordless.dev" @@ -436,8 +439,8 @@ "sync": { "message": "Cysoni" }, - "syncVaultNow": { - "message": "Cysoni'r gell nawr" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Wedi'i chysoni ddiwethaf:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Mewnforio eitemau" - }, "select": { "message": "Dewis" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Golygu" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Allforio'r gell" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Dysgu mwy" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Authenticator key (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Attachment saved" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Ffeil" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Dewis ffeil" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maximum file size is 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "Storfa 1GB wedi'i hamgryptio ar gyfer atodiadau ffeiliau." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Dewisiadau mewngofnodi dau gam perchenogol megis YubiKey a Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Eitem wedi'i dileu'n barhaol" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Adfer yr eitem" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Gwall" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4103,7 +4185,7 @@ "description": "Text to display in overlay when the account is locked." }, "unlockYourAccountToViewAutofillSuggestions": { - "message": "Unlock your account to view autofill suggestions", + "message": "Datglowch eich cyfrif i weld argymhellion llenwi awtomatig", "description": "Text to display in overlay when the account is locked." }, "unlockAccount": { @@ -4176,10 +4258,6 @@ "ignore": { "message": "Anwybyddu" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Diogelwch eich cyfrif" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Hysbysiadau" }, @@ -4882,7 +5005,7 @@ "message": "Download Bitwarden" }, "downloadBitwardenOnAllDevices": { - "message": "Download Bitwarden on all devices" + "message": "Lawrlwytho Bitwarden ar bob dyfais" }, "getTheMobileApp": { "message": "Get the mobile app" @@ -4912,7 +5035,7 @@ "message": "Premium" }, "unlockFeaturesWithPremium": { - "message": "Unlock reporting, emergency access, and more security features with Premium." + "message": "Datglowch nodweddion diogelwch megis adroddiadau, mynediad mewn argyfwng, a mwy drwy gyfrif Premium." }, "freeOrgsCannotUseAttachments": { "message": "Free organizations cannot use attachments" @@ -4921,7 +5044,7 @@ "message": "Filters" }, "filterVault": { - "message": "Filter vault" + "message": "Hidlo'r gell" }, "filterApplied": { "message": "One filter applied" @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Llydan iawn" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5801,7 +5930,7 @@ "message": "Great job securing your at-risk logins!" }, "upgradeNow": { - "message": "Upgrade now" + "message": "Uwchraddio nawr" }, "builtInAuthenticator": { "message": "Built-in authenticator" @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 865e6ff7dda..5add4d4b10c 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log ind med adgangsnøgle" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Brug Single Sign-On" }, @@ -436,8 +439,8 @@ "sync": { "message": "SynkronisÊr" }, - "syncVaultNow": { - "message": "SynkronisÊr boks nu" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Seneste synkronisering:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web-app" }, - "importItems": { - "message": "ImportÊr elementer" - }, "select": { "message": "VÃĻlg" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "RedigÊr" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "EksportÊr fra" }, - "exportVault": { - "message": "EksportÊr boks" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "LÃĻr mere" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Autentificeringsnøgle (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "VedhÃĻftning gemt" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Fil" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "VÃĻlg en fil" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maksimum filstørrelse er 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB krypteret lager til vedhÃĻftede filer." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Nødadgang" }, "premiumSignUpTwoStepOptions": { "message": "ProprietÃĻre totrins-login muligheder, sÃĨsom YubiKey og Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Adgangskodehygiejne, kontosundhed og rapporter om datalÃĻk til at holde din boks sikker." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Element slettet permanent" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Gendan element" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Ingen entydig identifikator fundet." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Fejl" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Dekrypteringsfejl" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "IgnorÊr" }, - "importData": { - "message": "ImportÊr data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Importfejl" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "Flere valgmuligheder - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin-konsol" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Kontosikkerhed" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifikationer" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Skjul matchdetektion $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autoudfyld ved sideindlÃĻsning?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Ekstra bred" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 86f49bb875e..8579ebdee3e 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Mit Passkey anmelden" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Single Sign-On verwenden" }, @@ -436,8 +439,8 @@ "sync": { "message": "Synchronisierung" }, - "syncVaultNow": { - "message": "Tresor jetzt synchronisieren" + "syncNow": { + "message": "Jetzt synchronisieren" }, "lastSync": { "message": "Zuletzt synchronisiert:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden Web-App" }, - "importItems": { - "message": "Einträge importieren" - }, "select": { "message": "Auswählen" }, @@ -574,7 +574,10 @@ "message": "Archivierte Einträge werden hier angezeigt und von allgemeinen Suchergebnissen sowie Vorschlägen zum automatischen AusfÃŧllen ausgeschlossen." }, "itemWasSentToArchive": { - "message": "Eintrag wurde ins Archiv verschoben" + "message": "Eintrag wurde archiviert" + }, + "itemWasUnarchived": { + "message": "Eintrag wird nicht mehr archiviert" }, "itemUnarchived": { "message": "Eintrag wird nicht mehr archiviert" @@ -582,12 +585,21 @@ "archiveItem": { "message": "Eintrag archivieren" }, - "archiveItemConfirmDesc": { - "message": "Archivierte Einträge werden von allgemeinen Suchergebnissen sowie Vorschlägen zum automatischen AusfÃŧllen ausgeschlossen. Bist du sicher, dass du diesen Eintrag archivieren mÃļchtest?" + "archiveItemDialogContent": { + "message": "Nach der Archivierung wird dieser Eintrag aus den Suchergebnissen und Auto-AusfÃŧllen-Vorschlägen ausgeschlossen." + }, + "archived": { + "message": "Archiviert" + }, + "unarchiveAndSave": { + "message": "Nicht mehr archivieren und speichern" }, "upgradeToUseArchive": { "message": "FÃŧr die Nutzung des Archivs ist eine Premium-Mitgliedschaft erforderlich." }, + "itemRestored": { + "message": "Eintrag wurde wiederhergestellt" + }, "edit": { "message": "Bearbeiten" }, @@ -1050,7 +1062,7 @@ "message": "Eintrag gespeichert" }, "savedWebsite": { - "message": "Website gespeichert" + "message": "Gespeicherte Website" }, "savedWebsites": { "message": "Gespeicherte Websites ($COUNT$)", @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export aus" }, - "exportVault": { - "message": "Tresor exportieren" + "exportVerb": { + "message": "Exportieren", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Erfahre mehr" }, + "migrationsFailed": { + "message": "Beim Aktualisieren der VerschlÃŧsselungseinstellungen ist ein Fehler aufgetreten." + }, + "updateEncryptionSettingsTitle": { + "message": "Aktualisiere deine VerschlÃŧsselungseinstellungen" + }, + "updateEncryptionSettingsDesc": { + "message": "Die neuen empfohlenen VerschlÃŧsselungseinstellungen verbessern deine Kontosicherheit. Gib dein Master-Passwort ein, um sie zu aktualisieren." + }, + "confirmIdentityToContinue": { + "message": "Bestätige deine Identität, um fortzufahren" + }, + "enterYourMasterPassword": { + "message": "Gib dein Master-Passwort ein" + }, + "updateSettings": { + "message": "Einstellungen aktualisieren" + }, + "later": { + "message": "Später" + }, "authenticatorKeyTotp": { "message": "AuthentifizierungsschlÃŧssel (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Anhang gespeichert" }, + "fixEncryption": { + "message": "VerschlÃŧsselung reparieren" + }, + "fixEncryptionTooltip": { + "message": "Diese Datei verwendet eine veraltete VerschlÃŧsselungsmethode." + }, + "attachmentUpdated": { + "message": "Anhang aktualisiert" + }, "file": { "message": "Datei" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Wähle eine Datei" }, + "itemsTransferred": { + "message": "Einträge wurden Ãŧbertragen" + }, "maxFileSize": { "message": "Die maximale DateigrÃļße beträgt 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB verschlÃŧsselter Speicherplatz fÃŧr Dateianhänge." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ verschlÃŧsselter Speicher fÃŧr Dateianhänge.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Notfallzugriff." }, "premiumSignUpTwoStepOptions": { "message": "Proprietäre Optionen fÃŧr die Zwei-Faktor Authentifizierung wie YubiKey und Duo." }, + "premiumSubscriptionEnded": { + "message": "Dein Premium-Abonnement ist abgelaufen" + }, + "archivePremiumRestart": { + "message": "Starte dein Premium-Abonnement neu, um den Zugriff auf dein Archiv wiederherzustellen. Wenn du die Details fÃŧr einen archivierten Eintrag vor dem Neustart bearbeitest, wird er wieder zurÃŧck in deinen Tresor verschoben." + }, + "restartPremium": { + "message": "Premium neu starten" + }, "ppremiumSignUpReports": { "message": "Berichte Ãŧber Kennworthygiene, Kontostatus und Datenschutzverletzungen, um deinen Tresor sicher zu halten." }, @@ -1710,7 +1786,7 @@ "message": "Auto-AusfÃŧllen bestätigen" }, "confirmAutofillDesc": { - "message": "Diese Website stimmt nicht mit deinen gespeicherten Zugangsdaten Ãŧberein. Bevor du deine Zugangsdaten eingibst, stelle sicher, dass es sich um eine vertrauenswÃŧrdige Website handelt." + "message": "Diese Website stimmt nicht mit deinen gespeicherten Zugangsdaten Ãŧberein. Stelle sicher, dass dies eine vertrauenswÃŧrdige Website ist, bevor du deine Zugangsdaten eingibst." }, "showInlineMenuLabel": { "message": "Vorschläge zum Auto-AusfÃŧllen in Formularfeldern anzeigen" @@ -1874,7 +1950,7 @@ "message": "Ablaufjahr" }, "monthly": { - "message": "month" + "message": "Monat" }, "expiration": { "message": "GÃŧltig bis" @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Eintrag dauerhaft gelÃļscht" }, + "archivedItemRestored": { + "message": "Archivierter Eintrag wiederhergestellt" + }, "restoreItem": { "message": "Eintrag wiederherstellen" }, @@ -2648,7 +2727,7 @@ "description": "A category title describing the concept of web domains" }, "blockedDomains": { - "message": "Gesperrte Domains" + "message": "Blockierte Domains" }, "learnMoreAboutBlockedDomains": { "message": "Erfahre mehr Ãŧber blockierte Domains" @@ -2666,7 +2745,7 @@ "message": "Automatisches AusfÃŧllen und andere zugehÃļrige Funktionen werden fÃŧr diese Webseiten nicht angeboten. Du musst die Seite neu laden, damit die Änderungen wirksam werden." }, "autofillBlockedNoticeV2": { - "message": "Automatisches AusfÃŧllen ist fÃŧr diese Website gesperrt." + "message": "Automatisches AusfÃŧllen ist fÃŧr diese Website blockiert." }, "autofillBlockedNoticeGuidance": { "message": "Dies in den Einstellungen ändern" @@ -2820,7 +2899,7 @@ } }, "blockedDomainsSavedSuccess": { - "message": "Änderungen gesperrter Domains gespeichert" + "message": "Änderungen blockierter Domains gespeichert" }, "excludedDomainsSavedSuccess": { "message": "Änderungen der ausgeschlossenen Domain gespeichert" @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Keine eindeutige Kennung gefunden." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "FÃŧr Mitglieder der folgenden Organisation ist kein Master-Passwort mehr erforderlich. Bitte bestätige die folgende Domain bei deinem Organisations-Administrator." - }, "organizationName": { "message": "Name der Organisation" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Fehler" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "EntschlÃŧsselungsfehler" }, @@ -4075,7 +4157,7 @@ "message": "Kein Auto-AusfÃŧllen mÃļglich" }, "cannotAutofillExactMatch": { - "message": "Die Standard-Übereinstimmungserkennung steht auf \"Exakte Übereinstimmung\". Die aktuelle Website stimmt nicht genau mit den gespeicherten Zugangsdaten fÃŧr diesen Eintrag Ãŧberein." + "message": "Die Standard-Übereinstimmungserkennung ist auf „Exakte Übereinstimmung“ eingestellt. Die aktuelle Website stimmt nicht genau mit den gespeicherten Zugangsdaten fÃŧr diesen Eintrag Ãŧberein." }, "okay": { "message": "Okay" @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignorieren" }, - "importData": { - "message": "Daten importieren", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Importfehler" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "Weitere Optionen" + }, "moreOptionsTitle": { "message": "Weitere Optionen - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Administrator-Konsole" }, + "admin": { + "message": "Administrator" + }, + "automaticUserConfirmation": { + "message": "Automatische Benutzerbestätigung" + }, + "automaticUserConfirmationHint": { + "message": "Ausstehende Benutzer automatisch bestätigen, während dieses Gerät entsperrt ist" + }, + "autoConfirmOnboardingCallout": { + "message": "Spare Zeit durch die automatische Benutzerbestätigung" + }, + "autoConfirmWarning": { + "message": "Dies kÃļnnte die Datensicherheit deiner Organisation beeinflussen. " + }, + "autoConfirmWarningLink": { + "message": "Erfahre mehr Ãŧber die Risiken" + }, + "autoConfirmSetup": { + "message": "Neue Benutzer automatisch bestätigen" + }, + "autoConfirmSetupDesc": { + "message": "Neue Benutzer werden automatisch bestätigt, während dieses Gerät entsperrt ist." + }, + "autoConfirmSetupHint": { + "message": "Was sind die mÃļglichen Sicherheitsrisiken?" + }, + "autoConfirmEnabled": { + "message": "Automatische Bestätigung aktiviert" + }, + "availableNow": { + "message": "Jetzt verfÃŧgbar" + }, "accountSecurity": { "message": "Kontosicherheit" }, + "phishingBlocker": { + "message": "Phishing-Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing-Erkennung" + }, + "enablePhishingDetectionDesc": { + "message": "Warnung vor dem Zugriff auf verdächtige Phishing-Seiten anzeigen" + }, "notifications": { "message": "Benachrichtigungen" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Übereinstimmungs-Erkennung verstecken $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Übereinstimmungserkennung anzeigen" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Übereinstimmungserkennung ausblenden" }, "autoFillOnPageLoad": { "message": "Auto-AusfÃŧllen beim Laden einer Seite?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra breit" }, + "narrow": { + "message": "Schmal" + }, "sshKeyWrongPassword": { "message": "Dein eingegebenes Passwort ist falsch." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Diese Zugangsdaten sind gefährdet und es fehlt eine Website. FÃŧge eine Website hinzu und ändere das Passwort fÃŧr mehr Sicherheit." }, + "vulnerablePassword": { + "message": "Gefährdetes Passwort." + }, + "changeNow": { + "message": "Jetzt ändern" + }, "missingWebsite": { "message": "Fehlende Website" }, @@ -5665,7 +5794,7 @@ "message": "Phishing-Versuch erkannt" }, "phishingPageSummary": { - "message": "Die Website, die du versuchst zu Ãļffnen, ist eine bekannte bÃļswillige Website und ein Sicherheitsrisiko." + "message": "Die Website, die du Ãļffnen mÃļchtest, ist als bÃļswillige Website bekannt und stellt ein Sicherheitsrisiko dar." }, "phishingPageCloseTabV2": { "message": "Diesen Tab schließen" @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "Und mehr!" }, - "planDescPremium": { - "message": "Umfassende Online-Sicherheit" + "advancedOnlineSecurity": { + "message": "Erweiterte Online-Sicherheit" }, "upgradeToPremium": { "message": "Upgrade auf Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Kartennummer" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Deine Organisation verwendet keine Master-PasswÃļrter mehr, um sich bei Bitwarden anzumelden. Verifiziere die Organisation und Domain, um fortzufahren." + }, + "continueWithLogIn": { + "message": "Mit der Anmeldung fortfahren" + }, + "doNotContinue": { + "message": "Nicht fortfahren" + }, + "domain": { + "message": "Domain" + }, + "keyConnectorDomainTooltip": { + "message": "Diese Domain speichert die VerschlÃŧsselungsschlÃŧssel deines Kontos. Stelle daher sicher, dass du ihr vertraust. Wenn du dir nicht sicher bist, wende dich an deinen Administrator." + }, + "verifyYourOrganization": { + "message": "Verifiziere deine Organisation, um dich anzumelden" + }, + "organizationVerified": { + "message": "Organisation verifiziert" + }, + "domainVerified": { + "message": "Domain verifiziert" + }, + "leaveOrganizationContent": { + "message": "Wenn du deine Organisation nicht verifizierst, wird dein Zugriff auf die Organisation widerrufen." + }, + "leaveNow": { + "message": "Jetzt verlassen" + }, + "verifyYourDomainToLogin": { + "message": "Verifiziere deine Domain, um dich anzumelden" + }, + "verifyYourDomainDescription": { + "message": "Verifiziere diese Domain, um mit der Anmeldung fortzufahren." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Um mit der Anmeldung fortzufahren, verifiziere die Organisation und Domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout-Aktion" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Diese Einstellung wird von deiner Organisation verwaltet." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Deine Organisation hat das maximale Sitzungs-Timeout auf $HOURS$ Stunde(n) und $MINUTES$ Minute(n) festgelegt.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Deine Organisation hat das Standard-Sitzungs-Timeout auf \"Sofort\" gesetzt." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Deine Organisation hat das Standard-Sitzungs-Timeout auf \"Wenn System gesperrt\" gesetzt." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Deine Organisation hat das Standard-Sitzungs-Timeout auf \"Bei Neustart des Browsers\" gesetzt." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Das maximale Timeout darf $HOURS$ Stunde(n) und $MINUTES$ Minute(n) nicht Ãŧberschreiten", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Bei Neustart des Browsers" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Stell eine Entsperrmethode ein, um deine Timeout-Aktion zu ändern" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Bist du sicher, dass du gehen willst?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Wenn du ablehnst, bleiben deine persÃļnlichen Einträge in deinem Konto erhalten, aber du wirst den Zugriff auf geteilte Einträge und Organisationsfunktionen verlieren." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Kontaktiere deinen Administrator, um wieder Zugriff zu erhalten." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "$ORGANIZATION$ verlassen", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Wie kann ich meinen Tresor verwalten?" + }, + "transferItemsToOrganizationTitle": { + "message": "Einträge zu $ORGANIZATION$ Ãŧbertragen", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ erfordert zur Sicherheit und Compliance, dass alle Einträge der Organisation gehÃļren. Klicke auf Akzeptieren, um den Besitz deiner Einträge zu Ãŧbertragen.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Übertragung annehmen" + }, + "declineAndLeave": { + "message": "Ablehnen und verlassen" + }, + "whyAmISeeingThis": { + "message": "Warum wird mir das angezeigt?" + }, + "resizeSideNavigation": { + "message": "GrÃļße der Seitennavigation ändern" } } diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 476a6165a8e..d1eebc0362c 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "ÎŖĪÎŊδÎĩĪƒÎˇ ÎŧÎĩ ÎēÎģÎĩΚδί Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Î§ĪÎŽĪƒÎˇ ÎĩÎŊÎšÎąÎ¯ÎąĪ‚ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚" }, @@ -436,8 +439,8 @@ "sync": { "message": "ÎŖĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧĪŒĪ‚" }, - "syncVaultNow": { - "message": "ÎŖĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧĪŒĪ‚ Î¸ÎˇĪƒÎąĪ…/ÎēΚÎŋĪ… Ī„ĪŽĪÎą" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "ΤÎĩÎģÎĩĪ…Ī„ÎąÎ¯ÎŋĪ‚ ĪƒĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧĪŒĪ‚:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "ΔιαδιÎēĪ„Ī…ÎąÎēÎŽ ÎĩĪ†ÎąĪÎŧÎŋÎŗÎŽ Bitwarden" }, - "importItems": { - "message": "Î•ÎšĪƒÎąÎŗĪ‰ÎŗÎŽ ĪƒĪ„ÎŋÎšĪ‡ÎĩÎ¯Ī‰ÎŊ" - }, "select": { "message": "Î•Ī€ÎšÎģÎŋÎŗÎŽ" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Î•Ī€ÎĩΞÎĩĪÎŗÎąĪƒÎ¯Îą" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Î•ÎžÎąÎŗĪ‰ÎŗÎŽ ÎąĪ€ĪŒ" }, - "exportVault": { - "message": "Î•ÎžÎąÎŗĪ‰ÎŗÎŽ Vault" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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": "Î¤ĪĪ€ÎŋĪ‚ ÎąĪĪ‡ÎĩίÎŋĪ…" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "ΜÎŦθÎĩĪ„Îĩ Ī€ÎĩĪÎšĪƒĪƒĪŒĪ„ÎĩĪÎą" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "ΚÎģÎĩΚδί ÎĩĪ€ÎąÎģΎθÎĩĪ…ĪƒÎˇĪ‚ (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "ΤÎŋ ĪƒĪ…ÎŊΡÎŧÎŧέÎŊÎŋ ÎąĪ€ÎŋθΡÎēÎĩĪĪ„ÎˇÎēÎĩ" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Î‘ĪĪ‡ÎĩίÎŋ" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Î•Ī€ÎšÎģÎ­ÎžĪ„Îĩ ÎąĪĪ‡ÎĩίÎŋ" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "ΤÎŋ ÎŧÎ­ÎŗÎšĪƒĪ„Îŋ ÎŧÎ­ÎŗÎĩθÎŋĪ‚ ÎąĪĪ‡ÎĩίÎŋĪ… ÎĩίÎŊιΚ 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB Îē΁΅΀΄ÎŋÎŗĪÎąĪ†ÎˇÎŧέÎŊÎŋ ÎąĪ€ÎŋθΡÎēÎĩĪ…Ī„ÎšÎēΌ Ī‡ĪŽĪÎŋ ÎŗÎšÎą ĪƒĪ…ÎŊΡÎŧÎŧέÎŊÎą ÎąĪĪ‡ÎĩÎ¯Îą." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Î ĪĪŒĪƒÎ˛ÎąĪƒÎˇ έÎēĪ„ÎąÎēĪ„ÎˇĪ‚ ÎąÎŊÎŦÎŗÎēÎˇĪ‚." }, "premiumSignUpTwoStepOptions": { "message": "Î ĪĪŒĪƒÎ¸ÎĩĪ„ÎĩĪ‚ ÎĩĪ€ÎšÎģÎŋÎŗÎ­Ī‚ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚ Î´ĪÎŋ βΡÎŧÎŦ΄ΉÎŊ, ĪŒĪ€Ī‰Ī‚ Ī„Îŋ YubiKey ÎēιΚ Ī„Îŋ Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Î‘ĪƒĪ†ÎŦÎģÎĩΚι ÎēĪ‰Î´ÎšÎēĪŽÎŊ, Ī…ÎŗÎĩÎ¯Îą ÎģÎŋÎŗÎąĪÎšÎąĪƒÎŧÎŋĪ ÎēιΚ ÎąÎŊÎąĪ†ÎŋĪÎ­Ī‚ Ī€ÎąĪÎąÎ˛Î¯ÎąĪƒÎˇĪ‚ δÎĩδÎŋÎŧέÎŊΉÎŊ ÎŗÎšÎą ÎŊÎą Î´ÎšÎąĪ„ÎˇĪÎŽĪƒÎĩĪ„Îĩ ÎąĪƒĪ†ÎąÎģÎ­Ī‚ Ī„Îŋ vault ĪƒÎąĪ‚." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "ΤÎŋ ÎąÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎŋ Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎĩ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Î•Ī€ÎąÎŊÎąĪ†Îŋ΁ÎŦ ÎąÎŊĪ„ÎšÎēÎĩΚÎŧέÎŊÎŋĪ…" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "ΔÎĩ Î˛ĪÎ­Î¸ÎˇÎēÎĩ ÎŧÎŋÎŊιδΚÎēΌ ÎąÎŊÎąÎŗÎŊĪ‰ĪÎšĪƒĪ„ÎšÎēΌ." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "ΌÎŊÎŋÎŧÎą ÎŋĪÎŗÎąÎŊÎšĪƒÎŧÎŋĪ" }, @@ -3294,6 +3370,12 @@ "error": { "message": "ÎŖĪ†ÎŦÎģÎŧÎą" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "ÎŖĪ†ÎŦÎģÎŧÎą ÎąĪ€ÎŋÎē΁΅΀΄ÎŋÎŗĪÎŦĪ†ÎˇĪƒÎˇĪ‚" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Î ÎąĪÎŦβÎģÎĩĪˆÎˇ" }, - "importData": { - "message": "Î•ÎšĪƒÎąÎŗĪ‰ÎŗÎŽ δÎĩδÎŋÎŧέÎŊΉÎŊ", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "ÎŖĪ†ÎŦÎģÎŧÎą ÎĩÎšĪƒÎąÎŗĪ‰ÎŗÎŽĪ‚" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "ΠÎĩĪÎšĪƒĪƒĪŒĪ„Îĩ΁ÎĩĪ‚ ÎĩĪ€ÎšÎģÎŋÎŗÎ­Ī‚ - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "ΚÎŋÎŊ΃ΌÎģÎą Î”ÎšÎąĪ‡ÎĩÎšĪÎšĪƒĪ„ÎŽ" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Î‘ĪƒĪ†ÎŦÎģÎĩΚι ÎģÎŋÎŗÎąĪÎšÎąĪƒÎŧÎŋĪ" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "ΕιδÎŋĪ€ÎŋÎšÎŽĪƒÎĩÎšĪ‚" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Î‘Ī€ĪŒÎēĪĪ…ĪˆÎˇ ÎąÎŊÎšĪ‡ÎŊÎĩĪĪƒÎĩΉÎŊ ÎąÎŊĪ„ÎšĪƒĪ„ÎŋÎ¯Ī‡ÎšĪƒÎˇĪ‚ $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Î‘Ī…Ī„ĪŒÎŧÎąĪ„Îˇ ĪƒĪ…ÎŧĪ€ÎģÎŽĪĪ‰ĪƒÎˇ ÎēÎąĪ„ÎŦ Ī„Îˇ Ī†ĪŒĪĪ„Ī‰ĪƒÎˇ Ī„ÎˇĪ‚ ΃ÎĩÎģÎ¯Î´ÎąĪ‚;" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Î•ÎžÎąÎšĪÎĩĪ„ÎšÎēÎŦ Ī†ÎąĪÎ´Ī" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 21149499485..8e2c3279687 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sync" }, - "syncVaultNow": { - "message": "Sync vault now" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Last sync:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Import items" - }, "select": { "message": "Select" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Edit" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Authenticator key (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Attachment saved" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "File" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Select a file" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maximum file size is 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restore item" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin" :{ + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout":{ + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -4878,6 +5001,9 @@ } } }, + "downloadAttachmentLabel": { + "message": "Download Attachment" + }, "downloadBitwarden": { "message": "Download Bitwarden" }, @@ -5017,14 +5143,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5685,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5736,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5950,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5981,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector":{ + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified":{ + "message": "Organization verified" + }, + "domainVerified":{ + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "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 103d45f0685..68cf36cacde 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sync" }, - "syncVaultNow": { - "message": "Sync vault now" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Last sync:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Import items" - }, "select": { "message": "Select" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Edit" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Authenticator key (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Attachment saved" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "File" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Select a file" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maximum file size is 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access" }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restore item" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organisation. Please confirm the domain below with your organisation administrator." - }, "organizationName": { "message": "Organisation name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organisation’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organisation is no longer using master passwords to log into Bitwarden. To continue, verify the organisation and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organisation to log in" + }, + "organizationVerified": { + "message": "Organisation verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organisation, your access to the organisation will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organisation and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organisation." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organisation has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organisation has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organisation has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organisation has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organisation features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ is requiring all items to be owned by the organisation for security and compliance. Click accept to transfer ownership of your items.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 2713381986c..216db1911f2 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sync" }, - "syncVaultNow": { - "message": "Sync vault now" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Last sync:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Import items" - }, "select": { "message": "Select" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Edit" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Authenticator key (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "The attachment has been saved." }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "File" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Select a file." }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maximum file size is 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access" }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Permanently deleted item" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restore item" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organisation. Please confirm the domain below with your organisation administrator." - }, "organizationName": { "message": "Organisation name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organisation’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organisation is no longer using master passwords to log into Bitwarden. To continue, verify the organisation and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organisation to log in" + }, + "organizationVerified": { + "message": "Organisation verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organisation, your access to the organisation will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organisation and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organisation." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organisation has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organisation has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organisation has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organisation has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organisation features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ is requiring all items to be owned by the organisation for security and compliance. Click accept to transfer ownership of your items.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index e19bd11ba28..6eca24db96e 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Iniciar sesiÃŗn con clave de acceso" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Usar inicio de sesiÃŗn Ãēnico" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sincronizar" }, - "syncVaultNow": { - "message": "Sincronizar caja fuerte" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Última sincronizaciÃŗn:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "AplicaciÃŗn web de Bitwarden" }, - "importItems": { - "message": "Importar elementos" - }, "select": { "message": "Seleccionar" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Editar" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Exportar desde" }, - "exportVault": { - "message": "Exportar caja fuerte" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "MÃĄs informaciÃŗn" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Clave de autenticaciÃŗn (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "El adjunto se ha guardado." }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Archivo" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Selecciona un archivo." }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "El tamaÃąo mÃĄximo de archivo es de 500MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB de espacio cifrado en disco para adjuntos." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Acceso de emergencia." }, "premiumSignUpTwoStepOptions": { "message": "Opciones de inicio de sesiÃŗn con autenticaciÃŗn de dos pasos propietarios como YubiKey y Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Higiene de contraseÃąa, salud de la cuenta e informes de violaciones de datos para mantener su caja fuerte segura." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Elemento eliminado de forma permanente" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restaurar elemento" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Identificador Ãēnico no encontrado." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Ya no es necesaria una contraseÃąa maestra para los miembros de la siguiente organizaciÃŗn. Confirma el dominio que aparece a continuaciÃŗn con el administrador de tu organizaciÃŗn." - }, "organizationName": { "message": "Nombre de la organizaciÃŗn" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Error de descifrado" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignorar" }, - "importData": { - "message": "Importar datos", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Error al importar" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "MÃĄs opciones - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Consola de administrador" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Seguridad de la cuenta" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notificaciones" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "ÂŋAutocompletar al cargar la pÃĄgina?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extraancho" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "La contraseÃąa introducida es incorrecta." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 7a9737f71ff..72f9c553569 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Logi sisse pääsuvÃĩtmega" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "SÃŧnkroniseeri" }, - "syncVaultNow": { - "message": "SÃŧnkroniseeri hoidla" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Viimane sÃŧnkronisatsioon:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwardeni veebirakendus" }, - "importItems": { - "message": "Impordi andmed" - }, "select": { "message": "Vali" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Muuda" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Ekspordi hoidla" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Loe edasi" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Autentimise vÃĩti (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Manus on salvestatud." }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Fail" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Vali fail." }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maksimaalne faili suurus on 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB ulatuses krÃŧpteeritud salvestusruum." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Parooli hÃŧgieen, konto seisukord ja andmelekete raportid aitavad hoidlat turvalisena hoida." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Kirje on jäädavalt kustutatud" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Taasta kirje" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Unikaalset identifikaatorit ei leitud." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Viga" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index b61213da989..04e673d2230 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sinkronizatu" }, - "syncVaultNow": { - "message": "Sinkronizatu kutxa gotorra orain" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Azken sinkronizazioa:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Inportatu elementuak" - }, "select": { "message": "Hautatu" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Editatu" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Esportatu kutxa gotorra" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Gehiago ikasi" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Autentifikazio-gakoa (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Eranskina gorde da." }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Fitxategia" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Hautatu fitxategia." }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Eranskinaren gehienezko tamaina 500MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "Eranskinentzako 1GB-eko zifratutako biltegia." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Pasahitzaren higienea, kontuaren egoera eta datu-bortxaketen txostenak, kutxa gotorra seguru mantentzeko." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Elementua betirako ezabatua" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Berreskuratu elementua" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Ez da identifikatzaile bakarrik aurkitu." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Akatsa" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ezikusi" }, - "importData": { - "message": "Inportatu datuak", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Errorea inportatzerakoan" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 550de006cf6..a3ea290de39 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "با ÚŠŲ„ÛŒØ¯ ØšØ¨ŲˆØą ŲˆØ§ØąØ¯ Ø´ŲˆÛŒØ¯" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Ø§ØŗØĒŲØ§Ø¯Ų‡ Ø§Ø˛ ŲˆØąŲˆØ¯ ØĒÚŠ Ų…ØąØ­Ų„Ų‡â€ŒØ§ÛŒ" }, @@ -436,8 +439,8 @@ "sync": { "message": "Ų‡Ų…Ú¯Ø§Ų…â€ŒØŗØ§Ø˛ÛŒ" }, - "syncVaultNow": { - "message": "Ų‡Ų…Ú¯Ø§Ų…â€ŒØŗØ§Ø˛ÛŒ Ú¯Ø§ŲˆØĩŲ†Ø¯ŲˆŲ‚" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "ØĸØŽØąÛŒŲ† Ų‡Ų…Ú¯Ø§Ų…â€ŒØŗØ§Ø˛ÛŒ:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Ø¨ØąŲ†Ø§Ų…Ų‡ ŲˆØ¨ Bitwarden" }, - "importItems": { - "message": "Ø¯ØąŲˆŲ† ØąÛŒØ˛ÛŒ Ų…ŲˆØ§ØąØ¯" - }, "select": { "message": "Ø§Ų†ØĒ؎اب" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "ŲˆÛŒØąØ§ÛŒØ´" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Ø¨ØąŲˆŲ† ØąÛŒØ˛ÛŒ Ø§Ø˛" }, - "exportVault": { - "message": "Ø¨ØąŲˆŲ† ØąÛŒØ˛ÛŒ Ú¯Ø§ŲˆØĩŲ†Ø¯ŲˆŲ‚" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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": "ŲØąŲ…ØĒ ŲžØąŲˆŲ†Ø¯Ų‡" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "بیشØĒØą Ø¨Ø¯Ø§Ų†ÛŒØ¯" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "ÚŠŲ„ÛŒØ¯ Ø§Ø­ØąØ§Ø˛ Ų‡ŲˆÛŒØĒ (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "ŲžÛŒŲˆØŗØĒ Ø°ØŽÛŒØąŲ‡ شد" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "ŲžØąŲˆŲ†Ø¯Ų‡" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "īēīģ§īē˜īē¨īēŽīē ÛŒÚŠ īēŽīģ­īģ§īēĒīģŠ" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "بیشØĒØąÛŒŲ† Ø­ØŦŲ… ŲžØąŲˆŲ†Ø¯Ų‡ ÛĩÛ°Û° Ų…Ú¯Ø§Ø¨Ø§ÛŒØĒ Ø§ØŗØĒ." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "Ûą گیگابایØĒ ؁Øļای Ø°ØŽÛŒØąŲ‡â€ŒØŗØ§Ø˛ÛŒ ØąŲ…Ø˛Ú¯Ø°Ø§ØąÛŒ Ø´Ø¯Ų‡ Ø¨ØąØ§ÛŒ ŲžÛŒŲˆØŗØĒâ€ŒŲ‡Ø§ÛŒ ŲžØąŲˆŲ†Ø¯Ų‡." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Ø¯ØŗØĒØąØŗÛŒ اØļØˇØąØ§ØąÛŒ." }, "premiumSignUpTwoStepOptions": { "message": "Ú¯Ø˛ÛŒŲ†Ų‡â€ŒŲ‡Ø§ÛŒ ŲˆØąŲˆØ¯ اØļØ§ŲÛŒ Ø¯Ųˆ Ų…ØąØ­Ų„Ų‡â€ŒØ§ÛŒ Ų…Ø§Ų†Ų†Ø¯ YubiKey ؈ Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Ú¯Ø˛Ø§ØąØ´â€ŒŲ‡Ø§ÛŒ Ø¨Ų‡Ø¯Ø§Ø´ØĒ ÚŠŲ„Ų…Ų‡ ØšØ¨ŲˆØąØŒ ØŗŲ„Ø§Ų…ØĒ Ø­ØŗØ§Ø¨ ÚŠØ§ØąØ¨ØąÛŒ ؈ Ų†Ų‚Øļ Ø¯Ø§Ø¯Ų‡â€ŒŲ‡Ø§ Ø¨ØąØ§ÛŒ Ø§ÛŒŲ…Ų† Ų†Ú¯Ų‡Ø¯Ø§Ø´ØĒŲ† Ú¯Ø§ŲˆØĩŲ†Ø¯ŲˆŲ‚ Ø´Ų…Ø§." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Ų…ŲˆØąØ¯ Ø¨ØąØ§ÛŒ Ų‡Ų…ÛŒØ´Ų‡ Ø­Ø°Ų شد" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Ø¨Ø§Ø˛ÛŒØ§Ø¨ÛŒ Ų…ŲˆØąØ¯" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Ø´Ų†Ø§ØŗŲ‡ Ų…Ų†Ø­ØĩØą Ø¨Ų‡ ŲØąØ¯ÛŒ ÛŒØ§ŲØĒ Ų†Ø´Ø¯." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Ø¨ØąØ§ÛŒ اؚØļای ØŗØ§Ø˛Ų…Ø§Ų† Ø˛ÛŒØąØŒ ÚŠŲ„Ų…Ų‡ ØšØ¨ŲˆØą اØĩŲ„ÛŒ Ø¯ÛŒÚ¯Øą Ų„Ø§Ø˛Ų… Ų†ÛŒØŗØĒ. Ų„ØˇŲØ§Ų‹ Ø¯Ø§Ų…Ų†Ų‡ Ø˛ÛŒØą ØąØ§ با Ų…Ø¯ÛŒØą ØŗØ§Ø˛Ų…Ø§Ų† ØŽŲˆØ¯ ØĒØŖÛŒÛŒØ¯ ÚŠŲ†ÛŒØ¯." - }, "organizationName": { "message": "Ų†Ø§Ų… ØŗØ§Ø˛Ų…Ø§Ų†" }, @@ -3294,6 +3370,12 @@ "error": { "message": "ØŽØˇØ§" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "ØŽØˇØ§ÛŒ ØąŲ…Ø˛Ú¯Ø´Ø§ÛŒÛŒ" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ų†Ø§Ø¯ÛŒØ¯Ų‡ Ú¯ØąŲØĒŲ†" }, - "importData": { - "message": "ŲˆØ§ØąØ¯ ÚŠØąØ¯Ų† Ø§ØˇŲ„Ø§ØšØ§ØĒ", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "ØŽØˇØ§ÛŒ Ø¯ØąŲˆŲ† ØąÛŒØ˛ÛŒ" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "Ú¯Ø˛ÛŒŲ†Ų‡â€ŒŲ‡Ø§ÛŒ بیشØĒØą - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "ÚŠŲ†ØŗŲˆŲ„ Ų…Ø¯ÛŒØą" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Ø§Ų…Ų†ÛŒØĒ Ø­ØŗØ§Ø¨ ÚŠØ§ØąØ¨ØąÛŒ" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Ø§ØšŲ„Ø§Ų†â€ŒŲ‡Ø§" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Ų…ØŽŲÛŒ ÚŠØąØ¯Ų† Ø´Ų†Ø§ØŗØ§ÛŒÛŒ ØĒØˇØ§Ø¨Ų‚ $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "ŲžØą ÚŠØąØ¯Ų† ØŽŲˆØ¯ÚŠØ§Øą Ų‡Ų†Ú¯Ø§Ų… Ø¨Ø§ØąÚ¯Ø°Ø§ØąÛŒ ØĩŲØ­Ų‡ØŸ" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "ØŽÛŒŲ„ÛŒ ØšØąÛŒØļ" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "ÚŠŲ„Ų…Ų‡ ØšØ¨ŲˆØą ŲˆØ§ØąØ¯ Ø´Ø¯Ų‡ اشØĒØ¨Ø§Ų‡ Ø§ØŗØĒ." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index bdf2ebd641c..0e19e256714 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Kirjaudu pääsyavaimella" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Käytä kertakirjautumista" }, @@ -436,8 +439,8 @@ "sync": { "message": "Synkronointi" }, - "syncVaultNow": { - "message": "Synkronoi holvi nyt" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Viimeisin synkronointi:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden Verkkosovellus" }, - "importItems": { - "message": "Tuo kohteita" - }, "select": { "message": "Valitse" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Arkistoi kohde" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Muokkaa" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Vie lähteestä" }, - "exportVault": { - "message": "Vie holvi" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Lue lisää" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Todennusavain (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Tiedostoliite tallennettiin" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Tiedosto" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Valitse tiedosto." }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Tiedoston enimmäiskoko on 500 Mt." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 Gt salattua tallennustilaa tiedostoliitteille." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "VarmuuskäyttÃļ" }, "premiumSignUpTwoStepOptions": { "message": "Kaksivaiheisen kirjautumisen erikoisvaihtoehdot, kuten YubiKey ja Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Salasanahygienian, tilin terveyden ja tietovuotojen raportointitoiminnot pitävät holvisi turvassa." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Kohde poistettiin pysyvästi" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Palauta kohde" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "YksilÃļllistä tunnistetta ei lÃļytynyt." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Pääsalasanaa ei enää tarvita tämän organisaation jäsenille. Ole hyvä ja vahvista alla oleva verkkotunnus organisaation ylläpitäjän kanssa." - }, "organizationName": { "message": "Organisaation nimi" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Virhe" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Salauksen purkuvirhe" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ohita" }, - "importData": { - "message": "Tuo tietoja", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Tuontivirhe" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "Lisää valintoja - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Hallintapaneelista" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Tilin suojaus" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Ilmoitukset" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Piilota vastaavuuden tunnistus $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "AutomaattitäytetäänkÃļ sivun avautuessa?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Erittäin leveä" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "SyÃļttämäsi salasana on virheellinen." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Kortin numero" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 31284265b2e..b44f5210ccd 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "Ikintal" }, - "syncVaultNow": { - "message": "Isingit ang Vault ngayon" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Huling sinkronisasyon:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Isingit ang Vault ngayon" - }, "select": { "message": "Piliin" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "I-edit" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "I-export vault" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Matuto nang higit pa" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Susi ng Authenticator (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Attachment na nai-save" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Mag-file" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Pumili ng File" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maximum na laki ng file ay 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage para sa mga file attachment." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Pagmamay-ari na dalawang hakbang na opsyon sa pag-log in gaya ng YubiKey at Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Pasahod higiyena, kalusugan ng account, at mga ulat sa data breach upang panatilihing ligtas ang iyong vault." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Item permanenteng tinanggal" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Ibalik ang item" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Walang natagpuang natatanging nag-identipikar." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Mali" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index dadcd0c041e..6b5348f564f 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Se connecter avec une clÊ d'accès" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Utiliser l'authentification unique" }, @@ -436,8 +439,8 @@ "sync": { "message": "Synchronisation" }, - "syncVaultNow": { - "message": "Synchroniser le coffre maintenant" + "syncNow": { + "message": "Synchroniser maintenant" }, "lastSync": { "message": "Dernière synchronisation :" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Application web Bitwarden" }, - "importItems": { - "message": "Importer des ÊlÊments" - }, "select": { "message": "SÊlectionner" }, @@ -576,17 +576,29 @@ "itemWasSentToArchive": { "message": "L'ÊlÊment a ÊtÊ envoyÊ à l'archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "L'ÊlÊment a ÊtÊ dÊsarchivÊ" }, "archiveItem": { "message": "Archiver l'ÊlÊment" }, - "archiveItemConfirmDesc": { - "message": "Les ÊlÊments archivÊs sont exclus des rÊsultats de recherche gÊnÊraux et des suggestions de remplissage automatique. Êtes-vous sÃģr de vouloir archiver cet ÊlÊment ?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { - "message": "A premium membership is required to use Archive." + "message": "Une adhÊsion premium est requise pour utiliser Archive." + }, + "itemRestored": { + "message": "Item has been restored" }, "edit": { "message": "Modifier" @@ -598,7 +610,7 @@ "message": "Tout afficher" }, "showAll": { - "message": "Show all" + "message": "Tout afficher" }, "viewLess": { "message": "Afficher moins" @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Exporter à partir de" }, - "exportVault": { - "message": "Exporter le coffre" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "En savoir plus" }, + "migrationsFailed": { + "message": "Une erreur s'est produite lors de la mise à jour des paramètres de chiffrement." + }, + "updateEncryptionSettingsTitle": { + "message": "Mettre à jour vos paramètres de chiffrement" + }, + "updateEncryptionSettingsDesc": { + "message": "Les nouveaux paramètres de chiffrement recommandÊs amÊlioreront la sÊcuritÊ de votre compte. Entrez votre mot de passe principal pour faire la mise à jour maintenant." + }, + "confirmIdentityToContinue": { + "message": "Confirmez votre identitÊ pour continuer" + }, + "enterYourMasterPassword": { + "message": "Entrez votre mot de passe principal" + }, + "updateSettings": { + "message": "Mettre à jour les paramètres" + }, + "later": { + "message": "Plus tard" + }, "authenticatorKeyTotp": { "message": "ClÊ Authenticator (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "La pièce jointe a ÊtÊ enregistrÊe." }, + "fixEncryption": { + "message": "Corriger le chiffrement" + }, + "fixEncryptionTooltip": { + "message": "Ce fichier utilise une mÊthode de chiffrement obsolète." + }, + "attachmentUpdated": { + "message": "Pièce jointe mise à jour" + }, "file": { "message": "Fichier" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "SÊlectionnez un fichier." }, + "itemsTransferred": { + "message": "ÉlÊments transfÊrÊs" + }, "maxFileSize": { "message": "La taille maximale du fichier est de 500 Mo." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 Go de stockage chiffrÊ pour les fichiers joints." }, + "premiumSignUpStorageV2": { + "message": "Stockage chiffrÊ de $SIZE$ pour les pièces jointes.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Accès d'urgence." }, "premiumSignUpTwoStepOptions": { "message": "Options de connexion propriÊtaires à deux facteurs telles que YubiKey et Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Hygiène du mot de passe, santÊ du compte et rapports sur les brèches de donnÊes pour assurer la sÊcuritÊ de votre coffre." }, @@ -1874,7 +1950,7 @@ "message": "AnnÊe d'expiration" }, "monthly": { - "message": "month" + "message": "mois" }, "expiration": { "message": "Expiration" @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "ÉlÊment dÊfinitivement supprimÊ" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restaurer l'ÊlÊment" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Aucun identifiant unique trouvÊ." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Un mot de passe principal n’est plus requis pour les membres de l’organisation suivante. Veuillez confirmer le domaine ci-dessous auprès de l'administrateur de votre organisation." - }, "organizationName": { "message": "Nom de l'organisation" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Erreur" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Erreur de dÊchiffrement" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignorer" }, - "importData": { - "message": "Importer des donnÊes", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Erreur lors de l'importation" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "Plus d'options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Console Admin" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "SÊcuritÊ du compte" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Masquer la dÊtection de correspondance $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Saisir automatiquement lors du chargement de la page ?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Très large" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "Le mot de passe saisi est incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Cet identifiant est à risques et manque un site web. Ajoutez un site web et changez le mot de passe pour une meilleure sÊcuritÊ." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Site Web manquant" }, @@ -5818,17 +5947,17 @@ "andMoreFeatures": { "message": "Et encore plus !" }, - "planDescPremium": { - "message": "SÊcuritÊ en ligne complète" + "advancedOnlineSecurity": { + "message": "SÊcuritÊ en ligne avancÊe" }, "upgradeToPremium": { "message": "Mettre à niveau vers Premium" }, "unlockAdvancedSecurity": { - "message": "Unlock advanced security features" + "message": "DÊbloquer les fonctionnalitÊs de sÊcuritÊ avancÊes" }, "unlockAdvancedSecurityDesc": { - "message": "A Premium subscription gives you more tools to stay secure and in control" + "message": "Un abonnement Premium vous donne plus d'outils pour rester en sÊcuritÊ et en contrôle" }, "explorePremium": { "message": "Explorer Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "NumÊro de carte" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Votre organisation n'utilise plus les mots de passe principaux pour se connecter à Bitwarden. Pour continuer, vÊrifiez l'organisation et le domaine." + }, + "continueWithLogIn": { + "message": "Continuer avec la connexion" + }, + "doNotContinue": { + "message": "Ne pas continuer" + }, + "domain": { + "message": "Domaine" + }, + "keyConnectorDomainTooltip": { + "message": "Ce domaine stockera les clÊs de chiffrement de votre compte, alors assurez-vous que vous lui faites confiance. Si vous n'ÃĒtes pas sÃģr, vÊrifiez auprès de votre administrateur." + }, + "verifyYourOrganization": { + "message": "VÊrifiez votre organisation pour vous connecter" + }, + "organizationVerified": { + "message": "Organisation vÊrifiÊe" + }, + "domainVerified": { + "message": "Domaine vÊrifiÊ" + }, + "leaveOrganizationContent": { + "message": "Si vous ne vÊrifiez pas votre organisation, votre accès à l'organisation sera rÊvoquÊ." + }, + "leaveNow": { + "message": "Quitter maintenant" + }, + "verifyYourDomainToLogin": { + "message": "VÊrifiez votre domaine pour vous connecter" + }, + "verifyYourDomainDescription": { + "message": "Pour continuer à vous connecter, vÊrifiez ce domaine." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Pour continuer à vous connecter, vÊrifiez l'organisation et le domaine." + }, "sessionTimeoutSettingsAction": { "message": "Action à l’expiration" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Ce paramètre est gÊrÊ par votre organisation." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Votre organisation a rÊglÊ le dÊlai d'expiration de session maximal à $HOURS$ heure(s) et $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Votre organisation a dÊfini le dÊlai d'expiration de session par dÊfaut sur ImmÊdiat." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Votre organisation a dÊfini le dÊlai d'expiration de session par dÊfaut sur Au verrouillage du système." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Votre organisation a dÊfini le dÊlai d'expiration de session par dÊfaut sur Au redÊmarrage du navigateur." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Le dÊlai d'expiration de session maximal ne peut pas dÊpasser $HOURS$ heure(s) et $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Au redÊmarrage du navigateur" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Configurez une mÊthode de dÊverrouillage pour changer le dÊlai d'expiration de votre coffre" + }, + "upgrade": { + "message": "Mettre à jour" + }, + "leaveConfirmationDialogTitle": { + "message": "Êtes-vous sÃģr de vouloir quitter ?" + }, + "leaveConfirmationDialogContentOne": { + "message": "En refusant, vos ÊlÊments personnels resteront dans votre compte, mais vous perdrez l'accès aux ÊlÊments partagÊs et aux fonctionnalitÊs de l'organisation." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contactez votre administrateur pour regagner l'accès." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Quitter $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Comment gÊrer mon coffre ?" + }, + "transferItemsToOrganizationTitle": { + "message": "TransfÊrer les ÊlÊments vers $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ exige que tous les ÊlÊments soient dÊtenus par l’organisation pour des raisons de sÊcuritÊ et de conformitÊ. Cliquez sur Accepter pour transfÊrer la propriÊtÊ de vos ÊlÊments.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index bd657b9d4b7..faf9faf755d 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Iniciar sesiÃŗn con Clave de acceso" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Usar inicio de sesiÃŗn Ãēnico" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sincronizar" }, - "syncVaultNow": { - "message": "Sincronizar caixa forte agora" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Última sincronizaciÃŗn:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "AplicaciÃŗn web de Bitwarden" }, - "importItems": { - "message": "Importar entradas" - }, "select": { "message": "Seleccionar" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Editar" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Exportar dende" }, - "exportVault": { - "message": "Exportar caixa forte" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "MÃĄis informaciÃŗn" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Clave de autenticaciÃŗn (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Anexo gardado" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Arquivo" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Selecciona un arquivo" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "O tamaÃąo mÃĄximo Ê de 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB de almacenamento cifrado para arquivos anexos." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Acceso de emerxencia." }, "premiumSignUpTwoStepOptions": { "message": "OpciÃŗns de verificaciÃŗn en 2 pasos privadas tales coma YubiKey ou Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Limpeza de contrasinais, saÃēde de contas e informes de filtraciÃŗn de datos para manter a tÃēa caixa forte segura." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Entrada eliminada permanente" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restaurar entrada" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Non se atopou ningÃēn identificador Ãēnico." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Erro" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Erro de descifrado" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignorar" }, - "importData": { - "message": "Importar datos", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Erro Ãŗ importar" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "MÃĄis opciÃŗns - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Consola do administrador" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Seguridade da conta" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "NotificaciÃŗns" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Agochar detecciÃŗn de coincidencia $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autoencher Ãŗ cargar a pÃĄxina?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Moi ancho" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 2e9243206b9..3d953f508a1 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "כניסה ×ĸם מפ×Ēח גישה" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "הש×Ēמ׊ בכניסה יחידה" }, @@ -436,8 +439,8 @@ "sync": { "message": "סנכרן" }, - "syncVaultNow": { - "message": "סנכרן א×Ē ×”×›×Ą×¤×Ē ×ĸכשיו" + "syncNow": { + "message": "סנכרון כ×ĸ×Ē" }, "lastSync": { "message": "סנכרון אחרון:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "יישום הרש×Ē ×Š×œ Bitwarden" }, - "importItems": { - "message": "ייבא פריטים" - }, "select": { "message": "בחר" }, @@ -576,17 +576,29 @@ "itemWasSentToArchive": { "message": "הפריט נשלח לארכיון" }, + "itemWasUnarchived": { + "message": "הפריט שוחזר מהארכיב" + }, "itemUnarchived": { "message": "הפריט הוסר מהארכיון" }, "archiveItem": { "message": "ה×ĸבר פריט לארכיון" }, - "archiveItemConfirmDesc": { - "message": "פריטים בארכיון מוחרגים מ×Ēו×Ļאו×Ē ×—×™×¤×•×Š כללי וה×Ļ×ĸו×Ē ×œ×ž×™×œ×•×™ אוטומטי. האם א×Ēה בטוח שבר×Ļונך לה×ĸביר פריט זה לארכיון?" + "archiveItemDialogContent": { + "message": "×ĸם ארכובו, יהיה הפריט מוחרג מ×Ēו×Ļאו×Ē ×”×—×™×¤×•×Š ומה×Ļ×ĸו×Ē ×”×ž×™×œ×•×™ האוטומטי." + }, + "archived": { + "message": "הו×ĸבר לארכיב" + }, + "unarchiveAndSave": { + "message": "שחזור מהארכיב ושמירה" }, "upgradeToUseArchive": { - "message": "A premium membership is required to use Archive." + "message": "נדרש×Ē ×—×‘×¨×•×Ē ×¤×¨×ž×™×•× כדי להש×Ēמ׊ בארכיב." + }, + "itemRestored": { + "message": "הפריט שוחזר" }, "edit": { "message": "×ĸרוך" @@ -598,7 +610,7 @@ "message": "ה×Ļג הכל" }, "showAll": { - "message": "Show all" + "message": "ה×Ļג×Ē ×”×›×œ" }, "viewLess": { "message": "ה×Ļג פחו×Ē" @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "יי×Ļא מ־" }, - "exportVault": { - "message": "יי×Ļא כספ×Ē" + "exportVerb": { + "message": "יי×Ļוא", + "description": "The verb form of the word Export" + }, + "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": "פורמט הקוב×Ĩ" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "למיד×ĸ × ×•×Ą×Ŗ" }, + "migrationsFailed": { + "message": "איר×ĸה שגיאה ב×ĸ×Ē ×ĸדכון הגדרו×Ē ×”×”×Ļפנה." + }, + "updateEncryptionSettingsTitle": { + "message": "×ĸדכון הגדרו×Ē ×”×”×Ļפנה שלך" + }, + "updateEncryptionSettingsDesc": { + "message": "הגדרו×Ē ×”×”×Ļפנה המומל×Ļו×Ē ×”×—×“×Š×•×Ē ×™×Š×¤×¨×• א×Ē ××‘×˜×—×Ē ×”×—×Š×‘×•×Ÿ שלך. יש להזין א×Ē ×”×Ą×™×Ą×ž×” הראשי×Ē ×Š×œ×š כדי ל×ĸדכן כ×ĸ×Ē." + }, + "confirmIdentityToContinue": { + "message": "יש לאשר א×Ē ×–×”×•×Ēך כדי להמשיך" + }, + "enterYourMasterPassword": { + "message": "נא להזין א×Ē ×”×Ą×™×Ą×ž×” הראשי×Ē ×Š×œ×š" + }, + "updateSettings": { + "message": "×ĸדכון ההגדרו×Ē" + }, + "later": { + "message": "מאוחר יו×Ēר" + }, "authenticatorKeyTotp": { "message": "מפ×Ēח מאמ×Ē (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "ה×Ļרופה נשמרה" }, + "fixEncryption": { + "message": "×Ēיקון הה×Ļפנה" + }, + "fixEncryptionTooltip": { + "message": "הקוב×Ĩ מוגדר בשיט×Ē ×”×Ļפנה לא ×ĸדכני×Ē." + }, + "attachmentUpdated": { + "message": "ה×Ļרופה ×ĸודכנה" + }, "file": { "message": "קוב×Ĩ" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "בחר קוב×Ĩ" }, + "itemsTransferred": { + "message": "הפריטים הו×ĸברו" + }, "maxFileSize": { "message": "גודל הקוב×Ĩ המרבי הוא 500MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 ג'יגה של מקום אחסון ×ĸבור קב×Ļים מ×Ļורפים." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ של אחסון מו×Ļפן ×ĸבור קב×Ļים מ×Ļורפים.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "גיש×Ē ×—×™×¨×•×." }, "premiumSignUpTwoStepOptions": { "message": "אפשרויו×Ē ×›× ×™×Ą×” דו־שלבי×Ē ×§× ×™×™× ×™×•×Ē ×›×’×•×Ÿ YubiKey ו־Duo." }, + "premiumSubscriptionEnded": { + "message": "מנוי הפרמיום שלך הס×Ēיים" + }, + "archivePremiumRestart": { + "message": "לשחזור הגישה לארכיב שלך יש לחדש א×Ē ×ž× ×•×™ הפרמיום שלך. אם ×Ēב×Ļ×ĸו ×ĸריכ×Ē ×¤×¨×˜×™× של פריט בארכיב לפני חידוש המנוי, הפריט ישוחזר אל הכספ×Ē ×Š×œ×›×." + }, + "restartPremium": { + "message": "חידוש מנוי הפרמיום" + }, "ppremiumSignUpReports": { "message": "היגיינ×Ē ×Ą×™×Ą×ž××•×Ē, מ×Ļב בריאו×Ē ×”×—×Š×‘×•×Ÿ, ודיווחים מ×ĸודכנים ×ĸל פר×Ļו×Ē ×—×“×Š×•×Ē ×‘×›×“×™ לשמור ×ĸל הכספ×Ē ×Š×œ×š בטוחה." }, @@ -1874,7 +1950,7 @@ "message": "׊נ×Ē ×Ēפוגה" }, "monthly": { - "message": "month" + "message": "חודש" }, "expiration": { "message": "×Ēוקת" @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "הפריט נמחק ל×Ļמי×Ēו×Ē" }, + "archivedItemRestored": { + "message": "פריט שוחזר מהארכיב" + }, "restoreItem": { "message": "שחזר פריט" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "לא נמ×Ļא מזהה ייחודי." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "סיסמה ראשי×Ē ××™× ×” נדרש×Ē ×ĸוד ×ĸבור חברים בארגון הבא. נא לאשר א×Ē ×”×“×•×ž×™×™×Ÿ שלהלן ×ĸם מנהל הארגון שלך." - }, "organizationName": { "message": "׊ם הארגון" }, @@ -3294,6 +3370,12 @@ "error": { "message": "שגיאה" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "שגיא×Ē ×¤×ĸנוח" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "ה×Ē×ĸלם" }, - "importData": { - "message": "ייבא × ×Ēונים", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "שגיא×Ē ×™×™×‘×•×" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "×ĸוד אפשרויו×Ē - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "×ž×Ą×•×Ŗ מנהל" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "אבטח×Ē ×”×—×Š×‘×•×Ÿ" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "ה×Ēראו×Ē" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "הס×Ēר זיהוי ה×Ēאמה $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "למלא אוטומטי×Ē ×‘×ĸ×Ē ×˜×ĸינ×Ē ×ĸמוד?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "רחב במיוחד" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "הסיסמה שהזנ×Ē ×Š×’×•×™×”." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "כניסה זו נמ×Ļא×Ē ×‘×Ą×™×›×•×Ÿ וחסר בה א×Ēר אינטרנט. ×”×•×Ą×Ŗ א×Ēר אינטרנט ושנה א×Ē ×”×Ą×™×Ą×ž×” לאבטחה חזקה יו×Ēר." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "לא נמ×Ļא א×Ēר אינטרנט" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "ו×ĸוד!" }, - "planDescPremium": { - "message": "השלם אבטחה מקוונ×Ē" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "שדרג לפרימיום" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "מספר כרטיס" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "פ×ĸול×Ē ×¤×Ą×§ זמן" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 58db7ac8ad6..ea0eb362a0d 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "⤏ā¤ŋ⤂⤗⤞ ā¤¸ā¤žā¤‡ā¤¨-⤑⤍ ā¤ĒāĨā¤°ā¤¯āĨ‹ā¤— ⤕⤰āĨ‡ā¤‚" }, @@ -436,8 +439,8 @@ "sync": { "message": "⤏ā¤ŋ⤂⤕" }, - "syncVaultNow": { - "message": "Sync Vault Now" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Last Sync:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Import Items" - }, "select": { "message": "⤚⤝⤍ ⤕⤰āĨ‡ā¤‚" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "⤏⤂ā¤Ēā¤žā¤Ļ⤍ ⤕⤰āĨ‡ā¤‚" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export Vault" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "⤅⤧ā¤ŋ⤕ ā¤œā¤žā¤¨āĨ‡ā¤‚" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Authenticator Key (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "ā¤…ā¤ŸāĨˆā¤šā¤ŽāĨ‡ā¤‚ā¤Ÿ ā¤Ŧ⤚ ā¤—ā¤¯ā¤ž ā¤šāĨˆāĨ¤" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "ā¤Ģā¤ŧā¤žā¤‡ā¤˛" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "ā¤Ģā¤ŧā¤žā¤‡ā¤˛ ā¤•ā¤ž ⤚⤝⤍ ⤕⤰āĨ‡ā¤‚āĨ¤" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "⤅⤧ā¤ŋā¤•ā¤¤ā¤Ž ā¤Ģā¤žā¤‡ā¤˛ ā¤†ā¤•ā¤žā¤° 500 MB ā¤šāĨˆāĨ¤" }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB of encrypted file storage." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "⤅ā¤Ē⤍āĨ€ ā¤ĩāĨ‰ā¤˛āĨā¤Ÿ ⤕āĨ‹ ⤏āĨā¤°ā¤•āĨā¤ˇā¤ŋ⤤ ⤰⤖⤍āĨ‡ ⤕āĨ‡ ⤞ā¤ŋā¤ ā¤Ēā¤žā¤¸ā¤ĩ⤰āĨā¤Ą ⤏āĨā¤ĩ⤚āĨā¤›ā¤¤ā¤ž, ā¤–ā¤žā¤¤ā¤ž ⤏āĨā¤ĩā¤žā¤¸āĨā¤ĨāĨā¤¯ ⤔⤰ ā¤ĄāĨ‡ā¤Ÿā¤ž ⤉⤞āĨā¤˛ā¤‚ā¤˜ā¤¨ ⤰ā¤ŋā¤ĒāĨ‹ā¤°āĨā¤ŸāĨ¤" }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "⤏āĨā¤Ĩā¤žā¤¯āĨ€ ⤰āĨ‚ā¤Ē ⤏āĨ‡ ā¤†ā¤‡ā¤Ÿā¤Ž ā¤šā¤Ÿā¤žā¤ā¤‚" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "ā¤†ā¤‡ā¤Ÿā¤Ž ā¤Ŧā¤šā¤žā¤˛ ⤕⤰āĨ‡ā¤‚" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "ā¤ā¤°ā¤°" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "⤉⤍āĨā¤¨ā¤¤ ā¤‘ā¤¨ā¤˛ā¤žā¤‡ā¤¨ ⤏āĨā¤°ā¤•āĨā¤ˇā¤ž" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 8a5a09aec9c..b7dbed3dcc0 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Prijava pristupnim ključem" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Jedinstvena prijava (SSO)" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sinkronizacija" }, - "syncVaultNow": { - "message": "Odmah sinkroniziraj trezor" + "syncNow": { + "message": "Sinkroniziraj" }, "lastSync": { "message": "Posljednja sinkronizacija:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web trezor" }, - "importItems": { - "message": "Uvoz stavki" - }, "select": { "message": "Odaberi" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Stavka poslana u arhivu" }, + "itemWasUnarchived": { + "message": "Stavka vraćena iz arhive" + }, "itemUnarchived": { "message": "Stavka vraćena iz arhive" }, "archiveItem": { "message": "Arhiviraj stavku" }, - "archiveItemConfirmDesc": { - "message": "Arhivirane stavke biti će izuzete iz rezultata općih pretraga i preporuka auto-ispune. Sigurno ÅželiÅĄ arhivirati?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Arhivirano" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Stavka je vraćena" + }, "edit": { "message": "Uredi" }, @@ -598,7 +610,7 @@ "message": "Vidi sve" }, "showAll": { - "message": "Show all" + "message": "PrikaÅži sve" }, "viewLess": { "message": "Vidi manje" @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Izvezi iz" }, - "exportVault": { - "message": "Izvezi trezor" + "exportVerb": { + "message": "Izvoz", + "description": "The verb form of the word Export" + }, + "exportNoun": { + "message": "Izvoz", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Uvoz", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Uvoz", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Format datoteke" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Saznaj viÅĄe" }, + "migrationsFailed": { + "message": "Dogodila se greÅĄka pri aÅžuriranju postavki ÅĄifriranja." + }, + "updateEncryptionSettingsTitle": { + "message": "AÅžuriraj svoje postakve ÅĄifriranja" + }, + "updateEncryptionSettingsDesc": { + "message": "Nove preporučene postavke ÅĄifriranja poboljÅĄat će sigurnost tvojeg računa. Za aÅžuriranje, unesi svoju glavnu lozinku." + }, + "confirmIdentityToContinue": { + "message": "Za nastavak, potvrdi svoj identitet" + }, + "enterYourMasterPassword": { + "message": "Unesi svoju glavnu lozinku" + }, + "updateSettings": { + "message": "AÅžuriraj postavke" + }, + "later": { + "message": "Kasnije" + }, "authenticatorKeyTotp": { "message": "Ključ autentifikatora (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Privitak spremljen" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Privitak aÅžuriran" + }, "file": { "message": "Datoteka" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Odaberi datoteku." }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Najveća veličina datoteke je 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB ÅĄifriranog prostora za pohranu podataka." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ ÅĄifriranog prostora za privitke.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Pristup u nuÅždi." }, "premiumSignUpTwoStepOptions": { "message": "Mogućnosti za prijavu u dva koraka kao ÅĄto su YubiKey i Duo." }, + "premiumSubscriptionEnded": { + "message": "Toja Premium pretplata je zavrÅĄila" + }, + "archivePremiumRestart": { + "message": "Za ponovni pristup svojoj arhivi, ponovno pokreni Premium pretplatu. Ako urediÅĄ detalje arhivirane stavke prije ponovnog pokretanja, ona će biti vraćena u tvoj trezor." + }, + "restartPremium": { + "message": "Ponovno Pokreni Premium" + }, "ppremiumSignUpReports": { "message": "Higijenu lozinki, zdravlje računa i izvjeÅĄtaje o krađi podatak radi zaÅĄtite svojeg trezora." }, @@ -1874,7 +1950,7 @@ "message": "Godina isteka" }, "monthly": { - "message": "month" + "message": "mjesečno" }, "expiration": { "message": "Istek" @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Stavka trajno izbrisana" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Vrati stavku" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Nije nađen jedinstveni identifikator." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Glavna lozinka viÅĄe nije obavezna za članove sljedeće organizacije. Provjeri prikazanu domenu sa svojim administratorom." - }, "organizationName": { "message": "Naziv Organizacije" }, @@ -3294,6 +3370,12 @@ "error": { "message": "PogreÅĄka" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "PogreÅĄka pri deÅĄifriranju" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Zanemari" }, - "importData": { - "message": "Uvezi podatke", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "GreÅĄka prilikom uvoza" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "ViÅĄe mogućnosti - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Konzola administratora" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Dostupno sada" + }, "accountSecurity": { "message": "Sigurnost računa" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Obavijesti" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Sakrij otkrivanje podudaranja $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Auto-ispuna kod učitavanja?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Ekstra ÅĄiroko" }, + "narrow": { + "message": "Usko" + }, "sshKeyWrongPassword": { "message": "Unesena lozinka nije ispravna." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Ova prijava je ugroÅžena i nedostaje joj web-stranica. Dodaj web-stranicu i promijeni lozinku za veću sigurnost." }, + "vulnerablePassword": { + "message": "Ranjiva lozinka." + }, + "changeNow": { + "message": "Promijeni sada" + }, "missingWebsite": { "message": "Nedostaje web-stranica" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "I viÅĄe!" }, - "planDescPremium": { - "message": "DovrÅĄi online sigurnost" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": " Nadogradi na Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Broj kartice" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Radnja kod isteka" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Prihvati prijenos" + }, + "declineAndLeave": { + "message": "Odbij i napusti" + }, + "whyAmISeeingThis": { + "message": "ZaÅĄto ovo vidim?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index bacda584ba1..fb9e327337c 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "BejelentkezÊs hozzÃĄfÊrÊsi kulccsal" }, + "unlockWithPasskey": { + "message": "HozzÃĄfÊrÊsi kulcs" + }, "useSingleSignOn": { "message": "Egyszeri bejelentkezÊs hasznÃĄlata" }, @@ -436,8 +439,8 @@ "sync": { "message": "SzinkronizÃĄlÃĄs" }, - "syncVaultNow": { - "message": "SzÊf szinkronizÃĄlÃĄsa most" + "syncNow": { + "message": "SzinkronizÃĄlÃĄs most" }, "lastSync": { "message": "UtolsÃŗ szinkronizÃĄlÃĄs:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden webes alkalmazÃĄs" }, - "importItems": { - "message": "Elemek importÃĄlÃĄsa" - }, "select": { "message": "KivÃĄlaszt" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Az elem az archivumba kerÃŧlt." }, + "itemWasUnarchived": { + "message": "Az elem visszavÊtelre kerÃŧlt az archivumbÃŗl." + }, "itemUnarchived": { "message": "Az elemek visszavÊelre kerÃŧltek az archivumbÃŗl." }, "archiveItem": { "message": "Elem archivÃĄlÃĄsa" }, - "archiveItemConfirmDesc": { - "message": "Az archivÃĄlt elemek ki vannak zÃĄrva az ÃĄltalÃĄnos keresÊsi eredmÊnyekből Ês az automatikus kitÃļltÊsi javaslatokbÃŗl. Biztosan archivÃĄlni szeretnÊnk ezt az elemet?" + "archiveItemDialogContent": { + "message": "Az archivÃĄlÃĄs utÃĄn ez az elem kizÃĄrÃĄsra kerÃŧl a keresÊsi eredmÊnyekből Ês az automatikus kitÃļltÊsi javaslatokbÃŗl." + }, + "archived": { + "message": "ArchivÃĄlva" + }, + "unarchiveAndSave": { + "message": "ArchivÃĄlÃĄs visszavonÃĄsa Ês mentÊs" }, "upgradeToUseArchive": { "message": "Az Archívum hasznÃĄlatÃĄhoz prÊmium tagsÃĄg szÃŧksÊges." }, + "itemRestored": { + "message": "Az elem visszaÃĄllítÃĄsra kerÃŧlt." + }, "edit": { "message": "SzerkesztÊs" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "ExportÃĄlÃĄs innen:" }, - "exportVault": { - "message": "SzÊf exportÃĄlÃĄsa" + "exportVerb": { + "message": "ExportÃĄlÃĄs", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Tudjon meg tÃļbbet" }, + "migrationsFailed": { + "message": "Hiba tÃļrtÊnt a titkosítÃĄsi beÃĄllítÃĄsok frissítÊsekor." + }, + "updateEncryptionSettingsTitle": { + "message": "A titkosítÃĄsi beÃĄllítÃĄsok frissítÊse" + }, + "updateEncryptionSettingsDesc": { + "message": "Az Ãēj ajÃĄnlott titkosítÃĄsi beÃĄllítÃĄsok javítjÃĄk a fiÃŗk biztonsÃĄgÃĄt. Adjuk meg a mesterjelszÃŗt a frissítÊshez most." + }, + "confirmIdentityToContinue": { + "message": "A folytatÃĄshoz meg kell erősíteni a szemÊlyazonossÃĄgot." + }, + "enterYourMasterPassword": { + "message": "MesterjelszÃŗ megadÃĄsa" + }, + "updateSettings": { + "message": "BeÃĄllítÃĄsok frissítÊse" + }, + "later": { + "message": "KÊsőbb" + }, "authenticatorKeyTotp": { "message": "Hitelesítő kulcs (egyszeri időalapÃē)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "A mellÊklet mentÊsre kerÃŧlt." }, + "fixEncryption": { + "message": "TitkosítÃĄs javítÃĄsa" + }, + "fixEncryptionTooltip": { + "message": "Ez a fÃĄjl elavult titkosítÃĄsi mÃŗdszert hasznÃĄl." + }, + "attachmentUpdated": { + "message": "A mellÊklet frissítÊsre kerÃŧlt." + }, "file": { "message": "FÃĄjl" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "VÃĄlasszunk egy fÃĄjlt." }, + "itemsTransferred": { + "message": "Az elemek ÃĄtvitelre kerÃŧltek." + }, "maxFileSize": { "message": "A naximÃĄlis fÃĄjlmÊret 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB titkosított tÃĄrhely a fÃĄjlmellÊkleteknek." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ titkosított tÃĄrhely a fÃĄjlmellÊkletekhez.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "SÃŧrgőssÊgi hozzÃĄfÊrÊs" }, "premiumSignUpTwoStepOptions": { "message": "SajÃĄt kÊtlÊpcsős bejelentkezÊsi lehetősÊgek mint a YubiKey Ês a Duo." }, + "premiumSubscriptionEnded": { + "message": "A PrÊmium előfizetÊs vÊget Êrt." + }, + "archivePremiumRestart": { + "message": "Az archívumhoz hozzÃĄfÊrÊs visszaszerzÊsÊhez indítsuk Ãējra a PrÊmium előfizetÊst. Ha az ÃējraindítÃĄs előtt szerkesztjÃŧk egy archivÃĄlt elem adatait, akkor az visszakerÃŧl a szÊfbe." + }, + "restartPremium": { + "message": "PrÊmium előfizetÊs ÃējraindítÃĄsa" + }, "ppremiumSignUpReports": { "message": "JelszÃŗ higiÊnia, fiÃŗk biztonsÃĄg Ês adatszivÃĄrgÃĄsi jelentÊsek a szÊf biztonsÃĄga ÊrdekÊben." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "VÊglegesen tÃļrÃļlt elem" }, + "archivedItemRestored": { + "message": "Az archivÃĄlt elem visszaÃĄllítÃĄsra kerÃŧlt." + }, "restoreItem": { "message": "Elem visszaÃĄllítÃĄsa" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Nincs egyedi azonosítÃŗ." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A kÃļvetkező szervezet tagjai szÃĄmÃĄra mÃĄr nincs szÃŧksÊg mesterjelszÃŗra. ErősítsÃŧk meg az alÃĄbbi tartomÃĄnyt a szervezet adminisztrÃĄtorÃĄval." - }, "organizationName": { "message": "Szervezet neve" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Hiba" }, + "prfUnlockFailed": { + "message": "Nem sikerÃŧlt a feloldÃĄs a hozzÊfÊrÊsi kulccsal. PrÃŗbÃĄljuk Ãējra vagy hasznÃĄljunk mÃĄs feloldÃĄsi metÃŗdust." + }, + "noPrfCredentialsAvailable": { + "message": "A feloldÃĄshoz nem ÃĄllnak rendelkezÊsre PRF kompatibilis hozzÃĄfÊrÊsi kucsok. ElőszÃļr jelentkezzÃŧnk be egy hozzÃĄfÊrÊsi kulccsal." + }, "decryptionError": { "message": "VisszafejtÊsi hiba" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Mellőz" }, - "importData": { - "message": "Adatok importÃĄlÃĄsa", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "ImportÃĄlÃĄsi hiba" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "TovÃĄbbi opciÃŗk" + }, "moreOptionsTitle": { "message": "TovÃĄbbi opciÃŗk - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "AdminisztrÃĄtori konzol" }, + "admin": { + "message": "AdminisztrÃĄtor" + }, + "automaticUserConfirmation": { + "message": "Automatikus felhasznÃĄlÃŗi megerősítÊs" + }, + "automaticUserConfirmationHint": { + "message": "A fÃŧggőben lÊvő felhasznÃĄlÃŗk automatikus megerősítÊse az eszkÃļz zÃĄrolÃĄsÃĄnak feloldÃĄsakor." + }, + "autoConfirmOnboardingCallout": { + "message": "Idő megtakarítÃĄs az automatikus felhasznÃĄlÃŗi megerősítÊssel" + }, + "autoConfirmWarning": { + "message": "Ez hatÃĄssal lehet a szervezet adatbiztonsÃĄgÃĄra." + }, + "autoConfirmWarningLink": { + "message": "TovÃĄbbi informÃĄciÃŗ a kockÃĄzatokrÃŗl" + }, + "autoConfirmSetup": { + "message": "Új felhasznÃĄlÃŗk automatikus megerősítÊse" + }, + "autoConfirmSetupDesc": { + "message": "Az Ãēj felhasznÃĄlÃŗk automatikusan megerősítÊsre kerÃŧlnek, amíg ez az eszkÃļz fel van oldva." + }, + "autoConfirmSetupHint": { + "message": "Melyek a lehetsÊges biztonsÃĄgi kockÃĄzatok?" + }, + "autoConfirmEnabled": { + "message": "Az automatikus megerősítÊs bekapcsolÃĄsra kerÃŧlt." + }, + "availableNow": { + "message": "ElÊrhető most" + }, "accountSecurity": { "message": "FiÃŗkbiztonsÃĄg" }, + "phishingBlocker": { + "message": "AdathalÃĄszat blokkolÃŗ" + }, + "enablePhishingDetection": { + "message": "AdathalÃĄszat ÊrzÊkelÊs" + }, + "enablePhishingDetectionDesc": { + "message": "FigyelmeztetÊs megjelenítÊse a gyanÃēs adathalÃĄsz webhelyek elÊrÊse előtt." + }, "notifications": { "message": "ÉrtesítÊsek" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "$WEBSITE$ egyező ÊrzÊkelÊs elrejtÊse", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "EgyezÊs felismerÊs megjelenítÊse" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "EgyezÊs felismerÊs elrejtÊse" }, "autoFillOnPageLoad": { "message": "Automatikus kitÃļltÊs oldalbetÃļltÊsnÊl?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra szÊles" }, + "narrow": { + "message": "Keskeny" + }, "sshKeyWrongPassword": { "message": "A megadott jelszÃŗ helytelen." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Ez a bejelentkezÊs veszÊlyben van Ês hiÃĄnyzik egy webhely. Adjunk hozzÃĄ egy webhelyet Ês mÃŗdosítsuk a jelszÃŗt az erősebb biztonsÃĄg ÊrdekÊben." }, + "vulnerablePassword": { + "message": "A jelszÃŗ sÊrÃŧlÊkeny." + }, + "changeNow": { + "message": "MÃŗdosítÃĄs most" + }, "missingWebsite": { "message": "HiÃĄnyzÃŗ webhely" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "És mÊg tÃļbb!" }, - "planDescPremium": { - "message": "Teljes kÃļrÅą online biztonsÃĄg" + "advancedOnlineSecurity": { + "message": "Bővített online biztonsÃĄg" }, "upgradeToPremium": { "message": "ÁttÊrÊs PrÊmium csomagra" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "KÃĄrtya szÃĄm" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "A szervezet mÃĄr nem hasznÃĄl mesterjelszavakat a Bitwardenbe bejelentkezÊshez. A folytatÃĄshoz ellenőrizzÃŧk a szervezetet Ês a tartomÃĄnyt." + }, + "continueWithLogIn": { + "message": "FolytatÃĄs bejelentkezÊssel" + }, + "doNotContinue": { + "message": "Nincs folytatÃĄs" + }, + "domain": { + "message": "TartomÃĄny" + }, + "keyConnectorDomainTooltip": { + "message": "Ez a tartomÃĄny tÃĄrolja a fiÃŗk titkosítÃĄsi kulcsait, ezÊrt győződjÃŧnk meg rÃŗla, hogy megbízunk-e benne. Ha nem vagyunk biztos benne, ÊrdeklődjÃŧnk adminisztrÃĄtornÃĄl." + }, + "verifyYourOrganization": { + "message": "Szervezet ellenőrzÊse a bejelentkezÊshez" + }, + "organizationVerified": { + "message": "A szervezet ellenőrzÊsre kerÃŧlt." + }, + "domainVerified": { + "message": "A tartomÃĄny ellenőrzÊsre kerÃŧlt." + }, + "leaveOrganizationContent": { + "message": "Ha nem ellenőrizzÃŧk a szervezetet, a szervezethez hozzÃĄfÊrÊs visszavonÃĄsra kerÃŧl." + }, + "leaveNow": { + "message": "ElhagyÃĄs most" + }, + "verifyYourDomainToLogin": { + "message": "TartomÃĄny ellenőrzÊse a bejelentkezÊshez" + }, + "verifyYourDomainDescription": { + "message": "A bejelentkezÊs folytatÃĄsÃĄhoz ellenőrizzÃŧk ezt a tartomÃĄnyt." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "A bejelentkezÊs folytatÃĄsÃĄhoz ellenőrizzÃŧk a szervezetet Ês a tartomÃĄnyt." + }, "sessionTimeoutSettingsAction": { "message": "IdőkifutÃĄsi mÅąvelet" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Ezt a beÃĄllítÃĄst a szervezet lezeli." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "A szervezet a munkamenet maximÃĄlis munkamenet időkifutÃĄsÃĄt $HOURS$ ÃŗrÃĄra Ês $MINUTES$ percre ÃĄllította be.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "A szervezet az alapÊrtelmezett munkamenet időkifutÃĄst Azonnal ÊrtÊkre ÃĄllította." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "A szervezet az alapÊrtelmezett munkamenet időkifutÃĄstr RendszerzÃĄr be ÊrtÊkre ÃĄllította." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "A szervezet az alapÊrtelmezett munkamenet időkifutÃĄst a BÃļngÊsző ÃējraindítÃĄsakor ÊrtÊkre ÃĄllította." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "A maximÃĄlis időtÃēllÊpÊs nem haladhatja meg a $HOURS$ Ãŗra Ês $MINUTES$ perc ÊrtÊket.", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "BÃļngÊsző ÃējraindítÃĄskor" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Állítsunk be egy feloldÃĄsi mÃŗdot a szÊf időkifutÃĄsi mÅąveletÊnek mÃŗdosítÃĄsÃĄhoz." + }, + "upgrade": { + "message": "ÁttÊrÊs" + }, + "leaveConfirmationDialogTitle": { + "message": "Biztosan szeretnÊnk kilÊpni?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Az elutasítÃĄssal a szemÊlyes elemek a fiÃŗkban maradnak, de elveszítjÃŧk hozzÃĄfÊrÊst a megosztott elemekhez Ês a szervezeti funkciÃŗkhoz." + }, + "leaveConfirmationDialogContentTwo": { + "message": "LÊpjÃŧnk kapcsolatba az adminisztrÃĄtorral a hozzÃĄfÊrÊs visszaszerzÊsÊÊrt." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "$ORGANIZATION$ elhagyÃĄsa", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Hogyan kezeljem a szÊfet?" + }, + "transferItemsToOrganizationTitle": { + "message": "Elemek ÃĄtvitele $ORGANIZATION$ szervezethez", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ megkÃļveteli, hogy minden elem a szervezet tulajdonÃĄban legyen a biztonsÃĄg Ês a megfelelősÊg ÊrdekÊben. KattintÃĄs az elfogadÃĄsra az elemek tulajdonjogÃĄnak ÃĄtruhÃĄzÃĄsÃĄhoz.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Átvitel elfogadÃĄsa" + }, + "declineAndLeave": { + "message": "ElutasítÃĄs Ês kilÊpÊs" + }, + "whyAmISeeingThis": { + "message": "MiÊrt lÃĄthatÃŗ ez?" + }, + "resizeSideNavigation": { + "message": "OldalnavigÃĄciÃŗ ÃĄtmÊretezÊs" } } diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 88147f804d1..064e67eb76f 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Masuk dengan kunci sandi" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Gunakan masuk tunggal" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sinkronisasi" }, - "syncVaultNow": { - "message": "Sinkronkan Brankas Sekarang" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Sinkronisasi Terakhir:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Aplikasi web Bitwarden" }, - "importItems": { - "message": "Impor Item" - }, "select": { "message": "Pilih" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Edit" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Ekspor dari" }, - "exportVault": { - "message": "Ekspor Brankas" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Pelajari lebih lanjut" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Kunci Otentikasi (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Lampiran telah disimpan." }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Berkas" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Pilih berkas." }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Ukuran berkas maksimal adalah 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB penyimpanan berkas yang dienkripsi." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Akses darurat." }, "premiumSignUpTwoStepOptions": { "message": "Pilihan masuk dua-langkah yang dipatenkan seperti YubiKey dan Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Kebersihan kata sandi, kesehatan akun, dan laporan kebocoran data untuk tetap menjaga keamanan brankas Anda." }, @@ -1874,7 +1950,7 @@ "message": "Tahun Kedaluwarsa" }, "monthly": { - "message": "month" + "message": "bulan" }, "expiration": { "message": "Masa Berlaku" @@ -2024,51 +2100,51 @@ "message": "Catatan" }, "newItemHeaderLogin": { - "message": "New Login", + "message": "Log Masuk Baru", "description": "Header for new login item type" }, "newItemHeaderCard": { - "message": "New Card", + "message": "Kartu Baru", "description": "Header for new card item type" }, "newItemHeaderIdentity": { - "message": "New Identity", + "message": "Identitas Baru", "description": "Header for new identity item type" }, "newItemHeaderNote": { - "message": "New Note", + "message": "Catatan Baru", "description": "Header for new note item type" }, "newItemHeaderSshKey": { - "message": "New SSH key", + "message": "Kunci SSH Baru", "description": "Header for new SSH key item type" }, "newItemHeaderTextSend": { - "message": "New Text Send", + "message": "Kirim Teks Baru", "description": "Header for new text send" }, "newItemHeaderFileSend": { - "message": "New File Send", + "message": "Kirim Berkas Baru", "description": "Header for new file send" }, "editItemHeaderLogin": { - "message": "Edit Login", + "message": "Sunting Log Masuk", "description": "Header for edit login item type" }, "editItemHeaderCard": { - "message": "Edit Card", + "message": "Sunting Kartu", "description": "Header for edit card item type" }, "editItemHeaderIdentity": { - "message": "Edit Identity", + "message": "Sunting Identitas", "description": "Header for edit identity item type" }, "editItemHeaderNote": { - "message": "Edit Note", + "message": "Sunting Catatan", "description": "Header for edit note item type" }, "editItemHeaderSshKey": { - "message": "Edit SSH key", + "message": "Sunting kunci SSH", "description": "Header for edit SSH key item type" }, "editItemHeaderTextSend": { @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Hapus Item Secara Permanen" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Pulihkan Item" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Tidak ada pengidentifikasi unik yang ditemukan." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Sebuah kata sandi utama tidak lagi dibutuhkan untuk para anggota dari organisasi berikut. Silakan konfirmasi domain berikut kepada pengelola organisasi Anda." - }, "organizationName": { "message": "Nama organisasi" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Galat" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Kesalahan dekripsi" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Abaikan" }, - "importData": { - "message": "Impor data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Kesalahan impor" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "Pilihan lainnya - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Konsol Admin" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Keamanan akun" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Pemberitahuan" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Sembunyikan deteksi kecocokan $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Isi otomatis ketika halaman dimuat?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Ekstra lebar" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "Kata sandi yang Anda masukkan tidak benar." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verifikasikan domain Anda untuk log masuk" + }, + "verifyYourDomainDescription": { + "message": "Untuk melanjutkan log masuk, verifikasikan domain ini." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Untuk melanjutkan log masuk, verifikasikan organisasi dan domain." + }, "sessionTimeoutSettingsAction": { - "message": "Timeout action" + "message": "Tindakan saat habis waktu" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Pengaturan ini dikelola oleh organisasi Anda." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index d4d032737b8..3e47b38f141 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Accedi con passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Usa il Single Sign-On" }, @@ -35,7 +38,7 @@ "message": "La tua organizzazione richiede un accesso Single Sign-On (SSO)." }, "welcomeBack": { - "message": "Bentornato/a" + "message": "Bentornato" }, "setAStrongPassword": { "message": "Imposta una password robusta" @@ -436,8 +439,8 @@ "sync": { "message": "Sincronizza" }, - "syncVaultNow": { - "message": "Sincronizza cassaforte ora" + "syncNow": { + "message": "Sincronizza" }, "lastSync": { "message": "Ultima sincronizzazione:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Importa elementi" - }, "select": { "message": "Seleziona" }, @@ -576,17 +576,29 @@ "itemWasSentToArchive": { "message": "Elemento archiviato" }, + "itemWasUnarchived": { + "message": "Elemento rimosso dall'archivio" + }, "itemUnarchived": { "message": "Elemento rimosso dall'archivio" }, "archiveItem": { "message": "Archivia elemento" }, - "archiveItemConfirmDesc": { - "message": "Gli elementi archiviati sono esclusi dai risultati di ricerca e suggerimenti di autoriempimento. Vuoi davvero archiviare questo elemento?" + "archiveItemDialogContent": { + "message": "Una volta archiviato, questo elemento sarà escluso dai risultati di ricerca e dai suggerimenti del completamento automatico." + }, + "archived": { + "message": "Archiviato" + }, + "unarchiveAndSave": { + "message": "Togli dall'archivio e salva" }, "upgradeToUseArchive": { - "message": "A premium membership is required to use Archive." + "message": "Per utilizzare Archivio è necessario un abbonamento premium." + }, + "itemRestored": { + "message": "L'elemento è stato ripristinato" }, "edit": { "message": "Modifica" @@ -598,7 +610,7 @@ "message": "Mostra tutto" }, "showAll": { - "message": "Show all" + "message": "Mostra tutto" }, "viewLess": { "message": "Vedi meno" @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Esporta da" }, - "exportVault": { - "message": "Esporta cassaforte" + "exportVerb": { + "message": "Esporta", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Ulteriori informazioni" }, + "migrationsFailed": { + "message": "Si è verificato un errore durante l'aggiornamento delle impostazioni di cifratura." + }, + "updateEncryptionSettingsTitle": { + "message": "Aggiorna le impostazioni di crittografia" + }, + "updateEncryptionSettingsDesc": { + "message": "Le nuove impostazioni di crittografia consigliate miglioreranno la sicurezza del tuo account. Inserisci la tua password principale per aggiornare." + }, + "confirmIdentityToContinue": { + "message": "Conferma la tua identità per continuare" + }, + "enterYourMasterPassword": { + "message": "Inserisci la tua password principale" + }, + "updateSettings": { + "message": "Aggiorna impostazioni" + }, + "later": { + "message": "PiÚ tardi" + }, "authenticatorKeyTotp": { "message": "Chiave di autenticazione (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Allegato salvato" }, + "fixEncryption": { + "message": "Correggi la crittografia" + }, + "fixEncryptionTooltip": { + "message": "Questo file usa un metodo di crittografia obsoleto." + }, + "attachmentUpdated": { + "message": "Allegato aggiornato" + }, "file": { "message": "File" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Seleziona un file" }, + "itemsTransferred": { + "message": "Elementi trasferiti" + }, "maxFileSize": { "message": "La dimensione massima del file è 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB di spazio di archiviazione crittografato per gli allegati." }, + "premiumSignUpStorageV2": { + "message": "Archivio crittografato di $SIZE$ per allegati.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Accesso di emergenza." }, "premiumSignUpTwoStepOptions": { "message": "Opzioni di verifica in due passaggi proprietarie come YubiKey e Duo." }, + "premiumSubscriptionEnded": { + "message": "Il tuo abbonamento Premium è terminato" + }, + "archivePremiumRestart": { + "message": "Per recuperare l'accesso al tuo archivio, riavvia il tuo abbonamento Premium. Se modifichi i dettagli di un elemento archiviato prima del riavvio, sarà spostato nella tua cassaforte." + }, + "restartPremium": { + "message": "Riavvia Premium" + }, "ppremiumSignUpReports": { "message": "Sicurezza delle password, integrità dell'account, e rapporti su violazioni di dati per mantenere sicura la tua cassaforte." }, @@ -1874,7 +1950,7 @@ "message": "Anno di scadenza" }, "monthly": { - "message": "month" + "message": "mese" }, "expiration": { "message": "Scadenza" @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Elemento eliminato definitivamente" }, + "archivedItemRestored": { + "message": "Elemento estratto dall'archivio" + }, "restoreItem": { "message": "Ripristina elemento" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Nessun identificatore univoco trovato." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "La password principale non è piÚ richiesta per i membri dell'organizzazione. Per favore, conferma il dominio qui sotto con l'amministratore." - }, "organizationName": { "message": "Nome organizzazione" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Errore" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Errore di decifrazione" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignora" }, - "importData": { - "message": "Importa dati", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Errore di importazione" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "Altre opzioni" + }, "moreOptionsTitle": { "message": "PiÚ opzioni - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Console di amministrazione" }, + "admin": { + "message": "Amministratore" + }, + "automaticUserConfirmation": { + "message": "Conferma automatica degli utenti" + }, + "automaticUserConfirmationHint": { + "message": "Conferma automaticamente gli utenti in sospeso mentre il dispositivo è sbloccato" + }, + "autoConfirmOnboardingCallout": { + "message": "Risparmia tempo con la conferma automatica degli utenti" + }, + "autoConfirmWarning": { + "message": "Potrebbe influenzare la sicurezza dei dati della tua organizzazione. " + }, + "autoConfirmWarningLink": { + "message": "Scopri quali sono i rischi" + }, + "autoConfirmSetup": { + "message": "Conferma automaticamente i nuovi utenti" + }, + "autoConfirmSetupDesc": { + "message": "I nuovi utenti saranno automaticamente confermati mentre questo dispositivo è sbloccato." + }, + "autoConfirmSetupHint": { + "message": "Quali sono i rischi potenziali per la sicurezza?" + }, + "autoConfirmEnabled": { + "message": "Conferma automatica attivata" + }, + "availableNow": { + "message": "Disponibile ora" + }, "accountSecurity": { "message": "Sicurezza dell'account" }, + "phishingBlocker": { + "message": "Blocco phishing" + }, + "enablePhishingDetection": { + "message": "Rilevazione phishing" + }, + "enablePhishingDetectionDesc": { + "message": "Mostra un avviso prima di accedere ai siti sospetti di phishing" + }, "notifications": { "message": "Notifiche" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Nascondi corrispondenza $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Mostra il criterio di corrispondenza dell'URL" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Nascondi il criterio di corrispondenza dell'URL" }, "autoFillOnPageLoad": { "message": "Riempi automaticamente al caricamento della pagina?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Molto larga" }, + "narrow": { + "message": "Stretto" + }, "sshKeyWrongPassword": { "message": "La parola d'accesso inserita non è corretta." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Questo login è a rischio e non contiene un sito web. Aggiungi un sito web e cambia la password per maggiore sicurezza." }, + "vulnerablePassword": { + "message": "Password vulnerabile." + }, + "changeNow": { + "message": "Cambiala subito!" + }, "missingWebsite": { "message": "Sito web mancante" }, @@ -5818,17 +5947,17 @@ "andMoreFeatures": { "message": "E molto altro!" }, - "planDescPremium": { - "message": "Sicurezza online completa" + "advancedOnlineSecurity": { + "message": "Sicurezza online avanzata" }, "upgradeToPremium": { "message": "Aggiorna a Premium" }, "unlockAdvancedSecurity": { - "message": "Unlock advanced security features" + "message": "Sblocca funzionalità di sicurezza avanzate" }, "unlockAdvancedSecurityDesc": { - "message": "A Premium subscription gives you more tools to stay secure and in control" + "message": "Un abbonamento Premium ti offre piÚ strumenti per rimanere sicuro e in controllo" }, "explorePremium": { "message": "Scopri Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Numero di carta" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "La tua organizzazione non utilizza piÚ le password principali per accedere a Bitwarden. Per continuare, verifica l'organizzazione e il dominio." + }, + "continueWithLogIn": { + "message": "Accedi e continua" + }, + "doNotContinue": { + "message": "Non continuare" + }, + "domain": { + "message": "Dominio" + }, + "keyConnectorDomainTooltip": { + "message": "Questo dominio memorizzerà le chiavi di crittografia del tuo account, quindi assicurati di impostarlo come affidabile. Se non hai la certezza che lo sia, verifica con l'amministratore." + }, + "verifyYourOrganization": { + "message": "Verifica la tua organizzazione per accedere" + }, + "organizationVerified": { + "message": "Organizzazione verificata" + }, + "domainVerified": { + "message": "Dominio verificato" + }, + "leaveOrganizationContent": { + "message": "Se non verifichi l'organizzazione, il tuo accesso sarà revocato." + }, + "leaveNow": { + "message": "Abbandona" + }, + "verifyYourDomainToLogin": { + "message": "Verifica il tuo dominio per accedere" + }, + "verifyYourDomainDescription": { + "message": "Per continuare con l'accesso, verifica questo dominio." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Per continuare con l'accesso, verifica l'organizzazione e il dominio." + }, "sessionTimeoutSettingsAction": { "message": "Azione al timeout" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Questa impostazione è gestita dalla tua organizzazione." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "La tua organizzazione ha impostato $HOURS$ ora/e e $MINUTES$ minuto/i come durata massima della sessione.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "In base alle impostazioni della tua organizzazione, la sessione terminerà immediatamente." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "In base alle impostazioni della tua organizzazione, la sessione terminerà al blocco del dispositivo." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "In base alle impostazioni della tua organizzazione, la sessione terminerà ogni volta che la pagina sarà chiusa o ricaricata." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "La durata della sessione non puÃ˛ superare $HOURS$ ora/e e $MINUTES$ minuto/i", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Al riavvio del browser" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Imposta un metodo di sblocco per modificare l'azione al timeout" + }, + "upgrade": { + "message": "Aggiorna" + }, + "leaveConfirmationDialogTitle": { + "message": "Vuoi davvero abbandonare?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Se rifiuti, tutti gli elementi esistenti resteranno nel tuo account, ma perderai l'accesso agli oggetti condivisi e alle funzioni organizzative." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contatta il tuo amministratore per recuperare l'accesso." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Abbandona $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Come si gestisce la cassaforte?" + }, + "transferItemsToOrganizationTitle": { + "message": "Trasferisci gli elementi in $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ richiede che tutti gli elementi siano di proprietà dell'organizzazione per motivi di conformità e sicurezza. Clicca su 'Accetta' per trasferire la proprietà.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accetta il trasferimento" + }, + "declineAndLeave": { + "message": "Rifiuta e abbandona" + }, + "whyAmISeeingThis": { + "message": "PerchÊ vedo questo avviso?" + }, + "resizeSideNavigation": { + "message": "Ridimensiona la navigazione laterale" } } diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index c13b139e13a..9784ad44f2a 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "パ゚キãƒŧã§ãƒ­ã‚°ã‚¤ãƒŗ" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "ã‚ˇãƒŗã‚°ãƒĢã‚ĩã‚¤ãƒŗã‚Ēãƒŗã‚’äŊŋį”¨ã™ã‚‹" }, @@ -436,8 +439,8 @@ "sync": { "message": "同期" }, - "syncVaultNow": { - "message": "äŋįŽĄåēĢを同期する" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "前回ぎ同期īŧš" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden ã‚Ļェブã‚ĸプãƒĒ" }, - "importItems": { - "message": "ã‚ĸã‚¤ãƒ†ãƒ ãŽã‚¤ãƒŗãƒãƒŧト" - }, "select": { "message": "選択" }, @@ -551,7 +551,7 @@ "message": "äŋįŽĄåēĢを検į´ĸ" }, "resetSearch": { - "message": "Reset search" + "message": "検į´ĸをãƒĒã‚ģット" }, "archiveNoun": { "message": "ã‚ĸãƒŧã‚Ģイブ", @@ -565,28 +565,40 @@ "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": { + "itemWasUnarchived": { "message": "Item was unarchived" }, - "archiveItem": { - "message": "Archive item" + "itemUnarchived": { + "message": "ã‚ĸイテムはã‚ĸãƒŧã‚Ģã‚¤ãƒ–ã‹ã‚‰č§Ŗé™¤ã•ã‚Œãžã—ãŸ" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItem": { + "message": "ã‚ĸイテムをã‚ĸãƒŧã‚Ģイブ" + }, + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { - "message": "A premium membership is required to use Archive." + "message": "ã‚ĸãƒŧã‚ĢイブをäŊŋį”¨ã™ã‚‹ãĢはプãƒŦミã‚ĸãƒ ãƒĄãƒŗãƒãƒŧã‚ˇãƒƒãƒ—ãŒåŋ…čĻã§ã™ã€‚" + }, + "itemRestored": { + "message": "Item has been restored" }, "edit": { "message": "ᎍ集" @@ -595,13 +607,13 @@ "message": "襨į¤ē" }, "viewAll": { - "message": "View all" + "message": "すずãĻ襨į¤ē" }, "showAll": { - "message": "Show all" + "message": "すずãĻ襨į¤ē" }, "viewLess": { - "message": "View less" + "message": "襨į¤ēを減らす" }, "viewLogin": { "message": "ãƒ­ã‚°ã‚¤ãƒŗæƒ…å ąã‚’čĄ¨į¤ē" @@ -749,7 +761,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", @@ -806,10 +818,10 @@ "message": "ロック時" }, "onIdle": { - "message": "On system idle" + "message": "ã‚ĸイドãƒĢ時" }, "onSleep": { - "message": "On system sleep" + "message": "゚ãƒĒãƒŧプ時" }, "onRestart": { "message": "ブナã‚Ļã‚ļ再čĩˇå‹•時" @@ -955,7 +967,7 @@ "message": "äģĨ下ぎ手順ãĢåž“ãŖãĻãƒ­ã‚°ã‚¤ãƒŗã‚’åŽŒäē†ã—ãĻください。" }, "followTheStepsBelowToFinishLoggingInWithSecurityKey": { - "message": "Follow the steps below to finish logging in with your security key." + "message": "ã‚ģキãƒĨãƒĒãƒ†ã‚Ŗã‚­ãƒŧã§ãƒ­ã‚°ã‚¤ãƒŗã‚’åŽŒäē†ã™ã‚‹ãĢは、äģĨ下ぎ手順ãĢåž“ãŖãĻください。" }, "restartRegistration": { "message": "į™ģéŒ˛ã‚’å†åēĻ始める" @@ -1140,7 +1152,7 @@ "message": "こぎパ゚ワãƒŧドを Bitwarden ãĢäŋå­˜ã—ぞすかīŧŸ" }, "notificationAddSave": { - "message": "äŋå­˜ã™ã‚‹" + "message": "äŋå­˜" }, "notificationViewAria": { "message": "View $ITEMNAME$, opens in new window", @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "エク゚ポãƒŧト元" }, - "exportVault": { - "message": "äŋįŽĄåēĢぎエク゚ポãƒŧト" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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": "ãƒ•ã‚Ąã‚¤ãƒĢåŊĸåŧ" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "さらãĢčŠŗã—ã" }, + "migrationsFailed": { + "message": "æš—åˇåŒ–č¨­åŽšãŽæ›´æ–°ä¸­ãĢエナãƒŧがį™ēį”Ÿã—ãžã—ãŸã€‚" + }, + "updateEncryptionSettingsTitle": { + "message": "æš—åˇåŒ–č¨­åŽšã‚’æ›´æ–°ã™ã‚‹" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "įļščĄŒã™ã‚‹ãĢはæœŦäēēįĸēčĒã‚’čĄŒãŖãĻください" + }, + "enterYourMasterPassword": { + "message": "マ゚ã‚ŋãƒŧパ゚ワãƒŧドをå…Ĩ力" + }, + "updateSettings": { + "message": "č¨­åŽšã‚’æ›´æ–°" + }, + "later": { + "message": "垌で" + }, "authenticatorKeyTotp": { "message": "čĒč¨ŧキãƒŧ (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "æˇģäģ˜ãƒ•ã‚Ąã‚¤ãƒĢをäŋå­˜ã—ぞした。" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "ãƒ•ã‚Ąã‚¤ãƒĢ" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "ãƒ•ã‚Ąã‚¤ãƒĢを選択しãĻください。" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "æœ€å¤§ãƒ•ã‚Ąã‚¤ãƒĢã‚ĩイã‚ē: 500 MB" }, @@ -1452,7 +1510,7 @@ "message": "ã‚ĩãƒŧãƒ“ã‚šãŒåˆŠį”¨ã§ããžã›ã‚“" }, "legacyEncryptionUnsupported": { - "message": "Legacy encryption is no longer supported. Please contact support to recover your account." + "message": "åž“æĨãŽæš—åˇåŒ–ã¯ã‚ĩポãƒŧトされãĻいぞせん。ã‚ĸã‚Ģã‚Ļãƒŗãƒˆã‚’åžŠå…ƒã™ã‚‹ãĢはã‚ĩポãƒŧトãĢお問い合わせください。" }, "premiumMembership": { "message": "プãƒŦミã‚ĸムäŧšå“Ą" @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1GB ãŽæš—åˇåŒ–ã•ã‚ŒãŸãƒ•ã‚Ąã‚¤ãƒĢ゚トãƒŦãƒŧジ" }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ ãŽæš—åˇåŒ–ã•ã‚ŒãŸãƒ•ã‚Ąã‚¤ãƒĢ゚トãƒŦãƒŧジ。", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "ᎊæ€Ĩã‚ĸクã‚ģ゚" }, "premiumSignUpTwoStepOptions": { "message": "YubiKey、Duo ãĒおぎプロプナイエã‚ŋãƒĒãĒ2æŽĩ階čĒč¨ŧã‚Ēãƒ—ã‚ˇãƒ§ãƒŗã€‚" }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "äŋįŽĄåēĢを厉全ãĢäŋã¤ãŸã‚ãŽã€ãƒ‘゚ワãƒŧドやã‚ĸã‚Ģã‚ĻãƒŗãƒˆãŽåĨ全性、デãƒŧã‚ŋäžĩåŽŗãĢé–ĸするãƒŦポãƒŧト" }, @@ -1710,16 +1786,16 @@ "message": "Confirm autofill" }, "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": "フりãƒŧãƒ ãƒ•ã‚ŖãƒŧãƒĢドãĢč‡Ē動å…ĨåŠ›ãŽå€™čŖœã‚’čĄ¨į¤ēする" }, "howDoesBitwardenProtectFromPhishing": { - "message": "How does Bitwarden protect your data from phishing?" + "message": "Bitwarden はおぎようãĢãƒ•ã‚Ŗãƒƒã‚ˇãƒŗã‚°ã‹ã‚‰ãƒ‡ãƒŧã‚ŋをäŋč­ˇã—ぞすかīŧŸ" }, "currentWebsite": { - "message": "Current website" + "message": "įžåœ¨ãŽã‚Ļェブã‚ĩイト" }, "autofillAndAddWebsite": { "message": "Autofill and add this website" @@ -1874,7 +1950,7 @@ "message": "有劚期限嚴" }, "monthly": { - "message": "month" + "message": "月" }, "expiration": { "message": "有劚期限" @@ -2048,7 +2124,7 @@ "description": "Header for new text send" }, "newItemHeaderFileSend": { - "message": "New File Send", + "message": "新しい Send ãƒ•ã‚Ąã‚¤ãƒĢ", "description": "Header for new file send" }, "editItemHeaderLogin": { @@ -2060,7 +2136,7 @@ "description": "Header for edit card item type" }, "editItemHeaderIdentity": { - "message": "Edit Identity", + "message": "ID ã‚’įˇ¨é›†", "description": "Header for edit identity item type" }, "editItemHeaderNote": { @@ -2076,7 +2152,7 @@ "description": "Header for edit text send" }, "editItemHeaderFileSend": { - "message": "Edit File Send", + "message": "Send ãƒ•ã‚Ąã‚¤ãƒĢã‚’įˇ¨é›†", "description": "Header for edit file send" }, "viewItemHeaderLogin": { @@ -2088,7 +2164,7 @@ "description": "Header for view card item type" }, "viewItemHeaderIdentity": { - "message": "View Identity", + "message": "ID ã‚’čĄ¨į¤ē", "description": "Header for view identity item type" }, "viewItemHeaderNote": { @@ -2222,7 +2298,7 @@ "message": "į¨ŽéĄž" }, "allItems": { - "message": "全ãĻぎã‚ĸイテム" + "message": "すずãĻぎã‚ĸイテム" }, "noPasswordsInList": { "message": "襨į¤ēするパ゚ワãƒŧドがありぞせん" @@ -2304,7 +2380,7 @@ "message": "Bitwarden ãŽãƒ­ãƒƒã‚¯ã‚’č§Ŗé™¤ã™ã‚‹ãŸã‚ãŽ PIN ã‚ŗãƒŧãƒ‰ã‚’č¨­åŽšã—ãžã™ã€‚ã‚ĸプãƒĒから厌全ãĢログã‚ĸã‚Ļトすると、PIN č¨­åŽšã¯ãƒĒã‚ģットされぞす。" }, "setPinCode": { - "message": "You can use this PIN to unlock Bitwarden. Your PIN will be reset if you ever fully log out of the application." + "message": "Bitwarden ãŽãƒ­ãƒƒã‚¯ã‚’č§Ŗé™¤ã™ã‚‹ãŸã‚ãĢ PIN をäŊŋᔍäŊŋį”¨ã™ã‚‹ã“ã¨ãŒã§ããžã™ã€‚ã‚ĸプãƒĒからログã‚ĸã‚Ļトすると PIN はãƒĒã‚ģットされぞす。" }, "pinRequired": { "message": "PIN ã‚ŗãƒŧドがåŋ…čĻã§ã™ã€‚" @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "厌全ãĢ削除されたã‚ĸイテム" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "ã‚ĸイテムをãƒĒ゚トã‚ĸ" }, @@ -2725,7 +2804,7 @@ } }, "atRiskChangePrompt": { - "message": "Your password for this site is at-risk. $ORGANIZATION$ has requested that you change it.", + "message": "こぎã‚ĩイトぎパ゚ワãƒŧãƒ‰ã¯åąé™ēです。$ORGANIZATION$ が変更をčĻæą‚ã—ãĻいぞす。", "placeholders": { "organization": { "content": "$1", @@ -3070,7 +3149,7 @@ "message": "こぎ抟čƒŊをäŊŋį”¨ã™ã‚‹ãĢã¯ãƒĄãƒŧãƒĢã‚ĸドãƒŦ゚をįĸēčĒã™ã‚‹åŋ…čĻãŒã‚ã‚Šãžã™ã€‚ã‚ĻェブäŋįŽĄåēĢã§ãƒĄãƒŧãƒĢã‚ĸドãƒŦ゚をįĸēčĒã§ããžã™ã€‚" }, "masterPasswordSuccessfullySet": { - "message": "Master password successfully set" + "message": "マ゚ã‚ŋãƒŧパ゚ワãƒŧãƒ‰ã‚’č¨­åŽšã—ãžã—ãŸ" }, "updatedMasterPassword": { "message": "マ゚ã‚ŋãƒŧパ゚ワãƒŧドを更新しぞした" @@ -3210,14 +3289,11 @@ "copyCustomFieldNameNotUnique": { "message": "ä¸€æ„ãŽč­˜åˆĨ子がčĻ‹ã¤ã‹ã‚Šãžã›ã‚“ã§ã—ãŸã€‚" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "įĩ„įš”å" }, "keyConnectorDomain": { - "message": "Key Connector domain" + "message": "キãƒŧã‚ŗãƒã‚¯ã‚ŋãƒŧãƒ‰ãƒĄã‚¤ãƒŗ" }, "leaveOrganization": { "message": "įĩ„įš”ã‹ã‚‰č„ąé€€ã™ã‚‹" @@ -3274,7 +3350,7 @@ } }, "exportingOrganizationVaultFromPasswordManagerWithDataOwnershipDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported.", + "message": "$ORGANIZATION$ ãĢé–ĸ逪äģ˜ã‘られたįĩ„įš”ãŽäŋįŽĄåēĢぎãŋがエク゚ポãƒŧトされぞす。", "placeholders": { "organization": { "content": "$1", @@ -3294,11 +3370,17 @@ "error": { "message": "エナãƒŧ" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "åžŠåˇã‚¨ãƒŠãƒŧ" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "č‡Ē動å…Ĩ力デãƒŧã‚ŋぎ取垗ãĢå¤ąæ•—ã—ãžã—ãŸ" }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden はäģĨ下ぎäŋįŽĄåēĢぎã‚ĸã‚¤ãƒ†ãƒ ã‚’åžŠåˇã§ããžã›ã‚“ã§ã—ãŸã€‚" @@ -3853,7 +3935,7 @@ "message": "ãƒ­ã‚°ã‚¤ãƒŗã‚’åŽŒäē†ã§ããžã›ã‚“" }, "loginOnTrustedDeviceOrAskAdminToAssignPassword": { - "message": "You need to log in on a trusted device or ask your administrator to assign you a password." + "message": "äŋĄé ŧできるデバイ゚ãĢãƒ­ã‚°ã‚¤ãƒŗã™ã‚‹ã‹ã€įŽĄį†č€…ãĢパ゚ワãƒŧãƒ‰ãŽå‰˛ã‚ŠåŊ“ãĻを䞝é ŧするåŋ…čĻãŒã‚ã‚Šãžã™ã€‚" }, "ssoIdentifierRequired": { "message": "įĩ„įš”ãŽ SSO ID がåŋ…čĻã§ã™ã€‚" @@ -3917,7 +3999,7 @@ "message": "äŋĄé ŧされたデバイ゚" }, "trustOrganization": { - "message": "Trust organization" + "message": "įĩ„įš”ã‚’äŋĄé ŧ" }, "trust": { "message": "äŋĄé ŧする" @@ -3938,7 +4020,7 @@ "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." }, "trustUser": { - "message": "Trust user" + "message": "ãƒĻãƒŧã‚ļãƒŧをäŋĄé ŧ" }, "sendsTitleNoItems": { "message": "æŠŸå¯†æƒ…å ąã‚’åŽ‰å…¨ãĢ送äŋĄ", @@ -4176,10 +4258,6 @@ "ignore": { "message": "į„ĄčĻ–" }, - "importData": { - "message": "デãƒŧã‚ŋãŽã‚¤ãƒŗãƒãƒŧト", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "ã‚¤ãƒŗãƒãƒŧト エナãƒŧ" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "そぎäģ–ぎã‚Ēãƒ—ã‚ˇãƒ§ãƒŗ - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "įŽĄį†ã‚ŗãƒŗã‚ŊãƒŧãƒĢ" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "ã‚ĸã‚Ģã‚ĻãƒŗãƒˆãŽã‚ģキãƒĨãƒĒãƒ†ã‚Ŗ" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "通įŸĨ" }, @@ -4888,7 +5011,7 @@ "message": "ãƒĸバイãƒĢã‚ĸプãƒĒをå…Ĩ手" }, "getTheMobileAppDesc": { - "message": "Access your passwords on the go with the Bitwarden mobile app." + "message": "Bitwarden ãƒĸバイãƒĢã‚ĸプãƒĒで外å‡ē先からでもパ゚ワãƒŧドãĢã‚ĸクã‚ģ゚しぞしょう。" }, "getTheDesktopApp": { "message": "デ゚クトップã‚ĸプãƒĒをå…Ĩ手" @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "ä¸€č‡´æ¤œå‡ē $WEBSITE$ã‚’éžčĄ¨į¤ē", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "ペãƒŧジčĒ­ãŋčžŧãŋ時ãĢč‡Ē動å…Ĩ力する" @@ -5275,7 +5395,7 @@ "message": "ã‚ĸã‚Ģã‚Ļãƒŗãƒˆã¸ãŽã‚ĸクã‚ģ゚がčĻæą‚ã•ã‚Œãžã—ãŸ" }, "confirmAccessAttempt": { - "message": "Confirm access attempt for $EMAIL$", + "message": "$EMAIL$ ãŽãƒ­ã‚°ã‚¤ãƒŗčŠĻčĄŒã‚’įĸēčĒ", "placeholders": { "email": { "content": "$1", @@ -5562,6 +5682,9 @@ "extraWide": { "message": "エク゚トナワイド" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "å…Ĩ力されたパ゚ワãƒŧãƒ‰ãŒé–“é•ãŖãĻいぞす。" }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "ã‚Ļェブã‚ĩイトがありぞせん" }, @@ -5662,16 +5791,16 @@ "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": "こぎã‚ŋブを閉じる" }, "phishingPageContinueV2": { - "message": "Continue to this site (not recommended)" + "message": "こぎã‚ĩイトãĢé€˛ã‚€ (非推åĨ¨)" }, "phishingPageExplanation1": { "message": "This site was found in ", @@ -5727,13 +5856,13 @@ "message": "With cards, easily autofill payment forms securely and accurately." }, "newIdentityNudgeTitle": { - "message": "Simplify creating accounts" + "message": "ã‚ĸã‚Ģã‚ĻãƒŗãƒˆäŊœæˆã‚’į°Ąį´ åŒ–" }, "newIdentityNudgeBody": { "message": "With identities, quickly autofill long registration or contact forms." }, "newNoteNudgeTitle": { - "message": "Keep your sensitive data safe" + "message": "抟密デãƒŧã‚ŋを厉全ãĢäŋįŽĄ" }, "newNoteNudgeBody": { "message": "With notes, securely store sensitive data like banking or insurance details." @@ -5755,7 +5884,7 @@ "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." }, @@ -5778,7 +5907,7 @@ "message": "You do not have permissions to view this page. Try logging in with a different account." }, "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": { @@ -5807,7 +5936,7 @@ "message": "čĒč¨ŧ抟を内č”ĩ" }, "secureFileStorage": { - "message": "Secure file storage" + "message": "ã‚ģキãƒĨã‚ĸãĒãƒ•ã‚Ąã‚¤ãƒĢ゚トãƒŦãƒŧジ" }, "emergencyAccess": { "message": "ᎊæ€Ĩã‚ĸクã‚ģ゚" @@ -5816,10 +5945,10 @@ "message": "Breach monitoring" }, "andMoreFeatures": { - "message": "And more!" + "message": "ãĒおãĒおīŧ" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "プãƒŦミã‚ĸムãĢã‚ĸップグãƒŦãƒŧド" @@ -5834,10 +5963,10 @@ "message": "Explore Premium" }, "loadingVault": { - "message": "Loading vault" + "message": "äŋįŽĄåēĢぎčĒ­ãŋčžŧãŋ中" }, "vaultLoaded": { - "message": "Vault loaded" + "message": "äŋįŽĄåēĢがčĒ­ãŋčžŧぞれぞした" }, "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "ã‚Ģãƒŧドį•Ēåˇ" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { - "message": "Timeout action" + "message": "ã‚ŋイムã‚ĸã‚Ļト時ぎã‚ĸã‚¯ã‚ˇãƒ§ãƒŗ" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index a54a0f2c657..d74b4f225fe 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "სინáƒĨრონიზაáƒĒია" }, - "syncVaultNow": { - "message": "Sync vault now" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "ბოლო სინáƒĨი:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Import items" - }, "select": { "message": "არჩევა" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "჊აქáƒŦორება" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "გაიგეთ მეáƒĸი" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Authenticator key (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Attachment saved" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "ფაილი" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "აირჩიეთ ფაილი" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maximum file size is 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restore item" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "შეáƒĒდომა" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "იგნორი" }, - "importData": { - "message": "მონაáƒĒემების შემოáƒĸანა", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "შემოáƒĸანის შეáƒĒდომა" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "ანგარიშის áƒŖáƒĄáƒáƒ¤áƒ áƒ—áƒŽáƒáƒ”áƒ‘áƒ" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "გაფრთხილებები" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index f829937ac51..c15ab367666 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sync" }, - "syncVaultNow": { - "message": "Sync vault now" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Last sync:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Import items" - }, "select": { "message": "Select" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Edit" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Authenticator key (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Attachment saved" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "File" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Select a file" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maximum file size is 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restore item" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index bd2be23828c..20e1cec5280 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "➏ā˛ŋā˛‚ā˛•āŗ" }, - "syncVaultNow": { - "message": "ā˛ĩā˛žā˛˛āŗā˛Ÿāŗ ā˛…ā˛¨āŗā˛¨āŗ ā˛ˆā˛— ➏ā˛ŋā˛‚ā˛•āŗ ā˛Žā˛žā˛Ąā˛ŋ" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "ā˛•āŗŠā˛¨āŗ†ā˛¯ ➏ā˛ŋā˛‚ā˛•āŗ" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "ā˛ĩā˛¸āŗā˛¤āŗā˛—ā˛ŗā˛¨āŗā˛¨āŗ ā˛†ā˛Žā˛Ļ⺁ ā˛Žā˛žā˛Ąā˛ŋ" - }, "select": { "message": "ā˛†ā˛¯āŗā˛•āŗ†ā˛Žā˛žā˛Ąā˛ŋ" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "ā˛Žā˛Ąā˛ŋā˛Ÿāŗ" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "➰ā˛Ģāŗā˛¤āŗ ā˛ĩā˛žā˛˛āŗā˛Ÿāŗ" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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": "ā˛•ā˛Ąā˛¤ā˛Ļ ā˛Žā˛žā˛Ļ➰ā˛ŋ" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "ā˛‡ā˛¨āŗā˛¨ā˛ˇāŗā˛Ÿāŗ ➤ā˛ŋ➺ā˛ŋ➝ā˛ŋ➰ā˛ŋ" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "ā˛Ļ⺃ā˛ĸ⺀➕➰➪ ➕⺀ (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "ā˛˛ā˛—ā˛¤āŗā˛¤ā˛¨āŗā˛¨āŗ ➉➺ā˛ŋā˛¸ā˛˛ā˛žā˛—ā˛ŋā˛Ļāŗ†." }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "ā˛Ģāŗˆā˛˛āŗ" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "ā˛•ā˛Ąā˛¤ā˛ĩā˛¨āŗā˛¨āŗ ā˛†ā˛¯āŗā˛•āŗ†ā˛Žā˛žā˛Ąāŗ." }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "➗➰ā˛ŋā˛ˇāŗā˛  ā˛Ģāŗˆā˛˛āŗ ā˛—ā˛žā˛¤āŗā˛° 500 ā˛Žā˛‚ā˛Ŧā˛ŋ." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "ā˛Ģāŗˆā˛˛āŗ ā˛˛ā˛—ā˛¤āŗā˛¤āŗā˛—ā˛ŗā˛ŋā˛—ā˛žā˛—ā˛ŋ 1 ➜ā˛ŋā˛Ŧā˛ŋ ā˛Žā˛¨āŗâ€Œā˛•āŗā˛°ā˛ŋā˛Ēāŗā˛Ÿāŗ ā˛Žā˛žā˛Ąā˛ŋā˛Ļ ā˛¸ā˛‚ā˛—āŗā˛°ā˛š." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "➍ā˛ŋā˛Žāŗā˛Ž ā˛ĩā˛žā˛˛āŗā˛Ÿāŗ ā˛…ā˛¨āŗā˛¨āŗ ā˛¸āŗā˛°ā˛•āŗā˛ˇā˛ŋ➤ā˛ĩā˛žā˛—ā˛ŋ➰ā˛ŋ➏➞⺁ ā˛Ēā˛žā˛¸āŗā˛ĩā˛°āŗā˛Ąāŗ ā˛¨āŗˆā˛°āŗā˛Žā˛˛āŗā˛¯, ā˛–ā˛žā˛¤āŗ† ā˛†ā˛°āŗ‹ā˛—āŗā˛¯ ā˛Žā˛¤āŗā˛¤āŗ ā˛Ąāŗ‡ā˛Ÿā˛ž ā˛‰ā˛˛āŗā˛˛ā˛‚ā˛˜ā˛¨āŗ† ā˛ĩ➰ā˛Ļā˛ŋ➗➺⺁." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "ā˛ļā˛žā˛ļāŗā˛ĩ➤ā˛ĩā˛žā˛—ā˛ŋ ➅➺ā˛ŋā˛¸ā˛˛ā˛žā˛Ļ ā˛ā˛Ÿā˛‚" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "ā˛ā˛Ÿā˛‚ ā˛…ā˛¨āŗā˛¨āŗ ā˛Žā˛°āŗā˛¸āŗā˛Ĩā˛žā˛Ēā˛ŋ➏ā˛ŋ" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 1ad08accfc9..8cedaf14acc 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "íŒ¨ėŠ¤í‚¤ëĨŧ ė‚ŦėšŠí•˜ė—Ŧ ëĄœęˇ¸ė¸í•˜ę¸°" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "í†ĩí•Šė¸ėĻ(SSO) ė‚ŦėšŠí•˜ę¸°" }, @@ -436,8 +439,8 @@ "sync": { "message": "동기화" }, - "syncVaultNow": { - "message": "ė§€ę¸ˆ ëŗ´ę´€í•¨ 동기화" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "ë§ˆė§€ë§‰ 동기화:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden ė›š ė•ą" }, - "importItems": { - "message": "항ëĒŠ ę°€ė ¸ė˜¤ę¸°" - }, "select": { "message": "ė„ íƒ" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "항ëĒŠė´ ëŗ´ę´€í•¨ėœŧ로 ė´ë™ë˜ė—ˆėŠĩ니다" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "항ëĒŠ ëŗ´ę´€ í•´ė œë¨" }, "archiveItem": { "message": "항ëĒŠ ëŗ´ę´€" }, - "archiveItemConfirmDesc": { - "message": "ëŗ´ę´€ëœ 항ëĒŠė€ ėŧ반 ę˛€ėƒ‰ 결ęŗŧ뙀 ėžë™ ė™„ė„ą ė œė•ˆė—ė„œ ė œė™¸ëŠë‹ˆë‹¤. ė´ 항ëĒŠė„ ëŗ´ę´€í•˜ė‹œę˛ ėŠĩ니까?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "íŽ¸ė§‘" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "~(ėœŧ)로ëļ€í„° ë‚´ëŗ´ë‚´ę¸°" }, - "exportVault": { - "message": "ëŗ´ę´€í•¨ ë‚´ëŗ´ë‚´ę¸°" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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": "파ėŧ í˜•ė‹" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "더 ė•Œė•„ëŗ´ę¸°" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "ė¸ėĻ 키 (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "랍ëļ€ íŒŒėŧė„ ė €ėžĨ했ėŠĩ니다." }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "파ėŧ" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "파ėŧė„ ė„ íƒí•˜ė„¸ėš”." }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "ėĩœëŒ€ 파ėŧ íŦ기는 500MBėž…ë‹ˆë‹¤." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1GBė˜ ė•”í˜¸í™”ëœ 파ėŧ ė €ėžĨė†Œ." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "ëš„ėƒ ė ‘ęˇŧ" }, "premiumSignUpTwoStepOptions": { "message": "YubiKey나 Duo뙀 ę°™ė€ ë…ė ė ė¸ 2ë‹¨ęŗ„ ëĄœęˇ¸ė¸ ė˜ĩė…˜" }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "ëŗ´ę´€í•¨ė„ ė•ˆė „í•˜ę˛Œ ėœ ė§€í•˜ę¸° ėœ„í•œ ė•”í˜¸ ėœ„ėƒ, ęŗ„ė • ėƒíƒœ, ë°ė´í„° 뜠ėļœ ëŗ´ęŗ ė„œ" }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "똁ęĩŦ렁ėœŧ로 ė‚­ė œëœ 항ëĒŠ" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "항ëĒŠ ëŗĩ뛐" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "溠뜠 ė‹ëŗ„ėžëĨŧ ė°žė„ 눘 ė—†ėŠĩ니다." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "똤ëĨ˜" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "ëŦ´ė‹œí•˜ę¸°" }, - "importData": { - "message": "ë°ė´í„° ę°€ė ¸ė˜¤ę¸°", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "ę°€ė ¸ė˜¤ę¸° 똤ëĨ˜" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "다ëĨ¸ ė˜ĩė…˜ - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "관ëĻŦėž ėŊ˜ė†”" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "ęŗ„ė • ëŗ´ė•ˆ" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "ė•ŒëĻŧ" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "$WEBSITE$ ėŧėš˜ ė¸ė‹ 눍揰揰", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "íŽ˜ė´ė§€ 로드 ė‹œ ėžë™ ė™„ė„ąė„ í• ęšŒėš”?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "ë§¤ėš° 넓게" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index 46ffb24e3df..eac510ea668 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Prisijungti naudojant prieigos raktą" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Naudoti vieningo prisijungimo sistemą" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sinchronizuoti" }, - "syncVaultNow": { - "message": "Sinchronizuoti saugyklą dabar" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Paskutinis sinchronizavimas:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden Interneto svetainė" }, - "importItems": { - "message": "Importuoti elementus" - }, "select": { "message": "Pasirinkti" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Keisti" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Eksportuoti saugyklą" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "SuÅžinoti daugiau" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Vienkartinio autentifikavimo raktas (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Priedas iÅĄsaugotas" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Failas" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Pasirinkite failą." }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "DidÅžiausias failo dydis – 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB uÅžÅĄifruotos vietos diske bylÅŗ prisegimams." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Patentuotos dviejÅŗ ÅžingsniÅŗ prisijungimo parinktys, tokios kaip YubiKey ir Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "SlaptaÅžodÅžio higiena, prieigos sveikata ir duomenÅŗ nutekinimo ataskaitos, kad tavo saugyklas bÅĢtÅŗ saugus." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "IÅĄtrintas visam laikui" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Atkurti elementą" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Unikalus identifikatorius nerastas." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Klaida" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignoruoti" }, - "importData": { - "message": "Importuoti duomenis", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Importavimo klaida" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Administratoriaus konsolės" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Paskyros saugumas" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "PraneÅĄimai" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 754781dc4f7..6c5ee5adb98 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Pieteikties ar piekÄŧuves atslēgu" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Izmantot vienoto pieteikÅĄanos" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sinhronizēt" }, - "syncVaultNow": { - "message": "Sinhronizēt glabātavu" + "syncNow": { + "message": "Sinhronizēt tÅĢlÄĢt" }, "lastSync": { "message": "Pēdējā sinhronizÄ“ÅĄana:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden tÄĢmekÄŧa lietotne" }, - "importItems": { - "message": "Ievietot vienumus" - }, "select": { "message": "Izvēlēties" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Vienums tika ievietots arhÄĢvā" }, + "itemWasUnarchived": { + "message": "Vienums tika izņemts no arhÄĢva" + }, "itemUnarchived": { "message": "Vienums tika izņemts no arhÄĢva" }, "archiveItem": { "message": "Arhivēt vienumu" }, - "archiveItemConfirmDesc": { - "message": "Arhivētie vienumi netiek iekÄŧauti vispārējās meklÄ“ÅĄanas iznākumos un automātiskās aizpildes ieteikumos. Vai tieÅĄÄm ahrivēt ÅĄo vienumu?" + "archiveItemDialogContent": { + "message": "Pēc ievietoÅĄanas arhÄĢvā ÅĄis vienums netiks iekÄŧauts meklÄ“ÅĄanas iznākumā un automātiskās aizpildes ieteikumos." + }, + "archived": { + "message": "Arhivēts" + }, + "unarchiveAndSave": { + "message": "Atcelt arhivÄ“ÅĄanu un saglabāt" }, "upgradeToUseArchive": { "message": "Ir nepiecieÅĄama Premium dalÄĢba, lai izmantotu arhÄĢvu." }, + "itemRestored": { + "message": "Vienums tika atjaunots" + }, "edit": { "message": "Labot" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "IzgÅĢt no" }, - "exportVault": { - "message": "IzgÅĢt glabātavas saturu" + "exportVerb": { + "message": "IzgÅĢt", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Uzzināt vairāk" }, + "migrationsFailed": { + "message": "AtgadÄĢjās kÄŧÅĢda ÅĄifrÄ“ÅĄanas iestatÄĢjumu atjauninÄÅĄanā." + }, + "updateEncryptionSettingsTitle": { + "message": "Atjaunini savus ÅĄifrÄ“ÅĄanas iestatÄĢjumus" + }, + "updateEncryptionSettingsDesc": { + "message": "Jaunie ieteicamie ÅĄifrÄ“ÅĄanas iestatÄĢjumi uzlabos Tava konta droÅĄÄĢbu. Jāievada sava galvenā parole, lai atjauninātu tagad." + }, + "confirmIdentityToContinue": { + "message": "Jāapliecina sava identitāte, lai turpinātu" + }, + "enterYourMasterPassword": { + "message": "Jāievada sava galvenā parole" + }, + "updateSettings": { + "message": "Atjaunināt IestatÄĢjumus" + }, + "later": { + "message": "Vēlāk" + }, "authenticatorKeyTotp": { "message": "Autentificētāja atslēga (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Pielikums tika saglabāts." }, + "fixEncryption": { + "message": "Salabot ÅĄifrÄ“ÅĄanu" + }, + "fixEncryptionTooltip": { + "message": "Å ÄĢ datne izmanto novecojuÅĄu ÅĄifrÄ“ÅĄanas veidu." + }, + "attachmentUpdated": { + "message": "Pielikums atjaunināts" + }, "file": { "message": "Datne" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "AtlasÄĢt datni" }, + "itemsTransferred": { + "message": "Vienumi pārcelti" + }, "maxFileSize": { "message": "Lielākais pieÄŧaujamais datnes izmērs ir 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB ÅĄifrētas krātuves datņu pielikumiem." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ ÅĄifrētas krātuves datņu pielikumiem.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Ārkārtas piekÄŧuve" }, "premiumSignUpTwoStepOptions": { "message": "Tādas slēgtā pirmavota divpakāpju pieteikÅĄanās iespējas kā YubiKey un Duo." }, + "premiumSubscriptionEnded": { + "message": "Tavs Premium abonements beidzās" + }, + "archivePremiumRestart": { + "message": "Lai atgÅĢtu piekÄŧuvi savam arhÄĢvam, jāatsāk Premium abonements. Ja labosi arhivēta vienuma informāciju pirms atsākÅĄanas, tas tiks pārvietots atpakaÄŧ Tavā glabātavā." + }, + "restartPremium": { + "message": "Atsākt Premium" + }, "ppremiumSignUpReports": { "message": "ParoÄŧu higiēnas, konta veselÄĢbas un datu noplÅĢÅžu pārskati, lai uzturētu glabātavu droÅĄu." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Vienums ir neatgriezeniski izdzēsts" }, + "archivedItemRestored": { + "message": "ArhÄĢva vienums atjaunots" + }, "restoreItem": { "message": "Atjaunot vienumu" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Nav atrasts neviens neatkārtojams identifikators" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Galvenā parole vairs nav nepiecieÅĄama turpmāk minētās apvienÄĢbas dalÄĢbniekiem. LÅĢgums saskaņot zemāk esoÅĄo domēnu ar savas apvienÄĢbas pārvaldÄĢtāju." - }, "organizationName": { "message": "ApvienÄĢbas nosaukums" }, @@ -3294,6 +3370,12 @@ "error": { "message": "KÄŧÅĢda" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "AtÅĄifrÄ“ÅĄanas kÄŧÅĢda" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Neņemt vērā" }, - "importData": { - "message": "Ievietot datus", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "IevietoÅĄanas kÄŧÅĢda" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "Vairāk iespēju" + }, "moreOptionsTitle": { "message": "Vairāk iespēju - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "pārvaldÄĢbas konsolē," }, + "admin": { + "message": "PārvaldÄĢtājs" + }, + "automaticUserConfirmation": { + "message": "Automātiska lietotāju apstiprinÄÅĄana" + }, + "automaticUserConfirmationHint": { + "message": "Automātiski apstiprināt ierindotos lietotājus, kamēr ÅĄÄĢ ierÄĢce ir atslēgta" + }, + "autoConfirmOnboardingCallout": { + "message": "Laika ietaupÄĢÅĄana ar automātisku lietotāju apstiprinÄÅĄanu" + }, + "autoConfirmWarning": { + "message": "Tas varētu ietekmēt apvienÄĢbas datu droÅĄÄĢbu. " + }, + "autoConfirmWarningLink": { + "message": "Uzzināt par riskiem" + }, + "autoConfirmSetup": { + "message": "Automātiski apstiprināt jaunus lietotājus" + }, + "autoConfirmSetupDesc": { + "message": "Jauni lietotāji tiks automātiski apstiprināti, kamēr ÅĄÄĢ ierÄĢce ir atslēgta." + }, + "autoConfirmSetupHint": { + "message": "Kādi ir iespējamie droÅĄÄĢbas riski?" + }, + "autoConfirmEnabled": { + "message": "Automātiska apstiprinÄÅĄana ieslēgta" + }, + "availableNow": { + "message": "Pieejams tagad" + }, "accountSecurity": { "message": "Konta droÅĄÄĢba" }, + "phishingBlocker": { + "message": "PikÅĄÄˇerÄ“ÅĄanas aizturētājs" + }, + "enablePhishingDetection": { + "message": "PikÅĄÄˇerÄ“ÅĄanas noteikÅĄana" + }, + "enablePhishingDetectionDesc": { + "message": "Attēlot brÄĢdinājumu pirms piekÄŧÅĢÅĄanas iespējamām piekÅĄÄˇerÄ“ÅĄanas vietnēm" + }, "notifications": { "message": "Paziņojumi" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Paslēpt atbilstÄĢbas noteikÅĄanu $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "RādÄĢt atbilstÄĢbas noteikÅĄanu" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Slēpt atbilstÄĢbas noteikÅĄanu" }, "autoFillOnPageLoad": { "message": "Automātiski aizpildÄĢt lapas ielādes brÄĢdÄĢ?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Äģoti plats" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "IevadÄĢtā parole ir nepareiza." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Å is pieteikÅĄanās vienums ir pakÄŧauts riskam, un tam nav norādÄĢta tÄĢmekÄŧvietne. Lielākai droÅĄÄĢbai jāpievieno tÄĢmekÄŧvietne un jānomaina parole." }, + "vulnerablePassword": { + "message": "Ievainojama parole." + }, + "changeNow": { + "message": "MainÄĢt tagad" + }, "missingWebsite": { "message": "Nav norādÄĢta tÄĢmekÄŧvietne" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "Un vēl!" }, - "planDescPremium": { - "message": "PilnÄĢga droÅĄÄĢba tieÅĄsaistē" + "advancedOnlineSecurity": { + "message": "Izvērsta tieÅĄsaistes droÅĄÄĢba" }, "upgradeToPremium": { "message": "Uzlabot uz Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Kartes numurs" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "ApvienÄĢba vairs neizmanto galvenās paroles, lai pieteiktos Bitwarden. Lai turpinātu, jāapliecina apvienÄĢba un domēns." + }, + "continueWithLogIn": { + "message": "Turpināt ar pieteikÅĄanos" + }, + "doNotContinue": { + "message": "Neturpināt" + }, + "domain": { + "message": "Domēns" + }, + "keyConnectorDomainTooltip": { + "message": "Å ajā domēnā tiks glabātas konta ÅĄifrÄ“ÅĄanas atslēgas, tādēÄŧ jāpārliecinās par uzticamÄĢbu. Ja nav pārliecÄĢbas, jāsazinās ar savu pārvaldÄĢtāju." + }, + "verifyYourOrganization": { + "message": "Jāapliecina apvienÄĢba, lai pieteiktos" + }, + "organizationVerified": { + "message": "ApvienÄĢba apliecināta" + }, + "domainVerified": { + "message": "Domēns ir apliecināts" + }, + "leaveOrganizationContent": { + "message": "Ja neapliecināsi apvienÄĢbu, tiks atsaukta piekÄŧuve tai." + }, + "leaveNow": { + "message": "Pamest tagad" + }, + "verifyYourDomainToLogin": { + "message": "Jāapliecina domēns, lai pieteiktos" + }, + "verifyYourDomainDescription": { + "message": "Lai turpinātu pieteikÅĄanos, jāapliecina ÅĄis domēns." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Lai turpinātu pieteikÅĄanos, jāapliecina apvienÄĢba un domēns." + }, "sessionTimeoutSettingsAction": { "message": "Noildzes darbÄĢba" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Uzlabot" + }, + "leaveConfirmationDialogTitle": { + "message": "Vai tieÅĄÄm pamest?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Pēc noraidÄĢÅĄanas personÄĢgie vienumi paliks Tavā kontā, bet Tu zaudēsi piekÄŧvuvi kopÄĢgotajiem vienumiem un apvienÄĢbu iespējām." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Jāsazinās ar savu pārvaldÄĢtāju, lai atgÅĢtu piekÄŧuvi." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Pamest $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Kā es varu pārvaldÄĢt savu glabātavu?" + }, + "transferItemsToOrganizationTitle": { + "message": "Pārcelt vienumus uz $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "MainÄĢt sānu pārvietoÅĄanās joslas izmēru" } } diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index 2f7f8d30e74..35ff7b94d4c 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "ā´¸ā´Žā´¨āĩā´ĩā´¯ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•" }, - "syncVaultNow": { - "message": "ā´ĩā´žāĩžā´Ÿāĩ ā´‡ā´Ēāĩā´Ēāĩ‹āĩž ā´¸ā´Žā´¨āĩā´ĩā´¯ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "ā´…ā´ĩā´¸ā´žā´¨ ā´¸ā´Žā´¨āĩā´ĩⴝⴂ:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "ⴇⴍⴙāĩā´™āĩž ā´‡ā´Žāĩā´Ēāĩ‹āĩŧⴟāĩ ⴚāĩ†ā´¯āĩā´¯āĩā´•" - }, "select": { "message": "ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "ā´¤ā´ŋā´°āĩā´¤āĩā´¤āĩā´•" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "ā´ĩā´žāĩžā´Ÿāĩ ā´Žā´•āĩā´¸āĩā´Ēāĩ‹āĩŧⴟāĩ" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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": "ā´Ģā´¯āĩŊ ā´Ģāĩ‹āĩŧā´Žā´žā´ąāĩā´ąāĩ" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "ā´•āĩ‚ā´Ÿāĩā´¤ā´˛ā´ąā´ŋā´ĩāĩ ā´¨āĩ‡ā´Ÿāĩā´•" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "ⴓⴤⴍāĩā´ąā´ŋā´•āĩā´•āĩ‡ā´ąāĩā´ąāĩŧ ā´•āĩ€ (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "ā´…ā´ąāĩā´ąā´žā´šāĩā´šāĩā´Žāĩ†ā´¨āĩā´ąāĩ ⴏⴂⴰⴕāĩā´ˇā´ŋⴚāĩā´šāĩ." }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "ā´Ģā´¯āĩŊ" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "ā´’ā´°āĩ ā´Ģā´¯āĩŊ ā´¤ā´ŋā´°ā´žāĩā´žāĩ†ā´Ÿāĩā´•āĩā´•āĩā´•" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "ā´Ēā´°ā´Žā´žā´ĩā´§ā´ŋ ā´Ģā´¯āĩŊ ā´ĩā´˛āĩā´Ēāĩā´Ēā´‚ 500 MB ⴆ⴪āĩ." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "ā´Ģā´¯āĩŊ ā´…ā´ąāĩā´ąā´žā´šāĩā´šāĩā´Žāĩ†ā´¨āĩā´ąāĩā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ 1 ⴜā´ŋā´Ŧā´ŋ ā´Žāĩģā´•āĩā´°ā´ŋā´Ēāĩā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩâ€Œā´¤ ⴏⴂⴭⴰ⴪ⴂ." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "ā´¨ā´ŋā´™āĩā´™ā´ŗāĩā´Ÿāĩ† ā´ĩā´žāĩžā´Ÿāĩ ā´¸āĩ‚ā´•āĩā´ˇā´ŋā´•āĩā´•āĩā´¨āĩā´¨ā´¤ā´ŋā´¨āĩ. ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´ļāĩā´šā´ŋā´¤āĩā´ĩā´‚, ā´…ā´•āĩā´•āĩ—ā´Ŗāĩā´Ÿāĩ ⴆⴰāĩ‹ā´—āĩā´¯ā´‚, ā´Ąā´žā´ąāĩā´ą ā´˛ā´‚ā´˜ā´¨ ā´ąā´ŋā´Ēāĩā´Ēāĩ‹āĩŧⴟāĩā´Ÿāĩā´•āĩž." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "ā´ļā´žā´ļāĩā´ĩā´¤ā´Žā´žā´¯ā´ŋ ⴇⴞāĩā´˛ā´žā´¤ā´žā´•āĩā´•ā´ŋā´¯ ⴇⴍⴂ" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "ⴇⴍⴂ ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩ†ā´Ÿāĩā´•āĩā´•āĩā´• " }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 70f8fe5393c..bae23dcd94d 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "ā¤¸ā¤‚ā¤•ā¤žā¤˛ā¤¨" }, - "syncVaultNow": { - "message": "⤤ā¤ŋ⤜āĨ‹ā¤°āĨ€ ā¤¸ā¤‚ā¤•ā¤žā¤˛ā¤¨ ā¤†ā¤¤ā¤ž ā¤•ā¤°ā¤ž" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "ā¤ļāĨ‡ā¤ĩ⤟⤚āĨ‡ ā¤¸ā¤‚ā¤•ā¤žā¤˛ā¤¨:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "ā¤ĩ⤏āĨā¤¤āĨ‚ ā¤†ā¤¯ā¤žā¤¤ ā¤•ā¤°ā¤ž" - }, "select": { "message": "Select" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Edit" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Authenticator key (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Attachment saved" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "File" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Select a file" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maximum file size is 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restore item" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index f829937ac51..c15ab367666 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sync" }, - "syncVaultNow": { - "message": "Sync vault now" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Last sync:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Import items" - }, "select": { "message": "Select" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Edit" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Authenticator key (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Attachment saved" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "File" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Select a file" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maximum file size is 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restore item" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 0eba9c953a3..993d7a1f0db 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Logg inn med passnøkkel" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Bruk singulÃĻr pÃĨlogging" }, @@ -436,8 +439,8 @@ "sync": { "message": "Synkroniser" }, - "syncVaultNow": { - "message": "Synkroniser hvelvet nÃĨ" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Forrige synkronisering:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwardens nett-app" }, - "importItems": { - "message": "Importer elementer" - }, "select": { "message": "Velg" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Rediger" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Eksporter fra" }, - "exportVault": { - "message": "Eksporter hvelvet" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "LÃĻr mer" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Autentiseringsnøkkel (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Vedlegget har blitt lagret." }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Fil" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Velg en fil." }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Den maksimale filstørrelsen er 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB med kryptert fillagring for filvedlegg." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Nødtilgang." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Passordhygiene, kontohelse, og databruddsrapporter som holder hvelvet ditt trygt." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Slettet objektet permanent" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Gjenopprett objekt" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Ingen unik identifikator ble funnet." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organisasjonens navn" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Feil" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Dekrypteringsfeil" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignorer" }, - "importData": { - "message": "Importer data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Importeringsfeil" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "Flere innstillinger - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Administrasjonskonsoll" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Kontosikkerhet" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Varsler" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Vil du auto-utfylle ved sideinnlasting?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Ekstra bred" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "Passordet du skrev inn er feil." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index f829937ac51..c15ab367666 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sync" }, - "syncVaultNow": { - "message": "Sync vault now" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Last sync:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Import items" - }, "select": { "message": "Select" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Edit" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Authenticator key (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Attachment saved" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "File" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Select a file" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maximum file size is 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restore item" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 601aac16f94..504868fc5c8 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Inloggen met passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Single sign-on gebruiken" }, @@ -436,8 +439,8 @@ "sync": { "message": "Synchroniseren" }, - "syncVaultNow": { - "message": "Kluis nu synchroniseren" + "syncNow": { + "message": "Nu synchroniseren" }, "lastSync": { "message": "Laatste synchronisatie:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden webapp" }, - "importItems": { - "message": "Items importeren" - }, "select": { "message": "Selecteren" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item naar archief verzonden" }, + "itemWasUnarchived": { + "message": "Item uit het archief gehaald" + }, "itemUnarchived": { "message": "Item uit het archief gehaald" }, "archiveItem": { "message": "Item archiveren" }, - "archiveItemConfirmDesc": { - "message": "Gearchiveerde items worden uitgesloten van algemene zoekresultaten en automatische invulsuggesties. Weet je zeker dat je dit item wilt archiveren?" + "archiveItemDialogContent": { + "message": "Eenmaal gearchiveerd wordt dit item uitgesloten van zoekresultaten en suggesties voor automatisch invullen." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "Je hebt een Premium-abonnement nodig om te kunnen archiveren." }, + "itemRestored": { + "message": "Item is hersteld" + }, "edit": { "message": "Bewerken" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Exporteren vanuit" }, - "exportVault": { - "message": "Kluis exporteren" + "exportVerb": { + "message": "Exporteren", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Meer informatie" }, + "migrationsFailed": { + "message": "Er is een fout opgetreden bij het bijwerken van de versleutelingsinstellingen." + }, + "updateEncryptionSettingsTitle": { + "message": "Je versleutelingsinstellingen bijwerken" + }, + "updateEncryptionSettingsDesc": { + "message": "De nieuwe aanbevolen versleutelingsinstellingen verbeteren de beveiliging van je account. Voer je hoofdwachtwoord in om nu bij te werken." + }, + "confirmIdentityToContinue": { + "message": "Bevestig je identiteit om door te gaan" + }, + "enterYourMasterPassword": { + "message": "Voer je hoofdwachtwoord in" + }, + "updateSettings": { + "message": "Instellingen bijwerken" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Authenticatiecode (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Bijlage opgeslagen" }, + "fixEncryption": { + "message": "Versleuteling repareren" + }, + "fixEncryptionTooltip": { + "message": "Dit bestand gebruikt een verouderde versleutelingsmethode." + }, + "attachmentUpdated": { + "message": "Bijlagen bijgewerkt" + }, "file": { "message": "Bestand" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Selecteer een bestand" }, + "itemsTransferred": { + "message": "Items overgedragen" + }, "maxFileSize": { "message": "Maximale bestandsgrootte is 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB versleutelde opslag voor bijlagen." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ versleutelde opslag voor bijlagen.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Noodtoegang." }, "premiumSignUpTwoStepOptions": { "message": "Eigen opties voor tweestapsaanmelding zoals YubiKey en Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "WachtwoordhygiÃĢne, gezondheid van je account en datalekken om je kluis veilig te houden." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Definitief verwijderd item" }, + "archivedItemRestored": { + "message": "Gearchiveerd item hersteld" + }, "restoreItem": { "message": "Item herstellen" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Geen unieke id gevonden." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Voor leden van de volgende organisatie is een hoofdwachtwoord niet langer nodig. Bevestig het domein hieronder met de beheerder van je organisatie." - }, "organizationName": { "message": "Organisatienaam" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Fout" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Ontsleutelingsfout" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Negeren" }, - "importData": { - "message": "Data importeren", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Fout bij importeren" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "Meer opties" + }, "moreOptionsTitle": { "message": "Meer opties - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Beheerconsole" }, + "admin": { + "message": "Beheerder" + }, + "automaticUserConfirmation": { + "message": "Automatische gebruikersbevestiging" + }, + "automaticUserConfirmationHint": { + "message": "Automatisch gebruikers in behandeling bevestigen wanneer dit apparaat is ontgrendeld" + }, + "autoConfirmOnboardingCallout": { + "message": "Bespaar tijd met automatische gebruikersbevestiging" + }, + "autoConfirmWarning": { + "message": "Dit kan van invloed zijn op de gegevensbeveiliging van je organisatie. " + }, + "autoConfirmWarningLink": { + "message": "Meer informatie over de risico's" + }, + "autoConfirmSetup": { + "message": "Automatisch nieuwe gebruikers bevestigen" + }, + "autoConfirmSetupDesc": { + "message": "Nieuwe gebruikers worden automatisch bevestigd wanneer dit apparaat is ontgrendeld." + }, + "autoConfirmSetupHint": { + "message": "Wat zijn de mogelijke veiligheidsrisico's?" + }, + "autoConfirmEnabled": { + "message": "Automatische bevestigen ingeschakeld" + }, + "availableNow": { + "message": "Nu beschikbaar" + }, "accountSecurity": { "message": "Accountbeveiliging" }, + "phishingBlocker": { + "message": "Phishing-blocker" + }, + "enablePhishingDetection": { + "message": "Phishingdetectie" + }, + "enablePhishingDetectionDesc": { + "message": "Waarschuwing weergeven voor het benaderen van vermoedelijke phishingsites" + }, "notifications": { "message": "Meldingen" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Overeenkomstdetectie verbergen $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Overeenkomstdetectie weergeven" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Overeenkomstdetectie verbergen" }, "autoFillOnPageLoad": { "message": "Automatisch invullen bij laden van pagina?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra breed" }, + "narrow": { + "message": "Smal" + }, "sshKeyWrongPassword": { "message": "Het wachtwoord dat je hebt ingevoerd is onjuist." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Deze login is in gevaar en mist een website. Voeg een website toe en verander het wachtwoord voor een sterkere beveiliging." }, + "vulnerablePassword": { + "message": "Kwetsbaar wachtwoord." + }, + "changeNow": { + "message": "Nu wijzigen" + }, "missingWebsite": { "message": "Ontbrekende website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "En meer!" }, - "planDescPremium": { - "message": "Online beveiliging voltooien" + "advancedOnlineSecurity": { + "message": "Geavanceerde online beveiliging" }, "upgradeToPremium": { "message": "Opwaarderen naar Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Kaartnummer" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Je organisatie maakt niet langer gebruik van hoofdwachtwoorden om in te loggen op Bitwarden. Controleer de organisatie en het domein om door te gaan." + }, + "continueWithLogIn": { + "message": "Doorgaan met inloggen" + }, + "doNotContinue": { + "message": "Niet verder gaan" + }, + "domain": { + "message": "Domein" + }, + "keyConnectorDomainTooltip": { + "message": "Dit domein zal de encryptiesleutels van je account opslaan, dus zorg ervoor dat je het vertrouwt. Als je het niet zeker weet, controleer dan bij je beheerder." + }, + "verifyYourOrganization": { + "message": "Verifieer je organisatie om in te loggen" + }, + "organizationVerified": { + "message": "Organisatie geverifieerd" + }, + "domainVerified": { + "message": "Domein geverifieerd" + }, + "leaveOrganizationContent": { + "message": "Als je je organisatie niet verifieert, wordt je toegang tot de organisatie ingetrokken." + }, + "leaveNow": { + "message": "Nu verlaten" + }, + "verifyYourDomainToLogin": { + "message": "Verifieer je domein om in te loggen" + }, + "verifyYourDomainDescription": { + "message": "Bevestig dit domein om verder te gaan met inloggen." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Bevestig organisatie en domein om verder te gaan met inloggen." + }, "sessionTimeoutSettingsAction": { "message": "Time-out actie" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Deze instelling wordt beheerd door je organisatie." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Je organisatie heeft de maximale sessietime-out ingesteld op $HOURS$ uur en $MINUTES$ minuten.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Je organisatie heeft de standaard sessie time-out ingesteld op Onmiddellijk." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Je organisatie heeft de standaard sessietime-out ingesteld op Systeem vergrendelen." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Je organisatie heeft de standaard sessietime-out ingesteld op Browser verversen." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximale time-out kan niet langer zijn dan $HOURS$ uur en $MINUTES$ minu(u) t(en)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Browser herstart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Stel een ontgrendelingsmethode in om je kluis time-out actie te wijzigen" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Weet je zeker dat je wilt verlaten?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Door te weigeren, blijven je persoonlijke items in je account, maar verlies je toegang tot gedeelde items en organisatiefunctionaliteit." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Neem contact op met je beheerder om weer toegang te krijgen." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "$ORGANIZATION$ verlaten", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Hoe beheer ik mijn kluis?" + }, + "transferItemsToOrganizationTitle": { + "message": "Items overdragen aan $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ vereist dat alle items eigendom zijn van de organisatie voor veiligheid en naleving. Klik op accepteren voor het overdragen van eigendom van je items.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Overdacht accepteren" + }, + "declineAndLeave": { + "message": "Weigeren en verlaten" + }, + "whyAmISeeingThis": { + "message": "Waarom zie ik dit?" + }, + "resizeSideNavigation": { + "message": "Formaat zijnavigatie wijzigen" } } diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index f829937ac51..c15ab367666 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sync" }, - "syncVaultNow": { - "message": "Sync vault now" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Last sync:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Import items" - }, "select": { "message": "Select" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Edit" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Authenticator key (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Attachment saved" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "File" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Select a file" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maximum file size is 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restore item" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index f829937ac51..c15ab367666 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sync" }, - "syncVaultNow": { - "message": "Sync vault now" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Last sync:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Import items" - }, "select": { "message": "Select" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Edit" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Authenticator key (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Attachment saved" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "File" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Select a file" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maximum file size is 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restore item" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 9741f94da36..f8d8d6bfd69 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Logowanie kluczem dostępu" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "UÅŧyj logowania jednokrotnego" }, @@ -436,8 +439,8 @@ "sync": { "message": "Synchronizacja" }, - "syncVaultNow": { - "message": "Synchronizuj sejf" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Ostatnia synchronizacja:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Aplikacja internetowa Bitwarden" }, - "importItems": { - "message": "Importuj elementy" - }, "select": { "message": "Wybierz" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Element został przeniesiony do archiwum" }, + "itemWasUnarchived": { + "message": "Element został usunięty z archiwum" + }, "itemUnarchived": { "message": "Element został usunięty z archiwum" }, "archiveItem": { "message": "Archiwizuj element" }, - "archiveItemConfirmDesc": { - "message": "Zarchiwizowane elementy są wykluczone z wynikÃŗw wyszukiwania i sugestii autouzupełniania. Czy na pewno chcesz archiwizować element?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Element został przywrÃŗcony" + }, "edit": { "message": "Edytuj" }, @@ -598,7 +610,7 @@ "message": "PokaÅŧ wszystko" }, "showAll": { - "message": "Show all" + "message": "PokaÅŧ wszystko" }, "viewLess": { "message": "PokaÅŧ mniej" @@ -1050,10 +1062,10 @@ "message": "Element został zapisany" }, "savedWebsite": { - "message": "Zapisana witryna" + "message": "Zapisana strona internetowa" }, "savedWebsites": { - "message": "Zapisane witryny ($COUNT$)", + "message": "Zapisane strony internetowe ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Eksportuj z" }, - "exportVault": { - "message": "Eksportuj sejf" + "exportVerb": { + "message": "Eksportuj", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Dowiedz się więcej" }, + "migrationsFailed": { + "message": "Wystąpił błąd podczas aktualizacji ustawień szyfrowania." + }, + "updateEncryptionSettingsTitle": { + "message": "Zaktualizuj ustawienia szyfrowania" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Wpisz hasło gÅ‚Ãŗwne" + }, + "updateSettings": { + "message": "Zaktualizuj ustawienia" + }, + "later": { + "message": "PÃŗÅēniej" + }, "authenticatorKeyTotp": { "message": "Klucz uwierzytelniający (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Załącznik został zapisany" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Plik" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Wybierz plik" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maksymalny rozmiar pliku to 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB miejsca na zaszyfrowane załączniki." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Dostęp awaryjny." }, "premiumSignUpTwoStepOptions": { "message": "Specjalne opcje logowania dwustopniowego, takie jak YubiKey i Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Raporty bezpieczeństwa haseł, konta i wyciekÃŗw danych, aby Twoje dane były bezpieczne." }, @@ -1719,13 +1795,13 @@ "message": "W jaki sposÃŗb Bitwarden chroni Twoje dane przed phishingiem?" }, "currentWebsite": { - "message": "Aktualna witryna" + "message": "Obecna strona internetowa" }, "autofillAndAddWebsite": { - "message": "Wypełnij automatycznie i dodaj tę witrynę" + "message": "Uzupełnij i dodaj stronę internetową" }, "autofillWithoutAdding": { - "message": "Automatyczne uzupełnianie bez dodawania" + "message": "Uzupełnij bez dodawania" }, "doNotAutofill": { "message": "Nie wypełniaj automatycznie" @@ -1874,7 +1950,7 @@ "message": "Rok wygaśnięcia" }, "monthly": { - "message": "month" + "message": "miesiąc" }, "expiration": { "message": "Data wygaśnięcia" @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Element został trwale usunięty" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "PrzywrÃŗÄ‡ element" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Nie znaleziono unikatowego identyfikatora." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Hasło gÅ‚Ãŗwne nie jest juÅŧ wymagane dla członkÃŗw następującej organizacji. PotwierdÅē poniÅŧszą domenę z administratorem organizacji." - }, "organizationName": { "message": "Nazwa organizacji" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Błąd" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Błąd odszyfrowywania" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignoruj" }, - "importData": { - "message": "Importuj dane", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Błąd importowania" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "Więcej opcji" + }, "moreOptionsTitle": { "message": "Więcej opcji - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Konsola administratora" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Bezpieczeństwo konta" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Powiadomienia" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Ukryj wykrywanie dopasowania $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "PokaÅŧ wykrywanie dopasowania" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Ukryj wykrywanie dopasowania" }, "autoFillOnPageLoad": { "message": "Włączyć autouzupełnianie po załadowaniu strony?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Bardzo szeroka" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "Hasło jest nieprawidłowe." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Dane logowania są zagroÅŧone i nie zawierają strony internetowej. Dodaj stronę internetową i zmień hasło." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Zmień teraz" + }, "missingWebsite": { "message": "Brak strony internetowej" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "I jeszcze więcej!" }, - "planDescPremium": { - "message": "Pełne bezpieczeństwo w Internecie" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Ulepsz do Premium" @@ -5840,7 +5969,7 @@ "message": "Sejf załadowany" }, "settingDisabledByPolicy": { - "message": "To ustawienie jest wyłączone zgodnie z zasadami polityki Twojej organizacji.", + "message": "Ustawienie jest wyłączone przez Twoją organizację.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." }, "zipPostalCodeLabel": { @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Numer karty" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Kontynuuj logowanie" + }, + "doNotContinue": { + "message": "Nie kontynuuj" + }, + "domain": { + "message": "Domena" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { - "message": "Akcja przekroczenia limitu czasu" + "message": "SposÃŗb blokady" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Opuść organizację $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Jak zarządzać sejfami?" + }, + "transferItemsToOrganizationTitle": { + "message": "Przenieś elementy do organizacji $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Zmień rozmiar nawigacji bocznej" } } diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index a7675274eae..a83d15be1b1 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Conectar-se com chave de acesso" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Usar autenticaÃ§ÃŖo Ãēnica" }, @@ -68,7 +71,7 @@ "message": "Uma dica de senha principal pode ajudÃĄ-lo(a) a lembrÃĄ-la caso vocÃĒ esqueça." }, "masterPassHintText": { - "message": "Se vocÃĒ esquecer sua senha, a dica de senha pode ser enviada ao seu e-mail. $CURRENT$/$MAXIMUM$ caracteres mÃĄximos.", + "message": "Se vocÃĒ esquecer sua senha, a dica da senha pode ser enviada ao seu e-mail. $CURRENT$ do mÃĄximo de $MAXIMUM$ caracteres.", "placeholders": { "current": { "content": "$1", @@ -203,13 +206,13 @@ "message": "Preenchimento automÃĄtico" }, "autoFillLogin": { - "message": "Preencher credencial automaticamente" + "message": "Preencher credencial" }, "autoFillCard": { - "message": "Preencher cartÃŖo automaticamente" + "message": "Preencher cartÃŖo" }, "autoFillIdentity": { - "message": "Preencher identidade automaticamente" + "message": "Preencher identidade" }, "fillVerificationCode": { "message": "Preencher cÃŗdigo de verificaÃ§ÃŖo" @@ -249,7 +252,7 @@ "message": "Conecte-se ao seu cofre" }, "autoFillInfo": { - "message": "NÃŖo hÃĄ credenciais disponíveis para preencher automaticamente na aba atual do navegador." + "message": "NÃŖo hÃĄ credenciais disponíveis para preencher na aba atual do navegador." }, "addLogin": { "message": "Adicionar uma credencial" @@ -436,8 +439,8 @@ "sync": { "message": "Sincronizar" }, - "syncVaultNow": { - "message": "Sincronizar cofre agora" + "syncNow": { + "message": "Sincronizar agora" }, "lastSync": { "message": "Última sincronizaÃ§ÃŖo:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Aplicativo web do Bitwarden" }, - "importItems": { - "message": "Importar itens" - }, "select": { "message": "Selecionar" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "O item foi enviado para o arquivo" }, + "itemWasUnarchived": { + "message": "O item foi desarquivado" + }, "itemUnarchived": { "message": "O item foi desarquivado" }, "archiveItem": { "message": "Arquivar item" }, - "archiveItemConfirmDesc": { - "message": "Itens arquivados sÃŖo excluídos dos resultados gerais de busca e das sugestÃĩes de preenchimento automÃĄtico. Tem certeza de que deseja arquivar este item?" + "archiveItemDialogContent": { + "message": "Ao arquivar, o item serÃĄ excluído dos resultados de busca e sugestÃĩes de preenchimento automÃĄtico." + }, + "archived": { + "message": "Arquivados" + }, + "unarchiveAndSave": { + "message": "Desarquivar e salvar" }, "upgradeToUseArchive": { "message": "Um plano Premium Ê necessÃĄrio para usar o arquivamento." }, + "itemRestored": { + "message": "O item foi restaurado" + }, "edit": { "message": "Editar" }, @@ -688,16 +700,16 @@ "message": "OpçÃĩes de desbloqueio" }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Configure um mÊtodo de desbloqueio para alterar a aÃ§ÃŖo do tempo limite do cofre." + "message": "Configure um mÊtodo de desbloqueio para alterar a aÃ§ÃŖo do limite de tempo do cofre." }, "unlockMethodNeeded": { "message": "Configure um mÊtodo de desbloqueio nas ConfiguraçÃĩes" }, "sessionTimeoutHeader": { - "message": "Tempo limite da sessÃŖo" + "message": "Limite de tempo da sessÃŖo" }, "vaultTimeoutHeader": { - "message": "Tempo limite do cofre" + "message": "Limite de tempo do cofre" }, "otherOptions": { "message": "Outras opçÃĩes" @@ -758,10 +770,10 @@ } }, "vaultTimeout": { - "message": "Tempo limite do cofre" + "message": "Limite de tempo do cofre" }, "vaultTimeout1": { - "message": "Tempo limite" + "message": "Limite de tempo" }, "lockNow": { "message": "Bloquear agora" @@ -861,7 +873,7 @@ "message": "A confirmaÃ§ÃŖo da senha principal nÃŖo corresponde." }, "newAccountCreated": { - "message": "A sua nova conta foi criada! Agora vocÃĒ pode conectar-se." + "message": "A sua conta nova foi criada! Agora vocÃĒ pode se conectar." }, "newAccountCreated2": { "message": "Sua nova conta foi criada!" @@ -870,7 +882,7 @@ "message": "VocÃĒ foi conectado!" }, "youSuccessfullyLoggedIn": { - "message": "VocÃĒ conectou-se à sua conta com sucesso" + "message": "VocÃĒ se conectou com sucesso" }, "youMayCloseThisWindow": { "message": "VocÃĒ pode fechar esta janela" @@ -898,7 +910,7 @@ } }, "autofillError": { - "message": "NÃŖo Ê possível preencher automaticamente o item selecionado nesta pÃĄgina. Em vez disso, copie e cole a informaÃ§ÃŖo." + "message": "NÃŖo Ê possível preencher o item selecionado nesta pÃĄgina. Em vez disso, copie e cole a informaÃ§ÃŖo." }, "totpCaptureError": { "message": "NÃŖo Ê possível ler o cÃŗdigo QR da pÃĄgina atual" @@ -1123,10 +1135,10 @@ "message": "Listar as identidades na pÃĄgina da aba para facilitar o preenchimento automÃĄtico." }, "clickToAutofillOnVault": { - "message": "Clique em itens na tela do Cofre para preencher automaticamente" + "message": "Clique em itens na tela do Cofre para preencher" }, "clickToAutofill": { - "message": "Clique em um item para preenchÃĒ-lo automaticamente" + "message": "Clicar itens na sugestÃŖo para preenchÃĒ-lo" }, "clearClipboard": { "message": "Limpar ÃĄrea de transferÃĒncia", @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Exportar de" }, - "exportVault": { - "message": "Exportar cofre" + "exportVerb": { + "message": "Exportar", + "description": "The verb form of the word Export" + }, + "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" @@ -1367,7 +1392,7 @@ "message": "Confirmar exportaÃ§ÃŖo do cofre" }, "exportWarningDesc": { - "message": "Esta exportaÃ§ÃŖo contÊm os dados do seu cofre em um formato nÃŖo criptografado. VocÃĒ nÃŖo deve armazenar ou enviar o arquivo exportado por canais inseguros (como e-mail). Apague o arquivo imediatamente apÃŗs terminar de usÃĄ-lo." + "message": "Esta exportaÃ§ÃŖo contÊm os dados do seu cofre em um formato sem criptografia. VocÃĒ nÃŖo deve armazenar ou enviar o arquivo exportado por canais inseguros (como e-mail). Apague o arquivo imediatamente apÃŗs terminar de usÃĄ-lo." }, "encExportKeyWarningDesc": { "message": "Esta exportaÃ§ÃŖo criptografa seus dados usando a chave de criptografia da sua conta. Se vocÃĒ rotacionar a chave de criptografia da sua conta, vocÃĒ deve exportar novamente, jÃĄ que vocÃĒ nÃŖo serÃĄ capaz de descriptografar este arquivo de exportaÃ§ÃŖo." @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Saiba mais" }, + "migrationsFailed": { + "message": "Ocorreu um erro ao atualizar as configuraçÃĩes de criptografia." + }, + "updateEncryptionSettingsTitle": { + "message": "Atualize suas configuraçÃĩes de criptografia" + }, + "updateEncryptionSettingsDesc": { + "message": "As novas configuraçÃĩes de criptografia recomendadas melhorarÃŖo a segurança da sua conta. Digite sua senha principal para atualizar agora." + }, + "confirmIdentityToContinue": { + "message": "Confirme sua identidade para continuar" + }, + "enterYourMasterPassword": { + "message": "Digite sua senha principal" + }, + "updateSettings": { + "message": "Atualizar configuraçÃĩes" + }, + "later": { + "message": "Depois" + }, "authenticatorKeyTotp": { "message": "Chave do autenticador (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Anexo salvo" }, + "fixEncryption": { + "message": "Corrigir criptografia" + }, + "fixEncryptionTooltip": { + "message": "Este arquivo estÃĄ usando um mÊtodo de criptografia desatualizado." + }, + "attachmentUpdated": { + "message": "Anexo atualizado" + }, "file": { "message": "Arquivo" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Selecione um arquivo" }, + "itemsTransferred": { + "message": "Itens transferidos" + }, "maxFileSize": { "message": "O tamanho mÃĄximo do arquivo Ê de 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB de armazenamento criptografado para anexo de arquivos." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ de armazenamento criptografado para anexos de arquivo.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Acesso de emergÃĒncia." }, "premiumSignUpTwoStepOptions": { "message": "OpçÃĩes de autenticaÃ§ÃŖo em duas etapas proprietÃĄrias como YubiKey e Duo." }, + "premiumSubscriptionEnded": { + "message": "Sua assinatura Premium terminou" + }, + "archivePremiumRestart": { + "message": "Para recuperar o seu acesso ao seu arquivo, retoma sua assinatura Premium. Se editar detalhes de um item arquivado antes de retomar, ele serÃĄ movido de volta para o seu cofre." + }, + "restartPremium": { + "message": "Retomar Premium" + }, "ppremiumSignUpReports": { "message": "RelatÃŗrios de higiene de senha, saÃēde da conta, e vazamentos de dados para manter o seu cofre seguro." }, @@ -1533,13 +1609,13 @@ "message": "Copiar TOTP automaticamente" }, "disableAutoTotpCopyDesc": { - "message": "Se uma credencial tiver uma chave de autenticador, copie o cÃŗdigo de verificaÃ§ÃŖo TOTP quando for preenchÃĒ-la automaticamente." + "message": "Se uma credencial tiver uma chave de autenticador, copie o cÃŗdigo de verificaÃ§ÃŖo TOTP quando for preenchÃĒ-la." }, "enableAutoBiometricsPrompt": { "message": "Pedir biometria ao abrir" }, "authenticationTimeout": { - "message": "Tempo limite da autenticaÃ§ÃŖo atingido" + "message": "Limite de tempo da autenticaÃ§ÃŖo atingido" }, "authenticationSessionTimedOut": { "message": "A sessÃŖo de autenticaÃ§ÃŖo expirou. Reinicie o processo de autenticaÃ§ÃŖo." @@ -1722,13 +1798,13 @@ "message": "Site atual" }, "autofillAndAddWebsite": { - "message": "Preencher automaticamente e adicionar este site" + "message": "Preencher e adicionar este site" }, "autofillWithoutAdding": { - "message": "Preencher automaticamente sem adicionar" + "message": "Preencher sem adicionar" }, "doNotAutofill": { - "message": "NÃŖo preencher automaticamente" + "message": "NÃŖo preencher" }, "showInlineMenuIdentitiesLabel": { "message": "Exibir identidades como sugestÃĩes" @@ -1761,10 +1837,10 @@ "description": "Overlay appearance select option for showing the field on click of the overlay icon" }, "enableAutoFillOnPageLoadSectionTitle": { - "message": "Preencher automaticamente ao carregar a pÃĄgina" + "message": "Preenchimento no carregamento da pÃĄgina" }, "enableAutoFillOnPageLoad": { - "message": "Preencher automaticamente ao carregar a pÃĄgina" + "message": "Preencher ao carregar a pÃĄgina" }, "enableAutoFillOnPageLoadDesc": { "message": "Se um formulÃĄrio de credencial for detectado, preencha automaticamente quando a pÃĄgina carregar." @@ -1788,10 +1864,10 @@ "message": "Usar configuraÃ§ÃŖo padrÃŖo" }, "autoFillOnPageLoadYes": { - "message": "Preencher automaticamente ao carregar a pÃĄgina" + "message": "Preencher ao carregar a pÃĄgina" }, "autoFillOnPageLoadNo": { - "message": "NÃŖo preencher automaticamente ao carregar a pÃĄgina" + "message": "NÃŖo preencher ao carregar a pÃĄgina" }, "commandOpenPopup": { "message": "Abrir pop-up do cofre" @@ -1800,13 +1876,13 @@ "message": "Abrir cofre na barra lateral" }, "commandAutofillLoginDesc": { - "message": "Preencher automaticamente a Ãēltima credencial utilizada para o site atual" + "message": "Preencher com a Ãēltima credencial utilizada para o site atual" }, "commandAutofillCardDesc": { - "message": "Preencher automaticamente o Ãēltimo cartÃŖo utilizado para o site atual" + "message": "Preencher com o Ãēltimo cartÃŖo utilizado para o site atual" }, "commandAutofillIdentityDesc": { - "message": "Preencher automaticamente a Ãēltima identidade usada para o site atual" + "message": "Preencher com a Ãēltima identidade usada para o site atual" }, "commandGeneratePasswordDesc": { "message": "Gere e copie uma nova senha aleatÃŗria para a ÃĄrea de transferÃĒncia" @@ -2375,10 +2451,10 @@ "message": "PersonalizaÃ§ÃŖo do cofre" }, "vaultTimeoutAction": { - "message": "AÃ§ÃŖo do tempo limite do cofre" + "message": "AÃ§ÃŖo do limite de tempo do cofre" }, "vaultTimeoutAction1": { - "message": "AÃ§ÃŖo do tempo limite" + "message": "AÃ§ÃŖo do limite de tempo" }, "lock": { "message": "Bloquear", @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Item apagado para sempre" }, + "archivedItemRestored": { + "message": "Item arquivado restaurado" + }, "restoreItem": { "message": "Restaurar item" }, @@ -2410,13 +2489,13 @@ "message": "JÃĄ tem uma conta?" }, "vaultTimeoutLogOutConfirmation": { - "message": "Desconectar-se irÃĄ remover todo o acesso ao seu cofre e requirirÃĄ autenticaÃ§ÃŖo online apÃŗs o período de tempo limite. Tem certeza de que deseja usar esta configuraÃ§ÃŖo?" + "message": "Desconectar-se irÃĄ remover todo o acesso ao seu cofre e requirirÃĄ autenticaÃ§ÃŖo online apÃŗs o período de limite de tempo. Tem certeza de que deseja usar esta configuraÃ§ÃŖo?" }, "vaultTimeoutLogOutConfirmationTitle": { - "message": "ConfirmaÃ§ÃŖo da aÃ§ÃŖo do tempo limite" + "message": "ConfirmaÃ§ÃŖo da aÃ§ÃŖo do limite de tempo" }, "autoFillAndSave": { - "message": "Preencher automaticamente e salvar" + "message": "Preencher e salvar" }, "fillAndSave": { "message": "Preencher e salvar" @@ -2434,7 +2513,7 @@ "message": "VocÃĒ ainda deseja preencher esta credencial?" }, "autofillIframeWarning": { - "message": "O formulÃĄrio estÃĄ hospedado em um domínio diferente do URI da sua credencial salva. Escolha OK para preencher automaticamente mesmo assim, ou Cancelar para parar." + "message": "O formulÃĄrio estÃĄ hospedado em um domínio diferente do URI da sua credencial salva. Escolha OK para preencher mesmo assim, ou Cancelar para parar." }, "autofillIframeWarningTip": { "message": "Para evitar este aviso no futuro, salve este URI, $HOSTNAME$, no seu item de credencial no Bitwarden para este site.", @@ -2482,16 +2561,16 @@ } }, "policyInEffectUppercase": { - "message": "ContÊm um ou mais caracteres em maiÃēsculo" + "message": "Conter um ou mais caracteres em maiÃēsculo" }, "policyInEffectLowercase": { - "message": "ContÊm um ou mais caracteres em minÃēsculo" + "message": "Conter um ou mais caracteres em minÃēsculo" }, "policyInEffectNumbers": { - "message": "ContÊm um ou mais nÃēmeros" + "message": "Conter um ou mais nÃēmeros" }, "policyInEffectSpecial": { - "message": "ContÊm um ou mais dos seguintes caracteres especiais $CHARS$", + "message": "Conter um ou mais dos seguintes caracteres especiais $CHARS$", "placeholders": { "chars": { "content": "$1", @@ -2760,7 +2839,7 @@ "message": "Altere senhas em risco mais rÃĄpido" }, "changeAtRiskPasswordsFasterDesc": { - "message": "Atualize suas configuraçÃĩes para poder preencher senhas automaticamente ou gerÃĄ-las automaticamente" + "message": "Atualize suas configuraçÃĩes para poder preencher ou gerar novas senhas" }, "reviewAtRiskLogins": { "message": "Revisar credenciais em risco" @@ -3128,10 +3207,10 @@ "message": "Minutos" }, "vaultTimeoutPolicyAffectingOptions": { - "message": "Os requisitos de política empresarial foram aplicados às suas opçÃĩes de tempo limite" + "message": "Os requisitos de política empresarial foram aplicados às suas opçÃĩes de limite de tempo" }, "vaultTimeoutPolicyInEffect": { - "message": "As políticas da sua organizaÃ§ÃŖo configuraram o seu mÃĄximo permitido do tempo limite do cofre para $HOURS$ hora(s) e $MINUTES$ minuto(s).", + "message": "As políticas da sua organizaÃ§ÃŖo configuraram o seu mÃĄximo permitido do limite de tempo do cofre para $HOURS$ hora(s) e $MINUTES$ minuto(s).", "placeholders": { "hours": { "content": "$1", @@ -3157,7 +3236,7 @@ } }, "vaultTimeoutPolicyMaximumError": { - "message": "Tempo limite excede a restriÃ§ÃŖo definida pela sua organizaÃ§ÃŖo: mÃĄximo de $HOURS$ hora(s) e $MINUTES$ minuto(s)", + "message": "Limite de tempo excede a restriÃ§ÃŖo definida pela sua organizaÃ§ÃŖo: mÃĄximo de $HOURS$ hora(s) e $MINUTES$ minuto(s)", "placeholders": { "hours": { "content": "$1", @@ -3170,7 +3249,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "As políticas da sua organizaÃ§ÃŖo estÃŖo afetando o tempo limite do seu cofre. \nO tempo limite mÃĄximo permitido para o cofre Ê $HOURS$ hora(s) e $MINUTES$ minuto(s). A aÃ§ÃŖo de tempo limite do seu cofre estÃĄ configurada como $ACTION$.", + "message": "As políticas da sua organizaÃ§ÃŖo estÃŖo afetando o limite de tempo do seu cofre. \nO limite de tempo mÃĄximo permitido para o cofre Ê $HOURS$ hora(s) e $MINUTES$ minuto(s). A aÃ§ÃŖo de limite de tempo do seu cofre estÃĄ configurada como $ACTION$.", "placeholders": { "hours": { "content": "$1", @@ -3187,7 +3266,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "As políticas da sua organizaÃ§ÃŖo configuraram a aÃ§ÃŖo do tempo limite do seu cofre para $ACTION$.", + "message": "As políticas da sua organizaÃ§ÃŖo configuraram a aÃ§ÃŖo do limite de tempo do seu cofre para $ACTION$.", "placeholders": { "action": { "content": "$1", @@ -3196,7 +3275,7 @@ } }, "vaultTimeoutTooLarge": { - "message": "O tempo limite do seu cofre excede as restriçÃĩes estabelecidas pela sua organizaÃ§ÃŖo." + "message": "O limite de tempo do seu cofre excede as restriçÃĩes estabelecidas pela sua organizaÃ§ÃŖo." }, "vaultExportDisabled": { "message": "ExportaÃ§ÃŖo de cofre indisponível" @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Nenhum identificador Ãēnico encontrado." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Uma senha principal nÃŖo Ê mais necessÃĄria para os membros da seguinte organizaÃ§ÃŖo. Confirme o domínio abaixo com o administrador da sua organizaÃ§ÃŖo." - }, "organizationName": { "message": "Nome da organizaÃ§ÃŖo" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Erro" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Erro de descriptografia" }, @@ -3308,7 +3390,7 @@ "description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'" }, "contactCSToAvoidDataLossPart2": { - "message": "para evitar a perca adicional dos dados.", + "message": "para evitar a perca de dados adicionais.", "description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'" }, "generateUsername": { @@ -3467,7 +3549,7 @@ } }, "forwarderNoAccountId": { - "message": "NÃŖo foi possível obter o ID da conta de e-mail mascarado $SERVICENAME$.", + "message": "NÃŖo Ê possível obter o ID da conta de e-mail mascarado do $SERVICENAME$.", "description": "Displayed when the forwarding service fails to return an account ID.", "placeholders": { "servicename": { @@ -3626,7 +3708,7 @@ "message": "SolicitaÃ§ÃŖo enviada" }, "loginRequestApprovedForEmailOnDevice": { - "message": "SolicitaÃ§ÃŖo de autenticaÃ§ÃŖo aprovada para $EMAIL$ em $DEVICE$", + "message": "SolicitaÃ§ÃŖo de acesso aprovada para $EMAIL$ em $DEVICE$", "placeholders": { "email": { "content": "$1", @@ -3639,7 +3721,7 @@ } }, "youDeniedLoginAttemptFromAnotherDevice": { - "message": "VocÃĒ negou uma tentativa de autenticaÃ§ÃŖo de outro dispositivo. Se era vocÃĒ, tente se conectar com o dispositivo novamente." + "message": "VocÃĒ negou uma tentativa de acesso de outro dispositivo. Se era vocÃĒ, tente se conectar com o dispositivo novamente." }, "device": { "message": "Dispositivo" @@ -3663,7 +3745,7 @@ "message": "Senha fraca identificada e encontrada em um vazamento de dados. Use uma senha forte e Ãēnica para proteger a sua conta. Tem certeza de que deseja usar essa senha?" }, "checkForBreaches": { - "message": "Conferir vazamentos de dados conhecidos por esta senha" + "message": "Conferir se esta senha vazou ao pÃēblico" }, "important": { "message": "Importante:" @@ -3808,13 +3890,13 @@ "message": "Tipo do dispositivo" }, "loginRequest": { - "message": "SolicitaÃ§ÃŖo de autenticaÃ§ÃŖo" + "message": "SolicitaÃ§ÃŖo de acesso" }, "thisRequestIsNoLongerValid": { "message": "Esta solicitaÃ§ÃŖo nÃŖo Ê mais vÃĄlida." }, "loginRequestHasAlreadyExpired": { - "message": "A solicitaÃ§ÃŖo de autenticaÃ§ÃŖo jÃĄ expirou." + "message": "A solicitaÃ§ÃŖo de acesso jÃĄ expirou." }, "justNow": { "message": "Agora hÃĄ pouco" @@ -3905,7 +3987,7 @@ "message": "Problemas para acessar?" }, "loginApproved": { - "message": "AutenticaÃ§ÃŖo aprovada" + "message": "Acesso aprovado" }, "userEmailMissing": { "message": "E-mail do usuÃĄrio ausente" @@ -4072,7 +4154,7 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "NÃŖo Ê possível preencher automaticamente" + "message": "NÃŖo Ê possível preencher" }, "cannotAutofillExactMatch": { "message": "A correspondÃĒncia padrÃŖo estÃĄ configurada como 'CorrespondÃĒncia exata'. O site atual nÃŖo corresponde exatamente aos detalhes salvos de credencial para este item." @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignorar" }, - "importData": { - "message": "Importar dados", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Erro ao importar" }, @@ -4632,7 +4710,7 @@ "message": "Itens sugeridos" }, "autofillSuggestionsTip": { - "message": "Salve um item de credencial para este site para preencher automaticamente" + "message": "Salve um item de credencial para este site para preencher" }, "yourVaultIsEmpty": { "message": "Seu cofre estÃĄ vazio" @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "Mais opçÃĩes" + }, "moreOptionsTitle": { "message": "Mais opçÃĩes - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4708,7 +4789,7 @@ } }, "autofillTitle": { - "message": "Preencher automaticamente - $ITEMNAME$", + "message": "Preencher - $ITEMNAME$", "description": "Title for a button that autofills a login item.", "placeholders": { "itemname": { @@ -4718,7 +4799,7 @@ } }, "autofillTitleWithField": { - "message": "Preencher automaticamente - $ITEMNAME$ - $FIELD$", + "message": "Preencher - $ITEMNAME$ - $FIELD$", "description": "Title for a button that autofills a login item.", "placeholders": { "itemname": { @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Painel de administraÃ§ÃŖo" }, + "admin": { + "message": "Administrador" + }, + "automaticUserConfirmation": { + "message": "ConfirmaÃ§ÃŖo automÃĄtica de usuÃĄrios" + }, + "automaticUserConfirmationHint": { + "message": "Confirme automaticamente usuÃĄrios pendentes quando este dispositivo for desbloqueado" + }, + "autoConfirmOnboardingCallout": { + "message": "Economize tempo com a confirmaÃ§ÃŖo automÃĄtica de usuÃĄrios" + }, + "autoConfirmWarning": { + "message": "Isso pode afetar a segurança dos dados da sua organizaÃ§ÃŖo. " + }, + "autoConfirmWarningLink": { + "message": "Saiba mais sobre os riscos" + }, + "autoConfirmSetup": { + "message": "Confirmar automaticamente usuÃĄrios novos" + }, + "autoConfirmSetupDesc": { + "message": "UsuÃĄrios novos serÃŖo confirmados automaticamente quando este dispositivo for desbloqueado." + }, + "autoConfirmSetupHint": { + "message": "Quais sÃŖo os possíveis problemas de segurança?" + }, + "autoConfirmEnabled": { + "message": "Ativou a confirmaÃ§ÃŖo automÃĄtica" + }, + "availableNow": { + "message": "Disponível agora" + }, "accountSecurity": { "message": "Segurança da conta" }, + "phishingBlocker": { + "message": "Bloqueador de phishing" + }, + "enablePhishingDetection": { + "message": "DetecÃ§ÃŖo de phishing" + }, + "enablePhishingDetectionDesc": { + "message": "Exiba um aviso antes de acessar sites suspeitos de phishing" + }, "notifications": { "message": "NotificaçÃĩes" }, @@ -4799,7 +4922,7 @@ } }, "new": { - "message": "Novo" + "message": "Criar" }, "removeItem": { "message": "Remover $NAME$", @@ -5017,17 +5140,14 @@ } } }, - "hideMatchDetection": { - "message": "Ocultar detecÃ§ÃŖo de correspondÃĒncia $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Mostrar detecÃ§ÃŖo de correspondÃĒncia" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Ocultar detecÃ§ÃŖo de correspondÃĒncia" }, "autoFillOnPageLoad": { - "message": "Preencher automaticamente ao carregar a pÃĄgina?" + "message": "Preencher ao carregar a pÃĄgina?" }, "cardExpiredTitle": { "message": "CartÃŖo vencido" @@ -5111,7 +5231,7 @@ "message": "Use campos ocultos para dados confidenciais como uma senha" }, "checkBoxHelpText": { - "message": "Use caixas de seleÃ§ÃŖo se gostaria de preencher automaticamente a caixa de seleÃ§ÃŖo de um formulÃĄrio, como um lembrar e-mail" + "message": "Use caixas de seleÃ§ÃŖo se gostaria de preencher a caixa de seleÃ§ÃŖo de um formulÃĄrio, como um lembrar e-mail" }, "linkedHelpText": { "message": "Use um campo vinculado quando estiver enfrentando problemas com o preenchimento automÃĄtico com um site específico." @@ -5269,7 +5389,7 @@ "message": "AçÃĩes da conta" }, "showNumberOfAutofillSuggestions": { - "message": "Mostrar nÃēmero de sugestÃĩes de preenchimento automÃĄtico de credenciais no ícone da extensÃŖo" + "message": "Mostrar nÃēmero de sugestÃĩes de preenchimento no ícone da extensÃŖo" }, "accountAccessRequested": { "message": "Acesso à conta solicitado" @@ -5320,7 +5440,7 @@ "message": "Tentar novamente" }, "vaultCustomTimeoutMinimum": { - "message": "Tempo limite mínimo personalizado Ê 1 minuto." + "message": "Limite de tempo mínimo personalizado Ê 1 minuto." }, "fileSavedToDevice": { "message": "Arquivo salvo no dispositivo. Gerencie a partir dos downloads do seu dispositivo." @@ -5383,7 +5503,7 @@ "message": "Desbloqueie seu cofre em segundos" }, "unlockVaultDesc": { - "message": "VocÃĒ pode personalizar suas configuraçÃĩes de desbloqueio e tempo limite para acessar seu cofre mais rapidamente." + "message": "VocÃĒ pode personalizar suas configuraçÃĩes de desbloqueio e limite de tempo para acessar seu cofre mais rapidamente." }, "unlockPinSet": { "message": "PIN de desbloqueio configurado" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra larga" }, + "narrow": { + "message": "Estreita" + }, "sshKeyWrongPassword": { "message": "A senha que vocÃĒ digitou estÃĄ incorreta." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Esta credencial estÃĄ em risco e estÃĄ sem um site. Adicione um site e altere a senha para segurança melhor." }, + "vulnerablePassword": { + "message": "Senha vulnerÃĄvel." + }, + "changeNow": { + "message": "Alterar agora" + }, "missingWebsite": { "message": "Site ausente" }, @@ -5694,7 +5823,7 @@ } }, "hasItemsVaultNudgeBodyOne": { - "message": "Preenche automaticamente itens para a pÃĄgina atual" + "message": "Preenche itens para a pÃĄgina atual" }, "hasItemsVaultNudgeBodyTwo": { "message": "Favorite itens para acesso rÃĄpido" @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "E mais!" }, - "planDescPremium": { - "message": "Segurança on-line completa" + "advancedOnlineSecurity": { + "message": "Segurança on-line avançada" }, "upgradeToPremium": { "message": "Faça upgrade para o Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "NÃēmero do cartÃŖo" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "A sua organizaÃ§ÃŖo nÃŖo estÃĄ mais usando senhas principais para se conectar ao Bitwarden. Para continuar, verifique a organizaÃ§ÃŖo e o domínio." + }, + "continueWithLogIn": { + "message": "Continuar acessando" + }, + "doNotContinue": { + "message": "NÃŖo continuar" + }, + "domain": { + "message": "Domínio" + }, + "keyConnectorDomainTooltip": { + "message": "Este domínio armazenarÃĄ as chaves de criptografia da sua conta, entÃŖo certifique-se que confia nele. Se nÃŖo tiver certeza, verifique com o seu administrador." + }, + "verifyYourOrganization": { + "message": "Verifique sua organizaÃ§ÃŖo para se conectar" + }, + "organizationVerified": { + "message": "OrganizaÃ§ÃŖo verificada" + }, + "domainVerified": { + "message": "Domínio verificado" + }, + "leaveOrganizationContent": { + "message": "Se vocÃĒ nÃŖo verificar a sua organizaÃ§ÃŖo, o seu acesso à organizaÃ§ÃŖo serÃĄ revogado." + }, + "leaveNow": { + "message": "Sair agora" + }, + "verifyYourDomainToLogin": { + "message": "Verifique seu domínio para se conectar" + }, + "verifyYourDomainDescription": { + "message": "Para continuar se conectando, verifique este domínio." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Para continuar se conectando, verifique a organizaÃ§ÃŖo e o domínio." + }, "sessionTimeoutSettingsAction": { - "message": "AÃ§ÃŖo do tempo limite" + "message": "AÃ§ÃŖo do limite de tempo" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Esta configuraÃ§ÃŖo Ê gerenciada pela sua organizaÃ§ÃŖo." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "A sua organizaÃ§ÃŖo configurou o limite de tempo mÃĄximo da sessÃŖo para $HOURS$ hora(s) e $MINUTES$ minuto(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "A sua organizaÃ§ÃŖo configurou o limite de tempo padrÃŖo da sessÃŖo para imediatamente." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "A sua organizaÃ§ÃŖo configurou o limite de tempo padrÃŖo da sessÃŖo para ser no bloqueio do sistema." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "A sua organizaÃ§ÃŖo configurou o limite de tempo padrÃŖo da sessÃŖo para ser no reinício do navegador." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "O limite de tempo mÃĄximo nÃŖo pode exceder $HOURS$ hora(s) e $MINUTES$ minuto(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "No reinício do navegador" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Configure um mÊtodo de desbloqueio para alterar a aÃ§ÃŖo do limite de tempo" + }, + "upgrade": { + "message": "Fazer upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Tem certeza de que quer sair?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Se recusar, seus itens pessoais continuarÃŖo na sua conta, mas vocÃĒ perderÃĄ o acesso aos itens compartilhados e os recursos de organizaÃ§ÃŖo." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Entre em contato com o seu administrador para recuperar o acesso." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Sair de $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Como gerencio meu cofre?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transferir itens para $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ exige que todos os itens sejam propriedade da organizaÃ§ÃŖo por segurança e conformidade. Clique em aceitar para transferir a propriedade dos seus itens.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Aceitar transferÃĒncia" + }, + "declineAndLeave": { + "message": "Recusar e sair" + }, + "whyAmISeeingThis": { + "message": "Por que estou vendo isso?" + }, + "resizeSideNavigation": { + "message": "Redimensionar navegaÃ§ÃŖo lateral" } } diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 6b8568ddb1b..2b40e2003a5 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Iniciar sessÃŖo com a chave de acesso" }, + "unlockWithPasskey": { + "message": "Desbloquear com chave de acesso" + }, "useSingleSignOn": { "message": "Utilizar início de sessÃŖo Ãēnico" }, @@ -200,7 +203,7 @@ "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": "Preencher automaticamente" + "message": "Preenchimento automÃĄtico" }, "autoFillLogin": { "message": "Preencher automaticamente credencial" @@ -436,8 +439,8 @@ "sync": { "message": "Sincronizar" }, - "syncVaultNow": { - "message": "Sincronizar o cofre agora" + "syncNow": { + "message": "Sincronizar agora" }, "lastSync": { "message": "Última sincronizaÃ§ÃŖo:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "AplicaÃ§ÃŖo web Bitwarden" }, - "importItems": { - "message": "Importar itens" - }, "select": { "message": "Selecionar" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "O item foi movido para o arquivo" }, + "itemWasUnarchived": { + "message": "O item foi desarquivado" + }, "itemUnarchived": { "message": "O item foi desarquivado" }, "archiveItem": { "message": "Arquivar item" }, - "archiveItemConfirmDesc": { - "message": "Os itens arquivados sÃŖo excluídos dos resultados gerais da pesquisa e das sugestÃĩes de preenchimento automÃĄtico. Tem a certeza de que pretende arquivar este item?" + "archiveItemDialogContent": { + "message": "Depois de arquivado, este item serÃĄ excluído dos resultados de pesquisa e das sugestÃĩes de preenchimento automÃĄtico." + }, + "archived": { + "message": "Arquivado" + }, + "unarchiveAndSave": { + "message": "Desarquivar e guardar" }, "upgradeToUseArchive": { "message": "É necessÃĄria uma subscriÃ§ÃŖo Premium para utilizar o Arquivo." }, + "itemRestored": { + "message": "O item foi restaurado" + }, "edit": { "message": "Editar" }, @@ -1062,7 +1074,7 @@ } }, "deleteItemConfirmation": { - "message": "Tem a certeza de que pretende eliminar este item?" + "message": "Pretende realmente mover este item para o lixo?" }, "deletedItem": { "message": "Item movido para o lixo" @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Exportar de" }, - "exportVault": { - "message": "Exportar cofre" + "exportVerb": { + "message": "Exportar", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Saber mais" }, + "migrationsFailed": { + "message": "Ocorreu um erro ao atualizar as definiçÃĩes de encriptaÃ§ÃŖo." + }, + "updateEncryptionSettingsTitle": { + "message": "Atualize as suas definiçÃĩes de encriptaÃ§ÃŖo" + }, + "updateEncryptionSettingsDesc": { + "message": "As novas definiçÃĩes de encriptaÃ§ÃŖo recomendadas irÃŖo melhorar a segurança da sua conta. Introduza a sua palavra-passe mestra para atualizar agora." + }, + "confirmIdentityToContinue": { + "message": "Confirme a sua identidade para continuar" + }, + "enterYourMasterPassword": { + "message": "Introduza a sua palavra-passe mestra" + }, + "updateSettings": { + "message": "Atualizar definiçÃĩes" + }, + "later": { + "message": "Mais tarde" + }, "authenticatorKeyTotp": { "message": "Chave de autenticaÃ§ÃŖo (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Anexo guardado" }, + "fixEncryption": { + "message": "Corrigir encriptaÃ§ÃŖo" + }, + "fixEncryptionTooltip": { + "message": "Este ficheiro estÃĄ a utilizar um mÊtodo de encriptaÃ§ÃŖo desatualizado." + }, + "attachmentUpdated": { + "message": "Anexo atualizado" + }, "file": { "message": "Ficheiro" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Selecionar um ficheiro" }, + "itemsTransferred": { + "message": "Itens transferidos" + }, "maxFileSize": { "message": "O tamanho mÃĄximo do ficheiro Ê de 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB de armazenamento encriptado para anexos de ficheiros." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ de armazenamento encriptado para anexos de ficheiros.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Acesso de emergÃĒncia." }, "premiumSignUpTwoStepOptions": { "message": "OpçÃĩes proprietÃĄrias de verificaÃ§ÃŖo de dois passos, como YubiKey e Duo." }, + "premiumSubscriptionEnded": { + "message": "A sua subscriÃ§ÃŖo Premium terminou" + }, + "archivePremiumRestart": { + "message": "Para recuperar o acesso ao seu arquivo, reinicie a sua subscriÃ§ÃŖo Premium. Se editar os detalhes de um item arquivado antes de reiniciar, ele serÃĄ movido de volta para o seu cofre." + }, + "restartPremium": { + "message": "Reiniciar o Premium" + }, "ppremiumSignUpReports": { "message": "Higiene de palavras-passe, saÃēde da conta e relatÃŗrios de violaÃ§ÃŖo de dados para manter o seu cofre seguro." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Item eliminado permanentemente" }, + "archivedItemRestored": { + "message": "Item arquivado restaurado" + }, "restoreItem": { "message": "Restaurar item" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "NÃŖo foi encontrado um identificador Ãēnico." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "JÃĄ nÃŖo Ê necessÃĄria uma palavra-passe mestra para os membros da seguinte organizaÃ§ÃŖo. Por favor, confirme o domínio abaixo com o administrador da sua organizaÃ§ÃŖo." - }, "organizationName": { "message": "Nome da organizaÃ§ÃŖo" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Erro" }, + "prfUnlockFailed": { + "message": "NÃŖo foi possível desbloquear com a chave de acesso. Por favor, tente novamente ou utilize outro mÊtodo de desbloqueio." + }, + "noPrfCredentialsAvailable": { + "message": "NÃŖo estÃŖo disponíveis chaves de acesso com PRF ativado para o desbloqueio. Por favor, inicie sessÃŖo primeiro com uma chave de acesso." + }, "decryptionError": { "message": "Erro de desencriptaÃ§ÃŖo" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignorar" }, - "importData": { - "message": "Importar dados", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Erro de importaÃ§ÃŖo" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "Mais opçÃĩes" + }, "moreOptionsTitle": { "message": "Mais opçÃĩes - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Consola de administraÃ§ÃŖo" }, + "admin": { + "message": "Administrador" + }, + "automaticUserConfirmation": { + "message": "ConfirmaÃ§ÃŖo automÃĄtica do utilizador" + }, + "automaticUserConfirmationHint": { + "message": "Confirmar automaticamente os utilizadores pendentes enquanto este dispositivo estiver desbloqueado" + }, + "autoConfirmOnboardingCallout": { + "message": "Poupe tempo com a confirmaÃ§ÃŖo automÃĄtica de utilizadores" + }, + "autoConfirmWarning": { + "message": "Isto pode afetar a segurança dos dados da sua organizaÃ§ÃŖo. " + }, + "autoConfirmWarningLink": { + "message": "Saiba mais sobre os riscos" + }, + "autoConfirmSetup": { + "message": "Confirmar automaticamente novos utilizadores" + }, + "autoConfirmSetupDesc": { + "message": "Os novos utilizadores serÃŖo automaticamente confirmados enquanto este dispositivo estiver desbloqueado." + }, + "autoConfirmSetupHint": { + "message": "Quais sÃŖo os riscos potenciais de segurança?" + }, + "autoConfirmEnabled": { + "message": "ConfirmaÃ§ÃŖo automÃĄtica ativada" + }, + "availableNow": { + "message": "JÃĄ disponível" + }, "accountSecurity": { "message": "Segurança da conta" }, + "phishingBlocker": { + "message": "Bloqueador de phishing" + }, + "enablePhishingDetection": { + "message": "DeteÃ§ÃŖo de phishing" + }, + "enablePhishingDetectionDesc": { + "message": "Mostrar aviso antes de aceder a sites suspeitos de phishing" + }, "notifications": { "message": "NotificaçÃĩes" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Ocultar deteÃ§ÃŖo de correspondÃĒncia para $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Mostrar deteÃ§ÃŖo de correspondÃĒncia" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Ocultar deteÃ§ÃŖo de correspondÃĒncia" }, "autoFillOnPageLoad": { "message": "Preencher automaticamente ao carregar a pÃĄgina?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Muito ampla" }, + "narrow": { + "message": "Estreito" + }, "sshKeyWrongPassword": { "message": "A palavra-passe que introduziu estÃĄ incorreta." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Esta credencial estÃĄ em risco e nÃŖo tem um site. Adicione um site e altere a palavra-passe para uma segurança mais forte." }, + "vulnerablePassword": { + "message": "Palavra-passe vulnerÃĄvel." + }, + "changeNow": { + "message": "Alterar agora" + }, "missingWebsite": { "message": "Site em falta" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "E muito mais!" }, - "planDescPremium": { - "message": "Segurança total online" + "advancedOnlineSecurity": { + "message": "Segurança online avançada" }, "upgradeToPremium": { "message": "Atualizar para o Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "NÃēmero do cartÃŖo" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "A sua organizaÃ§ÃŖo jÃĄ nÃŖo utiliza palavras-passe mestras para iniciar sessÃŖo no Bitwarden. Para continuar, verifique a organizaÃ§ÃŖo e o domínio." + }, + "continueWithLogIn": { + "message": "Continuar com o início de sessÃŖo" + }, + "doNotContinue": { + "message": "NÃŖo continuar" + }, + "domain": { + "message": "Domínio" + }, + "keyConnectorDomainTooltip": { + "message": "Este domínio armazenarÃĄ as chaves de encriptaÃ§ÃŖo da sua conta, portanto certifique-se de que confia nele. Se nÃŖo tiver a certeza, verifique com o seu administrador." + }, + "verifyYourOrganization": { + "message": "Verifique a sua organizaÃ§ÃŖo para iniciar sessÃŖo" + }, + "organizationVerified": { + "message": "OrganizaÃ§ÃŖo verificada" + }, + "domainVerified": { + "message": "Domínio verificado" + }, + "leaveOrganizationContent": { + "message": "Se nÃŖo verificar a sua organizaÃ§ÃŖo, o seu acesso à organizaÃ§ÃŖo serÃĄ revogado." + }, + "leaveNow": { + "message": "Sair agora" + }, + "verifyYourDomainToLogin": { + "message": "Verifique o seu domínio para iniciar sessÃŖo" + }, + "verifyYourDomainDescription": { + "message": "Para continuar com o início de sessÃŖo, verifique este domínio." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Para continuar com o início de sessÃŖo, verifique a organizaÃ§ÃŖo e o domínio." + }, "sessionTimeoutSettingsAction": { "message": "AÃ§ÃŖo de tempo limite" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Esta configuraÃ§ÃŖo Ê gerida pela sua organizaÃ§ÃŖo." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "A sua organizaÃ§ÃŖo definiu o tempo limite mÃĄximo da sessÃŖo para $HOURS$ hora(s) e $MINUTES$ minuto(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "A sua organizaÃ§ÃŖo definiu o tempo limite predefinido da sessÃŖo como Imediatamente." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "A sua organizaÃ§ÃŖo definiu o tempo limite de sessÃŖo predefinido para Ao bloquear o sistema." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "A sua organizaÃ§ÃŖo definiu o tempo limite de sessÃŖo predefinido para Ao reiniciar o navegador." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "O tempo limite mÃĄximo nÃŖo pode ser superior a $HOURS$ hora(s) e $MINUTES$ minuto(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Ao reiniciar o navegador" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Configure um mÊtodo de desbloqueio para alterar a sua aÃ§ÃŖo de tempo limite" + }, + "upgrade": { + "message": "Atualizar" + }, + "leaveConfirmationDialogTitle": { + "message": "Tem a certeza de que pretende sair?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Ao recusar, os seus itens pessoais permanecerÃŖo na sua conta, mas perderÃĄ o acesso aos itens partilhados e às funcionalidades da organizaÃ§ÃŖo." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Entre em contacto com o seu administrador para recuperar o acesso." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Sair de $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Como posso gerir o meu cofre?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transferir itens para $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ exige que todos os itens sejam propriedade da organizaÃ§ÃŖo por motivos de segurança e conformidade. Clique em Aceitar para transferir a propriedade dos seus itens.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Aceitar transferÃĒncia" + }, + "declineAndLeave": { + "message": "Recusar e sair" + }, + "whyAmISeeingThis": { + "message": "Porque Ê que estou a ver isto?" + }, + "resizeSideNavigation": { + "message": "Redimensionar navegaÃ§ÃŖo lateral" } } diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index db7a1b8c657..b071d8c765e 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Autentificare cu parolă" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Autentificare unică" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sincronizare" }, - "syncVaultNow": { - "message": "Sincronizare seif acum" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Ultima sincronizare:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Aplicația web Bitwarden" }, - "importItems": { - "message": "Import de articole" - }, "select": { "message": "Selectare" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Editare" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export seif" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Aflați mai multe" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Cheie de autentificare (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Atașamentul a fost salvat" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Fișier" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Selectare fișier" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Mărimea maximă a fișierului este de 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB spațiu de stocare criptat pentru atașamente de fișiere." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Opțiuni brevetate de conectare cu doi factori, cum ar fi YubiKey și Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Rapoarte privind igiena parolelor, sănătatea contului și breșele de date pentru a vă păstra seiful ÃŽn siguranță." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Articol șters permanent" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restabilire articol" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Nu a fost găsit niciun identificator unic." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Eroare" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 0535027daa9..c2b09803c06 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Đ’ĐžĐšŅ‚Đ¸ ҁ passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ ĐĩдиĐŊŅ‹Đš Đ˛Ņ…ĐžĐ´" }, @@ -436,7 +439,7 @@ "sync": { "message": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ" }, - "syncVaultNow": { + "syncNow": { "message": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ" }, "lastSync": { @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "ВĐĩĐą-ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ Bitwarden" }, - "importItems": { - "message": "ИĐŧĐŋĐžŅ€Ņ‚ ŅĐģĐĩĐŧĐĩĐŊŅ‚ĐžĐ˛" - }, "select": { "message": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Đ­ĐģĐĩĐŧĐĩĐŊŅ‚ ĐąŅ‹Đģ ĐžŅ‚ĐŋŅ€Đ°Đ˛ĐģĐĩĐŊ в Đ°Ņ€Ņ…Đ¸Đ˛" }, + "itemWasUnarchived": { + "message": "Đ­ĐģĐĩĐŧĐĩĐŊŅ‚ ĐąŅ‹Đģ Ņ€Đ°ĐˇĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊ" + }, "itemUnarchived": { "message": "Đ­ĐģĐĩĐŧĐĩĐŊŅ‚ ĐąŅ‹Đģ Ņ€Đ°ĐˇĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊ" }, "archiveItem": { "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ŅĐģĐĩĐŧĐĩĐŊŅ‚" }, - "archiveItemConfirmDesc": { - "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊŅ‹Đĩ ŅĐģĐĩĐŧĐĩĐŊ҂ҋ Đ¸ŅĐēĐģŅŽŅ‡ĐĩĐŊŅ‹ иС ĐžĐąŅ‰Đ¸Ņ… Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚ĐžĐ˛ ĐŋĐžĐ¸ŅĐēа и ĐŋŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊиК Đ°Đ˛Ņ‚ĐžĐˇĐ°ĐŋĐžĐģĐŊĐĩĐŊĐ¸Ņ. Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ŅŅ‚ĐžŅ‚ ŅĐģĐĩĐŧĐĩĐŊŅ‚?" + "archiveItemDialogContent": { + "message": "ĐŸĐžŅĐģĐĩ Đ°Ņ€Ņ…Đ¸Đ˛Đ°Ņ†Đ¸Đ¸ ŅŅ‚ĐžŅ‚ ŅĐģĐĩĐŧĐĩĐŊŅ‚ ĐąŅƒĐ´ĐĩŅ‚ Đ¸ŅĐēĐģŅŽŅ‡ĐĩĐŊ иС Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚ĐžĐ˛ ĐŋĐžĐ¸ŅĐēа и ĐŋŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊиК ĐŋĐž Đ°Đ˛Ņ‚ĐžĐˇĐ°ĐŋĐžĐģĐŊĐĩĐŊĐ¸ŅŽ." + }, + "archived": { + "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊ" + }, + "unarchiveAndSave": { + "message": "Đ Đ°ĐˇĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ и ŅĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ" }, "upgradeToUseArchive": { "message": "ДĐģŅ Đ¸ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°ĐŊĐ¸Ņ Đ°Ņ€Ņ…Đ¸Đ˛Đ° ҂ҀĐĩĐąŅƒĐĩŅ‚ŅŅ ĐŋŅ€ĐĩĐŧĐ¸ŅƒĐŧ-ŅŅ‚Đ°Ņ‚ŅƒŅ." }, + "itemRestored": { + "message": "Đ­ĐģĐĩĐŧĐĩĐŊŅ‚ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ" + }, "edit": { "message": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ" }, @@ -931,7 +943,7 @@ "message": "Đ’Ņ‹ Đ˛Ņ‹ŅˆĐģи иС ŅĐ˛ĐžĐĩĐŗĐž аĐēĐēĐ°ŅƒĐŊŅ‚Đ°." }, "loginExpired": { - "message": "Đ˜ŅŅ‚ĐĩĐē ŅŅ€ĐžĐē Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ Đ˛Đ°ŅˆĐĩĐŗĐž ҁĐĩаĐŊŅĐ°." + "message": "Đ˜ŅŅ‚ĐĩĐē ŅŅ€ĐžĐē Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ Đ˛Đ°ŅˆĐĩĐš ҁĐĩŅŅĐ¸Đ¸." }, "logIn": { "message": "Đ’ĐžĐšŅ‚Đ¸" @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Đ­ĐēҁĐŋĐžŅ€Ņ‚ иС" }, - "exportVault": { - "message": "Đ­ĐēҁĐŋĐžŅ€Ņ‚ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°" + "exportVerb": { + "message": "Đ­ĐēҁĐŋĐžŅ€Ņ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", + "description": "The verb form of the word Export" + }, + "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": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚ Ņ„Đ°ĐšĐģа" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "ĐŸĐžĐ´Ņ€ĐžĐąĐŊĐĩĐĩ" }, + "migrationsFailed": { + "message": "ĐŸŅ€ĐžĐ¸ĐˇĐžŅˆĐģа ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ОйĐŊОвĐģĐĩĐŊии ĐŊĐ°ŅŅ‚Ņ€ĐžĐĩĐē ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ." + }, + "updateEncryptionSettingsTitle": { + "message": "ОбĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ" + }, + "updateEncryptionSettingsDesc": { + "message": "ĐĐžĐ˛Ņ‹Đĩ Ņ€ĐĩĐēĐžĐŧĐĩĐŊĐ´ŅƒĐĩĐŧŅ‹Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ ĐŋĐžĐ˛Ņ‹ŅŅŅ‚ ĐąĐĩСОĐŋĐ°ŅĐŊĐžŅŅ‚ŅŒ Đ˛Đ°ŅˆĐĩĐŗĐž аĐēĐēĐ°ŅƒĐŊŅ‚Đ°. ВвĐĩĐ´Đ¸Ņ‚Đĩ ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ, Ņ‡Ņ‚ĐžĐąŅ‹ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ҁĐĩĐšŅ‡Đ°Ņ." + }, + "confirmIdentityToContinue": { + "message": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ Đ˛Đ°ŅˆŅƒ ĐģĐ¸Ņ‡ĐŊĐžŅŅ‚ŅŒ, Ņ‡Ņ‚ĐžĐąŅ‹ ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ" + }, + "enterYourMasterPassword": { + "message": "ВвĐĩĐ´Đ¸Ņ‚Đĩ Đ˛Đ°Ņˆ ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ" + }, + "updateSettings": { + "message": "ОбĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи" + }, + "later": { + "message": "ПозĐļĐĩ" + }, "authenticatorKeyTotp": { "message": "КĐģŅŽŅ‡ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžŅ€Đ° (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "ВĐģĐžĐļĐĩĐŊиĐĩ ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊĐž." }, + "fixEncryption": { + "message": "Đ˜ŅĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊиĐĩ" + }, + "fixEncryptionTooltip": { + "message": "Đ­Ņ‚ĐžŅ‚ Ņ„Đ°ĐšĐģ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚ ŅƒŅŅ‚Đ°Ņ€ĐĩĐ˛ŅˆĐ¸Đš ĐŧĐĩŅ‚ĐžĐ´ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ." + }, + "attachmentUpdated": { + "message": "ВĐģĐžĐļĐĩĐŊиĐĩ ОйĐŊОвĐģĐĩĐŊĐž" + }, "file": { "message": "ФаКĐģ" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Đ’Ņ‹ĐąŅ€Đ°Ņ‚ŅŒ Ņ„Đ°ĐšĐģ" }, + "itemsTransferred": { + "message": "Đ­ĐģĐĩĐŧĐĩĐŊ҂ҋ ĐŋĐĩŅ€ĐĩдаĐŊŅ‹" + }, "maxFileSize": { "message": "МаĐēŅĐ¸ĐŧаĐģҌĐŊŅ‹Đš Ņ€Đ°ĐˇĐŧĐĩŅ€ Ņ„Đ°ĐšĐģа 500 МБ." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 ГБ ĐˇĐ°ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐŊĐžĐŗĐž Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° Đ´ĐģŅ вĐģĐžĐļĐĩĐŊĐŊҋ҅ Ņ„Đ°ĐšĐģОв." }, + "premiumSignUpStorageV2": { + "message": "Đ—Đ°ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐŊĐžĐŗĐž Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° Đ´ĐģŅ вĐģĐžĐļĐĩĐŊĐŊҋ҅ Ņ„Đ°ĐšĐģОв: $SIZE$", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Đ­Đēҁ҂ҀĐĩĐŊĐŊŅ‹Đš Đ´ĐžŅŅ‚ŅƒĐŋ" }, "premiumSignUpTwoStepOptions": { "message": "ĐŸŅ€ĐžĐŋŅ€Đ¸ĐĩŅ‚Đ°Ņ€ĐŊŅ‹Đĩ Đ˛Đ°Ņ€Đ¸Đ°ĐŊ҂ҋ Đ´Đ˛ŅƒŅ…ŅŅ‚Đ°ĐŋĐŊОК Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸, Ņ‚Đ°ĐēиĐĩ ĐēаĐē YubiKey иĐģи Duo." }, + "premiumSubscriptionEnded": { + "message": "Đ’Đ°ŅˆĐ° ĐŋОдĐŋĐ¸ŅĐēа ĐŸŅ€ĐĩĐŧĐ¸ŅƒĐŧ СаĐēĐžĐŊŅ‡Đ¸ĐģĐ°ŅŅŒ" + }, + "archivePremiumRestart": { + "message": "Đ§Ņ‚ĐžĐąŅ‹ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đē ŅĐ˛ĐžĐĩĐŧ҃ Đ°Ņ€Ņ…Đ¸Đ˛Ņƒ, ĐŋОдĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ ĐŋОдĐŋĐ¸ŅĐē҃ ĐŸŅ€ĐĩĐŧĐ¸ŅƒĐŧ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐž. Đ•ŅĐģи Đ˛Ņ‹ иСĐŧĐĩĐŊĐ¸Ņ‚Đĩ ŅĐ˛ĐĩĐ´ĐĩĐŊĐ¸Ņ Ой Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊĐžĐŧ ŅĐģĐĩĐŧĐĩĐŊŅ‚Đĩ ĐŋĐĩŅ€ĐĩĐ´ ĐŋĐĩŅ€ĐĩĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊиĐĩĐŧ, ĐžĐŊ ĐąŅƒĐ´ĐĩŅ‚ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊ ĐžĐąŅ€Đ°Ņ‚ĐŊĐž в Đ˛Đ°ŅˆĐĩ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đĩ." + }, + "restartPremium": { + "message": "ПĐĩŅ€ĐĩĐŋОдĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐŸŅ€ĐĩĐŧĐ¸ŅƒĐŧ" + }, "ppremiumSignUpReports": { "message": "Đ“Đ¸ĐŗĐ¸ĐĩĐŊа ĐŋĐ°Ņ€ĐžĐģĐĩĐš, ĐˇĐ´ĐžŅ€ĐžĐ˛ŅŒĐĩ аĐēĐēĐ°ŅƒĐŊŅ‚Đ° и ĐžŅ‚Ņ‡Đĩ҂ҋ Ой ŅƒŅ‚Đĩ҇ĐēĐ°Ņ… даĐŊĐŊҋ҅ Đ´ĐģŅ ОйĐĩҁĐŋĐĩ҇ĐĩĐŊĐ¸Ņ ĐąĐĩСОĐŋĐ°ŅĐŊĐžŅŅ‚Đ¸ Đ˛Đ°ŅˆĐĩĐŗĐž Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°." }, @@ -1542,7 +1618,7 @@ "message": "ĐĸаКĐŧĐ°ŅƒŅ‚ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸" }, "authenticationSessionTimedOut": { - "message": "ĐĄĐĩаĐŊҁ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸ СавĐĩŅ€ŅˆĐ¸ĐģŅŅ ĐŋĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊи. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋĐžĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ Đ˛ĐžĐšŅ‚Đ¸ Đĩ҉Đĩ Ņ€Đ°Đˇ." + "message": "ĐĄĐĩŅŅĐ¸Ņ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸ СавĐĩŅ€ŅˆĐ¸ĐģĐ°ŅŅŒ ĐŋĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊи. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋĐĩŅ€ĐĩСаĐŋŅƒŅŅ‚Đ¸Ņ‚Đĩ ĐŋŅ€ĐžŅ†Đĩҁҁ Đ°Đ˛Ņ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Đ¸Đ¸." }, "verificationCodeEmailSent": { "message": "ĐžŅ‚ĐŋŅ€Đ°Đ˛ĐģĐĩĐŊĐž ĐŋĐ¸ŅŅŒĐŧĐž ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€ĐļĐ´ĐĩĐŊĐ¸Ņ ĐŊа $EMAIL$.", @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Đ­ĐģĐĩĐŧĐĩĐŊŅ‚ ŅƒĐ´Đ°ĐģĐĩĐŊ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°" }, + "archivedItemRestored": { + "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊŅ‹Đš ŅĐģĐĩĐŧĐĩĐŊŅ‚ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ" + }, "restoreItem": { "message": "Đ’ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ŅĐģĐĩĐŧĐĩĐŊŅ‚" }, @@ -3079,10 +3158,10 @@ "message": "ОбĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ" }, "updateMasterPasswordWarning": { - "message": "ĐœĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ ĐŊĐĩдавĐŊĐž ĐąŅ‹Đģ иСĐŧĐĩĐŊĐĩĐŊ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ĐžĐŧ Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸. Đ§Ņ‚ĐžĐąŅ‹ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đē Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Ņƒ, Đ˛Ņ‹ Đ´ĐžĐģĐļĐŊŅ‹ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐĩĐŗĐž ҁĐĩĐšŅ‡Đ°Ņ. В Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Đĩ Ņ‚ĐĩĐēŅƒŅ‰Đ¸Đš ҁĐĩаĐŊҁ ĐąŅƒĐ´ĐĩŅ‚ СавĐĩŅ€ŅˆĐĩĐŊ, ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒĐĩŅ‚ŅŅ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊŅ‹Đš Đ˛Ņ…ĐžĐ´. ĐĄĐĩаĐŊҁҋ ĐŊа Đ´Ņ€ŅƒĐŗĐ¸Ņ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Ņ… ĐŧĐžĐŗŅƒŅ‚ ĐžŅŅ‚Đ°Đ˛Đ°Ņ‚ŅŒŅŅ аĐēŅ‚Đ¸Đ˛ĐŊŅ‹Đŧи в Ņ‚Đĩ҇ĐĩĐŊиĐĩ ОдĐŊĐžĐŗĐž Ņ‡Đ°ŅĐ°." + "message": "ĐœĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ ĐŊĐĩдавĐŊĐž ĐąŅ‹Đģ иСĐŧĐĩĐŊĐĩĐŊ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ĐžĐŧ Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸. Đ§Ņ‚ĐžĐąŅ‹ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đē Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Ņƒ, Đ˛Ņ‹ Đ´ĐžĐģĐļĐŊŅ‹ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐĩĐŗĐž ҁĐĩĐšŅ‡Đ°Ņ. В Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Đĩ Ņ‚ĐĩĐēŅƒŅ‰Đ°Ņ ҁĐĩŅŅĐ¸Ņ ĐąŅƒĐ´ĐĩŅ‚ СавĐĩŅ€ŅˆĐĩĐŊа, ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒĐĩŅ‚ŅŅ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊŅ‹Đš Đ˛Ņ…ĐžĐ´. ĐĄĐĩŅŅĐ¸Đ¸ ĐŊа Đ´Ņ€ŅƒĐŗĐ¸Ņ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Ņ… ĐŧĐžĐŗŅƒŅ‚ ĐžŅŅ‚Đ°Đ˛Đ°Ņ‚ŅŒŅŅ аĐēŅ‚Đ¸Đ˛ĐŊŅ‹Đŧи в Ņ‚Đĩ҇ĐĩĐŊиĐĩ ОдĐŊĐžĐŗĐž Ņ‡Đ°ŅĐ°." }, "updateWeakMasterPasswordWarning": { - "message": "Đ’Đ°Ņˆ ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ ĐŊĐĩ ŅĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛ŅƒĐĩŅ‚ ҂ҀĐĩйОваĐŊĐ¸ŅĐŧ ĐŋĐžĐģĐ¸Ņ‚Đ¸Đēи Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸. ДĐģŅ Đ´ĐžŅŅ‚ŅƒĐŋа Đē Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Ņƒ Đ˛Ņ‹ Đ´ĐžĐģĐļĐŊŅ‹ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ŅĐ˛ĐžĐš ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ ĐŋŅ€ŅĐŧĐž ҁĐĩĐšŅ‡Đ°Ņ. ĐŸŅ€Đ¸ ŅŅ‚ĐžĐŧ Ņ‚ĐĩĐēŅƒŅ‰Đ¸Đš ҁĐĩаĐŊҁ ĐąŅƒĐ´ĐĩŅ‚ СавĐĩŅ€ŅˆĐĩĐŊ и ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒĐĩŅ‚ŅŅ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐ°Ņ Đ°Đ˛Ņ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Đ¸Ņ. ĐĄĐĩаĐŊҁҋ ĐŊа Đ´Ņ€ŅƒĐŗĐ¸Ņ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Ņ… ĐŧĐžĐŗŅƒŅ‚ ĐžŅŅ‚Đ°Đ˛Đ°Ņ‚ŅŒŅŅ аĐēŅ‚Đ¸Đ˛ĐŊŅ‹Đŧи в Ņ‚Đĩ҇ĐĩĐŊиĐĩ Ņ‡Đ°ŅĐ°." + "message": "Đ’Đ°Ņˆ ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ ĐŊĐĩ ŅĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛ŅƒĐĩŅ‚ ҂ҀĐĩйОваĐŊĐ¸ŅĐŧ ĐŋĐžĐģĐ¸Ņ‚Đ¸Đēи Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸. ДĐģŅ Đ´ĐžŅŅ‚ŅƒĐŋа Đē Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Ņƒ Đ˛Ņ‹ Đ´ĐžĐģĐļĐŊŅ‹ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ŅĐ˛ĐžĐš ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ ĐŋŅ€ŅĐŧĐž ҁĐĩĐšŅ‡Đ°Ņ. ĐŸŅ€Đ¸ ŅŅ‚ĐžĐŧ Ņ‚ĐĩĐēŅƒŅ‰Đ°Ņ ҁĐĩŅŅĐ¸Ņ ĐąŅƒĐ´ĐĩŅ‚ СавĐĩŅ€ŅˆĐĩĐŊа и ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒĐĩŅ‚ŅŅ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐ°Ņ Đ°Đ˛Ņ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Đ¸Ņ. ĐĄĐĩŅŅĐ¸Đ¸ ĐŊа Đ´Ņ€ŅƒĐŗĐ¸Ņ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Ņ… ĐŧĐžĐŗŅƒŅ‚ ĐžŅŅ‚Đ°Đ˛Đ°Ņ‚ŅŒŅŅ аĐēŅ‚Đ¸Đ˛ĐŊŅ‹Đŧи в Ņ‚Đĩ҇ĐĩĐŊиĐĩ Ņ‡Đ°ŅĐ°." }, "tdeDisabledMasterPasswordRequired": { "message": "В Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸ ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊĐž ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊиĐĩ дОвĐĩŅ€ĐĩĐŊĐŊҋ҅ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ŅƒŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ Đ´ĐģŅ Đ´ĐžŅŅ‚ŅƒĐŋа Đē Đ˛Đ°ŅˆĐĩĐŧ҃ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Ņƒ." @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "ĐŖĐŊиĐēаĐģҌĐŊŅ‹Đš идĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžŅ€ ĐŊĐĩ ĐŊаКдĐĩĐŊ." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "ĐœĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ йОĐģҌ҈Đĩ ĐŊĐĩ ҂ҀĐĩĐąŅƒĐĩŅ‚ŅŅ Đ´ĐģŅ ҇ĐģĐĩĐŊОв ҁĐģĐĩĐ´ŅƒŅŽŅ‰ĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ҃ĐēаСаĐŊĐŊŅ‹Đš ĐŊиĐļĐĩ Đ´ĐžĐŧĐĩĐŊ ҃ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸." - }, "organizationName": { "message": "НазваĐŊиĐĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸" }, @@ -3238,7 +3314,7 @@ "message": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ĐēĐžĐģĐ¸Ņ‡ĐĩŅŅ‚Đ˛Đž ŅĐ¸ĐŧвОĐģОв" }, "sessionTimeout": { - "message": "Đ’Ņ€ĐĩĐŧŅ Đ˛Đ°ŅˆĐĩĐŗĐž ҁĐĩаĐŊŅĐ° Đ¸ŅŅ‚ĐĩĐēĐģĐž. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, вĐĩŅ€ĐŊĐ¸Ņ‚ĐĩҁҌ и ĐŋĐžĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ Đ˛ĐžĐšŅ‚Đ¸ ҁĐŊОва." + "message": "Đ’Ņ€ĐĩĐŧŅ Đ˛Đ°ŅˆĐĩĐš ҁĐĩŅŅĐ¸Đ¸ Đ¸ŅŅ‚ĐĩĐēĐģĐž. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, вĐĩŅ€ĐŊĐ¸Ņ‚ĐĩҁҌ и ĐŋĐžĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ Đ˛ĐžĐšŅ‚Đ¸ ҁĐŊОва." }, "exportingPersonalVaultTitle": { "message": "Đ­ĐēҁĐŋĐžŅ€Ņ‚ ĐģĐ¸Ņ‡ĐŊĐžĐŗĐž Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°" @@ -3294,6 +3370,12 @@ "error": { "message": "ĐžŅˆĐ¸ĐąĐēа" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "ĐžŅˆĐ¸ĐąĐēа Ņ€Đ°ŅŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đēи" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Đ˜ĐŗĐŊĐžŅ€Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ" }, - "importData": { - "message": "ИĐŧĐŋĐžŅ€Ņ‚ даĐŊĐŊҋ҅", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "ĐžŅˆĐ¸ĐąĐēа иĐŧĐŋĐžŅ€Ņ‚Đ°" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "БоĐģҌ҈Đĩ ĐžĐŋŅ†Đ¸Đš" + }, "moreOptionsTitle": { "message": "БоĐģҌ҈Đĩ ĐžĐŋŅ†Đ¸Đš - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "ĐēĐžĐŊŅĐžĐģи адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°" }, + "admin": { + "message": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€" + }, + "automaticUserConfirmation": { + "message": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐĩ ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€ĐļĐ´ĐĩĐŊиĐĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģŅ" + }, + "automaticUserConfirmationHint": { + "message": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€ĐļĐ´Đ°Ņ‚ŅŒ ĐžĐļĐ¸Đ´Đ°ŅŽŅ‰Đ¸Ņ… ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš ĐŋĐžĐēа ŅŅ‚Đž ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž Ņ€Đ°ĐˇĐąĐģĐžĐēĐ¸Ņ€ĐžĐ˛Đ°ĐŊĐž" + }, + "autoConfirmOnboardingCallout": { + "message": "Đ­ĐēĐžĐŊĐžĐŧŅŒŅ‚Đĩ Đ˛Ņ€ĐĩĐŧŅ ĐąĐģĐ°ĐŗĐžĐ´Đ°Ņ€Ņ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐŧ҃ ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€ĐļĐ´ĐĩĐŊĐ¸ŅŽ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš" + }, + "autoConfirmWarning": { + "message": "Đ­Ņ‚Đž ĐŧĐžĐļĐĩŅ‚ ĐŋОвĐģĐ¸ŅŅ‚ŅŒ ĐŊа ĐąĐĩСОĐŋĐ°ŅĐŊĐžŅŅ‚ŅŒ даĐŊĐŊҋ҅ Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸. " + }, + "autoConfirmWarningLink": { + "message": "ĐŖĐˇĐŊĐ°ĐšŅ‚Đĩ Đž Ņ€Đ¸ŅĐēĐ°Ņ…" + }, + "autoConfirmSetup": { + "message": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€ĐļĐ´Đ°Ņ‚ŅŒ ĐŊĐžĐ˛Ņ‹Ņ… ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš" + }, + "autoConfirmSetupDesc": { + "message": "ĐĐžĐ˛Ņ‹Đĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģи ĐąŅƒĐ´ŅƒŅ‚ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€ĐļĐ´ĐĩĐŊŅ‹ ĐŋŅ€Đ¸ Ņ€Đ°ĐˇĐąĐģĐžĐēĐ¸Ņ€ĐžĐ˛ĐēĐĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°." + }, + "autoConfirmSetupHint": { + "message": "КаĐēĐžĐ˛Ņ‹ ĐŋĐžŅ‚ĐĩĐŊŅ†Đ¸Đ°ĐģҌĐŊŅ‹Đĩ Ņ€Đ¸ŅĐēи Đ´ĐģŅ ĐąĐĩСОĐŋĐ°ŅĐŊĐžŅŅ‚Đ¸?" + }, + "autoConfirmEnabled": { + "message": "ВĐēĐģŅŽŅ‡ĐĩĐŊĐž Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēĐžĐĩ ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€ĐļĐ´ĐĩĐŊиĐĩ" + }, + "availableNow": { + "message": "ĐŖĐļĐĩ Đ´ĐžŅŅ‚ŅƒĐŋĐŊĐž" + }, "accountSecurity": { "message": "БĐĩСОĐŋĐ°ŅĐŊĐžŅŅ‚ŅŒ аĐēĐēĐ°ŅƒĐŊŅ‚Đ°" }, + "phishingBlocker": { + "message": "БĐģĐžĐēĐ¸Ņ€ĐžĐ˛Ņ‰Đ¸Đē Ņ„Đ¸ŅˆĐ¸ĐŊĐŗĐ°" + }, + "enablePhishingDetection": { + "message": "ОбĐŊĐ°Ņ€ŅƒĐļĐĩĐŊиĐĩ Ņ„Đ¸ŅˆĐ¸ĐŊĐŗĐ°" + }, + "enablePhishingDetectionDesc": { + "message": "ĐžŅ‚ĐžĐąŅ€Đ°ĐļĐ°Ņ‚ŅŒ ĐŋŅ€ĐĩĐ´ŅƒĐŋŅ€ĐĩĐļĐ´ĐĩĐŊиĐĩ ĐŋĐĩŅ€ĐĩĐ´ Đ´ĐžŅŅ‚ŅƒĐŋĐžĐŧ Đē ĐŋĐžĐ´ĐžĐˇŅ€Đ¸Ņ‚ĐĩĐģҌĐŊŅ‹Đŧ Ņ„Đ¸ŅˆĐ¸ĐŊĐŗĐžĐ˛Ņ‹Đŧ ŅĐ°ĐšŅ‚Đ°Đŧ" + }, "notifications": { "message": "ĐŖĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊĐ¸Ņ" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "ĐĄĐēŅ€Ņ‹Ņ‚ŅŒ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊиĐĩ ŅĐžĐ˛ĐŋадĐĩĐŊиК $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "ПоĐēĐ°ĐˇĐ°Ņ‚ŅŒ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊиĐĩ ŅĐžĐ˛ĐŋадĐĩĐŊиК" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "ĐĄĐēŅ€Ņ‹Ņ‚ŅŒ ОйĐŊĐ°Ņ€ŅƒĐļĐĩĐŊиĐĩ ŅĐžĐ˛ĐŋадĐĩĐŊиК" }, "autoFillOnPageLoad": { "message": "ĐĐ˛Ņ‚ĐžĐˇĐ°ĐŋĐžĐģĐŊĐĩĐŊиĐĩ ĐŋŅ€Đ¸ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēĐĩ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Ņ‹?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "ĐžŅ‡ĐĩĐŊҌ ŅˆĐ¸Ņ€ĐžĐēĐžĐĩ" }, + "narrow": { + "message": "ĐŖĐˇĐēиК" + }, "sshKeyWrongPassword": { "message": "ВвĐĩĐ´ĐĩĐŊĐŊŅ‹Đš ĐŋĐ°Ņ€ĐžĐģҌ ĐŊĐĩвĐĩŅ€ĐĩĐŊ." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Đ­Ņ‚ĐžŅ‚ ĐģĐžĐŗĐ¸ĐŊ ĐŋОдвĐĩŅ€ĐļĐĩĐŊ Ņ€Đ¸ŅĐē҃ и ҃ ĐŊĐĩĐŗĐž ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒĐĩŅ‚ вĐĩĐą-ŅĐ°ĐšŅ‚. Đ”ĐžĐąĐ°Đ˛ŅŒŅ‚Đĩ вĐĩĐą-ŅĐ°ĐšŅ‚ и ҁĐŧĐĩĐŊĐ¸Ņ‚Đĩ ĐŋĐ°Ņ€ĐžĐģҌ Đ´ĐģŅ йОĐģҌ҈ĐĩĐš ĐąĐĩСОĐŋĐ°ŅĐŊĐžŅŅ‚Đ¸." }, + "vulnerablePassword": { + "message": "ĐŖŅĐˇĐ˛Đ¸ĐŧŅ‹Đš ĐŋĐ°Ņ€ĐžĐģҌ." + }, + "changeNow": { + "message": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ҁĐĩĐšŅ‡Đ°Ņ" + }, "missingWebsite": { "message": "ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒĐĩŅ‚ ŅĐ°ĐšŅ‚" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "И ĐŧĐŊĐžĐŗĐžĐĩ Đ´Ņ€ŅƒĐŗĐžĐĩ!" }, - "planDescPremium": { - "message": "ПоĐģĐŊĐ°Ņ ĐžĐŊĐģаКĐŊ-ĐˇĐ°Ņ‰Đ¸Ņ‰ĐĩĐŊĐŊĐžŅŅ‚ŅŒ" + "advancedOnlineSecurity": { + "message": "Đ Đ°ŅŅˆĐ¸Ņ€ĐĩĐŊĐŊĐ°Ņ ĐžĐŊĐģаКĐŊ-ĐąĐĩСОĐŋĐ°ŅĐŊĐžŅŅ‚ŅŒ" }, "upgradeToPremium": { "message": "ОбĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ´Đž ĐŸŅ€ĐĩĐŧĐ¸ŅƒĐŧ" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "НоĐŧĐĩŅ€ ĐēĐ°Ņ€Ņ‚Ņ‹" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ йОĐģҌ҈Đĩ ĐŊĐĩ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚ ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģи Đ´ĐģŅ Đ˛Ņ…ĐžĐ´Đ° в Bitwarden. Đ§Ņ‚ĐžĐąŅ‹ ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ, ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŽ и Đ´ĐžĐŧĐĩĐŊ." + }, + "continueWithLogIn": { + "message": "ĐŸŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ ҁ ĐģĐžĐŗĐ¸ĐŊĐžĐŧ" + }, + "doNotContinue": { + "message": "НĐĩ ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ°Ņ‚ŅŒ" + }, + "domain": { + "message": "ДоĐŧĐĩĐŊ" + }, + "keyConnectorDomainTooltip": { + "message": "В ŅŅ‚ĐžĐŧ Đ´ĐžĐŧĐĩĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ Ņ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒŅŅ ĐēĐģŅŽŅ‡Đ¸ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ Đ˛Đ°ŅˆĐĩĐŗĐž аĐēĐēĐ°ŅƒĐŊŅ‚Đ°, ĐŋĐžŅŅ‚ĐžĐŧ҃ ŅƒĐąĐĩĐ´Đ¸Ņ‚ĐĩҁҌ, Ņ‡Ņ‚Đž Đ˛Ņ‹ ĐĩĐŧ҃ дОвĐĩŅ€ŅĐĩŅ‚Đĩ. Đ•ŅĐģи Đ˛Ņ‹ ĐŊĐĩ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, ĐžĐąŅ€Đ°Ņ‚Đ¸Ņ‚ĐĩҁҌ Đē ŅĐ˛ĐžĐĩĐŧ҃ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Ņƒ." + }, + "verifyYourOrganization": { + "message": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ŅĐ˛ĐžŅŽ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŽ Đ´ĐģŅ Đ˛Ņ…ĐžĐ´Đ°" + }, + "organizationVerified": { + "message": "ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€ĐļĐ´ĐĩĐŊа" + }, + "domainVerified": { + "message": "ДоĐŧĐĩĐŊ вĐĩŅ€Đ¸Ņ„Đ¸Ņ†Đ¸Ņ€ĐžĐ˛Đ°ĐŊ" + }, + "leaveOrganizationContent": { + "message": "Đ•ŅĐģи Đ˛Ņ‹ ĐŊĐĩ ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ŅĐ˛ĐžŅŽ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŽ, Đ˛Đ°Ņˆ Đ´ĐžŅŅ‚ŅƒĐŋ Đē ĐŊĐĩĐš ĐąŅƒĐ´ĐĩŅ‚ аĐŊĐŊ҃ĐģĐ¸Ņ€ĐžĐ˛Đ°ĐŊ." + }, + "leaveNow": { + "message": "ПоĐēиĐŊŅƒŅ‚ŅŒ" + }, + "verifyYourDomainToLogin": { + "message": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ŅĐ˛ĐžĐš Đ´ĐžĐŧĐĩĐŊ Đ´ĐģŅ Đ˛Ņ…ĐžĐ´Đ°" + }, + "verifyYourDomainDescription": { + "message": "Đ§Ņ‚ĐžĐąŅ‹ ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ ҁ ĐģĐžĐŗĐ¸ĐŊĐžĐŧ, ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ŅŅ‚ĐžŅ‚ Đ´ĐžĐŧĐĩĐŊ." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Đ§Ņ‚ĐžĐąŅ‹ ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ ҁ ĐģĐžĐŗĐ¸ĐŊĐžĐŧ, ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŽ и Đ´ĐžĐŧĐĩĐŊ." + }, "sessionTimeoutSettingsAction": { "message": "ĐĸаКĐŧ-Đ°ŅƒŅ‚ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Đ­Ņ‚Đ° ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēа ҃ĐŋŅ€Đ°Đ˛ĐģŅĐĩŅ‚ŅŅ Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ĐĩĐš." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "В Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸ ĐŧаĐēŅĐ¸ĐŧаĐģҌĐŊŅ‹Đš Ņ‚Đ°ĐšĐŧ-Đ°ŅƒŅ‚ ҁĐĩŅŅĐ¸Đ¸ ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ Ņ€Đ°Đ˛ĐŊŅ‹Đŧ $HOURS$ Ņ‡Đ°Ņ. и $MINUTES$ ĐŧиĐŊ.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ĐŊĐĩ ŅƒŅŅ‚Đ°ĐŊОвиĐģа Ņ‚Đ°ĐšĐŧ-Đ°ŅƒŅ‚ ҁĐĩŅŅĐ¸Đ¸ ĐŊа НĐĩĐŧĐĩĐ´ĐģĐĩĐŊĐŊĐž." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ŅƒŅŅ‚Đ°ĐŊОвиĐģа Ņ‚Đ°ĐšĐŧ-Đ°ŅƒŅ‚ ҁĐĩŅŅĐ¸Đ¸ ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ ĐŊа ĐŸŅ€Đ¸ ĐąĐģĐžĐēĐ¸Ņ€ĐžĐ˛ĐēĐĩ ŅĐ¸ŅŅ‚ĐĩĐŧŅ‹." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ŅƒŅŅ‚Đ°ĐŊОвиĐģа Ņ‚Đ°ĐšĐŧ-Đ°ŅƒŅ‚ ҁĐĩŅŅĐ¸Đ¸ ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ ĐŊа ĐŸŅ€Đ¸ ĐŋĐĩŅ€ĐĩСаĐŋ҃ҁĐēĐĩ ĐąŅ€Đ°ŅƒĐˇĐĩŅ€Đ°." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "МаĐēŅĐ¸ĐŧаĐģҌĐŊŅ‹Đš Ņ‚Đ°ĐšĐŧ-Đ°ŅƒŅ‚ ĐŊĐĩ ĐŧĐžĐļĐĩŅ‚ ĐŋŅ€ĐĩĐ˛Ņ‹ŅˆĐ°Ņ‚ŅŒ $HOURS$ Ņ‡Đ°Ņ. и $MINUTES$ ĐŧиĐŊ.", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "ĐŸŅ€Đ¸ ĐŋĐĩŅ€ĐĩСаĐŋ҃ҁĐēĐĩ ĐąŅ€Đ°ŅƒĐˇĐĩŅ€Đ°" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚Đĩ ҁĐŋĐžŅĐžĐą Ņ€Đ°ĐˇĐąĐģĐžĐēĐ¸Ņ€ĐžĐ˛Đēи Đ´ĐģŅ иСĐŧĐĩĐŊĐĩĐŊĐ¸Ņ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ ĐŋŅ€Đ¸ Đ¸ŅŅ‚Đĩ҇ĐĩĐŊии Ņ‚Đ°ĐšĐŧ-Đ°ŅƒŅ‚Đ°" + }, + "upgrade": { + "message": "ПĐĩŅ€ĐĩĐšŅ‚Đ¸" + }, + "leaveConfirmationDialogTitle": { + "message": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŋĐžĐēиĐŊŅƒŅ‚ŅŒ?" + }, + "leaveConfirmationDialogContentOne": { + "message": "В ҁĐģŅƒŅ‡Đ°Đĩ ĐžŅ‚ĐēаСа Đ˛Đ°ŅˆĐ¸ ĐģĐ¸Ņ‡ĐŊŅ‹Đĩ даĐŊĐŊŅ‹Đĩ ĐžŅŅ‚Đ°ĐŊŅƒŅ‚ŅŅ в Đ˛Đ°ŅˆĐĩĐŧ аĐēĐēĐ°ŅƒĐŊŅ‚Đĩ, ĐŊĐž Đ˛Ņ‹ ĐŋĐžŅ‚ĐĩŅ€ŅĐĩŅ‚Đĩ Đ´ĐžŅŅ‚ŅƒĐŋ Đē ĐžĐąŅ‰Đ¸Đŧ ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°Đŧ и вОСĐŧĐžĐļĐŊĐžŅŅ‚ŅĐŧ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸." + }, + "leaveConfirmationDialogContentTwo": { + "message": "ĐĄĐ˛ŅĐļĐ¸Ņ‚ĐĩҁҌ ҁ Đ˛Đ°ŅˆĐ¸Đŧ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ĐžĐŧ Đ´ĐģŅ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐ¸Ņ Đ´ĐžŅŅ‚ŅƒĐŋа." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "ПоĐēиĐŊŅƒŅ‚ŅŒ $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "КаĐē Ņ ĐŧĐžĐŗŅƒ ҃ĐŋŅ€Đ°Đ˛ĐģŅŅ‚ŅŒ ŅĐ˛ĐžĐ¸Đŧ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰ĐĩĐŧ?" + }, + "transferItemsToOrganizationTitle": { + "message": "ПĐĩŅ€ĐĩĐŊĐĩŅŅ‚Đ¸ ŅĐģĐĩĐŧĐĩĐŊ҂ҋ в $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ ҂ҀĐĩĐąŅƒĐĩŅ‚, Ņ‡Ņ‚ĐžĐąŅ‹ Đ˛ŅĐĩ ŅĐģĐĩĐŧĐĩĐŊ҂ҋ ĐŋŅ€Đ¸ĐŊадĐģĐĩĐļаĐģи ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸ Đ´ĐģŅ ОйĐĩҁĐŋĐĩ҇ĐĩĐŊĐ¸Ņ ĐąĐĩСОĐŋĐ°ŅĐŊĐžŅŅ‚Đ¸ и ŅĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛Đ¸Ņ ҂ҀĐĩйОваĐŊĐ¸ŅĐŧ. НаĐļĐŧĐ¸Ņ‚Đĩ ĐŸŅ€Đ¸ĐŊŅŅ‚ŅŒ, Ņ‡Ņ‚ĐžĐąŅ‹ ĐŋĐĩŅ€ĐĩĐ´Đ°Ņ‚ŅŒ ŅĐžĐąŅŅ‚Đ˛ĐĩĐŊĐŊĐžŅŅ‚ŅŒ ĐŊа Đ˛Đ°ŅˆĐ¸ ŅĐģĐĩĐŧĐĩĐŊ҂ҋ.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "ĐŸŅ€Đ¸ĐŊŅŅ‚ŅŒ ĐŋĐĩŅ€ĐĩĐ´Đ°Ņ‡Ņƒ" + }, + "declineAndLeave": { + "message": "ĐžŅ‚ĐēĐģĐžĐŊĐ¸Ņ‚ŅŒ и ĐŋĐžĐēиĐŊŅƒŅ‚ŅŒ" + }, + "whyAmISeeingThis": { + "message": "ĐŸĐžŅ‡ĐĩĐŧ҃ Ņ ŅŅ‚Đž виĐļ҃?" + }, + "resizeSideNavigation": { + "message": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Ņ€Đ°ĐˇĐŧĐĩŅ€ йОĐēОвОК ĐŊĐ°Đ˛Đ¸ĐŗĐ°Ņ†Đ¸Đ¸" } } diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index bb46b283322..c2451a18133 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "⎃āļ¸āļ¸āˇ”⎄⎖āļģ⎊āļ­āļąāļē" }, - "syncVaultNow": { - "message": "āˇƒāˇ”āļģāļšāˇŠāˇ‚⎒āļ­āˇāļœāˇāļģāļē āļ¯āˇāļąāˇŠ ⎃āļ¸āļ¸āˇ”⎄⎔āļģ⎊āļ­ āļšāļģāļąāˇŠāļą" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "āļ…āˇ€āˇƒāļąāˇŠ ⎃āļ¸āļ¸āˇ”⎄⎔āļģ⎊āļ­ āļšāļģāļąāˇŠāļą:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "āļ†āļąāļēāļą āļ…āļē⎒āļ­āļ¸" - }, "select": { "message": "āļ­āˇāļģāļąāˇŠāļą" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "⎃āļ‚āˇƒāˇŠāļšāļģāļĢāļē" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "āļ…āļ´āļąāļēāļą āˇƒāˇ”āļģāļšāˇŠāˇ‚⎒āļ­āˇāļœāˇāļģāļē" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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": "āļœāˇœāļąāˇ” āļ†āļšāˇ˜āļ­āˇ’āļē" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "⎀⎐āļŠāˇ’āļ¯āˇ”āļģ āļ‰āļœāˇ™āļą āļœāļąāˇŠāļą" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "⎃āļ­āˇŠāļēāˇāļ´āļą āļēāļ­āˇ”āļģ (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "āļ‡āļ¸āˇ”āļĢ⎔āļ¸ āļœāˇāļŊ⎀⎓āļ¸ āļšāļģ āļ‡āļ­." }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "āļœāˇœāļąāˇ”⎀" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "āļœāˇœāļąāˇ”⎀āļšāˇŠ āļ­āˇāļģāļąāˇŠāļą." }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "āļ‹āļ´āļģ⎒āļ¸ āļœāˇœāļąāˇ” āļ´āˇŠāļģāļ¸āˇāļĢāļē 500 MB āˇ€āˇš." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "āļœāˇœāļąāˇ” āļ‡āļ¸āˇ”āļĢ⎔āļ¸āˇŠ ⎃āļŗāˇ„āˇ 1 GB ⎃āļ‚āļšāˇšāļ­āˇāļ­āˇŠāļ¸āļš āļœāļļāļŠāˇ." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "āļ”āļļāļœāˇš āˇƒāˇ”āļģāļšāˇŠāˇ‚⎒āļ­āˇāļœāˇāļģāļē āļ†āļģāļšāˇŠāˇ‚⎒āļ­āˇ€ āļ­āļļāˇ āļœāˇāļąāˇ“āļ¸ āˇƒāļŗāˇ„āˇ āļ¸āˇ”āļģāļ´āļ¯āļē ⎃āļąāˇ“āļ´āˇāļģāļšāˇŠāˇ‚āˇāˇ€, āļœāˇ’āļĢ⎔āļ¸āˇŠ āˇƒāˇžāļ›āˇŠāļēāļē āˇƒāˇ„ āļ¯āļ­āˇŠāļ­ āļ‹āļŊ⎊āļŊāļ‚ ach āļąāļē āˇ€āˇāļģ⎊āļ­āˇ āļšāļģāļē⎒." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "⎃⎊āļŽāˇ’āļģ⎀ āļ¸āļšāˇ āļ¯āˇāļ¸āˇ– āļ…āļē⎒āļ­āļ¸āļē" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "āļ…āļē⎒āļ­āļ¸āļē āļē⎅⎒ āļ´āˇ’⎄⎒āļ§āˇ”⎀āļąāˇŠāļą" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "āļ…āļ¯āˇŠāˇ€āˇ’āļ­āˇ“āļē ⎄āļŗāˇ”āļąāˇāļœāˇāļąāˇ“āļ¸āļšāˇŠ ⎃⎜āļēāˇāļœāļ­ āļąāˇœāˇ„⎐āļšāˇ’ ⎀⎒āļē." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 283442d95da..5e1511eebac 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "PrihlÃĄsiÅĨ sa s prístupovÃŊm kÄžÃēčom" }, + "unlockWithPasskey": { + "message": "OdomknÃēÅĨ pomocou prístupovÊho kÄžÃēča" + }, "useSingleSignOn": { "message": "PouÅžiÅĨ jednotnÊ prihlÃĄsenie" }, @@ -436,8 +439,8 @@ "sync": { "message": "SynchronizÃĄcia" }, - "syncVaultNow": { - "message": "SynchronizovaÅĨ trezor teraz" + "syncNow": { + "message": "SynchronizovaÅĨ teraz" }, "lastSync": { "message": "PoslednÃĄ synchronizÃĄcia:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "WebovÃĄ aplikÃĄcia Bitwarden" }, - "importItems": { - "message": "ImportovaÅĨ poloÅžky" - }, "select": { "message": "VybraÅĨ" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "PoloÅžka bola archivovanÃĄ" }, + "itemWasUnarchived": { + "message": "PoloÅžka bola odobranÃĄ z archívu" + }, "itemUnarchived": { "message": "PoloÅžka bola odobranÃĄ z archívu" }, "archiveItem": { "message": "ArchivovaÅĨ poloÅžku" }, - "archiveItemConfirmDesc": { - "message": "ArchivovanÊ poloÅžky sÃē vylÃēčenÊ zo vÅĄeobecnÊho vyhÄžadÃĄvania a z nÃĄvrhov automatickÊho vypÄēňania. Naozaj chcete archivovaÅĨ tÃēto poloÅžku?" + "archiveItemDialogContent": { + "message": "Po archivÃĄcii bude tÃĄto poloÅžka vylÃēčenÃĄ z vÃŊsledkov vyhÄžadÃĄvania a nÃĄvrhov automatickÊho vypÄēňania." + }, + "archived": { + "message": "ArchivovanÊ" + }, + "unarchiveAndSave": { + "message": "ZruÅĄiÅĨ archivÃĄciu a uloÅžiÅĨ" }, "upgradeToUseArchive": { "message": "Na pouÅžitie archívu je potrebnÊ prÊmiovÊ členstvo." }, + "itemRestored": { + "message": "PoloÅžka bola obnovenÃĄ" + }, "edit": { "message": "UpraviÅĨ" }, @@ -803,7 +815,7 @@ "message": "4 hodiny" }, "onLocked": { - "message": "Keď je systÊm uzamknutÃŊ" + "message": "Pri uzamknutí systÊmu" }, "onIdle": { "message": "Pri nečinnosti systÊmu" @@ -812,7 +824,7 @@ "message": "V reÅžime spÃĄnku" }, "onRestart": { - "message": "Po reÅĄtarte prehliadača" + "message": "Pri reÅĄtarte prehliadača" }, "never": { "message": "Nikdy" @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "ExportovaÅĨ z" }, - "exportVault": { - "message": "Export trezoru" + "exportVerb": { + "message": "ExportovaÅĨ", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "ZistiÅĨ viac" }, + "migrationsFailed": { + "message": "Pri aktualizÃĄcii nastavení ÅĄifrovania doÅĄlo k chybe." + }, + "updateEncryptionSettingsTitle": { + "message": "Aktualizujte nastavenie ÅĄifrovania" + }, + "updateEncryptionSettingsDesc": { + "message": "NovÊ odporÃēčanÊ nastavenia ÅĄifrovania zlepÅĄia bezpečnosÅĨ vÃĄÅĄho Ãēčtu. Ak ich chcete aktualizovaÅĨ teraz, zadajte hlavnÊ heslo." + }, + "confirmIdentityToContinue": { + "message": "Ak chcete pokračovaÅĨ, potvrďte svoju identitu" + }, + "enterYourMasterPassword": { + "message": "Zadajte hlavnÊ heslo" + }, + "updateSettings": { + "message": "AktualizovaÅĨ nastavenia" + }, + "later": { + "message": "Neskôr" + }, "authenticatorKeyTotp": { "message": "Overovací kÄžÃēč (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Príloha bola uloÅženÃĄ" }, + "fixEncryption": { + "message": "OpraviÅĨ ÅĄifrovanie" + }, + "fixEncryptionTooltip": { + "message": "Tento sÃēbor pouŞíva zastaranÃē metÃŗdu ÅĄifrovania." + }, + "attachmentUpdated": { + "message": "Príloha bola aktualizovanÃĄ" + }, "file": { "message": "SÃēbor" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Vyberte sÃēbor" }, + "itemsTransferred": { + "message": "PoloÅžky boli prenesenÊ" + }, "maxFileSize": { "message": "MaximÃĄlna veÄžkosÅĨ sÃēboru je 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB ÅĄifrovanÊho ÃēloÅžiska na prílohy." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ ÅĄifrovanÊho ÃēloÅžiska na prílohy.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "NÃēdzovÃŊ prístup." }, "premiumSignUpTwoStepOptions": { "message": "ProprietÃĄrne moÅžnosti dvojstupňovÊho prihlÃĄsenia ako napríklad YubiKey a Duo." }, + "premiumSubscriptionEnded": { + "message": "VaÅĄe predplatnÊ PrÊmium skončilo" + }, + "archivePremiumRestart": { + "message": "Ak chcete obnoviÅĨ prístup k svojmu archívu, reÅĄtartujte predplatnÊ PrÊmium. Ak pred reÅĄtartom upravíte podrobnosti archivovanej poloÅžky, bude presunutÃĄ späÅĨ do trezoru." + }, + "restartPremium": { + "message": "ReÅĄtartovaÅĨ PrÊmium" + }, "ppremiumSignUpReports": { "message": "SprÃĄvy o sile hesla, zabezpečení Ãēčtov a Ãēnikoch dÃĄt ktorÊ vÃĄm pomôŞu udrÅžaÅĨ vaÅĄe kontÃĄ v bezpečí." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "PoloÅžka bola natrvalo odstrÃĄnenÃĄ" }, + "archivedItemRestored": { + "message": "ArchivovanÃĄ poloÅžka bola obnovenÃĄ" + }, "restoreItem": { "message": "ObnoviÅĨ poloÅžku" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "NenaÅĄiel sa Åžiadny jedinečnÃŊ identifikÃĄtor." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "HlavnÊ heslo sa uÅž nevyÅžaduje pre členov tejto organizÃĄcie. NiÅžÅĄie uvedenÃē domÊnu potvrďte u sprÃĄvcu organizÃĄcie." - }, "organizationName": { "message": "NÃĄzov organizÃĄcie" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Chyba" }, + "prfUnlockFailed": { + "message": "Odomknutie pomocou prístupovÊho kÄžÃēča zlyhalo. SkÃēste to znovu alebo pouÅžite inÃē metÃŗdu odomknutia." + }, + "noPrfCredentialsAvailable": { + "message": "Na odomkntuie nie sÃē k dispozícii Åžiadne PRF-enabled prístupovÊ kÄžÃēče. Najskôr sa prihlÃĄste pomocou prístupovÊho kÄžÃēča." + }, "decryptionError": { "message": "Chyba deÅĄifrovania" }, @@ -3359,10 +3441,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Ê" @@ -4176,10 +4258,6 @@ "ignore": { "message": "IgnorovaÅĨ" }, - "importData": { - "message": "Import Ãēdajov", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Chyba importu" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "ĎalÅĄie moÅžnosti" + }, "moreOptionsTitle": { "message": "ĎalÅĄie moÅžnosti - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "SprÃĄvcovskÃĄ konzola" }, + "admin": { + "message": "SprÃĄvca" + }, + "automaticUserConfirmation": { + "message": "AutomatickÊ potvrdenie pouŞívateÄža" + }, + "automaticUserConfirmationHint": { + "message": "Automaticky potvrdzovaÅĨ čakajÃēcich pouŞívateÄžov, keď je toto zariadenie odomknutÊ" + }, + "autoConfirmOnboardingCallout": { + "message": "Å etrite čas automatickÃŊm potvrdzovaním pouŞívateÄža" + }, + "autoConfirmWarning": { + "message": "MôŞe maÅĨ vplyv na bezpečnosÅĨ Ãēdajov vaÅĄej organizÃĄcie. " + }, + "autoConfirmWarningLink": { + "message": "DozvedieÅĨ sa viac o rizikÃĄch" + }, + "autoConfirmSetup": { + "message": "Automaticky potvrdzovaÅĨ novÃŊch pouŞívateÄžov" + }, + "autoConfirmSetupDesc": { + "message": "Noví pouŞívatelia budÃē automaticky potvrdení, keď je toto zariadenie odomknutÊ." + }, + "autoConfirmSetupHint": { + "message": "AkÊ sÃē potenciÃĄlne bezpečnostnÊ rizikÃĄ?" + }, + "autoConfirmEnabled": { + "message": "ZapnutÊ automatickÊ potvrdzovanie" + }, + "availableNow": { + "message": "Teraz dostupnÊ" + }, "accountSecurity": { "message": "Zabezpečenie Ãēčtu" }, + "phishingBlocker": { + "message": "Blokovač phishingu" + }, + "enablePhishingDetection": { + "message": "Detekcia phishingu" + }, + "enablePhishingDetectionDesc": { + "message": "Zobrazí upozornenie pred prístupom na podozrivÊ phishingovÊ strÃĄnky" + }, "notifications": { "message": "Upozornenia" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "SkryÅĨ spôsob zisÅĨovania zhody $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "ZobraziÅĨ spôsob mapovania" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "SkryÅĨ spôsob mapovania" }, "autoFillOnPageLoad": { "message": "Automaticky vyplniÅĨ pri načítaní strÃĄnky?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra ÅĄirokÊ" }, + "narrow": { + "message": "Úzke" + }, "sshKeyWrongPassword": { "message": "ZadanÊ heslo je nesprÃĄvne." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Toto prihlÃĄsenie je v ohrození a chÃŊba mu webovÃĄ strÃĄnka. Pridajte webovÃē strÃĄnku a zmeňte heslo na silnejÅĄie zabezpečenie." }, + "vulnerablePassword": { + "message": "ZraniteÄžnÊ heslo." + }, + "changeNow": { + "message": "ZmeniÅĨ teraz" + }, "missingWebsite": { "message": "ChÃŊbajÃēca webovÃĄ strÃĄnka" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "A eÅĄte viac!" }, - "planDescPremium": { - "message": "ÚplnÊ online zabezpečenie" + "advancedOnlineSecurity": { + "message": "PokročilÃĄ online ochrana" }, "upgradeToPremium": { "message": "UpgradovaÅĨ na PrÊmium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Číslo karty" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "VaÅĄa organizÃĄcia uÅž nepouŞíva hlavnÊ heslÃĄ na prihlÃĄsenie do Bitwardenu. Ak chcete pokračovaÅĨ, overte organizÃĄciu a domÊnu." + }, + "continueWithLogIn": { + "message": "Pokračujte prihlÃĄsením" + }, + "doNotContinue": { + "message": "NepokračovaÅĨ" + }, + "domain": { + "message": "DomÊna" + }, + "keyConnectorDomainTooltip": { + "message": "TÃĄto domÊna bude ukladaÅĨ ÅĄifrovacie kÄžÃēče vÃĄÅĄho Ãēčtu, takÅže sa uistite, Åže jej dôverujete. Ak si nie ste istí, overte si to u sprÃĄvcu." + }, + "verifyYourOrganization": { + "message": "Na prihlÃĄsenie overte organizÃĄciu" + }, + "organizationVerified": { + "message": "OrganizÃĄcia je overenÃĄ" + }, + "domainVerified": { + "message": "DomÊna je overenÃĄ" + }, + "leaveOrganizationContent": { + "message": "Ak organizÃĄciu neoveríte, vÃĄÅĄ prístup k nej bude zruÅĄenÃŊ." + }, + "leaveNow": { + "message": "OpustiÅĨ teraz" + }, + "verifyYourDomainToLogin": { + "message": "Na prihlÃĄsenie overte domÊnu" + }, + "verifyYourDomainDescription": { + "message": "Na pokračovanie prihlÃĄsením, overte tÃēto domÊnu." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Na pokračovanie prihlÃĄsením, overte organizÃĄciu a domÊnu." + }, "sessionTimeoutSettingsAction": { "message": "Akcia pri vyprÅĄaní časovÊho limitu" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Toto nastavenie spravuje vaÅĄa organizÃĄcia." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "VaÅĄa organizÃĄcia nastavila maximÃĄlny časovÃŊ limit relÃĄcie na $HOURS$ hod. a $MINUTES$ min.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "VaÅĄa organizÃĄcia nastavila predvolenÃŊ časovÃŊ limit relÃĄcie na OkamÅžite." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "VaÅĄa organizÃĄcia nastavila predvolenÃŊ časovÃŊ limit relÃĄcie na Pri uzamknutí systÊmu." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "VaÅĄa organizÃĄcia nastavila predvolenÃŊ časovÃŊ limit relÃĄcie na Pri reÅĄtarte prehliadača." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "MaximÃĄlny časovÃŊ limit nesmie prekročiÅĨ $HOURS$ hod. a $MINUTES$ min.", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Pri reÅĄtarte prehliadača" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Nastavte metÃŗdu odomknutia, aby ste zmenili akciu pri vyprÅĄaní časovÊho limitu" + }, + "upgrade": { + "message": "UpgradovaÅĨ" + }, + "leaveConfirmationDialogTitle": { + "message": "Naozaj chcete odísÅĨ?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Ak odmietnete, vaÅĄe osobnÊ poloÅžky zostanÃē vo vaÅĄom Ãēčte, ale stratíte prístup k zdieÄžanÃŊm poloÅžkÃĄm a funkciÃĄm organizÃĄcie." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Ak chcete obnoviÅĨ prístup, obrÃĄÅĨte sa na sprÃĄvcu." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "OpustiÅĨ $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Ako môŞem spravovaÅĨ svoj trezor?" + }, + "transferItemsToOrganizationTitle": { + "message": "Prenos poloÅžiek do $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ vyÅžaduje, aby vÅĄetky poloÅžky boli vo vlastníctve organizÃĄcie z dôvodu bezpečnosti a dodrÅžiavania predpisov. Ak chcete previesÅĨ vlastníctvo poloÅžiek, kliknite na tlačidlo PrijaÅĨ.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "PrijaÅĨ prenos" + }, + "declineAndLeave": { + "message": "ZamietnuÅĨ a odísÅĨ" + }, + "whyAmISeeingThis": { + "message": "Prečo to vidím?" + }, + "resizeSideNavigation": { + "message": "ZmeniÅĨ veÄžkosÅĨ bočnej navigÃĄcie" } } diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 2d20050d7f1..23d0312caae 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sinhronizacija" }, - "syncVaultNow": { - "message": "Sinhroniziraj trezor zdaj" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Zadnja sinhronizacija:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Uvozi elemente" - }, "select": { "message": "Izberi" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Uredi" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Izvoz trezorja" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Več o tem" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Ključ avtentikatorja (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Priponka je bila shranjena." }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "Datoteka" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Izberite datoteko." }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Največja velikost datoteke je 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB ÅĄifriranega prostora za shrambo podatkov." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Higiena gesel, zdravje računa in poročila o kraji podatkov, ki vam pomagajo ohraniti varnost vaÅĄega trezorja." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Element trajno izbrisan" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Obnovi element" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Napaka" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 0c7562987fc..d3b5e961ef3 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Ņ‚Đĩ ҁĐĩ ŅĐ° ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋĐŊиĐŧ ĐēŅ™ŅƒŅ‡ĐĩĐŧ" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "ĐŖĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚Đ¸ ҘĐĩĐ´ĐŊĐžĐēŅ€Đ°Ņ‚ĐŊ҃ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ" }, @@ -436,8 +439,8 @@ "sync": { "message": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ°" }, - "syncVaultNow": { - "message": "ОдĐŧĐ°Ņ… ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇŅƒŅ˜ ҁĐĩŅ„" + "syncNow": { + "message": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇŅƒŅ˜ ŅĐ°Đ´Đ°" }, "lastSync": { "message": "Đ—Đ°Đ´ŅšĐ° ŅĐ¸ĐŊŅ€ĐžĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ°:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden вĐĩĐą аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ°" }, - "importItems": { - "message": "ĐŖĐ˛ĐžĐˇ ŅŅ‚Đ°Đ˛Đēи" - }, "select": { "message": "ИСайĐĩŅ€Đ¸" }, @@ -576,17 +576,29 @@ "itemWasSentToArchive": { "message": "ĐĄŅ‚Đ°Đ˛Đēа ҘĐĩ ĐŋĐžŅĐģĐ°Ņ‚Đ° ҃ Đ°Ņ€Ņ…Đ¸Đ˛Ņƒ" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "ĐĄŅ‚Đ°Đ˛Đēа Đ˛Ņ€Đ°Ņ›ĐĩĐŊа иС Đ°Ņ€Ņ…Đ¸Đ˛Đĩ" }, "archiveItem": { "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°Ņ˜ ŅŅ‚Đ°Đ˛Đē҃" }, - "archiveItemConfirmDesc": { - "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ŅŅ‚Đ°Đ˛ĐēĐĩ ҁ҃ Đ¸ŅĐēŅ™ŅƒŅ‡ĐĩĐŊĐĩ иС ĐžĐŋŅˆŅ‚Đ¸Ņ… Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ° ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐĩ и ĐŋŅ€ĐĩĐ´ĐģĐžĐŗĐ° Са Đ°ŅƒŅ‚Đž ĐŋĐžĐŋŅƒŅšĐ°Đ˛Đ°ŅšĐĩ. ЈĐĩҁ҂Đĩ Đģи ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°Ņ‚Đĩ ĐžĐ˛Ņƒ ŅŅ‚Đ°Đ˛Đē҃?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { - "message": "A premium membership is required to use Archive." + "message": "ĐŸŅ€ĐĩĐŧĐ¸Ņ˜ŅƒĐŧ ҇ĐģаĐŊŅŅ‚Đ˛Đž ҘĐĩ ĐŊĐĩĐžĐŋŅ…ĐžĐ´ĐŊĐž Са ҃ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒ ĐŅ€Ņ…Đ¸Đ˛Đĩ." + }, + "itemRestored": { + "message": "Item has been restored" }, "edit": { "message": "ĐŖŅ€Đĩди" @@ -598,10 +610,10 @@ "message": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´Đ°Ņ˜ ŅĐ˛Đĩ" }, "showAll": { - "message": "Show all" + "message": "ĐŸŅ€Đ¸ĐēаĐļи ŅĐ˛Đĩ" }, "viewLess": { - "message": "View less" + "message": "ĐŸŅ€Đ¸ĐēаĐļи ĐŧĐ°ŅšĐĩ" }, "viewLogin": { "message": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đĩ" @@ -806,10 +818,10 @@ "message": "На СаĐēŅ™ŅƒŅ‡Đ°Đ˛Đ°ŅšĐĩ ŅĐ¸ŅŅ‚ĐĩĐŧа" }, "onIdle": { - "message": "On system idle" + "message": "На ĐŧĐ¸Ņ€ĐžĐ˛Đ°ŅšĐĩ ŅĐ¸ŅŅ‚ĐĩĐŧа" }, "onSleep": { - "message": "On system sleep" + "message": "НаĐēĐžĐŊ ҁĐŋĐ°Đ˛Đ°ŅšĐ° ŅĐ¸ŅŅ‚ĐĩĐŧа" }, "onRestart": { "message": "На ĐŋĐžĐēŅ€ĐĩŅ‚Đ°ŅšĐĩ ĐŋŅ€ĐĩĐŗĐģĐĩĐ´Đ°Ņ‡Đ°" @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "ИСвОС Од" }, - "exportVault": { - "message": "ИСвОС ҁĐĩŅ„Đ°" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "ХаСĐŊĐ°Ņ˜ Đ˛Đ¸ŅˆĐĩ" }, + "migrationsFailed": { + "message": "Đ”ĐžŅˆĐģĐž ҘĐĩ Đ´Đž ĐŗŅ€Đĩ҈ĐēĐĩ ĐŋŅ€Đ¸ аĐļŅƒŅ€Đ¸Ņ€Đ°ŅšŅƒ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ŅšĐ°." + }, + "updateEncryptionSettingsTitle": { + "message": "АĐļŅƒŅ€Đ¸Ņ€Đ°Ņ˜ ŅĐ˛ĐžŅ˜Đĩ ĐŋĐžŅŅ‚Đ°Đ˛ĐēĐĩ Са ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ŅšĐĩ" + }, + "updateEncryptionSettingsDesc": { + "message": "Нова ĐŋŅ€ĐĩĐŋĐžŅ€ŅƒŅ‡ĐĩĐŊа ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ŅˆĐ¸Ņ„Ņ€Đ¸Ņ€Đ°ŅšĐ° ĐŋĐžĐąĐžŅ™ŅˆĐ°Ņ›Đĩ Đ˛Đ°ŅˆŅƒ ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚ ĐŊаĐģĐžĐŗĐ°. ĐŖĐŊĐĩŅĐ¸Ņ‚Đĩ ŅĐ˛ĐžŅ˜Ņƒ ĐŗĐģавĐŊ҃ ĐģОСиĐŊĐē҃ Са аĐļŅƒŅ€Đ¸Ņ€Đ°ŅšĐĩ." + }, + "confirmIdentityToContinue": { + "message": "Да ĐąĐ¸ŅŅ‚Đĩ ĐŊĐ°ŅŅ‚Đ°Đ˛Đ¸Đģи ĐŋĐžŅ‚Đ˛Ņ€Đ´Đ¸Ņ‚Đĩ Đ˛Đ°Ņˆ идĐĩĐŊŅ‚Đ¸Ņ‚ĐĩŅ‚" + }, + "enterYourMasterPassword": { + "message": "ĐŖĐŊĐĩŅ‚Đ¸ Đ˛Đ°ŅˆŅƒ ĐŗĐģавĐŊ҃ ĐģОСиĐŊĐē҃" + }, + "updateSettings": { + "message": "АĐļŅƒŅ€Đ¸Ņ€Đ°Ņ˜ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°" + }, + "later": { + "message": "ĐšĐ°ŅĐŊĐ¸Ņ˜Đĩ" + }, "authenticatorKeyTotp": { "message": "ЈĐĩĐ´ĐŊĐžĐēŅ€Đ°Ņ‚ĐŊи ĐēОд" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "ĐŸŅ€Đ¸ĐģĐžĐŗ ҘĐĩ ŅĐ°Ņ‡ŅƒĐ˛Đ°ĐŊ." }, + "fixEncryption": { + "message": "ПоĐŋŅ€Đ°Đ˛Đ¸ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ŅšĐĩ" + }, + "fixEncryptionTooltip": { + "message": "Ова Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ĐˇĐ°ŅŅ‚Đ°Ņ€ĐĩĐģи ĐŧĐĩŅ‚ĐžĐ´ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ŅšĐ°." + }, + "attachmentUpdated": { + "message": "ĐŸŅ€Đ¸ĐģĐžĐŗ ҘĐĩ аĐļŅƒŅ€Đ¸Ņ€Đ°ĐŊ" + }, "file": { "message": "Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēа" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "ИСайĐĩŅ€Đ¸ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐē҃." }, + "itemsTransferred": { + "message": "ĐŸŅ€ĐĩĐŊĐĩŅ‚Đĩ ŅŅ‚Đ°Đ˛ĐēĐĩ" + }, "maxFileSize": { "message": "МаĐēŅĐ¸ĐŧаĐģĐŊа вĐĩĐģĐ¸Ņ‡Đ¸ĐŊа ҘĐĩ 500МБ." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1ГБ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐž ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚Đĩ Са ĐŋŅ€Đ¸ĐģĐžĐŗĐĩ." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐž ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚Đĩ Са ĐŋŅ€Đ¸ĐģĐžĐŗĐĩ.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "ĐĨĐ¸Ņ‚Đ°ĐŊ ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋ." }, "premiumSignUpTwoStepOptions": { "message": "ĐŸŅ€Đ¸ĐžŅ€Đ¸Ņ‚Đ°Ņ€ĐŊĐĩ ĐžĐŋŅ†Đ¸Ņ˜Đĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đĩ ҃ два ĐēĐžŅ€Đ°Đēа ĐēаО ŅˆŅ‚Đž ҁ҃ YubiKey и Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "ИСвĐĩŅˆŅ‚Đ°Ņ˜Đ¸ Đž Ņ…Đ¸ĐŗĐ¸Ņ˜ĐĩĐŊи ĐģОСиĐŊĐēи, ĐˇĐ´Ņ€Đ°Đ˛ŅŅ‚Đ˛ĐĩĐŊĐžĐŧ ŅŅ‚Đ°ŅšŅƒ ĐŊаĐģĐžĐŗĐ° и ĐēŅ€ŅˆĐĩҚ҃ ĐŋĐžĐ´Đ°Ņ‚Đ°Đēа да ĐąĐ¸ŅŅ‚Đĩ ĐˇĐ°ŅˆŅ‚Đ¸Ņ‚Đ¸Đģи ҁĐĩŅ„." }, @@ -1874,7 +1950,7 @@ "message": "ГодиĐŊа Đ¸ŅŅ‚ĐĩĐēа" }, "monthly": { - "message": "month" + "message": "ĐŧĐĩҁĐĩ҆" }, "expiration": { "message": "Đ˜ŅŅ‚ĐĩĐē" @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "ĐĸŅ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅĐ°ĐŊа ŅŅ‚Đ°Đ˛Đēа" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Đ’Ņ€Đ°Ņ‚Đ¸ ŅŅ‚Đ°Đ˛Đē҃" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "ĐĐ¸Ņ˜Đĩ ĐŋŅ€ĐžĐŊĐ°Ņ’ĐĩĐŊ ĐŊĐ¸Ņ˜ĐĩдаĐŊ ҘĐĩдиĐŊŅŅ‚Đ˛ĐĩĐŊи идĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžŅ€." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "ГĐģавĐŊа ĐģОСиĐŊĐēа Đ˛Đ¸ŅˆĐĩ ĐŊĐ¸Ņ˜Đĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐŊа Са ҇ĐģаĐŊОвĐĩ ҁĐģĐĩĐ´ĐĩŅ›Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đĩ. МоĐģиĐŧĐž ĐŋĐžŅ‚Đ˛Ņ€Đ´Đ¸Ņ‚Đĩ Đ´ĐžĐŧĐĩĐŊ ŅĐ° адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ĐžĐŧ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đĩ." - }, "organizationName": { "message": "Назив ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đĩ" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Đ“Ņ€Đĩ҈Đēа" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ Đ´ĐĩĐēŅ€Đ¸ĐŋŅ†Đ¸Ņ˜Đ¸" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Đ˜ĐŗĐŊĐžŅ€Đ¸ŅˆĐ¸" }, - "importData": { - "message": "ĐŖĐ˛ĐĩСи ĐŋĐžĐ´Đ°Ņ‚ĐēĐĩ", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ŅƒĐ˛ĐžĐˇŅƒ" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "Đ’Đ¸ŅˆĐĩ ĐžĐŋŅ†Đ¸Ņ˜Đ° - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "АдĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ŅĐēа ĐēĐžĐŊСОĐģа" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "БĐĩСйĐĩĐ´ĐŊĐžŅŅ‚ ĐŊаĐģĐžĐŗĐ°" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "ОбавĐĩŅˆŅ‚ĐĩŅšĐ°" }, @@ -4912,7 +5035,7 @@ "message": "ĐŸŅ€ĐĩĐŧĐ¸Ņ˜ŅƒĐŧ" }, "unlockFeaturesWithPremium": { - "message": "Unlock reporting, emergency access, and more security features with Premium." + "message": "ĐžŅ‚ĐēŅ™ŅƒŅ‡Đ°Ņ˜Ņ‚Đĩ иСвĐĩŅˆŅ‚Đ°Đ˛Đ°ŅšĐĩ, ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋ Ņ…Đ¸Ņ‚ĐŊиĐŧ ҁĐģŅƒŅ‡Đ°Ņ˜ĐĩвиĐŧа и Đ˛Đ¸ŅˆĐĩ ĐąĐĩСйĐĩĐ´ĐŊĐžŅĐŊĐ¸Ņ… Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ° ŅƒĐˇ ĐŸŅ€ĐĩĐŧĐ¸ŅƒĐŧ." }, "freeOrgsCannotUseAttachments": { "message": "БĐĩҁĐŋĐģĐ°Ņ‚ĐŊĐĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đĩ ĐŊĐĩ ĐŧĐžĐŗŅƒ да ĐēĐžŅ€Đ¸ŅŅ‚Đĩ ĐŋŅ€Đ¸ĐģĐžĐŗĐĩ" @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "ХаĐēŅ€Đ¸Ņ˜ ĐžŅ‚ĐēŅ€Đ¸Đ˛Đ°ŅšĐĩ ĐŋĐžĐ´ŅƒĐ´Đ°Ņ€Đ°ŅšĐ° $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "ĐŅƒŅ‚Đž-ĐŋĐžĐŋŅƒŅšĐ°Đ˛Đ°ŅšĐĩ ĐŋŅ€Đ¸ ŅƒŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšŅƒ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đĩ?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Đ’Ņ€ĐģĐž ŅˆĐ¸Ņ€ĐžĐēĐž" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "ЛозиĐŊĐēа ĐēĐžŅ˜Ņƒ ҁ҂Đĩ ҃ĐŊĐĩĐģи ĐŊĐ¸Ņ˜Đĩ Ņ‚Đ°Ņ‡ĐŊа." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Ова ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đ° ҘĐĩ Ņ€Đ¸ĐˇĐ¸Ņ‡ĐŊа и ĐŊĐĩĐ´ĐžŅŅ‚Đ°Ņ˜Đĩ вĐĩĐą ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đ°. Đ”ĐžĐ´Đ°Ņ˜Ņ‚Đĩ вĐĩĐą ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Ņƒ и ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ ĐģОСиĐŊĐē҃ Са Ņ˜Đ°Ņ‡Ņƒ ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "НĐĩĐ´ĐžŅŅ‚Đ°Ņ˜Đĩ вĐĩĐą ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ°" }, @@ -5818,26 +5947,26 @@ "andMoreFeatures": { "message": "И Ņ˜ĐžŅˆ Đ˛Đ¸ŅˆĐĩ!" }, - "planDescPremium": { - "message": "ĐŸĐžŅ‚Đŋ҃ĐŊа ĐžĐŊĐģĐ°Ņ˜ĐŊ ĐąĐĩСйĐĩĐ´ĐŊĐžŅŅ‚" + "advancedOnlineSecurity": { + "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": "ĐŸŅ€ĐĩĐŧĐ¸ŅƒĐŧ ĐŋŅ€ĐĩŅ‚ĐŋĐģĐ°Ņ‚Đ° ваĐŧ Đ´Đ°Ņ˜Đĩ Đ˛Đ¸ŅˆĐĩ аĐģĐ°Ņ‚Đ° да ĐžŅŅ‚Đ°ĐŊĐĩŅ‚Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи и ĐŋОд ĐēĐžĐŊŅ‚Ņ€ĐžĐģĐžĐŧ" }, "explorePremium": { - "message": "Explore Premium" + "message": "ĐŸŅ€ĐĩĐŗĐģĐĩĐ´Đ°Ņ‚Đ¸ ĐŸŅ€ĐĩĐŧĐ¸Ņ˜ŅƒĐŧ" }, "loadingVault": { - "message": "Loading vault" + "message": "ĐŖŅ‡Đ¸Ņ‚Đ°Đ˛Đ°ŅšĐĩ ҁĐĩŅ„Đ°" }, "vaultLoaded": { - "message": "Vault loaded" + "message": "ĐĄĐĩŅ„ ŅƒŅ‡Đ¸Ņ‚Đ°ĐŊ" }, "settingDisabledByPolicy": { "message": "Ово ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐĩ ҘĐĩ ĐžĐŊĐĩĐŧĐžĐŗŅƒŅ›ĐĩĐŊĐž ҁĐŧĐĩŅ€ĐŊĐ¸Ņ†Đ°Đŧа Đ˛Đ°ŅˆĐĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đĩ.", @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Đ‘Ņ€ĐžŅ˜ ĐēĐ°Ņ€Ņ‚Đ¸Ņ†Đĩ" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ° Đ˛Đ¸ŅˆĐĩ ĐŊĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ĐŗĐģавĐŊĐĩ ĐģОСиĐŊĐēĐĩ Са ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ ĐŊа Bitwarden. Да ĐąĐ¸ŅŅ‚Đĩ ĐŊĐ°ŅŅ‚Đ°Đ˛Đ¸Đģи, вĐĩŅ€Đ¸Ņ„Đ¸ĐēŅƒŅ˜Ņ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Ņƒ и Đ´ĐžĐŧĐĩĐŊ." + }, + "continueWithLogIn": { + "message": "ĐĐ°ŅŅ‚Đ°Đ˛Đ¸Ņ‚Đ¸ ŅĐ° ĐŋŅ€Đ¸Ņ˜Đ°Đ˛ĐžĐŧ" + }, + "doNotContinue": { + "message": "НĐĩ ĐŊĐ°ŅŅ‚Đ°Đ˛Đ¸" + }, + "domain": { + "message": "ДоĐŧĐĩĐŊ" + }, + "keyConnectorDomainTooltip": { + "message": "ĐžĐ˛Đ°Ņ˜ Đ´ĐžĐŧĐĩĐŊ Ņ›Đĩ Ņ‡ŅƒĐ˛Đ°Ņ‚Đ¸ ĐēŅ™ŅƒŅ‡ĐĩвĐĩ Са ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ŅšĐĩ Đ˛Đ°ŅˆĐĩĐŗ ĐŊаĐģĐžĐŗĐ°, Đŋа ҁĐĩ ŅƒĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ да Đŧ҃ вĐĩŅ€ŅƒŅ˜ĐĩŅ‚Đĩ. АĐēĐž ĐŊĐ¸ŅŅ‚Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи, ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ ĐēОд ŅĐ˛ĐžĐŗ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°." + }, + "verifyYourOrganization": { + "message": "ВĐĩŅ€Đ¸Ņ„Đ¸ĐēŅƒŅ˜Ņ‚Đĩ ŅĐ˛ĐžŅ˜Ņƒ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Ņƒ да ĐąĐ¸ŅŅ‚Đĩ ҁĐĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Đģи" + }, + "organizationVerified": { + "message": "ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ° вĐĩŅ€Đ¸Ņ„Đ¸ĐēОваĐŊа" + }, + "domainVerified": { + "message": "ДоĐŧĐĩĐŊ вĐĩŅ€Đ¸Ņ„Đ¸ĐēОваĐŊ" + }, + "leaveOrganizationContent": { + "message": "АĐēĐž ĐŊĐĩ вĐĩŅ€Đ¸Ņ„Đ¸Đē҃ҘĐĩŅ‚Đĩ ŅĐ˛ĐžŅ˜Ņƒ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Ņƒ, Đ˛Đ°Ņˆ ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ¸ Ņ›Đĩ ĐąĐ¸Ņ‚Đ¸ ĐžĐŋОСваĐŊ." + }, + "leaveNow": { + "message": "НаĐŋŅƒŅŅ‚Đ¸ ŅĐ°Đ´Đ°" + }, + "verifyYourDomainToLogin": { + "message": "ВĐĩŅ€Đ¸Ņ„Đ¸ĐēŅƒŅ˜Ņ‚Đĩ Đ´ĐžĐŧĐĩĐŊ да ĐąĐ¸ŅŅ‚Đĩ ҁĐĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Đģи" + }, + "verifyYourDomainDescription": { + "message": "Да ĐąĐ¸ŅŅ‚Đĩ ĐŊĐ°ŅŅ‚Đ°Đ˛Đ¸Đģи ŅĐ° ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™Đ¸Đ˛Đ°ŅšĐĩĐŧ, вĐĩŅ€Đ¸Ņ„Đ¸ĐēŅƒŅ˜Ņ‚Đĩ ĐžĐ˛Đ°Ņ˜ Đ´ĐžĐŧĐĩĐŊ." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Да ĐąĐ¸ŅŅ‚Đĩ ĐŊĐ°ŅŅ‚Đ°Đ˛Đ¸Đģи ŅĐ° ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™Đ¸Đ˛Đ°ŅšĐĩĐŧ, вĐĩŅ€Đ¸Ņ„Đ¸ĐēŅƒŅ˜Ņ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Ņƒ и Đ´ĐžĐŧĐĩĐŊ." + }, "sessionTimeoutSettingsAction": { - "message": "Timeout action" + "message": "АĐēŅ†Đ¸Ņ˜Đ° Ņ‚Đ°Ņ˜ĐŧĐ°ŅƒŅ‚Đ°" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "ОвиĐŧ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐĩĐŧ ҃ĐŋŅ€Đ°Đ˛Ņ™Đ° Đ˛Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ°." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ° ҘĐĩ ĐŋОдĐĩŅĐ¸Đģа ĐŧаĐēŅĐ¸ĐŧаĐģĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐēĐž ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩҚĐĩ ҁĐĩŅĐ¸Ņ˜Đĩ ĐŊа $HOURS$ ŅĐ°Ņ‚Đ¸ и $MINUTES$ ĐŧиĐŊŅƒŅ‚Đ°.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ° ҘĐĩ ĐŋĐžŅŅ‚Đ°Đ˛Đ¸Đģа ĐŋĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐēĐž ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩҚĐĩ ҁĐĩŅĐ¸Ņ˜Đĩ ĐŊа ОдĐŧĐ°Ņ…." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ° ҘĐĩ ĐŋĐžŅŅ‚Đ°Đ˛Đ¸Đģа ĐŋĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐēĐž ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩҚĐĩ ҁĐĩŅĐ¸Ņ˜Đĩ ĐŊа БĐģĐžĐēĐ¸Ņ€Đ°ŅšĐĩ ŅĐ¸ŅŅ‚ĐĩĐŧа." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ° ҘĐĩ ĐŋĐžŅŅ‚Đ°Đ˛Đ¸Đģа ĐŋĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐēĐž ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩҚĐĩ ҁĐĩŅĐ¸Ņ˜Đĩ ĐŊа ĐŸŅ€Đ¸ Ņ€ĐĩŅŅ‚Đ°Ņ€Ņ‚ĐžĐ˛Đ°ŅšŅƒ ĐŋŅ€ĐĩĐŗĐģĐĩĐ´Đ°Ņ‡Đ°." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "МаĐēŅĐ¸ĐŧаĐģĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐēĐž ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩҚĐĩ ĐŊĐĩ ĐŧĐžĐļĐĩ да ĐŋŅ€ĐĩŅ’Đĩ $HOURS$ ŅĐ°Ņ‚(а) и $MINUTES$ ĐŧиĐŊŅƒŅ‚(а)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "На ĐŋĐžĐēŅ€ĐĩŅ‚Đ°ŅšĐĩ ĐŋŅ€ĐĩĐŗĐģĐĩĐ´Đ°Ņ‡Đ°" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "ПодĐĩŅĐ¸Ņ‚Đĩ ĐŧĐĩŅ‚ĐžĐ´ ĐžŅ‚ĐēŅ™ŅƒŅ‡Đ°Đ˛Đ°ŅšĐ° да ĐąĐ¸ŅŅ‚Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊиĐģи Ņ€Đ°Đ´ŅšŅƒ Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐēĐžĐŗ ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩŅšĐ°" + }, + "upgrade": { + "message": "ĐĐ°Đ´ĐžĐŗŅ€Đ°Đ´Đ¸" + }, + "leaveConfirmationDialogTitle": { + "message": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ĐŊаĐŋŅƒŅŅ‚Đ¸Ņ‚Đĩ?" + }, + "leaveConfirmationDialogContentOne": { + "message": "АĐēĐž ĐžĐ´ĐąĐ¸Ņ˜ĐĩŅ‚Đĩ, Đ˛Đ°ŅˆĐ¸ ĐģĐ¸Ņ‡ĐŊи ĐŋŅ€ĐĩĐ´ĐŧĐĩŅ‚Đ¸ Ņ›Đĩ ĐžŅŅ‚Đ°Ņ‚Đ¸ ĐŊа Đ˛Đ°ŅˆĐĩĐŧ ĐŊаĐģĐžĐŗŅƒ, аĐģи Ņ›ĐĩŅ‚Đĩ Đ¸ĐˇĐŗŅƒĐąĐ¸Ņ‚Đ¸ ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋ Đ´ĐĩŅ™ĐĩĐŊиĐŧ ŅŅ‚Đ°Đ˛ĐēаĐŧа и ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ĐžĐŊиĐŧ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ°Đŧа." + }, + "leaveConfirmationDialogContentTwo": { + "message": "КоĐŊŅ‚Đ°ĐēŅ‚Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ŅĐ˛ĐžĐŗ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° да ĐąĐ¸ŅŅ‚Đĩ ĐŋĐžĐŊОвО дОйиĐģи ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋ." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "НаĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "КаĐēĐž да ҃ĐŋŅ€Đ°Đ˛Ņ™Đ°Đŧ ŅĐ˛ĐžŅ˜Đ¸Đŧ ҁĐĩŅ„ĐžĐŧ?" + }, + "transferItemsToOrganizationTitle": { + "message": "ĐŸŅ€ĐĩĐŧĐĩŅŅ‚Đ¸ ŅŅ‚Đ°Đ˛ĐēĐĩ ҃ $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ ĐˇĐ°Ņ…Ņ‚Đĩва да ŅĐ˛Đĩ ŅŅ‚Đ°Đ˛ĐēĐĩ ĐąŅƒĐ´Ņƒ ҃ вĐģĐ°ŅĐŊĐ¸ŅˆŅ‚Đ˛Ņƒ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đĩ Ņ€Đ°Đ´Đ¸ ĐąĐĩСйĐĩĐ´ĐŊĐžŅŅ‚Đ¸ и ҃ҁĐēĐģĐ°Ņ’ĐĩĐŊĐžŅŅ‚Đ¸. КĐģиĐēĐŊĐ¸Ņ‚Đĩ ĐŊа ĐŋŅ€Đ¸Ņ…Đ˛Đ°Ņ‚Đ¸ да ĐąĐ¸ŅŅ‚Đĩ ĐŋŅ€ĐĩĐŊĐĩĐģи вĐģĐ°ŅĐŊĐ¸ŅˆŅ‚Đ˛Đž ĐŊад ŅĐ˛ĐžŅ˜Đ¸Đŧ ŅŅ‚Đ°Đ˛ĐēаĐŧа.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "ĐŸŅ€Đ¸Ņ…Đ˛Đ°Ņ‚Đ¸ Ņ‚Ņ€Đ°ĐŊҁ҄ĐĩŅ€" + }, + "declineAndLeave": { + "message": "ĐžĐ´ĐąĐ¸Ņ˜ и ĐŊаĐŋŅƒŅŅ‚Đ¸" + }, + "whyAmISeeingThis": { + "message": "Đ—Đ°ŅˆŅ‚Đž Đ˛Đ¸Đ´Đ¸Ņ‚Đĩ ОвО?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 2c364f20590..ca5984b672e 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Logga in med nyckel" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Använd Single Sign-On" }, @@ -436,8 +439,8 @@ "sync": { "message": "Synkronisera" }, - "syncVaultNow": { - "message": "Synkronisera valv nu" + "syncNow": { + "message": "Synkronisera nu" }, "lastSync": { "message": "Senaste synkronisering:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden webbapp" }, - "importItems": { - "message": "Importera objekt" - }, "select": { "message": "Välj" }, @@ -576,17 +576,29 @@ "itemWasSentToArchive": { "message": "Objektet skickades till arkivet" }, + "itemWasUnarchived": { + "message": "Objektet har avarkiverats" + }, "itemUnarchived": { "message": "Objektet har avarkiverats" }, "archiveItem": { "message": "Arkivera objekt" }, - "archiveItemConfirmDesc": { - "message": "Arkiverade objekt är exkluderade frÃĨn allmänna sÃļkresultat och fÃļrslag fÃļr autofyll. Är du säker pÃĨ att du vill arkivera detta objekt?" + "archiveItemDialogContent": { + "message": "När du har arkiverat kommer detta objekt att uteslutas frÃĨn sÃļkresultat och fÃļrslag till autofyll." + }, + "archived": { + "message": "Arkiverade" + }, + "unarchiveAndSave": { + "message": "Avarkivera och spara" }, "upgradeToUseArchive": { - "message": "A premium membership is required to use Archive." + "message": "Ett premium-medlemskap krävs fÃļr att använda Arkiv." + }, + "itemRestored": { + "message": "Objektet har ÃĨterställts" }, "edit": { "message": "Redigera" @@ -598,7 +610,7 @@ "message": "Visa alla" }, "showAll": { - "message": "Show all" + "message": "Visa alla" }, "viewLess": { "message": "Visa mindre" @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Exportera frÃĨn" }, - "exportVault": { - "message": "Exportera valv" + "exportVerb": { + "message": "Exportera", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Läs mer" }, + "migrationsFailed": { + "message": "Ett fel inträffade när krypteringsinställningarna skulle uppdateras." + }, + "updateEncryptionSettingsTitle": { + "message": "Uppdatera dina krypteringsinställningar" + }, + "updateEncryptionSettingsDesc": { + "message": "De nya rekommenderade krypteringsinställningarna kommer att fÃļrbättra säkerheten fÃļr ditt konto. Ange ditt huvudlÃļsenord fÃļr att uppdatera nu." + }, + "confirmIdentityToContinue": { + "message": "Bekräfta din identitet fÃļr att fortsätta" + }, + "enterYourMasterPassword": { + "message": "Ange ditt huvudlÃļsenord" + }, + "updateSettings": { + "message": "Uppdatera inställningar" + }, + "later": { + "message": "Senare" + }, "authenticatorKeyTotp": { "message": "Autentiseringsnyckel (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Bilaga sparad" }, + "fixEncryption": { + "message": "Fixa kryptering" + }, + "fixEncryptionTooltip": { + "message": "Denna fil använder en fÃļrÃĨldrad krypteringsmetod." + }, + "attachmentUpdated": { + "message": "Bilaga uppdaterad" + }, "file": { "message": "Fil" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Välj en fil" }, + "itemsTransferred": { + "message": "Objekt ÃļverfÃļrda" + }, "maxFileSize": { "message": "Filen fÃĨr vara maximalt 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB lagring av krypterade filer." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ krypterad lagring fÃļr filbilagor.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "NÃļdÃĨtkomst." }, "premiumSignUpTwoStepOptions": { "message": "Premium-alternativ fÃļr tvÃĨstegsverifiering, sÃĨsom YubiKey och Duo." }, + "premiumSubscriptionEnded": { + "message": "Ditt Premium-abonnemang avslutades" + }, + "archivePremiumRestart": { + "message": "FÃļr att ÃĨterfÃĨ ÃĨtkomst till ditt arkiv, starta om Premium-abonnemanget. Om du redigerar detaljer fÃļr ett arkiverat objekt innan du startar om kommer det att flyttas tillbaka till ditt valv." + }, + "restartPremium": { + "message": "Starta om Premium" + }, "ppremiumSignUpReports": { "message": "LÃļsenordshygien, kontohälsa och dataintrÃĨngsrapporter fÃļr att hÃĨlla ditt valv säkert." }, @@ -1874,7 +1950,7 @@ "message": "UtgÃĨngsÃĨr" }, "monthly": { - "message": "month" + "message": "mÃĨnad" }, "expiration": { "message": "UtgÃĨng" @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Raderade objekt permanent" }, + "archivedItemRestored": { + "message": "Arkiverat objekt ÃĨterställt" + }, "restoreItem": { "message": "Återställ objekt" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Ingen unik identifierare hittades." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Ett huvudlÃļsenord krävs inte längre fÃļr medlemmar i fÃļljande organisation. Vänligen bekräfta domänen nedan med din organisationsadministratÃļr." - }, "organizationName": { "message": "Organisationsnamn" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Fel" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Dekrypteringsfel" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignorera" }, - "importData": { - "message": "Importera data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Fel vid import" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "Fler alternativ" + }, "moreOptionsTitle": { "message": "Fler alternativ - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Adminkonsol" }, + "admin": { + "message": "AdministratÃļr" + }, + "automaticUserConfirmation": { + "message": "Automatisk bekräftelse av användare" + }, + "automaticUserConfirmationHint": { + "message": "Bekräfta automatiskt väntande användare medan enheten är olÃĨst" + }, + "autoConfirmOnboardingCallout": { + "message": "Spara tid med automatisk användarbekräftelse" + }, + "autoConfirmWarning": { + "message": "Detta kan pÃĨverka din organisations datasäkerhet. " + }, + "autoConfirmWarningLink": { + "message": "Läs mer om riskerna" + }, + "autoConfirmSetup": { + "message": "Bekräfta nya användare automatiskt" + }, + "autoConfirmSetupDesc": { + "message": "Nya användare kommer automatiskt att bekräftas när denna enhet är upplÃĨst." + }, + "autoConfirmSetupHint": { + "message": "Vilka är de potentiella säkerhetsriskerna?" + }, + "autoConfirmEnabled": { + "message": "Aktiverade automatisk bekräftelse" + }, + "availableNow": { + "message": "Tillgänglig nu" + }, "accountSecurity": { "message": "Kontosäkerhet" }, + "phishingBlocker": { + "message": "Nätfiskeblockerare" + }, + "enablePhishingDetection": { + "message": "Nätfiskedetektering" + }, + "enablePhishingDetectionDesc": { + "message": "Visa varning innan du Ãļppnar misstänkta nätfiskeplatser" + }, "notifications": { "message": "Aviseringar" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Detektering av dold matchning $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Visa matchningsdetektering" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "DÃļlj matchdetektering" }, "autoFillOnPageLoad": { "message": "Autofyll vid sidladdning?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra bred" }, + "narrow": { + "message": "Smal" + }, "sshKeyWrongPassword": { "message": "LÃļsenordet du har angett är felaktigt." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Denna inloggning är utsatt fÃļr risk och saknar en webbplats. Lägg till en webbplats och ändra lÃļsenordet fÃļr Ãļkad säkerhet." }, + "vulnerablePassword": { + "message": "SÃĨrbart lÃļsenord." + }, + "changeNow": { + "message": "Ändra nu" + }, "missingWebsite": { "message": "Saknar webbplats" }, @@ -5818,17 +5947,17 @@ "andMoreFeatures": { "message": "och mer!" }, - "planDescPremium": { - "message": "Komplett säkerhet online" + "advancedOnlineSecurity": { + "message": "Avancerad säkerhet online" }, "upgradeToPremium": { "message": "Uppgradera till Premium" }, "unlockAdvancedSecurity": { - "message": "Unlock advanced security features" + "message": "LÃĨs upp avancerade säkerhetsfunktioner" }, "unlockAdvancedSecurityDesc": { - "message": "A Premium subscription gives you more tools to stay secure and in control" + "message": "En Premium-prenumeration ger dig fler verktyg fÃļr att hÃĨlla dig säker och ha kontroll" }, "explorePremium": { "message": "Utforska Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Kortnummer" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Din organisation använder inte längre huvudlÃļsenord fÃļr att logga in pÃĨ Bitwarden. FÃļr att fortsätta, verifiera organisationen och domänen." + }, + "continueWithLogIn": { + "message": "Fortsätt med inloggning" + }, + "doNotContinue": { + "message": "Fortsätt inte" + }, + "domain": { + "message": "Domän" + }, + "keyConnectorDomainTooltip": { + "message": "Denna domän kommer att lagra dina krypteringsnycklar, sÃĨ se till att du litar pÃĨ den. Om du inte är säker, kontrollera med din administratÃļr." + }, + "verifyYourOrganization": { + "message": "Verifiera din organisation fÃļr att logga in" + }, + "organizationVerified": { + "message": "Organisation verifierad" + }, + "domainVerified": { + "message": "Domän verifierad" + }, + "leaveOrganizationContent": { + "message": "Om du inte verifierar din organisation kommer din ÃĨtkomst till organisationen att ÃĨterkallas." + }, + "leaveNow": { + "message": "Lämna nu" + }, + "verifyYourDomainToLogin": { + "message": "Verifiera din domän fÃļr att logga in" + }, + "verifyYourDomainDescription": { + "message": "FÃļr att fortsätta med inloggning, verifiera denna domän." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "FÃļr att fortsätta logga in, verifiera organisationen och domänen." + }, "sessionTimeoutSettingsAction": { "message": "TidsgränsÃĨtgärd" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Den här inställningen hanteras av din organisation." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Din organisation har ställt in maximal sessionstidsgräns till $HOURS$ timmar och $MINUTES$ minut(er).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Din organisation har ställt in tidsgräns fÃļr standardsessionen till Omedelbart." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Din organisation har ställt in tidsgräns fÃļr standardsessionen till Vid systemlÃĨsning." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Din organisation har ställt in tidsgräns fÃļr standardsessionen till Vid omstart av webbläsaren." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximal tidsgräns fÃĨr inte Ãļverstiga $HOURS$ timmar och $MINUTES$ minut(er)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Vid omstart av webbläsaren" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Ställ in en upplÃĨsningsmetod fÃļr att ändra din tidsgränsÃĨtgärd" + }, + "upgrade": { + "message": "Uppgradera" + }, + "leaveConfirmationDialogTitle": { + "message": "Är du säker pÃĨ att du vill lämna?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Genom att avbÃļja kommer dina personliga objekt att stanna pÃĨ ditt konto, men du kommer att fÃļrlora ÃĨtkomst till delade objekt och organisationsfunktioner." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Kontakta administratÃļren fÃļr att ÃĨterfÃĨ ÃĨtkomst." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Lämna $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Hur hanterar jag mitt valv?" + }, + "transferItemsToOrganizationTitle": { + "message": "ÖverfÃļr objekt till $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ kräver att alla objekt ägs av organisationen fÃļr säkerhet och efterlevnad. Klicka pÃĨ godkänn fÃļr att ÃļverfÃļra ägarskapet fÃļr dina objekt.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Godkänn ÃļverfÃļring" + }, + "declineAndLeave": { + "message": "AvbÃļj och lämna" + }, + "whyAmISeeingThis": { + "message": "VarfÃļr ser jag det här?" + }, + "resizeSideNavigation": { + "message": "Ändra storlek pÃĨ sidnavigering" } } diff --git a/apps/browser/src/_locales/ta/messages.json b/apps/browser/src/_locales/ta/messages.json index 4d185501855..44a284db9c6 100644 --- a/apps/browser/src/_locales/ta/messages.json +++ b/apps/browser/src/_locales/ta/messages.json @@ -28,11 +28,14 @@ "logInWithPasskey": { "message": "āŽĒāŽžāŽ¸ā¯āŽ•ā¯€āŽ¯ā¯āŽŸāŽŠā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽ¯āŽĩā¯āŽŽā¯" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "āŽ’āŽąā¯āŽąā¯ˆ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❈āŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽąā¯āŽĩāŽŠāŽ¤ā¯āŽ¤āŽŋāŽąā¯āŽ•ā¯ āŽ’āŽąā¯āŽąā¯ˆ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ āŽ¤ā¯‡āŽĩ❈." }, "welcomeBack": { "message": "āŽŽā¯€āŽŖā¯āŽŸā¯āŽŽā¯ āŽĩāŽ°ā¯āŽ•" @@ -436,8 +439,8 @@ "sync": { "message": "āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆ" }, - "syncVaultNow": { - "message": "āŽ‡āŽĒā¯āŽĒā¯‹āŽ¤ā¯ āŽĩāŽžāŽ˛ā¯āŽŸā¯āŽŸā¯ˆ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆ" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "āŽ•āŽŸā¯ˆāŽšāŽŋ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽĩ❁:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden āŽĩāŽ˛ā¯ˆ āŽĒāŽ¯āŽŠā¯āŽĒāŽžāŽŸā¯" }, - "importItems": { - "message": "āŽ‰āŽ°ā¯āŽĒā¯āŽĒāŽŸāŽŋāŽ•āŽŗā¯ˆ āŽ‡āŽąāŽ•ā¯āŽ•ā¯āŽŽāŽ¤āŽŋāŽšā¯†āŽ¯ā¯" - }, "select": { "message": "āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯" }, @@ -554,39 +554,51 @@ "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": { + "itemWasUnarchived": { "message": "Item was unarchived" }, - "archiveItem": { - "message": "Archive item" + "itemUnarchived": { + "message": "āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽŽā¯ āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItem": { + "message": "āŽ‰āŽ°ā¯āŽĒā¯āŽĒāŽŸāŽŋāŽ¯ā¯ˆāŽ•ā¯ āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯" + }, + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { - "message": "A premium membership is required to use Archive." + "message": "āŽ•āŽžāŽĒā¯āŽĒāŽ•āŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ āŽĒāŽŋāŽ°ā¯€āŽŽāŽŋāŽ¯āŽŽā¯ āŽ‰āŽąā¯āŽĒā¯āŽĒāŽŋāŽŠāŽ°ā¯ āŽ¤ā¯‡āŽĩ❈." + }, + "itemRestored": { + "message": "Item has been restored" }, "edit": { "message": "āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤ā¯" @@ -595,13 +607,13 @@ "message": "āŽ•āŽžāŽŖā¯" }, "viewAll": { - "message": "View all" + "message": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ•āŽžāŽŖā¯āŽ•" }, "showAll": { - "message": "Show all" + "message": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ˆāŽ¯ā¯āŽŽā¯ āŽ•āŽžāŽŸā¯āŽŸā¯" }, "viewLess": { - "message": "View less" + "message": "āŽ•ā¯āŽąā¯ˆāŽĩāŽžāŽ•āŽ•ā¯ āŽ•āŽžāŽŖā¯āŽ•" }, "viewLogin": { "message": "āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖā¯āŽ•" @@ -749,7 +761,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", @@ -806,10 +818,10 @@ "message": "āŽšāŽŋāŽ¸ā¯āŽŸāŽŽā¯ āŽĒā¯‚āŽŸā¯āŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸāŽĩā¯āŽŸāŽŠā¯" }, "onIdle": { - "message": "On system idle" + "message": "āŽ•āŽŖāŽŋāŽŠāŽŋ āŽšā¯†āŽ¯āŽ˛āŽąā¯āŽą āŽ¨āŽŋāŽ˛ā¯ˆāŽ¯āŽŋāŽ˛ā¯" }, "onSleep": { - "message": "On system sleep" + "message": "āŽ•āŽŖāŽŋāŽŠāŽŋ āŽ‰āŽąāŽ•ā¯āŽ•āŽ¨āŽŋāŽ˛ā¯ˆāŽ¯āŽŋāŽ˛ā¯" }, "onRestart": { "message": "āŽ‰āŽ˛āŽžāŽĩāŽŋ āŽŽāŽąā¯āŽ¤ā¯ŠāŽŸāŽ•ā¯āŽ•āŽŽā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŸāŽĩā¯āŽŸāŽŠā¯" @@ -1050,10 +1062,10 @@ "message": "āŽ‰āŽ°ā¯āŽĒā¯āŽĒāŽŸāŽŋ āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯" }, "savedWebsite": { - "message": "Saved website" + "message": "āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĩāŽ˛ā¯ˆāŽ¤ā¯āŽ¤āŽŗāŽŽā¯" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĩāŽ˛ā¯ˆāŽ¤ā¯āŽ¤āŽŗāŽ™ā¯āŽ•āŽŗā¯ ( $COUNT$ )", "placeholders": { "count": { "content": "$1", @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "āŽ‡āŽ¤āŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽāŽąā¯āŽąā¯āŽŽāŽ¤āŽŋāŽšā¯†āŽ¯ā¯" }, - "exportVault": { - "message": "āŽĩāŽžāŽ˛ā¯āŽŸā¯āŽŸā¯ˆ āŽāŽąā¯āŽąā¯āŽŽāŽ¤āŽŋāŽšā¯†āŽ¯ā¯" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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": "āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽĩāŽŸāŽŋāŽĩāŽŽā¯" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ…āŽąāŽŋāŽ•" }, + "migrationsFailed": { + "message": "āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ• āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽĒāŽŋāŽ´ā¯ˆ āŽāŽąā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯." + }, + "updateEncryptionSettingsTitle": { + "message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ• āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯" + }, + "updateEncryptionSettingsDesc": { + "message": "āŽĒāŽ°āŽŋāŽ¨ā¯āŽ¤ā¯āŽ°ā¯ˆāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ• āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽŖāŽ•ā¯āŽ•ā¯ āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽĒā¯āŽĒ❈ āŽŽā¯‡āŽŽā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽŽā¯. āŽ‡āŽĒā¯āŽĒā¯‹āŽ¤ā¯‡ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ• āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽŽā¯āŽ¤āŽŠā¯āŽŽā¯ˆ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯." + }, + "confirmIdentityToContinue": { + "message": "āŽ¤ā¯ŠāŽŸāŽ° āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽŸā¯ˆāŽ¯āŽžāŽŗāŽ¤ā¯āŽ¤ā¯ˆ āŽ‰āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯" + }, + "enterYourMasterPassword": { + "message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽŽā¯āŽ¤āŽŠā¯āŽŽā¯ˆ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĩā¯āŽŽā¯" + }, + "updateSettings": { + "message": "āŽ…āŽŽā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒā¯āŽ¤ā¯āŽĒā¯āŽĒāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯" + }, + "later": { + "message": "āŽĒāŽŋāŽŠā¯āŽŠāŽ°ā¯" + }, "authenticatorKeyTotp": { "message": "āŽ…āŽ™ā¯āŽ•ā¯€āŽ•āŽ°āŽŋāŽĒā¯āŽĒ❁ āŽĩāŽŋāŽšā¯ˆ (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❁ āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "āŽ•ā¯‹āŽĒā¯āŽĒ❁" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "āŽ’āŽ°ā¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯ˆāŽ¤ā¯ āŽ¤ā¯‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "āŽ…āŽ¤āŽŋāŽ•āŽĒāŽŸā¯āŽš āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽ…āŽŗāŽĩ❁ 500 MB āŽ†āŽ•ā¯āŽŽā¯." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ 1 GB āŽŽāŽŠā¯āŽ•ā¯āŽ°āŽŋāŽĒā¯āŽŸā¯ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ¸ā¯āŽŸā¯‹āŽ°ā¯‡āŽœā¯." }, + "premiumSignUpStorageV2": { + "message": "āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•āŽžāŽŠ $SIZE$ āŽŽāŽąā¯ˆāŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽŋāŽŸāŽŽā¯.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "āŽ…āŽĩāŽšāŽ° āŽ…āŽŖā¯āŽ•āŽ˛ā¯." }, "premiumSignUpTwoStepOptions": { "message": "YubiKey āŽŽāŽąā¯āŽąā¯āŽŽā¯ Duo āŽĒā¯‹āŽŠā¯āŽą āŽĒāŽŋāŽ°āŽ¤ā¯āŽ¯ā¯‡āŽ• āŽŸā¯‚-āŽ¸ā¯āŽŸā¯†āŽĒā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĩāŽžāŽ˛ā¯āŽŸā¯āŽŸā¯ˆāŽĒā¯ āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽĒā¯āŽĒāŽžāŽ• āŽĩā¯ˆāŽ¤ā¯āŽ¤āŽŋāŽ°ā¯āŽ•ā¯āŽ• āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽšā¯āŽ•āŽžāŽ¤āŽžāŽ°āŽŽā¯, āŽ•āŽŖāŽ•ā¯āŽ•āŽŋāŽŠā¯ āŽ†āŽ°ā¯‹āŽ•ā¯āŽ•āŽŋāŽ¯āŽŽā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽŸā¯‡āŽŸā¯āŽŸāŽž āŽŽā¯€āŽąāŽ˛ā¯ āŽ…āŽąāŽŋāŽ•ā¯āŽ•ā¯ˆāŽ•āŽŗā¯." }, @@ -1576,13 +1652,13 @@ "message": "āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽĒā¯āŽĒ❁ āŽĩāŽŋāŽšā¯ˆāŽ¯ā¯ˆāŽĒā¯ āŽĒāŽŸāŽŋ" }, "readingPasskeyLoading": { - "message": "Reading passkey..." + "message": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽąā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽŸāŽŋāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯..." }, "passkeyAuthenticationFailed": { - "message": "Passkey authentication failed" + "message": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽ…āŽ™ā¯āŽ•ā¯€āŽ•āŽžāŽ°āŽŽā¯ āŽ¤ā¯‹āŽ˛ā¯āŽĩāŽŋāŽ¯āŽŸā¯ˆāŽ¨ā¯āŽ¤āŽ¤ā¯" }, "useADifferentLogInMethod": { - "message": "Use a different log in method" + "message": "āŽĩā¯‡āŽąā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ āŽŽā¯āŽąā¯ˆāŽ¯ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯" }, "awaitingSecurityKeyInteraction": { "message": "āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽĒā¯āŽĒ❁ āŽĩāŽŋāŽšā¯ˆ āŽ¤ā¯ŠāŽŸāŽ°ā¯āŽĒā¯āŽ•ā¯ŠāŽŗā¯āŽŗāŽ•ā¯ āŽ•āŽžāŽ¤ā¯āŽ¤āŽŋāŽ°ā¯āŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯..." @@ -1651,7 +1727,7 @@ "message": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯‡āŽ¸ā¯ āŽšāŽ°ā¯āŽĩāŽ°ā¯ URL-āŽ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ•ā¯āŽąā¯ˆāŽ¨ā¯āŽ¤āŽ¤ā¯ āŽ’āŽ°ā¯ āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽ¯āŽŠā¯ āŽšā¯‚āŽ´āŽ˛ā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ•ā¯āŽ• āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯." }, "selfHostedEnvMustUseHttps": { - "message": "URLs must use HTTPS." + "message": "URLāŽ•āŽŗā¯ HTTPS āŽāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯." }, "customEnvironment": { "message": "āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽ¯āŽŠā¯ āŽšā¯‚āŽ´āŽ˛ā¯" @@ -1707,28 +1783,28 @@ "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": "āŽĒāŽŸāŽŋāŽĩāŽĒā¯ āŽĒā¯āŽ˛āŽ™ā¯āŽ•āŽŗāŽŋāŽ˛ā¯ āŽ†āŽŸā¯āŽŸā¯‹āŽƒāŽĒāŽŋāŽ˛ā¯ āŽĒāŽ°āŽŋāŽ¨ā¯āŽ¤ā¯āŽ°ā¯ˆāŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯" }, "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": "āŽĒāŽ°āŽŋāŽ¨ā¯āŽ¤ā¯āŽ°ā¯ˆāŽ•āŽŗāŽžāŽ• āŽ…āŽŸā¯ˆāŽ¯āŽžāŽŗāŽ™ā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŸā¯āŽŸā¯" @@ -1856,7 +1932,7 @@ "message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽĒā¯āŽĒā¯āŽ•ā¯ āŽ•ā¯āŽąāŽŋāŽ¯ā¯€āŽŸā¯āŽŸāŽŋāŽąā¯āŽ•āŽžāŽŠ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽŽāŽŋāŽŠā¯āŽŠāŽžā¯āŽšāŽ˛ā¯ˆāŽšā¯ āŽšāŽ°āŽŋāŽĒāŽžāŽ°ā¯āŽ•ā¯āŽ•, āŽĒāŽžāŽĒā¯āŽ…āŽĒā¯ āŽšāŽžāŽŗāŽ°āŽ¤ā¯āŽ¤āŽŋāŽąā¯āŽ•ā¯ āŽĩā¯†āŽŗāŽŋāŽ¯ā¯‡ āŽ•āŽŋāŽŗāŽŋāŽ•ā¯ āŽšā¯†āŽ¯ā¯āŽĩāŽ¤āŽžāŽ˛ā¯, āŽ‡āŽ¨ā¯āŽ¤ āŽĒāŽžāŽĒā¯āŽ…āŽĒā¯ āŽŽā¯‚āŽŸāŽĒā¯āŽĒāŽŸā¯āŽŽā¯. āŽ‡āŽ¨ā¯āŽ¤ āŽĒāŽžāŽĒā¯āŽ…āŽĒā¯ āŽŽā¯‚āŽŸāŽžāŽŽāŽ˛ā¯ āŽ‡āŽ°ā¯āŽ•ā¯āŽ•, āŽĒā¯āŽ¤āŽŋāŽ¯ āŽšāŽžāŽŗāŽ°āŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ¤āŽŋāŽąāŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?" }, "showIconsChangePasswordUrls": { - "message": "Show website icons and retrieve change password URLs" + "message": "āŽĩāŽ˛ā¯ˆāŽ¤ā¯āŽ¤āŽŗ āŽāŽ•āŽžāŽŠā¯āŽ•āŽŗā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖā¯āŽĒāŽŋ, āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽŽāŽžāŽąā¯āŽąā¯ URLāŽ•āŽŗā¯ˆ āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯" }, "cardholderName": { "message": "āŽ…āŽŸā¯āŽŸā¯ˆāŽ¤āŽžāŽ°āŽ°ā¯ āŽĒā¯†āŽ¯āŽ°ā¯" @@ -1874,7 +1950,7 @@ "message": "āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋ āŽ†āŽŖā¯āŽŸā¯" }, "monthly": { - "message": "month" + "message": "āŽŽāŽžāŽ¤āŽŽā¯" }, "expiration": { "message": "āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋ" @@ -2024,79 +2100,79 @@ "message": "āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒ❁" }, "newItemHeaderLogin": { - "message": "New Login", + "message": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁", "description": "Header for new login item type" }, "newItemHeaderCard": { - "message": "New Card", + "message": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ…āŽŸā¯āŽŸā¯ˆ", "description": "Header for new card item type" }, "newItemHeaderIdentity": { - "message": "New Identity", + "message": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ…āŽŸā¯ˆāŽ¯āŽžāŽŗāŽŽā¯", "description": "Header for new identity item type" }, "newItemHeaderNote": { - "message": "New Note", + "message": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ•ā¯āŽąāŽŋāŽĒā¯āŽĒ❁", "description": "Header for new note item type" }, "newItemHeaderSshKey": { - "message": "New SSH key", + "message": "āŽĒā¯āŽ¤āŽŋāŽ¯ SSH āŽĩāŽŋāŽšā¯ˆ", "description": "Header for new SSH key item type" }, "newItemHeaderTextSend": { - "message": "New Text Send", + "message": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ‰āŽ°ā¯ˆ āŽ…āŽŠā¯āŽĒā¯āŽĒā¯āŽ¤āŽ˛ā¯", "description": "Header for new text send" }, "newItemHeaderFileSend": { - "message": "New File Send", + "message": "āŽĒā¯āŽ¤āŽŋāŽ¯ āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽ…āŽŠā¯āŽĒā¯āŽĒā¯āŽ¤āŽ˛ā¯", "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": "āŽ‰āŽ°ā¯ˆ āŽ…āŽŠā¯āŽĒā¯āŽĒā¯āŽ¤āŽ˛ā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤ā¯", "description": "Header for edit text send" }, "editItemHeaderFileSend": { - "message": "Edit File Send", + "message": "āŽ•ā¯‹āŽĒā¯āŽĒā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽ°ā¯āŽ¤ā¯āŽ¤ā¯ āŽ…āŽŠā¯āŽĒā¯āŽĒ❁", "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": { @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "āŽĒā¯ŠāŽ°ā¯āŽŗā¯ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "āŽĒā¯ŠāŽ°ā¯āŽŗā¯ˆ āŽŽā¯€āŽŸā¯āŽŸāŽŽā¯ˆ" }, @@ -2446,7 +2525,7 @@ } }, "topLayerHijackWarning": { - "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + "message": "āŽ‡āŽ¨ā¯āŽ¤āŽĒā¯ āŽĒāŽ•ā¯āŽ•āŽŽā¯ āŽĒāŽŋāŽŸā¯āŽĩāŽžāŽ°ā¯āŽŸāŽŠā¯ āŽ…āŽŠā¯āŽĒāŽĩāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ•ā¯āŽąā¯āŽ•ā¯āŽ•āŽŋāŽŸā¯āŽ•āŽŋāŽąāŽ¤ā¯. āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽĒā¯āŽĒ❁ āŽ¨āŽŸāŽĩāŽŸāŽŋāŽ•ā¯āŽ•ā¯ˆāŽ¯āŽžāŽ• āŽĒāŽŋāŽŸā¯āŽĩāŽžāŽ°ā¯āŽŸāŽŠā¯ āŽ‡āŽŠā¯āŽ˛ā¯ˆāŽŠā¯ āŽŽā¯†āŽŠā¯ āŽ¤āŽąā¯āŽ•āŽžāŽ˛āŽŋāŽ•āŽŽāŽžāŽ• āŽŽā¯āŽŸāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸā¯āŽŗā¯āŽŗāŽ¤ā¯." }, "setMasterPassword": { "message": "āŽŽā¯āŽ¤āŽŠā¯āŽŽā¯ˆ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽ…āŽŽā¯ˆ" @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "āŽ¤āŽŠāŽŋāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ…āŽŸā¯ˆāŽ¯āŽžāŽŗāŽ™ā¯āŽ•āŽžāŽŸā¯āŽŸāŽŋ āŽŽāŽ¤ā¯āŽĩā¯āŽŽā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "āŽĒāŽŋāŽŠā¯āŽĩāŽ°ā¯āŽŽā¯ āŽ¨āŽŋāŽąā¯āŽĩāŽŠāŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽ‰āŽąā¯āŽĒā¯āŽĒāŽŋāŽŠāŽ°ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ‡āŽŠāŽŋ āŽ’āŽ°ā¯ āŽŽā¯āŽ¤āŽŠā¯āŽŽā¯ˆ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽ¤ā¯‡āŽĩā¯ˆāŽ¯āŽŋāŽ˛ā¯āŽ˛ā¯ˆ. āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽąā¯āŽĩāŽŠ āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ•āŽŋāŽ¯ā¯āŽŸāŽŠā¯ āŽ•ā¯€āŽ´ā¯‡ āŽ‰āŽŗā¯āŽŗ āŽŸā¯ŠāŽŽā¯ˆāŽŠā¯ˆ āŽ‰āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯." - }, "organizationName": { "message": "āŽ¨āŽŋāŽąā¯āŽĩāŽŠ āŽĒā¯†āŽ¯āŽ°ā¯" }, @@ -3274,7 +3350,7 @@ } }, "exportingOrganizationVaultFromPasswordManagerWithDataOwnershipDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported.", + "message": "$ORGANIZATION$ āŽ‰āŽŸāŽŠā¯ āŽ¤ā¯ŠāŽŸāŽ°ā¯āŽĒā¯āŽŸā¯ˆāŽ¯ āŽ¨āŽŋāŽąā¯āŽĩāŽŠ āŽĒā¯†āŽŸā¯āŽŸāŽ•āŽŽā¯ āŽŽāŽŸā¯āŽŸā¯āŽŽā¯‡ āŽāŽąā¯āŽąā¯āŽŽāŽ¤āŽŋ āŽšā¯†āŽ¯ā¯āŽ¯āŽĒā¯āŽĒāŽŸā¯āŽŽā¯.", "placeholders": { "organization": { "content": "$1", @@ -3283,7 +3359,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", @@ -3294,11 +3370,17 @@ "error": { "message": "āŽĒāŽŋāŽ´ā¯ˆ" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽŽā¯ āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯ āŽĒāŽŋāŽ´ā¯ˆ" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "āŽ¤āŽžāŽŠāŽŋāŽ¯āŽ™ā¯āŽ•ā¯āŽ¨āŽŋāŽ°āŽĒā¯āŽĒ❁ āŽ¤āŽ°āŽĩ❈āŽĒā¯ āŽĒā¯†āŽąā¯āŽĩāŽ¤āŽŋāŽ˛ā¯ āŽĒāŽŋāŽ´ā¯ˆ" }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden āŽ•ā¯€āŽ´ā¯‡ āŽĒāŽŸā¯āŽŸāŽŋāŽ¯āŽ˛āŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽĒā¯†āŽŸā¯āŽŸāŽ• āŽĒā¯ŠāŽ°ā¯āŽŗā¯ˆ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽŽā¯ āŽ¨ā¯€āŽ•ā¯āŽ• āŽŽā¯āŽŸāŽŋāŽ¯āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ." @@ -4072,13 +4154,13 @@ "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": "āŽĒāŽ•ā¯āŽ• āŽĩāŽ´āŽŋāŽšā¯†āŽ˛ā¯āŽ¤ā¯āŽ¤āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽąā¯" @@ -4176,10 +4258,6 @@ "ignore": { "message": "āŽĒā¯āŽąāŽ•ā¯āŽ•āŽŖāŽŋ" }, - "importData": { - "message": "āŽ¤āŽ°āŽĩ❈ āŽ‡āŽąāŽ•ā¯āŽ•ā¯āŽŽāŽ¤āŽŋ āŽšā¯†āŽ¯ā¯", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "āŽ‡āŽąāŽ•ā¯āŽ•ā¯āŽŽāŽ¤āŽŋ āŽĒāŽŋāŽ´ā¯ˆ" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ¤ā¯āŽ¤ā¯‡āŽ°ā¯āŽĩā¯āŽ•āŽŗā¯ - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ•āŽ•ā¯ āŽ•āŽŠā¯āŽšā¯‹āŽ˛ā¯" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "āŽ•āŽŖāŽ•ā¯āŽ•ā¯ āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽĒā¯āŽĒ❁" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "āŽ…āŽąāŽŋāŽĩāŽŋāŽĒā¯āŽĒā¯āŽ•āŽŗā¯" }, @@ -4912,7 +5035,7 @@ "message": "āŽĒāŽŋāŽ°ā¯€āŽŽāŽŋāŽ¯āŽŽā¯" }, "unlockFeaturesWithPremium": { - "message": "Unlock reporting, emergency access, and more security features with Premium." + "message": "Premium āŽŽā¯‚āŽ˛āŽŽā¯ āŽ…āŽąāŽŋāŽ•ā¯āŽ•ā¯ˆāŽ¯āŽŋāŽŸāŽ˛ā¯, āŽ…āŽĩāŽšāŽ°āŽ•āŽžāŽ˛ āŽ…āŽŖā¯āŽ•āŽ˛ā¯ āŽŽāŽąā¯āŽąā¯āŽŽā¯ āŽ•ā¯‚āŽŸā¯āŽ¤āŽ˛ā¯ āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽĒā¯āŽĒ❁ āŽ…āŽŽā¯āŽšāŽ™ā¯āŽ•āŽŗā¯ˆāŽ¤ā¯ āŽ¤āŽŋāŽąāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯." }, "freeOrgsCannotUseAttachments": { "message": "āŽ‡āŽ˛āŽĩāŽš āŽ¨āŽŋāŽąā¯āŽĩāŽŠāŽ™ā¯āŽ•āŽŗā¯ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ āŽŽā¯āŽŸāŽŋāŽ¯āŽžāŽ¤ā¯" @@ -4999,7 +5122,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": { @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "āŽĒā¯ŠāŽ°ā¯āŽ¤ā¯āŽ¤āŽŽāŽžāŽŠ āŽ•āŽŖā¯āŽŸāŽąāŽŋāŽ¤āŽ˛ā¯ˆ āŽŽāŽąā¯ˆ $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "āŽĒāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽāŽąā¯āŽąā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽ¤āŽžāŽŠāŽžāŽ• āŽ¨āŽŋāŽ°āŽĒā¯āŽĒ āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽāŽž?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "āŽ…āŽ¤āŽŋāŽ• āŽ…āŽ•āŽ˛āŽŽāŽžāŽŠāŽ¤ā¯" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ‰āŽŗā¯āŽŗāŽŋāŽŸāŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽ¤āŽĩāŽąāŽžāŽŠāŽ¤ā¯." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "āŽ‡āŽ¨ā¯āŽ¤ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ āŽ†āŽĒāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽ‰āŽŗā¯āŽŗāŽ¤ā¯, āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ…āŽ¤āŽŋāŽ˛ā¯ āŽ’āŽ°ā¯ āŽĩāŽ˛ā¯ˆāŽ¤ā¯āŽ¤āŽŗāŽŽā¯āŽŽā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ. āŽĩāŽ˛ā¯āŽĩāŽžāŽŠ āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽĒā¯āŽĒāŽŋāŽąā¯āŽ•āŽžāŽ• āŽ’āŽ°ā¯ āŽĩāŽ˛ā¯ˆāŽ¤ā¯āŽ¤āŽŗāŽ¤ā¯āŽ¤ā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯āŽ¤ā¯āŽ¤ā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "āŽ•āŽžāŽŖāŽžāŽŽāŽ˛ā¯ āŽĒā¯‹āŽŠ āŽĩāŽ˛ā¯ˆāŽ¤ā¯āŽ¤āŽŗāŽŽā¯" }, @@ -5662,30 +5791,30 @@ "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", @@ -5769,10 +5898,10 @@ "description": "Aria label for the body content of the generator nudge" }, "aboutThisSetting": { - "message": "About this setting" + "message": "āŽ‡āŽ¨ā¯āŽ¤ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒ❈āŽĒā¯ āŽĒāŽąā¯āŽąāŽŋ" }, "permitCipherDetailsDescription": { - "message": "Bitwarden will use saved login URIs to identify which icon or change password URL should be used to improve your experience. No information is collected or saved when you use this service." + "message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ…āŽŠā¯āŽĒāŽĩāŽ¤ā¯āŽ¤ā¯ˆ āŽŽā¯‡āŽŽā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ āŽŽāŽ¨ā¯āŽ¤ āŽāŽ•āŽžāŽŠā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽą URL āŽāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯ āŽŽāŽŠā¯āŽĒāŽ¤ā¯ˆ āŽ…āŽŸā¯ˆāŽ¯āŽžāŽŗāŽŽā¯ āŽ•āŽžāŽŖ āŽĒāŽŋāŽŸā¯āŽĩāŽžāŽ°ā¯āŽŸāŽŠā¯ āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ URIāŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽŽā¯. āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ‡āŽ¨ā¯āŽ¤ āŽšā¯‡āŽĩā¯ˆāŽ¯ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯ āŽŽāŽ¨ā¯āŽ¤ āŽ¤āŽ•āŽĩāŽ˛ā¯āŽŽā¯ āŽšā¯‡āŽ•āŽ°āŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽžāŽ¤ā¯ āŽ…āŽ˛ā¯āŽ˛āŽ¤ā¯ āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸāŽžāŽ¤ā¯." }, "noPermissionsViewPage": { "message": "āŽ‡āŽ¨ā¯āŽ¤ āŽĒāŽ•ā¯āŽ•āŽ¤ā¯āŽ¤ā¯ˆāŽ•ā¯ āŽ•āŽžāŽŖ āŽ‰āŽ™ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ…āŽŠā¯āŽŽāŽ¤āŽŋāŽ•āŽŗā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ. āŽĩā¯‡āŽąā¯ āŽ•āŽŖāŽ•ā¯āŽ•ā¯āŽŸāŽŠā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽ¯ āŽŽā¯āŽ¯āŽąā¯āŽšāŽŋāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯." @@ -5798,58 +5927,195 @@ "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": "āŽ‡āŽŠā¯āŽŠāŽŽā¯āŽŽā¯ āŽ…āŽ¤āŽŋāŽ•āŽŽāŽžāŽ•!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "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." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." }, "sessionTimeoutSettingsAction": { - "message": "Timeout action" + "message": "āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋ āŽ¨āŽŸāŽĩāŽŸāŽŋāŽ•ā¯āŽ•ā¯ˆ" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "āŽ‡āŽ¨ā¯āŽ¤ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒ❈ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽąā¯āŽĩāŽŠāŽŽā¯ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽąā¯āŽĩāŽŠāŽŽā¯ āŽ…āŽ¤āŽŋāŽ•āŽĒāŽŸā¯āŽš āŽ…āŽŽāŽ°ā¯āŽĩ❁ āŽ¨ā¯‡āŽ° āŽŽā¯āŽŸāŽŋāŽĩ❈ $HOURS$ āŽŽāŽŖāŽŋāŽ¨ā¯‡āŽ°āŽŽā¯(āŽ•āŽŗā¯) āŽŽāŽąā¯āŽąā¯āŽŽā¯ $MINUTES$ āŽ¨āŽŋāŽŽāŽŋāŽŸāŽŽā¯(āŽ•āŽŗā¯) āŽŽāŽŠ āŽ…āŽŽā¯ˆāŽ¤ā¯āŽ¤ā¯āŽŗā¯āŽŗāŽ¤ā¯.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽąā¯āŽĩāŽŠāŽŽā¯ āŽ‡āŽ¯āŽ˛ā¯āŽĒā¯āŽ¨āŽŋāŽ˛ā¯ˆ āŽ…āŽŽāŽ°ā¯āŽĩ❁ āŽ¨ā¯‡āŽ° āŽŽā¯āŽŸāŽŋāŽĩ❈ āŽ‰āŽŸāŽŠāŽŸāŽŋāŽ¯āŽžāŽ• āŽ…āŽŽā¯ˆāŽ¤ā¯āŽ¤ā¯āŽŗā¯āŽŗāŽ¤ā¯." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽąā¯āŽĩāŽŠāŽŽā¯ āŽ‡āŽ¯āŽ˛ā¯āŽĒā¯āŽ¨āŽŋāŽ˛ā¯ˆ āŽ…āŽŽāŽ°ā¯āŽĩ❁ āŽ¨ā¯‡āŽ° āŽŽā¯āŽŸāŽŋāŽĩ❈ āŽ•āŽŖāŽŋāŽŠāŽŋ āŽĒā¯‚āŽŸā¯āŽŸāŽŋāŽąā¯āŽ•ā¯ āŽ…āŽŽā¯ˆāŽ¤ā¯āŽ¤ā¯āŽŗā¯āŽŗāŽ¤ā¯." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽąā¯āŽĩāŽŠāŽŽā¯ āŽ‡āŽ¯āŽ˛ā¯āŽĒā¯āŽ¨āŽŋāŽ˛ā¯ˆ āŽ…āŽŽāŽ°ā¯āŽĩ❁ āŽ¨ā¯‡āŽ° āŽŽā¯āŽŸāŽŋāŽĩ❈ āŽ‰āŽ˛āŽžāŽĩāŽŋ āŽŽāŽąā¯āŽ¤ā¯ŠāŽŸāŽ•ā¯āŽ•āŽŽā¯ āŽ†āŽŠā¯ āŽŽāŽŠ āŽ…āŽŽā¯ˆāŽ¤ā¯āŽ¤ā¯āŽŗā¯āŽŗāŽ¤ā¯." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "āŽ…āŽ¤āŽŋāŽ•āŽĒāŽŸā¯āŽš āŽ¨ā¯‡āŽ° āŽŽā¯āŽŸāŽŋāŽĩ❁ $HOURS$ āŽŽāŽŖāŽŋāŽ¨ā¯‡āŽ°āŽŽā¯(āŽ•āŽŗā¯) āŽŽāŽąā¯āŽąā¯āŽŽā¯ $MINUTES$ āŽ¨āŽŋāŽŽāŽŋāŽŸāŽŽā¯(āŽ•āŽŗā¯) āŽ āŽĩāŽŋāŽŸ āŽ…āŽ¤āŽŋāŽ•āŽŽāŽžāŽ• āŽ‡āŽ°ā¯āŽ•ā¯āŽ•āŽ•ā¯āŽ•ā¯‚āŽŸāŽžāŽ¤ā¯", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "āŽ‰āŽ˛āŽžāŽĩāŽŋāŽ¯ā¯ˆ āŽŽāŽąā¯āŽ¤ā¯ŠāŽŸāŽ•ā¯āŽ•āŽŽā¯ āŽšā¯†āŽ¯ā¯āŽ¯ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ•āŽžāŽ˛āŽžāŽĩāŽ¤āŽŋ āŽšā¯†āŽ¯āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽą āŽ’āŽ°ā¯ āŽ¤āŽŋāŽąāŽ¤ā¯āŽ¤āŽ˛ā¯ āŽŽā¯āŽąā¯ˆāŽ¯ā¯ˆ āŽ…āŽŽā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index f829937ac51..c15ab367666 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Log in with passkey" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Use single sign-on" }, @@ -436,8 +439,8 @@ "sync": { "message": "Sync" }, - "syncVaultNow": { - "message": "Sync vault now" + "syncNow": { + "message": "Sync now" }, "lastSync": { "message": "Last sync:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web app" }, - "importItems": { - "message": "Import items" - }, "select": { "message": "Select" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { "message": "A premium membership is required to use Archive." }, + "itemRestored": { + "message": "Item has been restored" + }, "edit": { "message": "Edit" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportVerb": { + "message": "Export", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, + "later": { + "message": "Later" + }, "authenticatorKeyTotp": { "message": "Authenticator key (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Attachment saved" }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "file": { "message": "File" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Select a file" }, + "itemsTransferred": { + "message": "Items transferred" + }, "maxFileSize": { "message": "Maximum file size is 500 MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" + }, "ppremiumSignUpReports": { "message": "Password hygiene, account health, and data breach reports to keep your vault safe." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoreItem": { "message": "Restore item" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "No unique identifier found." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Error" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Decryption error" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Ignore" }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Import error" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { "message": "More options - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "Admin Console" }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Account security" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Notifications" }, @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "autoFillOnPageLoad": { "message": "Autofill on page load?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Extra wide" }, + "narrow": { + "message": "Narrow" + }, "sshKeyWrongPassword": { "message": "The password you entered is incorrect." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Card number" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Your organization has set the default session timeout to Immediately." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On browser restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On browser restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index ef6ba5b2077..d41ae49904d 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -1,12 +1,12 @@ { "appName": { - "message": "bitwarden" + "message": "Bitwarden" }, "appLogoLabel": { "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,31 @@ "message": "ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸šā¸ąā¸ā¸Šā¸ĩ" }, "newToBitwarden": { - "message": "āš€ā¸žā¸´āšˆā¸‡āš€ā¸Ŗā¸´āšˆā¸Ąāšƒā¸Šāš‰ Bitwarden āšƒā¸Šāšˆāš„ā¸Ģā¸Ą?" + "message": "āš€ā¸žā¸´āšˆā¸‡āš€ā¸„ā¸ĸāšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ Bitwarden āšƒā¸Šāšˆā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ" }, "logInWithPasskey": { "message": "āš€ā¸‚āš‰ā¸˛ā¸Ēā¸šāšˆā¸Ŗā¸°ā¸šā¸šā¸”āš‰ā¸§ā¸ĸā¸žā¸˛ā¸Ē⏄ā¸ĩā¸ĸāšŒ" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "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 +56,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 +84,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 +99,10 @@ } }, "joinOrganization": { - "message": "Join organization" + "message": "āš€ā¸‚āš‰ā¸˛ā¸Ŗāšˆā¸§ā¸Ąā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗ" }, "joinOrganizationName": { - "message": "Join $ORGANIZATIONNAME$", + "message": "āš€ā¸‚āš‰ā¸˛ā¸Ŗāšˆā¸§ā¸Ą $ORGANIZATIONNAME$", "placeholders": { "organizationName": { "content": "$1", @@ -108,7 +111,7 @@ } }, "finishJoiningThisOrganizationBySettingAMasterPassword": { - "message": "Finish joining this organization by setting a master password." + "message": "ā¸•ā¸ąāš‰ā¸‡ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸Ģā¸Ĩā¸ąā¸āš€ā¸žā¸ˇāšˆā¸­āš€ā¸Ēā¸Ŗāš‡ā¸ˆā¸Ēā¸´āš‰ā¸™ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ŗāšˆā¸§ā¸Ąā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸™ā¸ĩāš‰" }, "tab": { "message": "āšā¸—āš‡ā¸š" @@ -117,7 +120,7 @@ "message": "ā¸•ā¸šāš‰ā¸™ā¸´ā¸Ŗā¸ ā¸ąā¸ĸ" }, "myVault": { - "message": "My Vault" + "message": "ā¸•ā¸šāš‰ā¸™ā¸´ā¸Ŗā¸ ā¸ąā¸ĸā¸‚ā¸­ā¸‡ā¸‰ā¸ąā¸™" }, "allVaults": { "message": "ā¸•ā¸šāš‰ā¸™ā¸´ā¸Ŗā¸ ā¸ąā¸ĸā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”" @@ -135,10 +138,10 @@ "message": "ā¸„ā¸ąā¸”ā¸Ĩ⏭⏁⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™" }, "copyPassphrase": { - "message": "Copy passphrase" + "message": "ā¸„ā¸ąā¸”ā¸Ĩ⏭⏁⏧ā¸Ĩā¸ĩ⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™" }, "copyNote": { - "message": "Copy Note" + "message": "ā¸„ā¸ąā¸”ā¸Ĩā¸­ā¸āš‚ā¸™āš‰ā¸•" }, "copyUri": { "message": "ā¸„ā¸ąā¸”ā¸Ĩ⏭⏁ URI" @@ -150,34 +153,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 +189,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 +389,7 @@ "message": "āšā¸āš‰āš„ā¸‚āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒ" }, "editFolderWithName": { - "message": "Edit folder: $FOLDERNAME$", + "message": "āšā¸āš‰āš„ā¸‚āš‚ā¸Ÿā¸Ĩāš€ā¸”ā¸­ā¸ŖāšŒ: $FOLDERNAME$", "placeholders": { "foldername": { "content": "$1", @@ -395,22 +398,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,68 +422,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": "ā¸‹ā¸´ā¸‡ā¸„āšŒ" }, - "syncVaultNow": { - "message": "Sync Vault Now" + "syncNow": { + "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" - }, - "importItems": { - "message": "Import Items" + "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": "ā¸•ā¸ąā¸§āš€ā¸Ĩ⏎⏭⏁" @@ -489,11 +489,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": { @@ -501,7 +501,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": { @@ -509,7 +509,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": { @@ -517,100 +517,112 @@ "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": { + "itemWasUnarchived": { "message": "Item was unarchived" }, - "archiveItem": { - "message": "Archive item" + "itemUnarchived": { + "message": "āš€ā¸Ĩā¸´ā¸ā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šā¸–ā¸˛ā¸§ā¸Ŗā¸Ŗā¸˛ā¸ĸā¸ā¸˛ā¸Ŗāšā¸Ĩāš‰ā¸§" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItem": { + "message": "ā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šā¸Ŗā¸˛ā¸ĸ⏁⏞⏪⏖⏞⏧⏪" + }, + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Archived" + }, + "unarchiveAndSave": { + "message": "Unarchive and save" }, "upgradeToUseArchive": { - "message": "A premium membership is required to use Archive." + "message": "ā¸•āš‰ā¸­ā¸‡āš€ā¸›āš‡ā¸™ā¸Ēā¸Ąā¸˛ā¸Šā¸´ā¸ā¸žā¸Ŗā¸ĩāš€ā¸Ąā¸ĩā¸ĸā¸Ąā¸ˆā¸ļā¸‡ā¸ˆā¸°āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸Ÿā¸ĩāš€ā¸ˆā¸­ā¸ŖāšŒā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šā¸–ā¸˛ā¸§ā¸Ŗāš„ā¸”āš‰" + }, + "itemRestored": { + "message": "Item has been restored" }, "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": "ā¸Šā¸ˇāšˆā¸­ā¸œā¸šāš‰āšƒā¸Šāš‰" @@ -619,28 +631,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": "āš‚ā¸™āš‰ā¸•" @@ -658,13 +670,13 @@ "message": "ā¸”ā¸šā¸Ŗā¸˛ā¸ĸ⏁⏞⏪" }, "launch": { - "message": "āš€ā¸Ŗā¸´āšˆā¸Ą" + "message": "āš€ā¸›ā¸´ā¸”" }, "launchWebsite": { - "message": "Launch website" + "message": "āš€ā¸›ā¸´ā¸”āš€ā¸§āš‡ā¸šāš„ā¸‹ā¸•āšŒ" }, "launchWebsiteName": { - "message": "Launch website $ITEMNAME$", + "message": "āš€ā¸›ā¸´ā¸”āš€ā¸§āš‡ā¸šāš„ā¸‹ā¸•āšŒ $ITEMNAME$", "placeholders": { "itemname": { "content": "$1", @@ -676,7 +688,7 @@ "message": "āš€ā¸§āš‡ā¸šāš„ā¸‹ā¸•āšŒ" }, "toggleVisibility": { - "message": "Toggle Visibility" + "message": "ā¸Ēā¸Ĩā¸ąā¸šā¸ā¸˛ā¸Ŗāšā¸Ēā¸”ā¸‡ā¸œā¸Ĩ" }, "manage": { "message": "ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗ" @@ -685,55 +697,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", @@ -749,7 +761,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", @@ -758,16 +770,16 @@ } }, "vaultTimeout": { - "message": "⏪⏰ā¸ĸā¸°āš€ā¸§ā¸Ĩ⏞ā¸Ĩāš‡ā¸­ā¸ā¸•ā¸šāš‰āš€ā¸‹ā¸Ÿ" + "message": "⏪⏰ā¸ĸā¸°āš€ā¸§ā¸Ĩ⏞ā¸Ģā¸Ąā¸”āš€ā¸§ā¸Ĩā¸˛ā¸•ā¸šāš‰ā¸™ā¸´ā¸Ŗā¸ ā¸ąā¸ĸ" }, "vaultTimeout1": { - "message": "Timeout" + "message": "ā¸Ģā¸Ąā¸”āš€ā¸§ā¸Ĩ⏞" }, "lockNow": { - "message": "Lock Now" + "message": "ā¸Ĩāš‡ā¸­ā¸ā¸—ā¸ąā¸™ā¸—ā¸ĩ" }, "lockAll": { - "message": "Lock all" + "message": "ā¸Ĩāš‡ā¸­ā¸ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”" }, "immediately": { "message": "ā¸—ā¸ąā¸™ā¸—ā¸ĩ" @@ -803,52 +815,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": { @@ -858,37 +870,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": { @@ -898,127 +910,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": "ā¸„ā¸ąā¸”ā¸Ĩ⏭⏁⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āšā¸Ĩāš‰ā¸§" @@ -1037,23 +1049,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", @@ -1062,88 +1074,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" @@ -1152,18 +1164,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": { @@ -1173,15 +1185,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": { @@ -1193,35 +1205,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" @@ -1230,7 +1242,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" @@ -1242,77 +1254,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": "ā¸Ąā¸ˇā¸”", @@ -1323,72 +1335,85 @@ "description": "Light color" }, "exportFrom": { - "message": "Export from" + "message": "ā¸Ēāšˆā¸‡ā¸­ā¸­ā¸ā¸ˆā¸˛ā¸" }, - "exportVault": { - "message": "Export Vault" + "exportVerb": { + "message": "ā¸Ēāšˆā¸‡ā¸­ā¸­ā¸", + "description": "The verb form of the word Export" + }, + "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,19 +1426,40 @@ } }, "moveToOrgDesc": { - "message": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸—ā¸ĩāšˆā¸„ā¸¸ā¸“ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸ĸāš‰ā¸˛ā¸ĸ⏪⏞ā¸ĸ⏁⏞⏪⏙ā¸ĩāš‰āš„ā¸› ⏁⏞⏪ā¸ĸāš‰ā¸˛ā¸ĸāš„ā¸›ā¸ĸā¸ąā¸‡ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸ˆā¸°āš‚ā¸­ā¸™ā¸„ā¸§ā¸˛ā¸Ąāš€ā¸›āš‡ā¸™āš€ā¸ˆāš‰ā¸˛ā¸‚ā¸­ā¸‡ā¸Ŗā¸˛ā¸ĸā¸ā¸˛ā¸Ŗāš„ā¸›ā¸ĸā¸ąā¸‡ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸™ā¸ąāš‰ā¸™ ā¸„ā¸¸ā¸“ā¸ˆā¸°āš„ā¸Ąāšˆāš„ā¸”āš‰āš€ā¸›āš‡ā¸™āš€ā¸ˆāš‰ā¸˛ā¸‚ā¸­ā¸‡āš‚ā¸”ā¸ĸ⏕⏪⏇⏂⏭⏇⏪⏞ā¸ĸ⏁⏞⏪⏙ā¸ĩāš‰ā¸­ā¸ĩā¸ā¸•āšˆā¸­āš„ā¸›āš€ā¸Ąā¸ˇāšˆā¸­ā¸Ąā¸ĩ⏁⏞⏪ā¸ĸāš‰ā¸˛ā¸ĸāšā¸Ĩāš‰ā¸§" + "message": "āš€ā¸Ĩā¸ˇā¸­ā¸ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸—ā¸ĩāšˆā¸„ā¸¸ā¸“ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸ĸāš‰ā¸˛ā¸ĸ⏪⏞ā¸ĸ⏁⏞⏪⏙ā¸ĩāš‰āš„ā¸› ⏁⏞⏪ā¸ĸāš‰ā¸˛ā¸ĸāš„ā¸›ā¸—ā¸ĩāšˆā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸ˆā¸°āš‚ā¸­ā¸™ā¸ā¸Ŗā¸Ŗā¸Ąā¸Ēā¸´ā¸—ā¸˜ā¸´āšŒā¸‚ā¸­ā¸‡ā¸Ŗā¸˛ā¸ĸā¸ā¸˛ā¸Ŗā¸™ā¸ąāš‰ā¸™āš„ā¸›ā¸ĸā¸ąā¸‡ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗ ā¸„ā¸¸ā¸“ā¸ˆā¸°āš„ā¸Ąāšˆāš€ā¸›āš‡ā¸™āš€ā¸ˆāš‰ā¸˛ā¸‚ā¸­ā¸‡āš‚ā¸”ā¸ĸ⏕⏪⏇⏂⏭⏇⏪⏞ā¸ĸ⏁⏞⏪⏙ā¸ĩāš‰ā¸­ā¸ĩā¸ā¸•āšˆā¸­āš„ā¸›ā¸Ģā¸Ĩā¸ąā¸‡ā¸ˆā¸˛ā¸ā¸ĸāš‰ā¸˛ā¸ĸāšā¸Ĩāš‰ā¸§" }, "learnMore": { "message": "āš€ā¸Ŗā¸ĩā¸ĸā¸™ā¸Ŗā¸šāš‰āš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ą" }, + "migrationsFailed": { + "message": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩā¸˛ā¸”āšƒā¸™ā¸ā¸˛ā¸Ŗā¸­ā¸ąā¸›āš€ā¸”ā¸•ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸Ģā¸ąā¸Ē" + }, + "updateEncryptionSettingsTitle": { + "message": "ā¸­ā¸ąā¸›āš€ā¸”ā¸•ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸Ģā¸ąā¸Ē" + }, + "updateEncryptionSettingsDesc": { + "message": "ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸Ģā¸ąā¸Ēāšƒā¸Ģā¸Ąāšˆā¸—ā¸ĩāšˆāšā¸™ā¸°ā¸™ā¸ŗā¸ˆā¸°ā¸Šāšˆā¸§ā¸ĸā¸›ā¸Ŗā¸ąā¸šā¸›ā¸Ŗā¸¸ā¸‡ā¸„ā¸§ā¸˛ā¸Ąā¸›ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸā¸‚ā¸­ā¸‡ā¸šā¸ąā¸ā¸Šā¸ĩ ā¸›āš‰ā¸­ā¸™ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸Ģā¸Ĩā¸ąā¸āš€ā¸žā¸ˇāšˆā¸­ā¸­ā¸ąā¸›āš€ā¸”ā¸•ā¸—ā¸ąā¸™ā¸—ā¸ĩ" + }, + "confirmIdentityToContinue": { + "message": "ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸•ā¸ąā¸§ā¸•ā¸™āš€ā¸žā¸ˇāšˆā¸­ā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸•āšˆā¸­" + }, + "enterYourMasterPassword": { + "message": "ā¸›āš‰ā¸­ā¸™ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸Ģā¸Ĩā¸ąā¸" + }, + "updateSettings": { + "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": "āš„ā¸Ÿā¸ĨāšŒāšā¸™ā¸š" @@ -1422,7 +1468,7 @@ "message": "ā¸Ĩā¸šāš„ā¸Ÿā¸ĨāšŒāšā¸™ā¸š" }, "deleteAttachmentConfirmation": { - "message": "ā¸„ā¸¸ā¸“ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸Ĩā¸šāš„ā¸Ÿā¸ĨāšŒāšā¸™ā¸šā¸™ā¸ĩāš‰āšƒā¸Šāšˆā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ?" + "message": "ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸—ā¸ĩāšˆā¸ˆā¸°ā¸Ĩā¸šāš„ā¸Ÿā¸ĨāšŒāšā¸™ā¸šā¸™ā¸ĩāš‰ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ" }, "deletedAttachment": { "message": "ā¸Ĩā¸šāš„ā¸Ÿā¸ĨāšŒāšā¸™ā¸šāšā¸Ĩāš‰ā¸§" @@ -1436,80 +1482,110 @@ "attachmentSaved": { "message": "ā¸šā¸ąā¸™ā¸—ā¸ļā¸āš„ā¸Ÿā¸ĨāšŒāšā¸™ā¸šāšā¸Ĩāš‰ā¸§" }, + "fixEncryption": { + "message": "ā¸‹āšˆā¸­ā¸Ąāšā¸‹ā¸Ąā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸Ģā¸ąā¸Ē" + }, + "fixEncryptionTooltip": { + "message": "āš„ā¸Ÿā¸ĨāšŒā¸™ā¸ĩāš‰āšƒā¸Šāš‰ā¸§ā¸´ā¸˜ā¸ĩā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸Ģā¸ąā¸Ē⏗ā¸ĩāšˆā¸Ĩāš‰ā¸˛ā¸Ēā¸Ąā¸ąā¸ĸ" + }, + "attachmentUpdated": { + "message": "ā¸­ā¸ąā¸›āš€ā¸”ā¸•āš„ā¸Ÿā¸ĨāšŒāšā¸™ā¸šāšā¸Ĩāš‰ā¸§" + }, "file": { "message": "āš„ā¸Ÿā¸ĨāšŒ" }, "fileToShare": { - "message": "File to share" + "message": "āš„ā¸Ÿā¸ĨāšŒā¸—ā¸ĩāšˆā¸ˆā¸°āšā¸Šā¸ŖāšŒ" }, "selectFile": { "message": "āš€ā¸Ĩā¸ˇā¸­ā¸āš„ā¸Ÿā¸ĨāšŒ" }, + "itemsTransferred": { + "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$", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } }, "premiumSignUpEmergency": { - "message": "Emergency access." + "message": "ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡ā¸‰ā¸¸ā¸āš€ā¸‰ā¸´ā¸™" }, "premiumSignUpTwoStepOptions": { - "message": "Proprietary two-step login options such as YubiKey and Duo." + "message": "ā¸•ā¸ąā¸§āš€ā¸Ĩā¸ˇā¸­ā¸ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ēā¸šāšˆā¸Ŗā¸°ā¸šā¸š 2 ā¸‚ā¸ąāš‰ā¸™ā¸•ā¸­ā¸™āšā¸šā¸šā¸žā¸´āš€ā¸¨ā¸Š āš€ā¸Šāšˆā¸™ YubiKey āšā¸Ĩ⏰ Duo" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "archivePremiumRestart": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it'll be moved back into your vault." + }, + "restartPremium": { + "message": "Restart Premium" }, "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", @@ -1518,7 +1594,7 @@ } }, "premiumPriceV2": { - "message": "All for just $PRICE$ per year!", + "message": "ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”ā¸™ā¸ĩāš‰āš€ā¸žā¸ĩā¸ĸ⏇ $PRICE$ ā¸•āšˆā¸­ā¸›ā¸ĩ!", "placeholders": { "price": { "content": "$1", @@ -1527,25 +1603,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", @@ -1554,148 +1630,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", @@ -1704,162 +1780,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": "ā¸Ģā¸Ąā¸˛ā¸ĸāš€ā¸Ĩ⏂" @@ -1868,13 +1944,13 @@ "message": "āšā¸šā¸Ŗā¸™ā¸”āšŒ" }, "expirationMonth": { - "message": "Expiration Month" + "message": "āš€ā¸”ā¸ˇā¸­ā¸™ā¸—ā¸ĩāšˆā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸ⏏" }, "expirationYear": { - "message": "Expiration Year" + "message": "⏛ā¸ĩ⏗ā¸ĩāšˆā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸ⏏" }, "monthly": { - "message": "month" + "message": "āš€ā¸”ā¸ˇā¸­ā¸™" }, "expiration": { "message": "ā¸§ā¸ąā¸™ā¸Ģā¸Ąā¸”ā¸­ā¸˛ā¸ĸ⏏" @@ -1916,13 +1992,13 @@ "message": "ā¸˜ā¸ąā¸™ā¸§ā¸˛ā¸„ā¸Ą" }, "securityCode": { - "message": "Security Code" + "message": "⏪ā¸Ģā¸ąā¸Ēā¸„ā¸§ā¸˛ā¸Ąā¸›ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸ" }, "cardNumber": { - "message": "card number" + "message": "ā¸Ģā¸Ąā¸˛ā¸ĸāš€ā¸Ĩā¸‚ā¸šā¸ąā¸•ā¸Ŗ" }, "ex": { - "message": "ex." + "message": "ā¸•ā¸ąā¸§ā¸­ā¸ĸāšˆā¸˛ā¸‡" }, "title": { "message": "⏄⏺⏙⏺ā¸Ģā¸™āš‰ā¸˛" @@ -1940,22 +2016,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": "ā¸šā¸Ŗā¸´ā¸Šā¸ąā¸—" @@ -1967,7 +2043,7 @@ "message": "ā¸Ģā¸Ąā¸˛ā¸ĸāš€ā¸Ĩ⏂ā¸Ģā¸™ā¸ąā¸‡ā¸Ēā¸ˇā¸­āš€ā¸”ā¸´ā¸™ā¸—ā¸˛ā¸‡" }, "licenseNumber": { - "message": "ā¸Ģā¸Ąā¸˛ā¸ĸāš€ā¸Ĩā¸‚āšƒā¸šā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•" + "message": "ā¸Ģā¸Ąā¸˛ā¸ĸāš€ā¸Ĩā¸‚āšƒā¸šā¸‚ā¸ąā¸šā¸‚ā¸ĩāšˆ" }, "email": { "message": "⏭ā¸ĩāš€ā¸Ąā¸Ĩ" @@ -1988,7 +2064,7 @@ "message": "⏗ā¸ĩāšˆā¸­ā¸ĸā¸šāšˆ 3" }, "cityTown": { - "message": "āš€ā¸Ąā¸ˇā¸­ā¸‡" + "message": "āš€ā¸Ąā¸ˇā¸­ā¸‡ / ā¸•ā¸ŗā¸šā¸Ĩ" }, "stateProvince": { "message": "ā¸Ŗā¸ąā¸ / ā¸ˆā¸ąā¸‡ā¸Ģā¸§ā¸ąā¸”" @@ -2000,116 +2076,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": "ā¸ĸāš‰ā¸­ā¸™ā¸ā¸Ĩā¸ąā¸š" @@ -2118,7 +2194,7 @@ "message": "⏄⏭ā¸Ĩāš€ā¸Ĩā¸ā¸Šā¸ąā¸™" }, "nCollections": { - "message": "$COUNT$ collections", + "message": "$COUNT$ ⏄⏭ā¸Ĩāš€ā¸Ĩā¸ā¸Šā¸ąā¸™", "placeholders": { "count": { "content": "$1", @@ -2130,7 +2206,7 @@ "message": "⏪⏞ā¸ĸā¸ā¸˛ā¸Ŗāš‚ā¸›ā¸Ŗā¸”" }, "popOutNewWindow": { - "message": "āš€ā¸›ā¸´ā¸”ā¸Ģā¸™āš‰ā¸˛ā¸•āšˆā¸˛ā¸‡āšƒā¸Ģā¸Ąāšˆ" + "message": "āšā¸ĸ⏁ā¸Ģā¸™āš‰ā¸˛ā¸•āšˆā¸˛ā¸‡āšƒā¸Ģā¸Ąāšˆ" }, "refresh": { "message": "⏪ā¸ĩāš€ā¸Ÿā¸Ŗā¸Š" @@ -2142,23 +2218,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", @@ -2167,14 +2243,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": { @@ -2186,25 +2262,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 ā¸›ā¸ąā¸ˆā¸ˆā¸¸ā¸šā¸ąā¸™", @@ -2219,7 +2295,7 @@ "description": "An entity of multiple related people (ex. a team or business organization)." }, "types": { - "message": "ā¸Šā¸™ā¸´ā¸”" + "message": "ā¸›ā¸Ŗā¸°āš€ā¸ ā¸—" }, "allItems": { "message": "⏪⏞ā¸ĸā¸ā¸˛ā¸Ŗā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”" @@ -2228,64 +2304,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", @@ -2295,43 +2371,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": "ā¸„ā¸¸ā¸“ā¸•āš‰ā¸­ā¸‡āš€ā¸Ĩ⏎⏭⏁⏭ā¸ĸāšˆā¸˛ā¸‡ā¸™āš‰ā¸­ā¸ĸā¸Ģ⏙ā¸ļāšˆā¸‡ā¸„ā¸­ā¸Ĩāš€ā¸Ĩā¸ā¸Šā¸ąā¸™" @@ -2343,42 +2419,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": "ā¸Ĩāš‡ā¸­ā¸", @@ -2392,52 +2468,55 @@ "message": "ā¸„āš‰ā¸™ā¸Ģā¸˛āšƒā¸™ā¸–ā¸ąā¸‡ā¸‚ā¸ĸ⏰" }, "permanentlyDeleteItem": { - "message": "ā¸Ĩ⏚⏪⏞ā¸ĸ⏁⏞⏪⏭ā¸ĸāšˆā¸˛ā¸‡ā¸–ā¸˛ā¸§ā¸Ŗ" + "message": "ā¸Ĩ⏚⏪⏞ā¸ĸ⏁⏞⏪⏖⏞⏧⏪" }, "permanentlyDeleteItemConfirmation": { - "message": "ā¸„ā¸¸ā¸“āšā¸™āšˆāšƒā¸ˆā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆā¸§āšˆā¸˛ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸Ĩ⏚⏪⏞ā¸ĸ⏁⏞⏪⏙ā¸ĩāš‰ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸–ā¸˛ā¸§ā¸Ŗ?" + "message": "ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸—ā¸ĩāšˆā¸ˆā¸°ā¸Ĩ⏚⏪⏞ā¸ĸ⏁⏞⏪⏙ā¸ĩāš‰ā¸–ā¸˛ā¸§ā¸Ŗā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ" }, "permanentlyDeletedItem": { - "message": "ā¸Ĩ⏚⏪⏞ā¸ĸ⏁⏞⏪⏭ā¸ĸāšˆā¸˛ā¸‡ā¸–ā¸˛ā¸§ā¸Ŗāšā¸Ĩāš‰ā¸§" + "message": "ā¸Ĩ⏚⏪⏞ā¸ĸā¸ā¸˛ā¸Ŗā¸–ā¸˛ā¸§ā¸Ŗāšā¸Ĩāš‰ā¸§" + }, + "archivedItemRestored": { + "message": "Archived item restored" }, "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", @@ -2446,22 +2525,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$", @@ -2473,7 +2552,7 @@ } }, "policyInEffectMinLength": { - "message": "ā¸„ā¸§ā¸˛ā¸Ąā¸ĸ⏞⏧⏭ā¸ĸāšˆā¸˛ā¸‡ā¸™āš‰ā¸­ā¸ĸ $LENGTH$ ā¸­ā¸ąā¸ā¸‚ā¸Ŗā¸°", + "message": "ā¸„ā¸§ā¸˛ā¸Ąā¸ĸā¸˛ā¸§ā¸‚ā¸ąāš‰ā¸™ā¸•āšˆā¸ŗ $LENGTH$ ā¸•ā¸ąā¸§ā¸­ā¸ąā¸ā¸Šā¸Ŗ", "placeholders": { "length": { "content": "$1", @@ -2482,16 +2561,16 @@ } }, "policyInEffectUppercase": { - "message": "ā¸Ąā¸ĩā¸•ā¸ąā¸§ā¸žā¸´ā¸Ąā¸žāšŒāšƒā¸Ģā¸āšˆā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸™āš‰ā¸­ā¸ĸ 1 ā¸•ā¸ąā¸§" + "message": "ā¸Ąā¸ĩā¸•ā¸ąā¸§ā¸­ā¸ąā¸ā¸Šā¸Ŗā¸žā¸´ā¸Ąā¸žāšŒāšƒā¸Ģā¸āšˆā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸™āš‰ā¸­ā¸ĸā¸Ģ⏙ā¸ļāšˆā¸‡ā¸•ā¸ąā¸§" }, "policyInEffectLowercase": { - "message": "ā¸Ąā¸ĩā¸•ā¸ąā¸§ā¸žā¸´ā¸Ąā¸žāšŒāš€ā¸Ĩāš‡ā¸ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸™āš‰ā¸­ā¸ĸ 1 ā¸•ā¸ąā¸§" + "message": "ā¸Ąā¸ĩā¸•ā¸ąā¸§ā¸­ā¸ąā¸ā¸Šā¸Ŗā¸žā¸´ā¸Ąā¸žāšŒāš€ā¸Ĩāš‡ā¸ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸™āš‰ā¸­ā¸ĸā¸Ģ⏙ā¸ļāšˆā¸‡ā¸•ā¸ąā¸§" }, "policyInEffectNumbers": { - "message": "ā¸Ąā¸ĩā¸•ā¸ąā¸§āš€ā¸Ĩ⏂⏭ā¸ĸāšˆā¸˛ā¸‡ā¸™āš‰ā¸­ā¸ĸ 1 ā¸•ā¸ąā¸§" + "message": "ā¸Ąā¸ĩā¸•ā¸ąā¸§āš€ā¸Ĩ⏂⏭ā¸ĸāšˆā¸˛ā¸‡ā¸™āš‰ā¸­ā¸ĸā¸Ģ⏙ā¸ļāšˆā¸‡ā¸•ā¸ąā¸§" }, "policyInEffectSpecial": { - "message": "ā¸Ąā¸ĩā¸­ā¸ąā¸ā¸‚ā¸Ŗā¸°ā¸žā¸´āš€ā¸¨ā¸Šā¸•āšˆā¸­āš„ā¸›ā¸™ā¸ĩāš‰ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸™āš‰ā¸­ā¸ĸ 1 ā¸­ā¸ąā¸ā¸‚ā¸Ŗā¸°: $CHARS$ ", + "message": "ā¸Ąā¸ĩā¸­ā¸ąā¸ā¸‚ā¸Ŗā¸°ā¸žā¸´āš€ā¸¨ā¸Šā¸•āšˆā¸­āš„ā¸›ā¸™ā¸ĩāš‰ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸™āš‰ā¸­ā¸ĸā¸Ģ⏙ā¸ļāšˆā¸‡ā¸•ā¸ąā¸§ $CHARS$", "placeholders": { "chars": { "content": "$1", @@ -2500,186 +2579,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", @@ -2688,13 +2767,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", @@ -2703,7 +2782,7 @@ } }, "atRiskPasswordsDescSingleOrgPlural": { - "message": "$ORGANIZATION$ is requesting you change the $COUNT$ passwords because they are at-risk.", + "message": "$ORGANIZATION$ ā¸Ŗāš‰ā¸­ā¸‡ā¸‚ā¸­āšƒā¸Ģāš‰ā¸„ā¸¸ā¸“āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ $COUNT$ ⏪⏞ā¸ĸā¸ā¸˛ā¸Ŗāš€ā¸™ā¸ˇāšˆā¸­ā¸‡ā¸ˆā¸˛ā¸ā¸Ąā¸ĩā¸„ā¸§ā¸˛ā¸Ąāš€ā¸Ēā¸ĩāšˆā¸ĸ⏇", "placeholders": { "organization": { "content": "$1", @@ -2716,7 +2795,7 @@ } }, "atRiskPasswordsDescMultiOrgPlural": { - "message": "Your organizations are requesting you change the $COUNT$ passwords because they are at-risk.", + "message": "ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸Ŗāš‰ā¸­ā¸‡ā¸‚ā¸­āšƒā¸Ģāš‰ā¸„ā¸¸ā¸“āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ $COUNT$ ⏪⏞ā¸ĸā¸ā¸˛ā¸Ŗāš€ā¸™ā¸ˇāšˆā¸­ā¸‡ā¸ˆā¸˛ā¸ā¸Ąā¸ĩā¸„ā¸§ā¸˛ā¸Ąāš€ā¸Ēā¸ĩāšˆā¸ĸ⏇", "placeholders": { "count": { "content": "$1", @@ -2725,7 +2804,7 @@ } }, "atRiskChangePrompt": { - "message": "Your password for this site is at-risk. $ORGANIZATION$ has requested that you change it.", + "message": "⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šāš€ā¸§āš‡ā¸šāš„ā¸‹ā¸•āšŒā¸™ā¸ĩāš‰ā¸Ąā¸ĩā¸„ā¸§ā¸˛ā¸Ąāš€ā¸Ēā¸ĩāšˆā¸ĸ⏇ $ORGANIZATION$ āš„ā¸”āš‰ā¸Ŗāš‰ā¸­ā¸‡ā¸‚ā¸­āšƒā¸Ģāš‰ā¸„ā¸¸ā¸“āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™", "placeholders": { "organization": { "content": "$1", @@ -2735,7 +2814,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", @@ -2745,10 +2824,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", @@ -2757,52 +2836,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", @@ -2811,7 +2890,7 @@ } }, "excludedDomainsInvalidDomain": { - "message": "$DOMAIN$ is not a valid domain", + "message": "$DOMAIN$ āš„ā¸Ąāšˆāšƒā¸Šāšˆāš‚ā¸”āš€ā¸Ąā¸™ā¸—ā¸ĩāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡", "placeholders": { "domain": { "content": "$1", @@ -2820,20 +2899,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": { @@ -2847,14 +2926,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": "āš„ā¸Ÿā¸ĨāšŒ" @@ -2864,36 +2943,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": { @@ -2901,21 +2980,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": { @@ -2923,14 +3002,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 ā¸§ā¸ąā¸™" @@ -2945,41 +3024,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": { @@ -2989,11 +3068,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": { @@ -3003,113 +3082,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", @@ -3118,20 +3197,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", @@ -3144,7 +3223,7 @@ } }, "vaultTimeoutPolicyInEffect1": { - "message": "$HOURS$ hour(s) and $MINUTES$ minute(s) maximum.", + "message": "ā¸Ēā¸šā¸‡ā¸Ē⏏⏔ $HOURS$ ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡ $MINUTES$ ⏙⏞⏗ā¸ĩ", "placeholders": { "hours": { "content": "$1", @@ -3157,7 +3236,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", @@ -3170,7 +3249,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", @@ -3187,7 +3266,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "Your organization policies have set your vault timeout action to $ACTION$.", + "message": "ā¸™āš‚ā¸ĸ⏚⏞ā¸ĸā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸ā¸ŗā¸Ģā¸™ā¸”ā¸ā¸˛ā¸Ŗā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗāš€ā¸Ąā¸ˇāšˆā¸­ā¸•ā¸šāš‰ā¸™ā¸´ā¸Ŗā¸ ā¸ąā¸ĸā¸Ģā¸Ąā¸”āš€ā¸§ā¸Ĩā¸˛āš€ā¸›āš‡ā¸™ $ACTION$", "placeholders": { "action": { "content": "$1", @@ -3196,55 +3275,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." - }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." + "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", @@ -3253,7 +3329,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", @@ -3262,10 +3338,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", @@ -3274,7 +3350,7 @@ } }, "exportingOrganizationVaultFromPasswordManagerWithDataOwnershipDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported.", + "message": "āš€ā¸‰ā¸žā¸˛ā¸°ā¸•ā¸šāš‰ā¸™ā¸´ā¸Ŗā¸ ā¸ąā¸ĸā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸—ā¸ĩāšˆāš€ā¸Šā¸ˇāšˆā¸­ā¸Ąāš‚ā¸ĸā¸‡ā¸ā¸ąā¸š $ORGANIZATION$ āš€ā¸—āšˆā¸˛ā¸™ā¸ąāš‰ā¸™ā¸—ā¸ĩāšˆā¸ˆā¸°ā¸–ā¸šā¸ā¸Ēāšˆā¸‡ā¸­ā¸­ā¸", "placeholders": { "organization": { "content": "$1", @@ -3283,7 +3359,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", @@ -3292,33 +3368,39 @@ } }, "error": { - "message": "Error" + "message": "ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔" + }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." }, "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": { @@ -3332,7 +3414,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": { @@ -3342,7 +3424,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": { @@ -3352,46 +3434,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": { @@ -3405,11 +3487,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": { @@ -3419,7 +3501,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": { @@ -3429,7 +3511,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": { @@ -3443,7 +3525,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": { @@ -3453,7 +3535,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": { @@ -3467,7 +3549,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": { @@ -3477,7 +3559,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": { @@ -3487,7 +3569,7 @@ } }, "forwarderNoUrl": { - "message": "Invalid $SERVICENAME$ url.", + "message": "URL ⏂⏭⏇ $SERVICENAME$ āš„ā¸Ąāšˆā¸–ā¸šā¸ā¸•āš‰ā¸­ā¸‡", "description": "Displayed when the url of the forwarding service wasn't supplied.", "placeholders": { "servicename": { @@ -3497,7 +3579,7 @@ } }, "forwarderUnknownError": { - "message": "Unknown $SERVICENAME$ error occurred.", + "message": "āš€ā¸ā¸´ā¸”ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔ $SERVICENAME$ ⏗ā¸ĩāšˆāš„ā¸Ąāšˆā¸Ŗā¸šāš‰ā¸ˆā¸ąā¸", "description": "Displayed when the forwarding service failed due to an unknown error.", "placeholders": { "servicename": { @@ -3507,7 +3589,7 @@ } }, "forwarderUnknownForwarder": { - "message": "Unknown forwarder: '$SERVICENAME$'.", + "message": "āš„ā¸Ąāšˆā¸Ŗā¸šāš‰ā¸ˆā¸ąā¸ā¸šā¸Ŗā¸´ā¸ā¸˛ā¸Ŗā¸Ēāšˆā¸‡ā¸•āšˆā¸­: '$SERVICENAME$'", "description": "Displayed when the forwarding service is not supported.", "placeholders": { "servicename": { @@ -3517,29 +3599,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", @@ -3548,16 +3630,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", @@ -3566,7 +3648,7 @@ } }, "lastSeenOn": { - "message": "last seen on: $DATE$", + "message": "āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ā¸Ĩāšˆā¸˛ā¸Ēā¸¸ā¸”āš€ā¸Ąā¸ˇāšˆā¸­: $DATE$", "placeholders": { "date": { "content": "$1", @@ -3575,58 +3657,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", @@ -3639,40 +3721,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", @@ -3681,10 +3763,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", @@ -3693,31 +3775,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", @@ -3726,7 +3808,7 @@ } }, "autofillShortcutTextSafari": { - "message": "Default autofill shortcut: $COMMAND$.", + "message": "⏗⏞⏇ā¸Ĩā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸›āš‰ā¸­ā¸™ā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´āš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™: $COMMAND$", "placeholders": { "command": { "content": "$1", @@ -3735,34 +3817,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" @@ -3772,22 +3854,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", @@ -3796,31 +3878,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", @@ -3829,136 +3911,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", @@ -3967,7 +4049,7 @@ } }, "inputMaxLength": { - "message": "Input must not exceed $COUNT$ characters in length.", + "message": "ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸•āš‰ā¸­ā¸‡ā¸Ąā¸ĩā¸„ā¸§ā¸˛ā¸Ąā¸ĸā¸˛ā¸§āš„ā¸Ąāšˆāš€ā¸ā¸´ā¸™ $COUNT$ ā¸•ā¸ąā¸§ā¸­ā¸ąā¸ā¸Šā¸Ŗ", "placeholders": { "count": { "content": "$1", @@ -3976,7 +4058,7 @@ } }, "inputForbiddenCharacters": { - "message": "The following characters are not allowed: $CHARACTERS$", + "message": "āš„ā¸Ąāšˆā¸­ā¸™ā¸¸ā¸ā¸˛ā¸•āšƒā¸Ģāš‰āšƒā¸Šāš‰ā¸­ā¸ąā¸ā¸‚ā¸Ŗā¸°ā¸•āšˆā¸­āš„ā¸›ā¸™ā¸ĩāš‰: $CHARACTERS$", "placeholders": { "characters": { "content": "$1", @@ -3985,7 +4067,7 @@ } }, "inputMinValue": { - "message": "Input value must be at least $MIN$.", + "message": "ā¸„āšˆā¸˛ā¸—ā¸ĩāšˆā¸›āš‰ā¸­ā¸™ā¸•āš‰ā¸­ā¸‡ā¸Ąā¸ĩā¸„āšˆā¸˛ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸™āš‰ā¸­ā¸ĸ $MIN$", "placeholders": { "min": { "content": "$1", @@ -3994,7 +4076,7 @@ } }, "inputMaxValue": { - "message": "Input value must not exceed $MAX$.", + "message": "ā¸„āšˆā¸˛ā¸—ā¸ĩāšˆā¸›āš‰ā¸­ā¸™ā¸•āš‰ā¸­ā¸‡ā¸Ąā¸ĩā¸„āšˆā¸˛āš„ā¸Ąāšˆāš€ā¸ā¸´ā¸™ $MAX$", "placeholders": { "max": { "content": "$1", @@ -4003,17 +4085,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", @@ -4022,10 +4104,10 @@ } }, "singleFieldNeedsAttention": { - "message": "1 field needs your attention." + "message": "1 ⏟⏴ā¸Ĩā¸”āšŒā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸•ā¸Ŗā¸§ā¸ˆā¸Ē⏭⏚" }, "multipleFieldsNeedAttention": { - "message": "$COUNT$ fields need your attention.", + "message": "$COUNT$ ⏟⏴ā¸Ĩā¸”āšŒā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸ā¸˛ā¸Ŗā¸•ā¸Ŗā¸§ā¸ˆā¸Ē⏭⏚", "placeholders": { "count": { "content": "$1", @@ -4034,22 +4116,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", @@ -4058,145 +4140,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" - }, - "importData": { - "message": "Import data", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" + "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", @@ -4205,46 +4283,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", @@ -4253,67 +4331,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": { @@ -4323,200 +4401,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", @@ -4525,126 +4603,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": { @@ -4654,7 +4732,7 @@ } }, "copyNoteTitle": { - "message": "Copy Note - $ITEMNAME$", + "message": "ā¸„ā¸ąā¸”ā¸Ĩā¸­ā¸āš‚ā¸™āš‰ā¸• - $ITEMNAME$", "description": "Title for a button copies a note to the clipboard.", "placeholders": { "itemname": { @@ -4664,7 +4742,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": { @@ -4673,8 +4751,11 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "More options" + }, "moreOptionsTitle": { - "message": "More options - $ITEMNAME$", + "message": "ā¸•ā¸ąā¸§āš€ā¸Ĩā¸ˇā¸­ā¸āš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ą - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", "placeholders": { "itemname": { @@ -4684,7 +4765,7 @@ } }, "viewItemTitle": { - "message": "View item - $ITEMNAME$", + "message": "ā¸”ā¸šā¸Ŗā¸˛ā¸ĸ⏁⏞⏪ - $ITEMNAME$", "description": "Title for a link that opens a view for an item.", "placeholders": { "itemname": { @@ -4694,7 +4775,7 @@ } }, "viewItemTitleWithField": { - "message": "View item - $ITEMNAME$ - $FIELD$", + "message": "ā¸”ā¸šā¸Ŗā¸˛ā¸ĸ⏁⏞⏪ - $ITEMNAME$ - $FIELD$", "description": "Title for a link that opens a view for an item.", "placeholders": { "itemname": { @@ -4708,7 +4789,7 @@ } }, "autofillTitle": { - "message": "Autofill - $ITEMNAME$", + "message": "ā¸›āš‰ā¸­ā¸™ā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´ - $ITEMNAME$", "description": "Title for a button that autofills a login item.", "placeholders": { "itemname": { @@ -4718,7 +4799,7 @@ } }, "autofillTitleWithField": { - "message": "Autofill - $ITEMNAME$ - $FIELD$", + "message": "ā¸›āš‰ā¸­ā¸™ā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´ - $ITEMNAME$ - $FIELD$", "description": "Title for a button that autofills a login item.", "placeholders": { "itemname": { @@ -4732,7 +4813,7 @@ } }, "copyFieldCipherName": { - "message": "Copy $FIELD$, $CIPHERNAME$", + "message": "ā¸„ā¸ąā¸”ā¸Ĩ⏭⏁ $FIELD$, $CIPHERNAME$", "description": "Title for a button that copies a field value to the clipboard.", "placeholders": { "field": { @@ -4746,40 +4827,82 @@ } }, "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": "ā¸„ā¸­ā¸™āš‚ā¸‹ā¸Ĩā¸œā¸šāš‰ā¸”ā¸šāšā¸Ĩ⏪⏰⏚⏚" + }, + "admin": { + "message": "Admin" + }, + "automaticUserConfirmation": { + "message": "Automatic user confirmation" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Automatically confirm new users" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" }, "accountSecurity": { "message": "ā¸„ā¸§ā¸˛ā¸Ąā¸›ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸā¸‚ā¸­ā¸‡ā¸šā¸ąā¸ā¸Šā¸ĩ" }, + "phishingBlocker": { + "message": "ā¸•ā¸ąā¸§ā¸šā¸Ĩāš‡ā¸­ā¸ā¸Ÿā¸´ā¸Šā¸Šā¸´ā¸‡" + }, + "enablePhishingDetection": { + "message": "ā¸ā¸˛ā¸Ŗā¸•ā¸Ŗā¸§ā¸ˆā¸ˆā¸ąā¸šā¸Ÿā¸´ā¸Šā¸Šā¸´ā¸‡" + }, + "enablePhishingDetectionDesc": { + "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": { @@ -4789,7 +4912,7 @@ } }, "backTo": { - "message": "Back to $NAME$", + "message": "⏁ā¸Ĩā¸ąā¸šāš„ā¸›ā¸—ā¸ĩāšˆ $NAME$", "description": "Navigate back to a previous folder or collection", "placeholders": { "name": { @@ -4799,10 +4922,10 @@ } }, "new": { - "message": "New" + "message": "āšƒā¸Ģā¸Ąāšˆ" }, "removeItem": { - "message": "Remove $NAME$", + "message": "āš€ā¸­ā¸˛ $NAME$ ⏭⏭⏁", "description": "Remove a selected option, such as a folder or collection", "placeholders": { "name": { @@ -4812,44 +4935,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": "ā¸­ā¸ąā¸›āš‚ā¸Ģā¸Ĩ⏔" @@ -4858,10 +4981,10 @@ "message": "āš€ā¸žā¸´āšˆā¸Ąāš„ā¸Ÿā¸ĨāšŒāšā¸™ā¸š" }, "maxFileSizeSansPunctuation": { - "message": "ā¸‚ā¸™ā¸˛ā¸”āš„ā¸Ÿā¸ĨāšŒā¸Ēā¸šā¸‡ā¸Ē⏏⏔ ⏄⏎⏭ 500 MB" + "message": "ā¸‚ā¸™ā¸˛ā¸”āš„ā¸Ÿā¸ĨāšŒā¸Ēā¸šā¸‡ā¸Ē⏏⏔⏄⏎⏭ 500 MB" }, "deleteAttachmentName": { - "message": "Delete attachment $NAME$", + "message": "ā¸Ĩā¸šāš„ā¸Ÿā¸ĨāšŒāšā¸™ā¸š $NAME$", "placeholders": { "name": { "content": "$1", @@ -4870,7 +4993,7 @@ } }, "downloadAttachmentName": { - "message": "Download $NAME$", + "message": "ā¸”ā¸˛ā¸§ā¸™āšŒāš‚ā¸Ģā¸Ĩ⏔ $NAME$", "placeholders": { "name": { "content": "$1", @@ -4879,55 +5002,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", @@ -4936,16 +5059,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", @@ -4954,23 +5077,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": { @@ -4980,16 +5103,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": { @@ -4999,7 +5122,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": { @@ -5009,7 +5132,7 @@ } }, "showMatchDetection": { - "message": "Show match detection $WEBSITE$", + "message": "āšā¸Ēā¸”ā¸‡ā¸ā¸˛ā¸Ŗā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸šā¸„ā¸šāšˆ $WEBSITE$", "placeholders": { "website": { "content": "$1", @@ -5017,29 +5140,26 @@ } } }, - "hideMatchDetection": { - "message": "Hide match detection $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Show match detection" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Hide match detection" }, "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", @@ -5048,40 +5168,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", @@ -5096,34 +5216,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", @@ -5132,7 +5252,7 @@ } }, "deleteCustomField": { - "message": "Delete $LABEL$", + "message": "ā¸Ĩ⏚ $LABEL$", "placeholders": { "label": { "content": "$1", @@ -5150,7 +5270,7 @@ } }, "reorderToggleButton": { - "message": "Reorder $LABEL$. Use arrow key to move item up or down.", + "message": "ā¸ˆā¸ąā¸”ā¸Ĩā¸ŗā¸”ā¸ąā¸š $LABEL$ āšƒā¸Ģā¸Ąāšˆ āšƒā¸Šāš‰ā¸›ā¸¸āšˆā¸Ąā¸Ĩā¸šā¸ā¸¨ā¸Ŗāš€ā¸žā¸ˇāšˆā¸­āš€ā¸Ĩā¸ˇāšˆā¸­ā¸™ā¸Ŗā¸˛ā¸ĸ⏁⏞⏪⏂ā¸ļāš‰ā¸™ā¸Ģ⏪⏎⏭ā¸Ĩ⏇", "placeholders": { "label": { "content": "$1", @@ -5159,10 +5279,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", @@ -5179,13 +5299,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", @@ -5194,7 +5314,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", @@ -5203,7 +5323,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", @@ -5216,13 +5336,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", @@ -5231,7 +5351,7 @@ } }, "itemMovedToOrg": { - "message": "Item moved to $ORGNAME$", + "message": "ā¸ĸāš‰ā¸˛ā¸ĸ⏪⏞ā¸ĸā¸ā¸˛ā¸Ŗāš„ā¸›ā¸—ā¸ĩāšˆ $ORGNAME$ āšā¸Ĩāš‰ā¸§", "placeholders": { "orgname": { "content": "$1", @@ -5240,7 +5360,7 @@ } }, "reorderFieldDown": { - "message": "$LABEL$ moved down, position $INDEX$ of $LENGTH$", + "message": "āš€ā¸Ĩā¸ˇāšˆā¸­ā¸™ $LABEL$ ā¸Ĩ⏇ ā¸•ā¸ŗāšā¸Ģā¸™āšˆā¸‡ā¸—ā¸ĩāšˆ $INDEX$ ⏈⏞⏁ $LENGTH$", "placeholders": { "label": { "content": "$1", @@ -5257,25 +5377,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", @@ -5284,25 +5404,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" @@ -5317,58 +5437,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", @@ -5377,41 +5497,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": { @@ -5419,23 +5539,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": { @@ -5443,154 +5563,157 @@ "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": "ā¸ā¸§āš‰ā¸˛ā¸‡ā¸žā¸´āš€ā¸¨ā¸Š" + }, + "narrow": { + "message": "Narrow" }, "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", @@ -5599,93 +5722,99 @@ } }, "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": "ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāš€ā¸‚āš‰ā¸˛ā¸Ēā¸šāšˆā¸Ŗā¸°ā¸šā¸šā¸™ā¸ĩāš‰ā¸Ąā¸ĩā¸„ā¸§ā¸˛ā¸Ąāš€ā¸Ēā¸ĩāšˆā¸ĸā¸‡āšā¸Ĩā¸°āš„ā¸Ąāšˆā¸Ąā¸ĩāš€ā¸§āš‡ā¸šāš„ā¸‹ā¸•āšŒ āš€ā¸žā¸´āšˆā¸Ąāš€ā¸§āš‡ā¸šāš„ā¸‹ā¸•āšŒāšā¸Ĩā¸°āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āš€ā¸žā¸ˇāšˆā¸­ā¸„ā¸§ā¸˛ā¸Ąā¸›ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸ⏗ā¸ĩāšˆā¸Ŗā¸ąā¸”ā¸ā¸¸ā¸Ąā¸ĸā¸´āšˆā¸‡ā¸‚ā¸ļāš‰ā¸™" + }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" }, "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", @@ -5694,162 +5823,299 @@ } }, "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": "āšā¸Ĩā¸°ā¸­ā¸ˇāšˆā¸™ āš†!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "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": "ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš„ā¸Ąāšˆāšƒā¸Šāš‰ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸Ģā¸Ĩā¸ąā¸āšƒā¸™ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ēā¸šāšˆā¸Ŗā¸°ā¸šā¸š Bitwarden ⏭ā¸ĩā¸ā¸•āšˆā¸­āš„ā¸› ā¸Ģā¸˛ā¸ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸•āšˆā¸­ āšƒā¸Ģāš‰ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗāšā¸Ĩā¸°āš‚ā¸”āš€ā¸Ąā¸™" + }, + "continueWithLogIn": { + "message": "ā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ēā¸šāšˆā¸Ŗā¸°ā¸šā¸šā¸•āšˆā¸­" + }, + "doNotContinue": { + "message": "āš„ā¸Ąāšˆā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸•āšˆā¸­" + }, + "domain": { + "message": "āš‚ā¸”āš€ā¸Ąā¸™" + }, + "keyConnectorDomainTooltip": { + "message": "āš‚ā¸”āš€ā¸Ąā¸™ā¸™ā¸ĩāš‰ā¸ˆā¸°ā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸šā¸ā¸¸ā¸āšā¸ˆāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸Ģā¸ąā¸Ēā¸šā¸ąā¸ā¸Šā¸ĩ⏂⏭⏇⏄⏏⏓ ā¸”ā¸ąā¸‡ā¸™ā¸ąāš‰ā¸™āš‚ā¸›ā¸Ŗā¸”ā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šāšƒā¸Ģāš‰āšā¸™āšˆāšƒā¸ˆā¸§āšˆā¸˛ā¸„ā¸¸ā¸“āš€ā¸Šā¸ˇāšˆā¸­ā¸–ā¸ˇā¸­ ā¸Ģā¸˛ā¸āš„ā¸Ąāšˆāšā¸™āšˆāšƒā¸ˆ āšƒā¸Ģāš‰ā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šā¸ā¸ąā¸šā¸œā¸šāš‰ā¸”ā¸šāšā¸Ĩ⏪⏰⏚⏚" + }, + "verifyYourOrganization": { + "message": "ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš€ā¸žā¸ˇāšˆā¸­āš€ā¸‚āš‰ā¸˛ā¸Ēā¸šāšˆā¸Ŗā¸°ā¸šā¸š" + }, + "organizationVerified": { + "message": "ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗāšā¸Ĩāš‰ā¸§" + }, + "domainVerified": { + "message": "ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™āš‚ā¸”āš€ā¸Ąā¸™āšā¸Ĩāš‰ā¸§" + }, + "leaveOrganizationContent": { + "message": "ā¸Ģā¸˛ā¸ā¸„ā¸¸ā¸“āš„ā¸Ąāšˆā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗ ā¸Ēā¸´ā¸—ā¸˜ā¸´āšŒā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸ˆā¸°ā¸–ā¸šā¸āš€ā¸žā¸´ā¸ā¸–ā¸­ā¸™" + }, + "leaveNow": { + "message": "⏭⏭⏁⏕⏭⏙⏙ā¸ĩāš‰" + }, + "verifyYourDomainToLogin": { + "message": "ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™āš‚ā¸”āš€ā¸Ąā¸™ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš€ā¸žā¸ˇāšˆā¸­āš€ā¸‚āš‰ā¸˛ā¸Ēā¸šāšˆā¸Ŗā¸°ā¸šā¸š" + }, + "verifyYourDomainDescription": { + "message": "ā¸Ģā¸˛ā¸ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ēā¸šāšˆā¸Ŗā¸°ā¸šā¸šā¸•āšˆā¸­ āšƒā¸Ģāš‰ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™āš‚ā¸”āš€ā¸Ąā¸™ā¸™ā¸ĩāš‰" + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "ā¸Ģā¸˛ā¸ā¸•āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ēā¸šāšˆā¸Ŗā¸°ā¸šā¸šā¸•āšˆā¸­ āšƒā¸Ģāš‰ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗāšā¸Ĩā¸°āš‚ā¸”āš€ā¸Ąā¸™" }, "sessionTimeoutSettingsAction": { - "message": "Timeout action" + "message": "ā¸ā¸˛ā¸Ŗā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗāš€ā¸Ąā¸ˇāšˆā¸­ā¸Ģā¸Ąā¸”āš€ā¸§ā¸Ĩ⏞" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "ā¸ā¸˛ā¸Ŗā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸™ā¸ĩāš‰āš„ā¸”āš‰ā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗāš‚ā¸”ā¸ĸā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“" + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸ā¸ŗā¸Ģā¸™ā¸”āš€ā¸§ā¸Ĩ⏞ā¸Ģā¸Ąā¸”āš€ā¸§ā¸Ĩā¸˛āš€ā¸‹ā¸Ēā¸Šā¸ąā¸™ā¸Ēā¸šā¸‡ā¸Ēā¸¸ā¸”āš„ā¸§āš‰ā¸—ā¸ĩāšˆ $HOURS$ ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡ $MINUTES$ ⏙⏞⏗ā¸ĩ", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸ā¸ŗā¸Ģā¸™ā¸”āš€ā¸§ā¸Ĩ⏞ā¸Ģā¸Ąā¸”āš€ā¸§ā¸Ĩā¸˛āš€ā¸‹ā¸Ēā¸Šā¸ąā¸™āš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™āš€ā¸›āš‡ā¸™ ā¸—ā¸ąā¸™ā¸—ā¸ĩ" + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸ā¸ŗā¸Ģā¸™ā¸”āš€ā¸§ā¸Ĩ⏞ā¸Ģā¸Ąā¸”āš€ā¸§ā¸Ĩā¸˛āš€ā¸‹ā¸Ēā¸Šā¸ąā¸™āš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™āš€ā¸›āš‡ā¸™ āš€ā¸Ąā¸ˇāšˆā¸­ā¸Ĩāš‡ā¸­ā¸ā¸Ŗā¸°ā¸šā¸š" + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸ā¸ŗā¸Ģā¸™ā¸”āš€ā¸§ā¸Ĩ⏞ā¸Ģā¸Ąā¸”āš€ā¸§ā¸Ĩā¸˛āš€ā¸‹ā¸Ēā¸Šā¸ąā¸™āš€ā¸Ŗā¸´āšˆā¸Ąā¸•āš‰ā¸™āš€ā¸›āš‡ā¸™ āš€ā¸Ąā¸ˇāšˆā¸­ā¸Ŗā¸ĩā¸Ēā¸•ā¸˛ā¸ŖāšŒā¸•āš€ā¸šā¸Ŗā¸˛ā¸§āšŒāš€ā¸‹ā¸­ā¸ŖāšŒ" + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "āš€ā¸§ā¸Ĩ⏞ā¸Ģā¸Ąā¸”āš€ā¸§ā¸Ĩ⏞ā¸Ēā¸šā¸‡ā¸Ēā¸¸ā¸”ā¸•āš‰ā¸­ā¸‡āš„ā¸Ąāšˆāš€ā¸ā¸´ā¸™ $HOURS$ ā¸Šā¸ąāšˆā¸§āš‚ā¸Ąā¸‡ $MINUTES$ ⏙⏞⏗ā¸ĩ", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "āš€ā¸Ąā¸ˇāšˆā¸­ā¸Ŗā¸ĩā¸Ēā¸•ā¸˛ā¸ŖāšŒā¸•āš€ā¸šā¸Ŗā¸˛ā¸§āšŒāš€ā¸‹ā¸­ā¸ŖāšŒ" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "ā¸•ā¸ąāš‰ā¸‡ā¸„āšˆā¸˛ā¸§ā¸´ā¸˜ā¸ĩ⏁⏞⏪⏛ā¸Ĩ⏔ā¸Ĩāš‡ā¸­ā¸āš€ā¸žā¸ˇāšˆā¸­āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸā¸™ā¸ā¸˛ā¸Ŗā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗāš€ā¸Ąā¸ˇāšˆā¸­ā¸Ģā¸Ąā¸”āš€ā¸§ā¸Ĩ⏞" + }, + "upgrade": { + "message": "ā¸­ā¸ąā¸›āš€ā¸ā¸Ŗā¸”" + }, + "leaveConfirmationDialogTitle": { + "message": "ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸—ā¸ĩāšˆā¸ˆā¸°ā¸­ā¸­ā¸ā¸Ģā¸Ŗā¸ˇā¸­āš„ā¸Ąāšˆ" + }, + "leaveConfirmationDialogContentOne": { + "message": "ā¸Ģā¸˛ā¸ā¸›ā¸ā¸´āš€ā¸Ē⏘ ⏪⏞ā¸ĸ⏁⏞⏪ā¸Ēāšˆā¸§ā¸™ā¸•ā¸ąā¸§ā¸ˆā¸°ā¸ĸā¸ąā¸‡ā¸„ā¸‡ā¸­ā¸ĸā¸šāšˆāšƒā¸™ā¸šā¸ąā¸ā¸Šā¸ĩ⏂⏭⏇⏄⏏⏓ āšā¸•āšˆā¸„ā¸¸ā¸“ā¸ˆā¸°āš€ā¸Ēā¸ĩā¸ĸā¸Ēā¸´ā¸—ā¸˜ā¸´āšŒāš€ā¸‚āš‰ā¸˛ā¸–ā¸ļ⏇⏪⏞ā¸ĸ⏁⏞⏪⏗ā¸ĩāšˆāšā¸Šā¸ŖāšŒāšā¸Ĩ⏰⏟ā¸ĩāš€ā¸ˆā¸­ā¸ŖāšŒā¸‚ā¸­ā¸‡ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗ" + }, + "leaveConfirmationDialogContentTwo": { + "message": "ā¸•ā¸´ā¸”ā¸•āšˆā¸­ā¸œā¸šāš‰ā¸”ā¸šāšā¸Ĩā¸Ŗā¸°ā¸šā¸šāš€ā¸žā¸ˇāšˆā¸­ā¸‚ā¸­ā¸Ŗā¸ąā¸šā¸Ēā¸´ā¸—ā¸˜ā¸´āšŒāš€ā¸‚āš‰ā¸˛ā¸–ā¸ļ⏇⏭ā¸ĩā¸ā¸„ā¸Ŗā¸ąāš‰ā¸‡" + }, + "leaveConfirmationDialogConfirmButton": { + "message": "⏭⏭⏁⏈⏞⏁ $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "ā¸‰ā¸ąā¸™ā¸ˆā¸°ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸•ā¸šāš‰ā¸™ā¸´ā¸Ŗā¸ ā¸ąā¸ĸāš„ā¸”āš‰ā¸­ā¸ĸāšˆā¸˛ā¸‡āš„ā¸Ŗ" + }, + "transferItemsToOrganizationTitle": { + "message": "āš‚ā¸­ā¸™ā¸ĸāš‰ā¸˛ā¸ĸ⏪⏞ā¸ĸā¸ā¸˛ā¸Ŗāš„ā¸›ā¸ĸā¸ąā¸‡ $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ ⏁⏺ā¸Ģā¸™ā¸”āšƒā¸Ģāš‰ā¸Ŗā¸˛ā¸ĸā¸ā¸˛ā¸Ŗā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”ā¸•āš‰ā¸­ā¸‡āš€ā¸›āš‡ā¸™ā¸‚ā¸­ā¸‡ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗāš€ā¸žā¸ˇāšˆā¸­ā¸„ā¸§ā¸˛ā¸Ąā¸›ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸāšā¸Ĩā¸°ā¸ā¸˛ā¸Ŗā¸›ā¸ā¸´ā¸šā¸ąā¸•ā¸´ā¸•ā¸˛ā¸Ąā¸‚āš‰ā¸­ā¸ā¸ŗā¸Ģ⏙⏔ ⏄ā¸Ĩ⏴⏁ā¸ĸā¸­ā¸Ąā¸Ŗā¸ąā¸šāš€ā¸žā¸ˇāšˆā¸­āš‚ā¸­ā¸™ā¸ā¸Ŗā¸Ŗā¸Ąā¸Ēā¸´ā¸—ā¸˜ā¸´āšŒā¸Ŗā¸˛ā¸ĸ⏁⏞⏪⏂⏭⏇⏄⏏⏓", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "ā¸ĸā¸­ā¸Ąā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗāš‚ā¸­ā¸™ā¸ĸāš‰ā¸˛ā¸ĸ" + }, + "declineAndLeave": { + "message": "ā¸›ā¸ā¸´āš€ā¸Ēā¸˜āšā¸Ĩ⏰⏭⏭⏁" + }, + "whyAmISeeingThis": { + "message": "ā¸—ā¸ŗāš„ā¸Ąā¸‰ā¸ąā¸™ā¸ˆā¸ļā¸‡āš€ā¸Ģāš‡ā¸™ā¸Ēā¸´āšˆā¸‡ā¸™ā¸ĩāš‰" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" } } diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index e6004ef387f..83461d1a8a0 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Geçiş anahtarÄąyla giriş yap" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "Çoklu oturum açma kullan" }, @@ -436,8 +439,8 @@ "sync": { "message": "Eşitle" }, - "syncVaultNow": { - "message": "KasayÄą şimdi eşitle" + "syncNow": { + "message": "Şimdi eşitle" }, "lastSync": { "message": "Son eşitleme:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden web uygulamasÄą" }, - "importItems": { - "message": "HesaplarÄą içe aktar" - }, "select": { "message": "Seç" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "KayÄąt arşive gÃļnderildi" }, + "itemWasUnarchived": { + "message": "KayÄąt arşivden Ã§ÄąkarÄąldÄą" + }, "itemUnarchived": { "message": "KayÄąt arşivden Ã§ÄąkarÄąldÄą" }, "archiveItem": { "message": "KaydÄą arşivle" }, - "archiveItemConfirmDesc": { - "message": "Arşivlenmiş kayÄątlar genel arama sonuçlarÄą ve otomatik doldurma Ãļnerilerinden hariç tutulur. Bu kaydÄą arşivlemek istediğine emin misin?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "archived": { + "message": "Arşivlendi" + }, + "unarchiveAndSave": { + "message": "Arşivden Ã§Äąkar ve kaydet" }, "upgradeToUseArchive": { "message": "Arşivi kullanmak için premium Ãŧyelik gereklidir." }, + "itemRestored": { + "message": "KayÄąt geri yÃŧklendi" + }, "edit": { "message": "DÃŧzenle" }, @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "DÄąÅŸa aktarÄąlacak konum" }, - "exportVault": { - "message": "KasayÄą dÄąÅŸa aktar" + "exportVerb": { + "message": "DÄąÅŸa aktar", + "description": "The verb form of the word Export" + }, + "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" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "Daha fazla bilgi al" }, + "migrationsFailed": { + "message": "Şifreleme ayarlarÄą gÃŧncellenirken bir hata oluştu." + }, + "updateEncryptionSettingsTitle": { + "message": "Şifreleme ayarlarÄąnÄązÄą gÃŧncelleyin" + }, + "updateEncryptionSettingsDesc": { + "message": "Önerilen yeni şifreleme ayarlarÄą hesap gÃŧvenliğinizi artÄąracaktÄąr. Şimdi gÃŧncellemek için ana parolanÄązÄą girin." + }, + "confirmIdentityToContinue": { + "message": "Devam etmek için kimliğinizi doğrulayÄąn" + }, + "enterYourMasterPassword": { + "message": "Ana parolanÄązÄą girin" + }, + "updateSettings": { + "message": "AyarlarÄą gÃŧncelle" + }, + "later": { + "message": "Daha sonra" + }, "authenticatorKeyTotp": { "message": "Kimlik doğrulama anahtarÄą (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "Dosya kaydedildi" }, + "fixEncryption": { + "message": "Şifrelemeyi dÃŧzelt" + }, + "fixEncryptionTooltip": { + "message": "Bu dosya eski bir şifreleme yÃļntemi kullanÄąyor." + }, + "attachmentUpdated": { + "message": "Ek gÃŧncellendi" + }, "file": { "message": "Dosya" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Bir dosya seçin" }, + "itemsTransferred": { + "message": "KayÄątlar aktarÄąldÄą" + }, "maxFileSize": { "message": "Maksimum dosya boyutu 500 MB'dir." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "Dosya ekleri için 1 GB şifrelenmiş depolama." }, + "premiumSignUpStorageV2": { + "message": "Dosya ekleri için $SIZE$ şifrelenmiş depolama.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Acil durum erişimi." }, "premiumSignUpTwoStepOptions": { "message": "YubiKey ve Duo gibi marka bazlÄą iki aşamalÄą giriş seçenekleri." }, + "premiumSubscriptionEnded": { + "message": "Premium aboneliğiniz sona erdi" + }, + "archivePremiumRestart": { + "message": "Arşivinize yeniden erişebilmek için Premium aboneliğinizi yeniden başlatÄąn. Yeniden başlatmadan Ãļnce arşivlenmiş bir kaydÄąn ayrÄąntÄąlarÄąnÄą dÃŧzenlerseniz kayÄąt tekrar kasanÄąza taÅŸÄąnÄąr." + }, + "restartPremium": { + "message": "Premium’u yeniden başlat" + }, "ppremiumSignUpReports": { "message": "KasanÄązÄą gÃŧvende tutmak için parola hijyeni, hesap sağlığı ve veri ihlali raporlarÄą." }, @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "KayÄąt kalÄącÄą olarak silindi" }, + "archivedItemRestored": { + "message": "Arşivlenmiş kayÄąt geri getirildi" + }, "restoreItem": { "message": "KaydÄą geri yÃŧkle" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Benzersiz tanÄąmlayÄącÄą bulunamadÄą." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Aşağıdaki organizasyonun Ãŧyeleri için artÄąk ana parola gerekmemektedir. LÃŧtfen alan adÄąnÄą organizasyon yÃļneticinizle doğrulayÄąn." - }, "organizationName": { "message": "Kuruluş adÄą" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Hata" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Şifre çÃļzme sorunu" }, @@ -4075,7 +4157,7 @@ "message": "Otomatik doldurulamÄąyor" }, "cannotAutofillExactMatch": { - "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + "message": "VarsayÄąlan eşleştirme \"Tam eşleşme\" olarak ayarlÄą. Geçerli web sitesi, bu kayÄąt için kaydedilen hesap bilgileriyle tam olarak eşleşmiyor." }, "okay": { "message": "Tamam" @@ -4176,10 +4258,6 @@ "ignore": { "message": "Yok say" }, - "importData": { - "message": "Verileri içe aktar", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "İçe aktarma hatasÄą" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "Diğer seçenekler" + }, "moreOptionsTitle": { "message": "Diğer seçenekler - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "YÃļnetici Konsolu" }, + "admin": { + "message": "YÃļnetici" + }, + "automaticUserConfirmation": { + "message": "Otomatik kullanÄącÄą onayÄą" + }, + "automaticUserConfirmationHint": { + "message": "Automatically confirm pending users while this device is unlocked" + }, + "autoConfirmOnboardingCallout": { + "message": "Save time with automatic user confirmation" + }, + "autoConfirmWarning": { + "message": "This could impact your organization’s data security. " + }, + "autoConfirmWarningLink": { + "message": "Learn about the risks" + }, + "autoConfirmSetup": { + "message": "Yeni kullanÄącÄąlarÄą otomatik onayla" + }, + "autoConfirmSetupDesc": { + "message": "New users will be automatically confirmed while this device is unlocked." + }, + "autoConfirmSetupHint": { + "message": "What are the potential security risks?" + }, + "autoConfirmEnabled": { + "message": "Turned on automatic confirmation" + }, + "availableNow": { + "message": "Available now" + }, "accountSecurity": { "message": "Hesap gÃŧvenliği" }, + "phishingBlocker": { + "message": "Phishing Blocker" + }, + "enablePhishingDetection": { + "message": "Phishing detection" + }, + "enablePhishingDetectionDesc": { + "message": "Display warning before accessing suspected phishing sites" + }, "notifications": { "message": "Bildirimler" }, @@ -4912,7 +5035,7 @@ "message": "Premium" }, "unlockFeaturesWithPremium": { - "message": "Unlock reporting, emergency access, and more security features with Premium." + "message": "Raporlama, acil erişim ve daha fazla gÃŧvenlik Ãļzelliğinin kilidini Premium ile aÃ§Äąn." }, "freeOrgsCannotUseAttachments": { "message": "Ücretsiz kuruluşlar dosya eklerini kullanamaz" @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "$WEBSITE$ eşleşme tespitini gizle", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Eşleşme tespitini gÃļster" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "Eşleşme tespitini gizle" }, "autoFillOnPageLoad": { "message": "Sayfa yÃŧklenince otomatik doldur" @@ -5162,7 +5282,7 @@ "message": "Web sitesi URI'sini yeniden sÄąralayÄąn. KayÄątÄą yukarÄą veya aşağı taÅŸÄąmak için ok tuşunu kullanÄąn." }, "reorderFieldUp": { - "message": "$LABEL$ yukarÄą taÅŸÄąndÄą, konum: $LENGTH$'in $INDEX$'i", + "message": "$LABEL$ yukarÄą taÅŸÄąndÄą. Konum: $LENGTH$/$INDEX$", "placeholders": { "label": { "content": "$1", @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Ekstra geniş" }, + "narrow": { + "message": "Dar" + }, "sshKeyWrongPassword": { "message": "Girdiğiniz parola yanlÄąÅŸ." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Bu hesap risk altÄąnda ve web sitesi eksik. Bir web sitesi ekleyin ve gÃŧvenliğinizi artÄąrmak için parolayÄą değiştirin." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Şimdi değiştir" + }, "missingWebsite": { "message": "Web sitesi eksik" }, @@ -5674,18 +5803,18 @@ "message": "Siteye devam et (Ãļnerilmez)" }, "phishingPageExplanation1": { - "message": "This site was found in ", + "message": "Bu site şurada bulundu ", "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": ", kişisel ve hassas bilgileri çalmak için kullanÄąlan bilinen oltalama sitelerini içeren aÃ§Äąk kaynaklÄą bir liste.", "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": "Oltalama tespiti hakkÄąnda daha fazla bilgi edinin" }, "protectedBy": { - "message": "Protected by $PRODUCT$", + "message": "$PRODUCT$ ile korunuyor", "placeholders": { "product": { "content": "$1", @@ -5798,7 +5927,7 @@ "message": "Key Connector alan adÄąnÄą doğrulayÄąn" }, "atRiskLoginsSecured": { - "message": "Great job securing your at-risk logins!" + "message": "Risk altÄąndaki hesaplarÄąnÄązÄą gÃŧvene alarak harika bir iş Ã§ÄąkardÄąnÄąz!" }, "upgradeNow": { "message": "Şimdi yÃŧkselt" @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "Ve daha fazlasÄą!" }, - "planDescPremium": { - "message": "Eksiksiz çevrimiçi gÃŧvenlik" + "advancedOnlineSecurity": { + "message": "Gelişmiş çevrimiçi gÃŧvenlik" }, "upgradeToPremium": { "message": "Premium'a yÃŧkselt" @@ -5831,7 +5960,7 @@ "message": "Premium abonelik size daha fazla gÃŧvenlik ve kontrol olanağı sunan ek araçlara erişmenizi sağlar" }, "explorePremium": { - "message": "Explore Premium" + "message": "Premium’u keşfet" }, "loadingVault": { "message": "Kasa yÃŧkleniyor" @@ -5840,7 +5969,7 @@ "message": "Kasa yÃŧklendi" }, "settingDisabledByPolicy": { - "message": "This setting is disabled by your organization's policy.", + "message": "Bu ayar, kuruluşunuzun ilkesi tarafÄąndan devre dÄąÅŸÄą bÄąrakÄąldÄą.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." }, "zipPostalCodeLabel": { @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Kart numarasÄą" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Zaman aÅŸÄąmÄą eylemi" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Bu ayar kuruluşunuz tarafÄąndan yÃļnetiliyor." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Kuruluşunuz maksimum kasa zaman aÅŸÄąmÄąnÄą $HOURS$ saat $MINUTES$ dakika olarak belirlemiş.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Kuruluşunuz varsayÄąlan oturum zaman aÅŸÄąmÄąnÄą “Hemen” olarak ayarlamÄąÅŸ." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Kuruluşunuz varsayÄąlan oturum zaman aÅŸÄąmÄąnÄą “Sistem kilitlenince” olarak ayarlamÄąÅŸ." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Kuruluşunuz varsayÄąlan oturum zaman aÅŸÄąmÄąnÄą “TarayÄącÄą yeniden başlatÄąlÄąnca” olarak ayarlamÄąÅŸ." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maksimum zaman aÅŸÄąmÄą en fazla $HOURS$ saat $MINUTES$ dakika olabilir", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "TarayÄącÄą yeniden başlatÄąlÄąnca" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Zaman aÅŸÄąmÄą eyleminizi değiştirmek için kilit açma yÃļnteminizi ayarlayÄąn" + }, + "upgrade": { + "message": "YÃŧkselt" + }, + "leaveConfirmationDialogTitle": { + "message": "AyrÄąlmak istediğinizden emin misiniz?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Reddederseniz kişisel kayÄątlarÄąnÄąz hesabÄąnÄązda kalÄąr, ancak paylaÅŸÄąlan kayÄątlara ve kuruluş Ãļzelliklerine erişiminizi kaybedersiniz." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Erişiminizi yeniden kazanmak için yÃļneticinizle iletişime geçin." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "$ORGANIZATION$ kuruluşundan ayrÄąl", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "KasamÄą nasÄąl yÃļnetebilirim?" + }, + "transferItemsToOrganizationTitle": { + "message": "KayÄątlarÄą $ORGANIZATION$ kuruluşuna aktar", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$, gÃŧvenlik ve mevzuata uyum amacÄąyla tÃŧm kayÄątlarÄąn kuruluşa ait olmasÄąnÄą zorunlu kÄąlÄąyor. KayÄątlarÄąnÄązÄąn sahipliğini devretmek için \"Kabul et\"e tÄąklayÄąn.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "AktarÄąmÄą kabul et" + }, + "declineAndLeave": { + "message": "Reddet ve ayrÄąl" + }, + "whyAmISeeingThis": { + "message": "Bunu neden gÃļrÃŧyorum?" + }, + "resizeSideNavigation": { + "message": "Kenar menÃŧsÃŧnÃŧ yeniden boyutlandÄąr" } } diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index a1922a5abd8..fdbd2508c44 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "ĐŖĐ˛Ņ–ĐšŅ‚Đ¸ С ĐēĐģŅŽŅ‡ĐĩĐŧ Đ´ĐžŅŅ‚ŅƒĐŋ҃" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "ВиĐēĐžŅ€Đ¸ŅŅ‚Đ°Ņ‚Đ¸ Ņ”Đ´Đ¸ĐŊиК Đ˛Ņ…Ņ–Đ´" }, @@ -436,8 +439,8 @@ "sync": { "message": "ХиĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ" }, - "syncVaultNow": { - "message": "ХиĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇŅƒĐ˛Đ°Ņ‚Đ¸ ĐˇĐ°Ņ€Đ°Đˇ" + "syncNow": { + "message": "ХиĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇŅƒĐ˛Đ°Ņ‚Đ¸" }, "lastSync": { "message": "ĐžŅŅ‚Đ°ĐŊĐŊŅ ŅĐ¸ĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "ВĐĩĐąĐŋŅ€ĐžĐŗŅ€Đ°Đŧа Bitwarden" }, - "importItems": { - "message": "ІĐŧĐŋĐžŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ СаĐŋĐ¸ŅĐ¸" - }, "select": { "message": "ĐžĐąŅ€Đ°Ņ‚Đ¸" }, @@ -576,17 +576,29 @@ "itemWasSentToArchive": { "message": "ЗаĐŋĐ¸Ņ Đ°Ņ€Ņ…Ņ–Đ˛ĐžĐ˛Đ°ĐŊĐž" }, + "itemWasUnarchived": { + "message": "ЗаĐŋĐ¸Ņ Ņ€ĐžĐˇĐ°Ņ€Ņ…Ņ–Đ˛ĐžĐ˛Đ°ĐŊĐž" + }, "itemUnarchived": { "message": "ЗаĐŋĐ¸Ņ Ņ€ĐžĐˇĐ°Ņ€Ņ…Ņ–Đ˛ĐžĐ˛Đ°ĐŊĐž" }, "archiveItem": { "message": "ĐŅ€Ņ…Ņ–Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸ СаĐŋĐ¸Ņ" }, - "archiveItemConfirmDesc": { - "message": "ĐŅ€Ņ…Ņ–Đ˛ĐžĐ˛Đ°ĐŊŅ– СаĐŋĐ¸ŅĐ¸ виĐēĐģŅŽŅ‡Đ°ŅŽŅ‚ŅŒŅŅ С Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Ņ–Đ˛ ĐˇĐ˛Đ¸Ņ‡Đ°ĐšĐŊĐžĐŗĐž ĐŋĐžŅˆŅƒĐē҃ Ņ‚Đ° ĐŋŅ€ĐžĐŋĐžĐˇĐ¸Ņ†Ņ–Đš Đ°Đ˛Ņ‚ĐžĐˇĐ°ĐŋОвĐŊĐĩĐŊĐŊŅ. Ви Đ´Ņ–ĐšŅĐŊĐž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ Đ°Ņ€Ņ…Ņ–Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸ ҆ĐĩĐš СаĐŋĐ¸Ņ?" + "archiveItemDialogContent": { + "message": "ĐŸŅ–ŅĐģŅ Đ°Ņ€Ņ…Ņ–Đ˛Đ°Ņ†Ņ–Ņ— ҆ĐĩĐš СаĐŋĐ¸Ņ ĐąŅƒĐ´Đĩ виĐēĐģŅŽŅ‡ĐĩĐŊĐž С Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Ņ–Đ˛ ĐŋĐžŅˆŅƒĐē҃ Ņ– ĐŋŅ€ĐžĐŋĐžĐˇĐ¸Ņ†Ņ–Đš Đ°Đ˛Ņ‚ĐžĐˇĐ°ĐŋОвĐŊĐĩĐŊĐŊŅ." + }, + "archived": { + "message": "ĐŅ€Ņ…Ņ–Đ˛ĐžĐ˛Đ°ĐŊĐž" + }, + "unarchiveAndSave": { + "message": "Đ ĐžĐˇĐ°Ņ€Ņ…Ņ–Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸ Đš СйĐĩŅ€ĐĩĐŗŅ‚Đ¸" }, "upgradeToUseArchive": { - "message": "A premium membership is required to use Archive." + "message": "ДĐģŅ виĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐŊĐŊŅ Đ°Ņ€Ņ…Ņ–Đ˛Ņƒ ĐŊĐĩĐžĐąŅ…Ņ–Đ´ĐŊа ĐŋĐĩŅ€ĐĩĐ´ĐŋĐģĐ°Ņ‚Đ° Premium." + }, + "itemRestored": { + "message": "ЗаĐŋĐ¸Ņ Đ˛Ņ–Đ´ĐŊОвĐģĐĩĐŊĐž" }, "edit": { "message": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸" @@ -598,10 +610,10 @@ "message": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ Đ˛ŅĐĩ" }, "showAll": { - "message": "Show all" + "message": "ПоĐēĐ°ĐˇĐ°Ņ‚Đ¸ Đ˛ŅĐĩ" }, "viewLess": { - "message": "View less" + "message": "ПоĐēĐ°ĐˇĐ°Ņ‚Đ¸ ĐŧĐĩĐŊ҈Đĩ" }, "viewLogin": { "message": "ПĐĩŅ€ĐĩĐŗĐģŅĐŊŅƒŅ‚Đ¸ СаĐŋĐ¸Ņ" @@ -806,10 +818,10 @@ "message": "З ĐąĐģĐžĐēŅƒĐ˛Đ°ĐŊĐŊŅĐŧ ŅĐ¸ŅŅ‚ĐĩĐŧи" }, "onIdle": { - "message": "On system idle" + "message": "БĐĩĐˇĐ´Ņ–ŅĐģҌĐŊŅ–ŅŅ‚ŅŒ ŅĐ¸ŅŅ‚ĐĩĐŧи" }, "onSleep": { - "message": "On system sleep" + "message": "Đ ĐĩĐļиĐŧ ҁĐŊ҃" }, "onRestart": { "message": "З ĐŋĐĩŅ€ĐĩСаĐŋ҃ҁĐēĐžĐŧ ĐąŅ€Đ°ŅƒĐˇĐĩŅ€Đ°" @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "ЕĐēҁĐŋĐžŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ С" }, - "exportVault": { - "message": "ЕĐēҁĐŋĐžŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đĩ" + "exportVerb": { + "message": "ЕĐēҁĐŋĐžŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸", + "description": "The verb form of the word Export" + }, + "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": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚ Ņ„Đ°ĐšĐģ҃" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "ДоĐēĐģадĐŊŅ–ŅˆĐĩ" }, + "migrationsFailed": { + "message": "ĐĄŅ‚Đ°ĐģĐ°ŅŅ ĐŋĐžĐŧиĐģĐēа ĐŋŅ–Đ´ Ņ‡Đ°Ņ ĐžĐŊОвĐģĐĩĐŊĐŊŅ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊҌ ŅˆĐ¸Ņ„Ņ€ŅƒĐ˛Đ°ĐŊĐŊŅ." + }, + "updateEncryptionSettingsTitle": { + "message": "ОĐŊĐžĐ˛Ņ–Ņ‚ŅŒ ŅĐ˛ĐžŅ— ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ŅˆĐ¸Ņ„Ņ€ŅƒĐ˛Đ°ĐŊĐŊŅ" + }, + "updateEncryptionSettingsDesc": { + "message": "ĐĐžĐ˛Ņ– Ņ€ĐĩĐēĐžĐŧĐĩĐŊдОваĐŊŅ– ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ ŅˆĐ¸Ņ„Ņ€ŅƒĐ˛Đ°ĐŊĐŊŅ ĐŋĐžĐēŅ€Đ°Ņ‰Đ°Ņ‚ŅŒ ĐąĐĩСĐŋĐĩĐē҃ Đ˛Đ°ŅˆĐžĐŗĐž ОйĐģŅ–ĐēĐžĐ˛ĐžĐŗĐž СаĐŋĐ¸ŅŅƒ. ЊОй ĐžĐŊĐžĐ˛Đ¸Ņ‚Đ¸ Ņ—Ņ…, ввĐĩĐ´Ņ–Ņ‚ŅŒ ŅĐ˛Ņ–Đš ĐŗĐžĐģОвĐŊиК ĐŋĐ°Ņ€ĐžĐģҌ." + }, + "confirmIdentityToContinue": { + "message": "ЊОй ĐŋŅ€ĐžĐ´ĐžĐ˛ĐļĐ¸Ņ‚Đ¸, ĐŋŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ŅŒŅ‚Đĩ ŅĐ˛ĐžŅ— ОйĐģŅ–ĐēĐžĐ˛Ņ– даĐŊŅ–" + }, + "enterYourMasterPassword": { + "message": "ВвĐĩĐ´Ņ–Ņ‚ŅŒ ĐŗĐžĐģОвĐŊиК ĐŋĐ°Ņ€ĐžĐģҌ" + }, + "updateSettings": { + "message": "ОĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ĐŊаĐģĐ°ŅˆŅ‚ŅƒĐ˛Đ°ĐŊĐŊŅ" + }, + "later": { + "message": "ĐŸŅ–ĐˇĐŊŅ–ŅˆĐĩ" + }, "authenticatorKeyTotp": { "message": "КĐģŅŽŅ‡ Đ°Đ˛Ņ‚ĐĩĐŊŅ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ†Ņ–Ņ— (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "ВĐēĐģадĐĩĐŊĐŊŅ СйĐĩŅ€ĐĩĐļĐĩĐŊĐž" }, + "fixEncryption": { + "message": "ВиĐŋŅ€Đ°Đ˛Đ¸Ņ‚Đ¸ ŅˆĐ¸Ņ„Ņ€ŅƒĐ˛Đ°ĐŊĐŊŅ" + }, + "fixEncryptionTooltip": { + "message": "ДĐģŅ Ņ†ŅŒĐžĐŗĐž Ņ„Đ°ĐšĐģ҃ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ”Ņ‚ŅŒŅŅ ĐˇĐ°ŅŅ‚Đ°Ņ€Ņ–ĐģиК ĐŧĐĩŅ‚ĐžĐ´ ŅˆĐ¸Ņ„Ņ€ŅƒĐ˛Đ°ĐŊĐŊŅ." + }, + "attachmentUpdated": { + "message": "ВĐēĐģадĐĩĐŊĐŊŅ ĐžĐŊОвĐģĐĩĐŊĐž" + }, "file": { "message": "ФаКĐģ" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "ОбĐĩŅ€Ņ–Ņ‚ŅŒ Ņ„Đ°ĐšĐģ" }, + "itemsTransferred": { + "message": "ЗаĐŋĐ¸ŅĐ¸ ĐŋĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊĐž" + }, "maxFileSize": { "message": "МаĐēŅĐ¸ĐŧаĐģҌĐŊиК Ņ€ĐžĐˇĐŧŅ–Ņ€ Ņ„Đ°ĐšĐģ҃ 500 МБ." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1 ГБ ĐˇĐ°ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐžĐŗĐž ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ° Đ´ĐģŅ Ņ„Đ°ĐšĐģŅ–Đ˛." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ ĐˇĐ°ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐžĐŗĐž ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ° Đ´ĐģŅ вĐēĐģадĐĩĐŊĐ¸Ņ… Ņ„Đ°ĐšĐģŅ–Đ˛.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "ЕĐēҁ҂ҀĐĩĐŊиК Đ´ĐžŅŅ‚ŅƒĐŋ." }, "premiumSignUpTwoStepOptions": { "message": "Đ”ĐžĐ´Đ°Ņ‚ĐēĐžĐ˛Ņ– ĐŧĐžĐļĐģĐ¸Đ˛ĐžŅŅ‚Ņ– двОĐĩŅ‚Đ°ĐŋĐŊĐžŅ— Đ°Đ˛Ņ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Ņ–Ņ—, ŅĐē-ĐžŅ‚ YubiKey Ņ‚Đ° Duo." }, + "premiumSubscriptionEnded": { + "message": "Đ’Đ°ŅˆĐ° ĐŋĐĩŅ€ĐĩĐ´ĐŋĐģĐ°Ņ‚Đ° Premium СавĐĩŅ€ŅˆĐ¸ĐģĐ°ŅŅŒ" + }, + "archivePremiumRestart": { + "message": "ЊОй Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž Đ°Ņ€Ņ…Ņ–Đ˛Ņƒ, ĐŋĐžĐŊĐžĐ˛Ņ–Ņ‚ŅŒ ĐŋĐĩŅ€ĐĩĐ´ĐŋĐģĐ°Ņ‚Ņƒ Premium. Đ¯ĐēŅ‰Đž ви Ņ€ĐĩĐ´Đ°ĐŗŅƒŅ”Ņ‚Đĩ Đ°Ņ€Ņ…Ņ–Đ˛ĐžĐ˛Đ°ĐŊиК СаĐŋĐ¸Ņ ĐŋĐĩŅ€ĐĩĐ´ ĐŋĐžĐŊОвĐģĐĩĐŊĐŊŅĐŧ, ĐšĐžĐŗĐž ĐąŅƒĐ´Đĩ ĐŋОвĐĩŅ€ĐŊŅƒŅ‚Đž ĐŊаСад ҃ Đ˛Đ°ŅˆĐĩ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đĩ." + }, + "restartPremium": { + "message": "ПоĐŊĐžĐ˛Đ¸Ņ‚Đ¸ Premium" + }, "ppremiumSignUpReports": { "message": "Đ“Ņ–ĐŗŅ–Ņ”ĐŊа ĐŋĐ°Ņ€ĐžĐģŅ–Đ˛, ĐˇĐ´ĐžŅ€ĐžĐ˛'Ņ ОйĐģŅ–ĐēĐžĐ˛ĐžĐŗĐž СаĐŋĐ¸ŅŅƒ, а Ņ‚Đ°ĐēĐžĐļ ĐˇĐ˛Ņ–Ņ‚Đ¸ ĐŋŅ€Đž Đ˛Ņ€Đ°ĐˇĐģĐ¸Đ˛ĐžŅŅ‚Ņ– даĐŊĐ¸Ņ…, Ņ‰ĐžĐą СйĐĩŅ€Ņ–ĐŗĐ°Ņ‚Đ¸ Đ˛Đ°ŅˆĐĩ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đĩ в ĐąĐĩСĐŋĐĩ҆Җ." }, @@ -1874,7 +1950,7 @@ "message": "Đ Ņ–Đē СавĐĩŅ€ŅˆĐĩĐŊĐŊŅ" }, "monthly": { - "message": "month" + "message": "ĐŧŅ–ŅŅŅ†ŅŒ" }, "expiration": { "message": "ĐĸĐĩŅ€ĐŧŅ–ĐŊ Đ´Ņ–Ņ—" @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "ЗаĐŋĐ¸Ņ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊĐž" }, + "archivedItemRestored": { + "message": "ĐŅ€Ņ…Ņ–Đ˛ĐžĐ˛Đ°ĐŊиК СаĐŋĐ¸Ņ Đ˛Ņ–Đ´ĐŊОвĐģĐĩĐŊĐž" + }, "restoreItem": { "message": "Đ’Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ СаĐŋĐ¸Ņ" }, @@ -2446,7 +2525,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": "Đ’ŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ ĐŗĐžĐģОвĐŊиК ĐŋĐ°Ņ€ĐžĐģҌ" @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "НĐĩ СĐŊаКдĐĩĐŊĐž ҃ĐŊŅ–ĐēаĐģҌĐŊиК Ņ–Đ´ĐĩĐŊŅ‚Đ¸Ņ„Ņ–ĐēĐ°Ņ‚ĐžŅ€." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "ГоĐģОвĐŊиК ĐŋĐ°Ņ€ĐžĐģҌ ĐąŅ–ĐģҌ҈Đĩ ĐŊĐĩ Ņ” ОйОв'ŅĐˇĐēОвиĐŧ Đ´ĐģŅ ŅƒŅ‡Đ°ŅĐŊиĐēŅ–Đ˛ СаСĐŊĐ°Ņ‡ĐĩĐŊĐžŅ— ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ—. ĐŸŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ŅŒŅ‚Đĩ вĐēаСаĐŊиК ĐŊиĐļ҇Đĩ Đ´ĐžĐŧĐĩĐŊ С адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ĐžĐŧ Đ˛Đ°ŅˆĐžŅ— ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ—." - }, "organizationName": { "message": "Назва ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ—" }, @@ -3294,6 +3370,12 @@ "error": { "message": "ПоĐŧиĐģĐēа" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "ПоĐŧиĐģĐēа Ņ€ĐžĐˇŅˆĐ¸Ņ„Ņ€ŅƒĐ˛Đ°ĐŊĐŊŅ" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Đ†ĐŗĐŊĐžŅ€ŅƒĐ˛Đ°Ņ‚Đ¸" }, - "importData": { - "message": "ІĐŧĐŋĐžŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ даĐŊŅ–", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "ПоĐŧиĐģĐēа Ņ–ĐŧĐŋĐžŅ€Ņ‚Ņƒ" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "Đ‘Ņ–ĐģҌ҈Đĩ ĐžĐŋŅ†Ņ–Đš" + }, "moreOptionsTitle": { "message": "ІĐŊŅˆŅ– ĐŧĐžĐļĐģĐ¸Đ˛ĐžŅŅ‚Ņ– – $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "ĐēĐžĐŊŅĐžĐģŅ– адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°," }, + "admin": { + "message": "АдĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€" + }, + "automaticUserConfirmation": { + "message": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐĩ ĐŋŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ĐļĐĩĐŊĐŊŅ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–Đ˛" + }, + "automaticUserConfirmationHint": { + "message": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ĐļŅƒĐ˛Đ°Ņ‚Đ¸ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–Đ˛, ŅĐēŅ– ĐŋĐĩŅ€ĐĩĐąŅƒĐ˛Đ°ŅŽŅ‚ŅŒ ҃ ҇ĐĩŅ€ĐˇŅ–, ĐŋĐžĐēи ҆ĐĩĐš ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš Ņ€ĐžĐˇĐąĐģĐžĐēОваĐŊиК" + }, + "autoConfirmOnboardingCallout": { + "message": "Đ—Đ°ĐžŅ‰Đ°Đ´ĐļŅƒĐšŅ‚Đĩ Ņ‡Đ°Ņ ĐˇĐ°Đ˛Đ´ŅĐēи Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐžĐŧ҃ ĐŋŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ĐļĐĩĐŊĐŊŅŽ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–Đ˛" + }, + "autoConfirmWarning": { + "message": "ĐĻĐĩ ĐŧĐžĐļĐĩ вĐŋĐģиĐŊŅƒŅ‚Đ¸ ĐŊа ĐąĐĩСĐŋĐĩĐē҃ даĐŊĐ¸Ņ… Đ˛Đ°ŅˆĐžŅ— ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ—. " + }, + "autoConfirmWarningLink": { + "message": "Đ”Ņ–ĐˇĐŊĐ°Ņ‚Đ¸ŅŅ ĐŋŅ€Đž Ņ€Đ¸ĐˇĐ¸Đēи" + }, + "autoConfirmSetup": { + "message": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ĐļŅƒĐ˛Đ°Ņ‚Đ¸ ĐŊĐžĐ˛Đ¸Ņ… ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–Đ˛" + }, + "autoConfirmSetupDesc": { + "message": "ĐĐžĐ˛Ņ– ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ– ĐąŅƒĐ´ŅƒŅ‚ŅŒ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ĐļĐĩĐŊŅ–, ŅĐēŅ‰Đž ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš Ņ€ĐžĐˇĐąĐģĐžĐēОваĐŊĐž." + }, + "autoConfirmSetupHint": { + "message": "Đ¯ĐēŅ– ĐŋĐžŅ‚ĐĩĐŊŅ†Ņ–ĐšĐŊŅ– Ņ€Đ¸ĐˇĐ¸Đēи ĐąĐĩСĐŋĐĩĐēи?" + }, + "autoConfirmEnabled": { + "message": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐĩ ĐŋŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ĐļĐĩĐŊĐŊŅ ŅƒĐ˛Ņ–ĐŧĐēĐŊĐĩĐŊĐž" + }, + "availableNow": { + "message": "Đ”ĐžŅŅ‚ŅƒĐŋĐŊĐž ĐˇĐ°Ņ€Đ°Đˇ" + }, "accountSecurity": { "message": "БĐĩСĐŋĐĩĐēа ОйĐģŅ–ĐēĐžĐ˛ĐžĐŗĐž СаĐŋĐ¸ŅŅƒ" }, + "phishingBlocker": { + "message": "БĐģĐžĐēŅƒĐ˛Đ°ĐģҌĐŊиĐē ŅˆĐ°Ņ…Ņ€Đ°ĐšŅŅ‚Đ˛Đ°" + }, + "enablePhishingDetection": { + "message": "Đ’Đ¸ŅĐ˛ĐģĐĩĐŊĐŊŅ ŅˆĐ°Ņ…Ņ€Đ°ĐšŅŅ‚Đ˛Đ°" + }, + "enablePhishingDetectionDesc": { + "message": "ПоĐēĐ°ĐˇŅƒĐ˛Đ°Ņ‚Đ¸ ĐŋĐžĐŋĐĩŅ€ĐĩĐ´ĐļĐĩĐŊĐŊŅ ĐŋĐĩŅ€ĐĩĐ´ Đ˛Ņ–Đ´Đ˛Ņ–Đ´ŅƒĐ˛Đ°ĐŊĐŊŅĐŧ ĐŋŅ–Đ´ĐžĐˇŅ€ŅŽĐ˛Đ°ĐŊĐ¸Ņ… ŅˆĐ°Ņ…Ņ€Đ°ĐšŅŅŒĐēĐ¸Ņ… ŅĐ°ĐšŅ‚Ņ–Đ˛" + }, "notifications": { "message": "ĐĄĐŋĐžĐ˛Ņ–Ņ‰ĐĩĐŊĐŊŅ" }, @@ -4912,7 +5035,7 @@ "message": "Premium" }, "unlockFeaturesWithPremium": { - "message": "Unlock reporting, emergency access, and more security features with Premium." + "message": "РОСйĐģĐžĐēŅƒĐšŅ‚Đĩ ĐˇĐ˛Ņ–Ņ‚Đ¸, ĐĩĐēҁ҂ҀĐĩĐŊиК Đ´ĐžŅŅ‚ŅƒĐŋ Ņ‚Đ° Ņ–ĐŊŅˆŅ– Ņ„ŅƒĐŊĐē҆Җҗ ĐąĐĩСĐŋĐĩĐēи, ĐŋĐĩŅ€ĐĩĐ´ĐŋĐģĐ°Ņ‚Đ¸Đ˛ŅˆĐ¸ Premium." }, "freeOrgsCannotUseAttachments": { "message": "ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ— ĐąĐĩС ĐŋĐĩŅ€ĐĩĐ´ĐŋĐģĐ°Ņ‚Đ¸ ĐŊĐĩ ĐŧĐžĐļŅƒŅ‚ŅŒ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒĐ˛Đ°Ņ‚Đ¸ вĐēĐģадĐĩĐŊĐŊŅ" @@ -4999,7 +5122,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": { @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "ĐŸŅ€Đ¸Ņ…ĐžĐ˛Đ°Ņ‚Đ¸ Đ˛Đ¸ŅĐ˛ĐģĐĩĐŊĐŊŅ ĐˇĐąŅ–ĐŗŅ–Đ˛ $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "ПоĐēĐ°ĐˇĐ°Ņ‚Đ¸ Đ˛Đ¸ŅĐ˛ĐģĐĩĐŊĐŊŅ ĐˇĐąŅ–ĐŗŅ–Đ˛" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "ĐŸŅ€Đ¸Ņ…ĐžĐ˛Đ°Ņ‚Đ¸ Đ˛Đ¸ŅĐ˛ĐģĐĩĐŊĐŊŅ ĐˇĐąŅ–ĐŗŅ–Đ˛" }, "autoFillOnPageLoad": { "message": "ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž СаĐŋОвĐŊŅŽĐ˛Đ°Ņ‚Đ¸ ĐŋŅ–Đ´ Ņ‡Đ°Ņ СаваĐŊŅ‚Đ°ĐļĐĩĐŊĐŊŅ ŅŅ‚ĐžŅ€Ņ–ĐŊĐēи?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Đ”ŅƒĐļĐĩ ŅˆĐ¸Ņ€ĐžĐēĐĩ" }, + "narrow": { + "message": "Đ’ŅƒĐˇŅŒĐēиК" + }, "sshKeyWrongPassword": { "message": "Ви ввĐĩĐģи ĐŊĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģҌĐŊиК ĐŋĐ°Ņ€ĐžĐģҌ." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "ĐĻĐĩĐš СаĐŋĐ¸Ņ Ņ€Đ¸ĐˇĐ¸ĐēОваĐŊиК, Ņ– ĐŊĐĩ ĐŧĐ°Ņ” Đ°Đ´Ņ€ĐĩŅĐ¸ вĐĩĐąŅĐ°ĐšŅ‚Ņƒ. Đ”ĐžĐ´Đ°ĐšŅ‚Đĩ Đ°Đ´Ņ€Đĩҁ҃ вĐĩĐąŅĐ°ĐšŅ‚Ņƒ Ņ– СĐŧŅ–ĐŊŅ–Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ Đ´ĐģŅ Đ˛Đ´ĐžŅĐēĐžĐŊаĐģĐĩĐŊĐŊŅ ĐąĐĩСĐŋĐĩĐēи." }, + "vulnerablePassword": { + "message": "Đ’Ņ€Đ°ĐˇĐģивиК ĐŋĐ°Ņ€ĐžĐģҌ." + }, + "changeNow": { + "message": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ĐˇĐ°Ņ€Đ°Đˇ" + }, "missingWebsite": { "message": "НĐĩĐŧĐ°Ņ” вĐĩĐąŅĐ°ĐšŅ‚Ņƒ" }, @@ -5818,26 +5947,26 @@ "andMoreFeatures": { "message": "ІĐŊŅˆŅ– ĐŧĐžĐļĐģĐ¸Đ˛ĐžŅŅ‚Ņ–!" }, - "planDescPremium": { - "message": "ПовĐŊа ĐžĐŊĐģаКĐŊ-ĐąĐĩСĐŋĐĩĐēа" + "advancedOnlineSecurity": { + "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": "ĐĻĐĩĐš ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁ виĐŧĐēĐŊĐĩĐŊĐž ĐŋĐžĐģŅ–Ņ‚Đ¸ĐēĐžŅŽ Đ˛Đ°ŅˆĐžŅ— ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ—.", @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "НоĐŧĐĩŅ€ ĐēĐ°Ņ€Ņ‚Đēи" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ ĐąŅ–ĐģҌ҈Đĩ ĐŊĐĩ виĐēĐžŅ€Đ¸ŅŅ‚ĐžĐ˛ŅƒŅ” ĐŗĐžĐģОвĐŊŅ– ĐŋĐ°Ņ€ĐžĐģŅ– Đ´ĐģŅ Đ˛Ņ…ĐžĐ´Ņƒ в Bitwarden. ЊОй ĐŋŅ€ĐžĐ´ĐžĐ˛ĐļĐ¸Ņ‚Đ¸, ĐŋŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´Ņ–Ņ‚ŅŒ ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–ŅŽ Ņ‚Đ° Đ´ĐžĐŧĐĩĐŊ." + }, + "continueWithLogIn": { + "message": "ПĐĩŅ€ĐĩĐšŅ‚Đ¸ Đ´Đž Đ˛Ņ…ĐžĐ´Ņƒ" + }, + "doNotContinue": { + "message": "НĐĩ ĐŋŅ€ĐžĐ´ĐžĐ˛ĐļŅƒĐ˛Đ°Ņ‚Đ¸" + }, + "domain": { + "message": "ДоĐŧĐĩĐŊ" + }, + "keyConnectorDomainTooltip": { + "message": "ĐĻĐĩĐš Đ´ĐžĐŧĐĩĐŊ СйĐĩŅ€Ņ–ĐŗĐ°Ņ‚Đ¸ĐŧĐĩ ĐēĐģŅŽŅ‡Ņ– ŅˆĐ¸Ņ„Ņ€ŅƒĐ˛Đ°ĐŊĐŊŅ Đ˛Đ°ŅˆĐžĐŗĐž ОйĐģŅ–ĐēĐžĐ˛ĐžĐŗĐž СаĐŋĐ¸ŅŅƒ, Ņ‚ĐžĐŧ҃ ĐŋĐĩŅ€ĐĩĐēĐžĐŊĐ°ĐšŅ‚ĐĩŅŅ в ĐšĐžĐŗĐž ĐŊĐ°Đ´Ņ–ĐšĐŊĐžŅŅ‚Ņ–. Đ¯ĐēŅ‰Đž ви ĐŊĐĩ вĐŋĐĩвĐŊĐĩĐŊŅ–, СвĐĩŅ€ĐŊŅ–Ņ‚ŅŒŅŅ Đ´Đž ŅĐ˛ĐžĐŗĐž адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°." + }, + "verifyYourOrganization": { + "message": "ĐŸŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ŅŒŅ‚Đĩ ŅĐ˛ĐžŅŽ ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–ŅŽ, Ņ‰ĐžĐą ŅƒĐ˛Ņ–ĐšŅ‚Đ¸" + }, + "organizationVerified": { + "message": "ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–ŅŽ ĐŋŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ĐļĐĩĐŊĐž" + }, + "domainVerified": { + "message": "ДоĐŧĐĩĐŊ ĐŋŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ĐļĐĩĐŊĐž" + }, + "leaveOrganizationContent": { + "message": "Đ¯ĐēŅ‰Đž ви ĐŊĐĩ ĐŋŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ŅĐ˛ĐžŅŽ ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–ŅŽ, Đ˛Đ°Ņˆ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž ĐŊĐĩŅ— ĐąŅƒĐ´Đĩ Đ˛Ņ–Đ´ĐēĐģиĐēаĐŊиК." + }, + "leaveNow": { + "message": "ПоĐēиĐŊŅƒŅ‚Đ¸" + }, + "verifyYourDomainToLogin": { + "message": "ĐŸŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ŅŒŅ‚Đĩ ŅĐ˛Ņ–Đš Đ´ĐžĐŧĐĩĐŊ, Ņ‰ĐžĐą ŅƒĐ˛Ņ–ĐšŅ‚Đ¸" + }, + "verifyYourDomainDescription": { + "message": "ЊОй ĐŋĐĩŅ€ĐĩĐšŅ‚Đ¸ Đ´Đž Đ˛Ņ…ĐžĐ´Ņƒ, ĐŋŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ŅŒŅ‚Đĩ ҆ĐĩĐš Đ´ĐžĐŧĐĩĐŊ." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "ЊОй ĐŋĐĩŅ€ĐĩĐšŅ‚Đ¸ Đ´Đž Đ˛Ņ…ĐžĐ´Ņƒ, ĐŋŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ŅŒŅ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–ŅŽ Ņ– Đ´ĐžĐŧĐĩĐŊ." + }, "sessionTimeoutSettingsAction": { - "message": "Timeout action" + "message": "Đ”Ņ–Ņ ĐŋҖҁĐģŅ Ņ‡Đ°ŅŅƒ ĐžŅ‡Ņ–ĐēŅƒĐ˛Đ°ĐŊĐŊŅ" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "ĐĻиĐŧ ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€ĐžĐŧ ĐēĐĩŅ€ŅƒŅ” Đ˛Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ Đ˛ŅŅ‚Đ°ĐŊОвиĐģа ĐŧаĐēŅĐ¸ĐŧаĐģҌĐŊиК Ņ‡Đ°Ņ ĐžŅ‡Ņ–ĐēŅƒĐ˛Đ°ĐŊĐŊŅ ҁĐĩаĐŊҁ҃ $HOURS$ ĐŗĐžĐ´ Ņ– $MINUTES$ Ņ…Đ˛.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ Đ˛ŅŅ‚Đ°ĐŊОвиĐģа ĐŧаĐēŅĐ¸ĐŧаĐģҌĐŊиК Ņ‡Đ°Ņ ĐžŅ‡Ņ–ĐēŅƒĐ˛Đ°ĐŊĐŊŅ ҁĐĩаĐŊҁ҃ \"НĐĩĐŗĐ°ĐšĐŊĐž\"." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ Đ˛ŅŅ‚Đ°ĐŊОвиĐģа ĐŧаĐēŅĐ¸ĐŧаĐģҌĐŊиК Ņ‡Đ°Ņ ĐžŅ‡Ņ–ĐēŅƒĐ˛Đ°ĐŊĐŊŅ ҁĐĩаĐŊҁ҃ \"БĐģĐžĐēŅƒĐ˛Đ°ĐŊĐŊŅ ŅĐ¸ŅŅ‚ĐĩĐŧи\"." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ Đ˛ŅŅ‚Đ°ĐŊОвиĐģа ĐŧаĐēŅĐ¸ĐŧаĐģҌĐŊиК Ņ‡Đ°Ņ ĐžŅ‡Ņ–ĐēŅƒĐ˛Đ°ĐŊĐŊŅ ҁĐĩаĐŊҁ҃ \"З ĐŋĐĩŅ€ĐĩСаĐŋ҃ҁĐēĐžĐŧ ĐąŅ€Đ°ŅƒĐˇĐĩŅ€Đ°\"." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "МаĐēŅĐ¸ĐŧаĐģҌĐŊиК Ņ‡Đ°Ņ ĐžŅ‡Ņ–ĐēŅƒĐ˛Đ°ĐŊĐŊŅ ĐŊĐĩ ĐŧĐžĐļĐĩ ĐŋĐĩŅ€ĐĩĐ˛Đ¸Ņ‰ŅƒĐ˛Đ°Ņ‚Đ¸ $HOURS$ ĐŗĐžĐ´ Ņ– $MINUTES$ Ņ…Đ˛", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "З ĐŋĐĩŅ€ĐĩСаĐŋ҃ҁĐēĐžĐŧ ĐąŅ€Đ°ŅƒĐˇĐĩŅ€Đ°" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Đ’ŅŅ‚Đ°ĐŊĐžĐ˛Ņ–Ņ‚ŅŒ ҁĐŋĐžŅŅ–Đą Ņ€ĐžĐˇĐąĐģĐžĐēŅƒĐ˛Đ°ĐŊĐŊŅ, Ņ‰ĐžĐą СĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ Đ´Ņ–ŅŽ ĐŋŅ–Đ´ Ņ‡Đ°Ņ ĐžŅ‡Ņ–ĐēŅƒĐ˛Đ°ĐŊĐŊŅ" + }, + "upgrade": { + "message": "ОĐŊĐžĐ˛Đ¸Ņ‚Đ¸" + }, + "leaveConfirmationDialogTitle": { + "message": "Ви Đ´Ņ–ĐšŅĐŊĐž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ĐŋĐžĐēиĐŊŅƒŅ‚Đ¸?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Đ’Ņ–Đ´Ņ…Đ¸ĐģĐ¸Đ˛ŅˆĐ¸, Đ˛Đ°ŅˆŅ– ĐžŅĐžĐąĐ¸ŅŅ‚Ņ– СаĐŋĐ¸ŅĐ¸ СаĐģĐ¸ŅˆĐ°Ņ‚ŅŒŅŅ ҃ Đ˛Đ°ŅˆĐžĐŧ҃ ОйĐģŅ–ĐēОвОĐŧ҃ СаĐŋĐ¸ŅŅ–, аĐģĐĩ ви Đ˛Ņ‚Ņ€Đ°Ņ‚Đ¸Ņ‚Đĩ Đ´ĐžŅŅ‚ŅƒĐŋ Đ´Đž ҁĐŋŅ–ĐģҌĐŊĐ¸Ņ… СаĐŋĐ¸ŅŅ–Đ˛ Ņ‚Đ° Ņ„ŅƒĐŊĐēŅ†Ņ–Đš ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ—." + }, + "leaveConfirmationDialogContentTwo": { + "message": "ЗвĐĩŅ€ĐŊŅ–Ņ‚ŅŒŅŅ Đ´Đž ŅĐ˛ĐžĐŗĐž адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°, Ņ‰ĐžĐą Đ˛Ņ–Đ´ĐŊĐžĐ˛Đ¸Ņ‚Đ¸ Đ´ĐžŅŅ‚ŅƒĐŋ." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "ПоĐēиĐŊŅƒŅ‚Đ¸ $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Đ¯Đē ĐēĐĩŅ€ŅƒĐ˛Đ°Ņ‚Đ¸ ŅĐ˛ĐžŅ—Đŧ ŅŅ…ĐžĐ˛Đ¸Ņ‰ĐĩĐŧ?" + }, + "transferItemsToOrganizationTitle": { + "message": "ПĐĩŅ€ĐĩĐŧŅ–ŅŅ‚Đ¸Ņ‚Đ¸ СаĐŋĐ¸ŅĐ¸ Đ´Đž $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "З ĐŧŅ–Ņ€ĐēŅƒĐ˛Đ°ĐŊҌ ĐąĐĩСĐŋĐĩĐēи Ņ‚Đ° Đ´ĐģŅ СайĐĩСĐŋĐĩ҇ĐĩĐŊĐŊŅ Đ˛Ņ–Đ´ĐŋĐžĐ˛Ņ–Đ´ĐŊĐžŅŅ‚Ņ– $ORGANIZATION$ виĐŧĐ°ĐŗĐ°Ņ”, Ņ‰ĐžĐą вĐģĐ°ŅĐŊиĐēĐžĐŧ Đ˛ŅŅ–Ņ… ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Ņ–Đ˛ ĐąŅƒĐģа ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ. ĐĐ°Ņ‚Đ¸ŅĐŊŅ–Ņ‚ŅŒ ĐēĐŊĐžĐŋĐē҃ \"ĐŋŅ€Đ¸ĐšĐŊŅŅ‚Đ¸\", Ņ‰ĐžĐą ĐŋĐĩŅ€ĐĩĐ´Đ°Ņ‚Đ¸ ĐŋŅ€Đ°Đ˛Đ° вĐģĐ°ŅĐŊĐžŅŅ‚Ņ– ĐŊа Đ˛Đ°ŅˆŅ– ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "ĐĄŅ…Đ˛Đ°ĐģĐ¸Ņ‚Đ¸ ĐŋĐĩŅ€ĐĩĐŧҖ҉ĐĩĐŊĐŊŅ" + }, + "declineAndLeave": { + "message": "Đ’Ņ–Đ´Ņ…Đ¸ĐģĐ¸Ņ‚Đ¸ Ņ– ĐŋĐžĐēиĐŊŅƒŅ‚Đ¸" + }, + "whyAmISeeingThis": { + "message": "ЧОĐŧ҃ Ņ ҆Đĩ ĐąĐ°Ņ‡Ņƒ?" + }, + "resizeSideNavigation": { + "message": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ Ņ€ĐžĐˇĐŧŅ–Ņ€ ĐąŅ–Ņ‡ĐŊĐžŅ— ĐŋаĐŊĐĩĐģŅ–" } } diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 415cf474af0..fdac572e550 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "Đăng nháē­p báēąng khÃŗa truy cáē­p" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "DÚng đăng nháē­p máģ™t láē§n" }, @@ -436,8 +439,8 @@ "sync": { "message": "Đáģ“ng báģ™" }, - "syncVaultNow": { - "message": "Đáģ“ng báģ™ kho lưu tráģ¯ ngay" + "syncNow": { + "message": "Đáģ“ng báģ™ ngay" }, "lastSync": { "message": "Đáģ“ng báģ™ láē§n cuáģ‘i:" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "áģ¨ng dáģĨng web Bitwarden" }, - "importItems": { - "message": "Nháē­p vào kho" - }, "select": { "message": "Cháģn" }, @@ -576,17 +576,29 @@ "itemWasSentToArchive": { "message": "MáģĨc Ä‘ÃŖ đưáģŖc chuyáģƒn vào kho lưu tráģ¯" }, + "itemWasUnarchived": { + "message": "MáģĨc Ä‘ÃŖ đưáģŖc báģ lưu tráģ¯" + }, "itemUnarchived": { "message": "MáģĨc Ä‘ÃŖ đưáģŖc báģ lưu tráģ¯" }, "archiveItem": { "message": "Lưu tráģ¯ máģĨc" }, - "archiveItemConfirmDesc": { - "message": "CÃĄc máģĨc Ä‘ÃŖ lưu tráģ¯ sáēŊ báģ‹ loáēĄi kháģi káēŋt quáēŖ tÃŦm kiáēŋm chung và gáģŖi ÃŊ táģą Ä‘áģ™ng điáģn. BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n lưu tráģ¯ máģĨc này không?" + "archiveItemDialogContent": { + "message": "Khi Ä‘ÃŖ lưu tráģ¯, máģĨc này sáēŊ báģ‹ loáēĄi kháģi káēŋt quáēŖ tÃŦm kiáēŋm và gáģŖi ÃŊ táģą Ä‘áģ™ng điáģn." + }, + "archived": { + "message": "ÄÃŖ lưu tráģ¯" + }, + "unarchiveAndSave": { + "message": "Báģ lưu tráģ¯ và lưu" }, "upgradeToUseArchive": { - "message": "A premium membership is required to use Archive." + "message": "Cáē§n là thành viÃĒn cao cáēĨp đáģƒ sáģ­ dáģĨng tính năng Lưu tráģ¯." + }, + "itemRestored": { + "message": "MáģĨc Ä‘ÃŖ đưáģŖc khôi pháģĨc" }, "edit": { "message": "Sáģ­a" @@ -598,10 +610,10 @@ "message": "Xem táēĨt cáēŖ" }, "showAll": { - "message": "Show all" + "message": "Hiáģ‡n táēĨt cáēŖ" }, "viewLess": { - "message": "View less" + "message": "áē¨n báģ›t" }, "viewLogin": { "message": "Xem đăng nháē­p" @@ -806,10 +818,10 @@ "message": "Máģ—i khi khÃŗa mÃĄy" }, "onIdle": { - "message": "On system idle" + "message": "Khi háģ‡ tháģ‘ng nhàn ráģ—i" }, "onSleep": { - "message": "On system sleep" + "message": "Khi háģ‡ tháģ‘ng ngáģ§" }, "onRestart": { "message": "Máģ—i khi kháģŸi đáģ™ng láēĄi trÃŦnh duyáģ‡t" @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "XuáēĨt táģĢ" }, - "exportVault": { - "message": "XuáēĨt kho" + "exportVerb": { + "message": "XuáēĨt", + "description": "The verb form of the word Export" + }, + "exportNoun": { + "message": "XuáēĨt", + "description": "The noun form of the word Export" + }, + "importNoun": { + "message": "Nháē­p", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Nháē­p", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Đáģ‹nh dáēĄng táē­p tin" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "TÃŦm hiáģƒu thÃĒm" }, + "migrationsFailed": { + "message": "ÄÃŖ xáēŖy ra láģ—i khi cáē­p nháē­t cài đáēˇt mÃŖ hÃŗa." + }, + "updateEncryptionSettingsTitle": { + "message": "Cáē­p nháē­t cài đáēˇt mÃŖ hÃŗa cáģ§a báēĄn" + }, + "updateEncryptionSettingsDesc": { + "message": "Cài đáēˇt mÃŖ hÃŗa đưáģŖc khuyáēŋn ngháģ‹ sáēŊ cáēŖi thiáģ‡n báēŖo máē­t cho tài khoáēŖn cáģ§a báēĄn. Nháē­p máē­t kháēŠu chính đáģƒ cáē­p nháē­t ngay." + }, + "confirmIdentityToContinue": { + "message": "XÃĄc minh danh tính đáģƒ tiáēŋp táģĨc" + }, + "enterYourMasterPassword": { + "message": "Nháē­p máē­t kháēŠu chính cáģ§a báēĄn" + }, + "updateSettings": { + "message": "Cáē­p nháē­t cài đáēˇt" + }, + "later": { + "message": "Đáģƒ sau" + }, "authenticatorKeyTotp": { "message": "KhÃŗa xÃĄc tháģąc (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "ÄÃŖ lưu táģ‡p đính kèm" }, + "fixEncryption": { + "message": "Sáģ­a mÃŖ hÃŗa" + }, + "fixEncryptionTooltip": { + "message": "Táģ‡p này đang sáģ­ dáģĨng phÆ°ÆĄng phÃĄp mÃŖ hÃŗa láģ—i tháģi." + }, + "attachmentUpdated": { + "message": "Táģ‡p đính kèm Ä‘ÃŖ đưáģŖc cáē­p nháē­t" + }, "file": { "message": "Táē­p tin" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "Cháģn táē­p tin" }, + "itemsTransferred": { + "message": "CÃĄc máģĨc Ä‘ÃŖ chuyáģƒn" + }, "maxFileSize": { "message": "Kích thưáģ›c táģ‘i đa cáģ§a táē­p tin là 500MB." }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "1GB báģ™ nháģ› lưu tráģ¯ Ä‘Æ°áģŖc mÃŖ hÃŗa cho cÃĄc táģ‡p đính kèm." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ báģ™ nháģ› lưu tráģ¯ Ä‘Æ°áģŖc mÃŖ hÃŗa cho cÃĄc táģ‡p đính kèm.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Truy cáē­p kháēŠn cáēĨp." }, "premiumSignUpTwoStepOptions": { "message": "CÃĄc tÚy cháģn xÃĄc minh hai bưáģ›c như YubiKey và Duo." }, + "premiumSubscriptionEnded": { + "message": "GÃŗi đăng kÃŊ Cao cáēĨp cáģ§a báēĄn Ä‘ÃŖ káēŋt thÃēc" + }, + "archivePremiumRestart": { + "message": "Đáģƒ láēĨy láēĄi quyáģn truy cáē­p vào lưu tráģ¯ cáģ§a báēĄn, hÃŖy kháģŸi đáģ™ng láēĄi gÃŗi đăng kÃŊ Cao cáēĨp. Náēŋu báēĄn cháģ‰nh sáģ­a chi tiáēŋt cho máģ™t máģĨc Ä‘ÃŖ lưu tráģ¯ trưáģ›c khi kháģŸi đáģ™ng láēĄi, máģĨc Ä‘Ãŗ sáēŊ đưáģŖc chuyáģƒn tráģŸ láēĄi kho cáģ§a báēĄn." + }, + "restartPremium": { + "message": "KháģŸi đáģ™ng láēĄi gÃŗi Cao cáēĨp" + }, "ppremiumSignUpReports": { "message": "Thanh láģc máē­t kháēŠu, kiáģƒm tra an toàn tài khoáēŖn và cÃĄc bÃĄo cÃĄo rÃ˛ ráģ‰ dáģ¯ liáģ‡u đáģƒ báēŖo váģ‡ kho dáģ¯ liáģ‡u cáģ§a báēĄn." }, @@ -1874,7 +1950,7 @@ "message": "Năm háēŋt háēĄn" }, "monthly": { - "message": "month" + "message": "thÃĄng" }, "expiration": { "message": "Háēŋt háēĄn" @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "ÄÃŖ xÃŗa vÄŠnh viáģ…n máģĨc" }, + "archivedItemRestored": { + "message": "MáģĨc lưu tráģ¯ Ä‘ÃŖ đưáģŖc khôi pháģĨc" + }, "restoreItem": { "message": "Khôi pháģĨc máģĨc" }, @@ -2446,7 +2525,7 @@ } }, "topLayerHijackWarning": { - "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + "message": "Trang này đang làm giÃĄn đoáēĄn tráēŖi nghiáģ‡m Bitwarden. Menu náģ™i tuyáēŋn cáģ§a Bitwarden Ä‘ÃŖ táēĄm tháģi báģ‹ táē¯t đáģƒ Ä‘áēŖm báēŖo an toàn." }, "setMasterPassword": { "message": "Đáēˇt máē­t kháēŠu chính" @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "Không tÃŦm tháēĨy danh tính duy nháēĨt." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Máē­t kháēŠu chính không cÃ˛n đưáģŖc yÃĒu cáē§u đáģ‘i váģ›i cÃĄc thành viÃĒn cáģ§a táģ• cháģŠc sau đÃĸy. Vui lÃ˛ng xÃĄc nháē­n tÃĒn miáģn bÃĒn dưáģ›i váģ›i quáēŖn tráģ‹ viÃĒn cáģ§a táģ• cháģŠc." - }, "organizationName": { "message": "TÃĒn táģ• cháģŠc" }, @@ -3294,6 +3370,12 @@ "error": { "message": "Láģ—i" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "Láģ—i giáēŖi mÃŖ" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "Báģ qua" }, - "importData": { - "message": "Nháē­p dáģ¯ liáģ‡u", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "Láģ—i khi nháē­p" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "ThÃĒm tuáģŗ cháģn" + }, "moreOptionsTitle": { "message": "ThÃĒm tÚy cháģn - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "BáēŖng điáģu khiáģƒn dành cho quáēŖn tráģ‹ viÃĒn" }, + "admin": { + "message": "QuáēŖn tráģ‹" + }, + "automaticUserConfirmation": { + "message": "Táģą Ä‘áģ™ng xÃĄc nháē­n ngưáģi dÚng" + }, + "automaticUserConfirmationHint": { + "message": "Táģą Ä‘áģ™ng xÃĄc nháē­n ngưáģi dÚng đang cháģ trong khi thiáēŋt báģ‹ này đưáģŖc máģŸ khÃŗa" + }, + "autoConfirmOnboardingCallout": { + "message": "Tiáēŋt kiáģ‡m tháģi gian váģ›i xÃĄc nháē­n ngưáģi dÚng táģą Ä‘áģ™ng" + }, + "autoConfirmWarning": { + "message": "Điáģu này cÃŗ tháģƒ áēŖnh hưáģŸng đáēŋn báēŖo máē­t dáģ¯ liáģ‡u cáģ§a táģ• cháģŠc báēĄn. " + }, + "autoConfirmWarningLink": { + "message": "TÃŦm hiáģƒu váģ cÃĄc ráģ§i ro" + }, + "autoConfirmSetup": { + "message": "Táģą Ä‘áģ™ng xÃĄc nháē­n ngưáģi dÚng máģ›i" + }, + "autoConfirmSetupDesc": { + "message": "Ngưáģi dÚng máģ›i sáēŊ đưáģŖc táģą Ä‘áģ™ng xÃĄc nháē­n trong khi thiáēŋt báģ‹ này đưáģŖc máģŸ khÃŗa." + }, + "autoConfirmSetupHint": { + "message": "Nháģ¯ng ráģ§i ro báēŖo máē­t tiáģm áēŠn là gÃŦ?" + }, + "autoConfirmEnabled": { + "message": "ÄÃŖ báē­t xÃĄc nháē­n táģą Ä‘áģ™ng" + }, + "availableNow": { + "message": "KháēŖ dáģĨng bÃĸy giáģ" + }, "accountSecurity": { "message": "BáēŖo máē­t tài khoáēŖn" }, + "phishingBlocker": { + "message": "TrÃŦnh cháēˇn láģĢa đáēŖo" + }, + "enablePhishingDetection": { + "message": "PhÃĄt hiáģ‡n láģĢa đáēŖo" + }, + "enablePhishingDetectionDesc": { + "message": "Hiáģƒn tháģ‹ cáēŖnh bÃĄo trưáģ›c khi truy cáē­p cÃĄc trang web nghi ngáģ láģĢa đáēŖo" + }, "notifications": { "message": "Thông bÃĄo" }, @@ -4882,7 +5005,7 @@ "message": "TáēŖi xuáģ‘ng Bitwarden" }, "downloadBitwardenOnAllDevices": { - "message": "TáēŖi xuáģ‘ng Bitwarden trÃĒn táēĨt cáēŖ cÃĄc thiáēŋt báģ‹" + "message": "TáēŖi váģ Bitwarden trÃĒn máģi thiáēŋt báģ‹" }, "getTheMobileApp": { "message": "TáēŖi áģŠng dáģĨng di đáģ™ng" @@ -4912,7 +5035,7 @@ "message": "Cao cáēĨp" }, "unlockFeaturesWithPremium": { - "message": "Unlock reporting, emergency access, and more security features with Premium." + "message": "MáģŸ khÃŗa tính năng bÃĄo cÃĄo, quyáģn truy cáē­p kháēŠn cáēĨp và nhiáģu tính năng báēŖo máē­t khÃĄc váģ›i gÃŗi Cao cáēĨp." }, "freeOrgsCannotUseAttachments": { "message": "CÃĄc táģ• cháģŠc miáģ…n phí không tháģƒ sáģ­ dáģĨng táģ‡p đính kèm" @@ -4999,7 +5122,7 @@ } }, "defaultLabelWithValue": { - "message": "Default ( $VALUE$ )", + "message": "Máēˇc đáģ‹nh ( $VALUE$ )", "description": "A label that indicates the default value for a field with the current default value in parentheses.", "placeholders": { "value": { @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "áē¨n phÃĄt hiáģ‡n trÚng kháģ›p $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "Hiáģƒn tháģ‹ phÃĄt hiáģ‡n trÚng kháģ›p" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "áē¨n phÃĄt hiáģ‡n trÚng kháģ›p" }, "autoFillOnPageLoad": { "message": "Táģą Ä‘áģ™ng điáģn khi táēŖi trang?" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "Ráģ™ng hÆĄn" }, + "narrow": { + "message": "Háēšp" + }, "sshKeyWrongPassword": { "message": "Máē­t kháēŠu báēĄn Ä‘ÃŖ nháē­p không đÃēng." }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Thông tin đăng nháē­p này cÃŗ ráģ§i ro và thiáēŋu máģ™t trang web. HÃŖy thÃĒm trang web và đáģ•i máē­t kháēŠu đáģƒ tăng cưáģng báēŖo máē­t." }, + "vulnerablePassword": { + "message": "Máē­t kháēŠu dáģ… báģ‹ táēĨn công." + }, + "changeNow": { + "message": "Thay đáģ•i ngay" + }, "missingWebsite": { "message": "Thiáēŋu trang web" }, @@ -5818,26 +5947,26 @@ "andMoreFeatures": { "message": "Và nhiáģu hÆĄn náģ¯a!" }, - "planDescPremium": { - "message": "BáēŖo máē­t tráģąc tuyáēŋn toàn diáģ‡n" + "advancedOnlineSecurity": { + "message": "BáēŖo máē­t tráģąc tuyáēŋn nÃĸng cao" }, "upgradeToPremium": { "message": "NÃĸng cáēĨp lÃĒn gÃŗi Cao cáēĨp" }, "unlockAdvancedSecurity": { - "message": "Unlock advanced security features" + "message": "MáģŸ khÃŗa cÃĄc tính năng báēŖo máē­t nÃĸng cao" }, "unlockAdvancedSecurityDesc": { - "message": "A Premium subscription gives you more tools to stay secure and in control" + "message": "Đăng kÃŊ gÃŗi Cao cáēĨp cung cáēĨp nhiáģu công cáģĨ đáģƒ báēĄn luôn an toàn và kiáģƒm soÃĄt táģ‘t hÆĄn" }, "explorePremium": { - "message": "Explore Premium" + "message": "KhÃĄm phÃĄ gÃŗi Cao cáēĨp" }, "loadingVault": { - "message": "Loading vault" + "message": "Đang táēŖi kho" }, "vaultLoaded": { - "message": "Vault loaded" + "message": "ÄÃŖ táēŖi kho" }, "settingDisabledByPolicy": { "message": "Cài đáēˇt này báģ‹ vô hiáģ‡u hÃŗa báģŸi chính sÃĄch táģ• cháģŠc cáģ§a báēĄn.", @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "Sáģ‘ tháēģ" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Táģ• cháģŠc cáģ§a báēĄn không cÃ˛n sáģ­ dáģĨng máē­t kháēŠu chính đáģƒ Ä‘Äƒng nháē­p vào Bitwarden. Đáģƒ tiáēŋp táģĨc, hÃŖy xÃĄc minh táģ• cháģŠc và tÃĒn miáģn." + }, + "continueWithLogIn": { + "message": "Tiáēŋp táģĨc đăng nháē­p" + }, + "doNotContinue": { + "message": "Không tiáēŋp táģĨc" + }, + "domain": { + "message": "TÃĒn miáģn" + }, + "keyConnectorDomainTooltip": { + "message": "TÃĒn miáģn này sáēŊ lưu tráģ¯ cÃĄc khÃŗa mÃŖ hÃŗa tài khoáēŖn cáģ§a báēĄn, vÃŦ váē­y hÃŖy đáēŖm báēŖo báēĄn tin tưáģŸng nÃŗ. Náēŋu báēĄn không cháē¯c cháē¯n, hÃŖy kiáģƒm tra váģ›i quáēŖn tráģ‹ viÃĒn cáģ§a báēĄn." + }, + "verifyYourOrganization": { + "message": "XÃĄc minh táģ• cháģŠc cáģ§a báēĄn đáģƒ Ä‘Äƒng nháē­p" + }, + "organizationVerified": { + "message": "Táģ• cháģŠc Ä‘ÃŖ đưáģŖc xÃĄc minh" + }, + "domainVerified": { + "message": "TÃĒn miáģn Ä‘ÃŖ đưáģŖc xÃĄc minh" + }, + "leaveOrganizationContent": { + "message": "Náēŋu báēĄn không xÃĄc minh táģ• cháģŠc cáģ§a mÃŦnh, quyáģn truy cáē­p vào táģ• cháģŠc sáēŊ báģ‹ thu háģ“i." + }, + "leaveNow": { + "message": "Ráģi kháģi ngay" + }, + "verifyYourDomainToLogin": { + "message": "XÃĄc minh tÃĒn miáģn cáģ§a báēĄn đáģƒ Ä‘Äƒng nháē­p" + }, + "verifyYourDomainDescription": { + "message": "Đáģƒ tiáēŋp táģĨc đăng nháē­p, hÃŖy xÃĄc minh tÃĒn miáģn này." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Đáģƒ tiáēŋp táģĨc đăng nháē­p, hÃŖy xÃĄc minh táģ• cháģŠc và tÃĒn miáģn." + }, "sessionTimeoutSettingsAction": { - "message": "Timeout action" + "message": "Hành đáģ™ng sau khi Ä‘Ãŗng kho" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Cài đáēˇt này do táģ• cháģŠc cáģ§a báēĄn quáēŖn lÃŊ." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Táģ• cháģŠc cáģ§a báēĄn Ä‘ÃŖ đáēˇt tháģi gian cháģ phiÃĒn táģ‘i đa là $HOURS$ giáģ và $MINUTES$ phÃēt.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "Táģ• cháģŠc cáģ§a báēĄn Ä‘ÃŖ đáēˇt tháģi gian cháģ phiÃĒn máēˇc đáģ‹nh là Ngay láē­p táģŠc." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Táģ• cháģŠc cáģ§a báēĄn Ä‘ÃŖ đáēˇt tháģi gian cháģ phiÃĒn máēˇc đáģ‹nh là Máģ—i khi khÃŗa mÃĄy." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Táģ• cháģŠc cáģ§a báēĄn Ä‘ÃŖ đáēˇt tháģi gian cháģ phiÃĒn máēˇc đáģ‹nh là Máģ—i khi kháģŸi đáģ™ng láēĄi trÃŦnh duyáģ‡t." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Tháģi gian cháģ táģ‘i đa không tháģƒ vưáģŖt quÃĄ $HOURS$ giáģ và $MINUTES$ phÃēt", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Máģ—i khi kháģŸi đáģ™ng láēĄi trÃŦnh duyáģ‡t" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Đáēˇt phÆ°ÆĄng tháģŠc máģŸ khÃŗa đáģƒ thay đáģ•i hành đáģ™ng khi háēŋt tháģi gian cháģ" + }, + "upgrade": { + "message": "NÃĸng cáēĨp" + }, + "leaveConfirmationDialogTitle": { + "message": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n ráģi đi không?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Báēąng viáģ‡c táģĢ cháģ‘i, cÃĄc máģĨc cÃĄ nhÃĸn sáēŊ váēĢn náēąm trong tài khoáēŖn cáģ§a báēĄn, nhưng báēĄn sáēŊ máēĨt quyáģn truy cáē­p vào cÃĄc máģĨc đưáģŖc chia sáēģ và tính năng táģ• cháģŠc." + }, + "leaveConfirmationDialogContentTwo": { + "message": "LiÃĒn háģ‡ quáēŖn tráģ‹ viÃĒn cáģ§a báēĄn đáģƒ láēĨy láēĄi quyáģn truy cáē­p." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Ráģi kháģi $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Tôi quáēŖn lÃŊ kho cáģ§a mÃŦnh như tháēŋ nào?" + }, + "transferItemsToOrganizationTitle": { + "message": "Chuyáģƒn cÃĄc máģĨc đáēŋn $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ yÃĒu cáē§u táēĨt cáēŖ cÃĄc máģĨc pháēŖi thuáģ™c sáģŸ háģ¯u cáģ§a táģ• cháģŠc đáģƒ Ä‘áēŖm báēŖo an ninh và tuÃĸn tháģ§. NháēĨp cháēĨp nháē­n đáģƒ chuyáģƒn quyáģn sáģŸ háģ¯u cÃĄc máģĨc cáģ§a báēĄn.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "CháēĨp nháē­n chuyáģƒn" + }, + "declineAndLeave": { + "message": "TáģĢ cháģ‘i và ráģi đi" + }, + "whyAmISeeingThis": { + "message": "TáēĄi sao tôi tháēĨy điáģu này?" + }, + "resizeSideNavigation": { + "message": "Thay đáģ•i kích thưáģ›c thanh bÃĒn" } } diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index 92bcb8cb90f..a4dee24b56a 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "äŊŋį”¨é€ščĄŒå¯†é’Ĩį™ģåŊ•" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "äŊŋį”¨å•į‚šį™ģåŊ•" }, @@ -436,8 +439,8 @@ "sync": { "message": "同æ­Ĩ" }, - "syncVaultNow": { - "message": "įĢ‹åŗåŒæ­Ĩ坆᠁åē“" + "syncNow": { + "message": "įĢ‹åŗåŒæ­Ĩ" }, "lastSync": { "message": "上æŦĄåŒæ­Ĩīŧš" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden įŊ‘éĄĩ App" }, - "importItems": { - "message": "å¯ŧå…ĨéĄšį›Ž" - }, "select": { "message": "选拊" }, @@ -576,18 +576,30 @@ "itemWasSentToArchive": { "message": "éĄšį›Žåˇ˛å‘é€åˆ°åŊ’æĄŖ" }, + "itemWasUnarchived": { + "message": "éĄšį›Žåˇ˛å–æļˆåŊ’æĄŖ" + }, "itemUnarchived": { "message": "éĄšį›Žåˇ˛å–æļˆåŊ’æĄŖ" }, "archiveItem": { "message": "åŊ’æĄŖéĄšį›Ž" }, - "archiveItemConfirmDesc": { - "message": "厞åŊ’æĄŖįš„éĄšį›Žå°†čĸĢæŽ’é™¤åœ¨ä¸€čˆŦ搜į´ĸį쓿žœå’Œč‡Ē动åĄĢ充åģēčŽŽäš‹å¤–ã€‚įĄŽåŽščρåŊ’æĄŖæ­¤éĄšį›Žå—īŧŸ" + "archiveItemDialogContent": { + "message": "åŊ’æĄŖåŽīŧŒæ­¤éĄšį›Žå°†čĸĢæŽ’é™¤åœ¨ä¸€čˆŦ搜į´ĸį쓿žœå’Œč‡Ē动åĄĢ充åģēčŽŽäš‹å¤–ã€‚" + }, + "archived": { + "message": "厞åŊ’æĄŖ" + }, + "unarchiveAndSave": { + "message": "取æļˆåŊ’æĄŖåšļäŋå­˜" }, "upgradeToUseArchive": { "message": "需čρé̘įē§äŧšå‘˜æ‰čƒŊäŊŋᔍåŊ’æĄŖã€‚" }, + "itemRestored": { + "message": "éĄšį›Žåˇ˛æĸ复" + }, "edit": { "message": "įŧ–čž‘" }, @@ -601,7 +613,7 @@ "message": "昞į¤ē全部" }, "viewLess": { - "message": "æŸĨįœ‹æ›´å°‘" + "message": "昞į¤ē更少" }, "viewLogin": { "message": "æŸĨįœ‹į™ģåŊ•" @@ -991,7 +1003,7 @@ "message": "文äģļå¤šåˇ˛æˇģ加" }, "twoStepLoginConfirmation": { - "message": "两æ­Ĩį™ģåŊ•čĻæą‚æ‚¨äģŽå…ļäģ–čŽžå¤‡īŧˆäž‹åĻ‚åŽ‰å…¨å¯†é’Ĩ、éĒŒč¯å™¨ Appã€įŸ­äŋĄã€į”ĩč¯æˆ–č€…į”ĩ子邎äģļīŧ‰æĨéĒŒč¯æ‚¨įš„į™ģåŊ•īŧŒčŋ™čƒŊäŊŋæ‚¨įš„č´Ļæˆˇæ›´åŠ åŽ‰å…¨ã€‚ä¸¤æ­Ĩį™ģåŊ•需čρ圍 bitwarden.com įŊ‘éĄĩį‰ˆå¯†į åē“中莞įŊŽã€‚įŽ°åœ¨čŽŋé—Žæ­¤įŊ‘į̙吗īŧŸ" + "message": "两æ­Ĩį™ģåŊ•čĻæą‚æ‚¨äģŽå…ļäģ–čŽžå¤‡īŧˆäž‹åĻ‚åŽ‰å…¨å¯†é’Ĩ、éĒŒč¯å™¨ Appã€įŸ­äŋĄã€į”ĩč¯æˆ–č€…į”ĩ子邎äģļīŧ‰æĨéĒŒč¯æ‚¨įš„į™ģåŊ•īŧŒčŋ™čƒŊäŊŋæ‚¨įš„č´Ļæˆˇæ›´åŠ åŽ‰å…¨ã€‚ä¸¤æ­Ĩį™ģåŊ•需čρ圍 bitwarden.com įŊ‘éĄĩį‰ˆå¯†į åē“中莞įŊŽã€‚įŽ°åœ¨čρčŽŋé—Žæ­¤įŊ‘į̙吗īŧŸ" }, "twoStepLoginConfirmationContent": { "message": "在 Bitwarden įŊ‘éĄĩ App 中莞įŊŽä¸¤æ­Ĩį™ģåŊ•īŧŒčŽŠæ‚¨įš„č´Ļæˆˇæ›´åŠ åŽ‰å…¨ã€‚" @@ -1275,7 +1287,7 @@ "message": "č¯ĸ问äŋå­˜æ–°įš„é€ščĄŒå¯†é’Ĩ或äŊŋį”¨å­˜å‚¨åœ¨å¯†į åē“ä¸­įš„é€ščĄŒå¯†é’Ĩį™ģåŊ•ã€‚é€‚į”¨äēŽæ‰€æœ‰åˇ˛į™ģåŊ•įš„č´Ļæˆˇã€‚" }, "notificationChangeDesc": { - "message": "是åĻčρ圍 Bitwarden ä¸­æ›´æ–°æ­¤å¯†į īŧŸ" + "message": "čρ圍 Bitwarden ä¸­æ›´æ–°æ­¤å¯†į å—īŧŸ" }, "notificationChangeSave": { "message": "更新" @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "å¯ŧå‡ēč‡Ē" }, - "exportVault": { - "message": "å¯ŧå‡ē坆᠁åē“" + "exportVerb": { + "message": "å¯ŧå‡ē", + "description": "The verb form of the word Export" + }, + "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": "文äģļæ ŧåŧ" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "čŋ›ä¸€æ­Ĩäē†č§Ŗ" }, + "migrationsFailed": { + "message": "æ›´æ–°åŠ å¯†čŽžįŊŽæ—ļå‘į”Ÿé”™č¯¯ã€‚" + }, + "updateEncryptionSettingsTitle": { + "message": "æ›´æ–°æ‚¨įš„åŠ å¯†čŽžįŊŽ" + }, + "updateEncryptionSettingsDesc": { + "message": "æ–°æŽ¨čįš„åŠ å¯†čŽžįŊŽå°†æéĢ˜æ‚¨įš„č´ĻæˆˇåŽ‰å…¨æ€§ã€‚čž“å…Ĩæ‚¨įš„ä¸ģ坆᠁äģĨįĢ‹åŗæ›´æ–°ã€‚" + }, + "confirmIdentityToContinue": { + "message": "įĄŽčŽ¤æ‚¨įš„čēĢäģŊäģĨįģ§įģ­" + }, + "enterYourMasterPassword": { + "message": "输å…Ĩæ‚¨įš„ä¸ģ坆᠁" + }, + "updateSettings": { + "message": "æ›´æ–°čŽžįŊŽ" + }, + "later": { + "message": "į¨åŽ" + }, "authenticatorKeyTotp": { "message": "éĒŒč¯å™¨å¯†é’Ĩ (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "附äģļ厞äŋå­˜" }, + "fixEncryption": { + "message": "äŋŽå¤åР坆" + }, + "fixEncryptionTooltip": { + "message": "此文äģļæ­Ŗåœ¨äŊŋᔍčŋ‡æ—ļįš„åŠ å¯†æ–šåŧã€‚" + }, + "attachmentUpdated": { + "message": "附äģļåˇ˛æ›´æ–°" + }, "file": { "message": "文äģļ" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "选拊一ä¸Ē文äģļ" }, + "itemsTransferred": { + "message": "éĄšį›Žåˇ˛čŊŦį§ģ" + }, "maxFileSize": { "message": "文äģᅵ€å¤§ä¸ē 500 MB。" }, @@ -1461,7 +1519,7 @@ "message": "įŽĄį†äŧšå‘˜čĩ„æ ŧ" }, "premiumManageAlert": { - "message": "您可äģĨ在 bitwarden.com įŊ‘éĄĩį‰ˆå¯†į åē“įŽĄį†æ‚¨įš„äŧšå‘˜čĩ„æ ŧã€‚įŽ°åœ¨čρčŽŋ闎吗īŧŸ" + "message": "您可äģĨ在 bitwarden.com įŊ‘éĄĩį‰ˆå¯†į åē“įŽĄį†æ‚¨įš„äŧšå‘˜čĩ„æ ŧã€‚įŽ°åœ¨čρčŽŋé—Žæ­¤įŊ‘į̙吗īŧŸ" }, "premiumRefresh": { "message": "åˆˇæ–°äŧšå‘˜čĩ„æ ŧ" @@ -1473,7 +1531,16 @@ "message": "æŗ¨å†Œé̘įē§äŧšå‘˜å°†čŽˇåž—īŧš" }, "ppremiumSignUpStorage": { - "message": "1 GB 文äģļ附äģļ加密存储。" + "message": "1 GB 文äģļ附äģļ加密存储įŠē间。" + }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ 文äģļ附äģļ加密存储įŠē间。", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } }, "premiumSignUpEmergency": { "message": "į´§æ€ĨčŽŋ闎。" @@ -1481,6 +1548,15 @@ "premiumSignUpTwoStepOptions": { "message": "ä¸“æœ‰įš„ä¸¤æ­Ĩį™ģåŊ•选饚īŧŒåĻ‚ YubiKey 和 Duo。" }, + "premiumSubscriptionEnded": { + "message": "æ‚¨įš„é̘įē§į‰ˆčŽĸé˜…åˇ˛į쓿Ÿ" + }, + "archivePremiumRestart": { + "message": "čĻé‡æ–°čŽˇå–åŊ’æĄŖå†…åŽšįš„čŽŋ闎权限īŧŒč¯ˇé‡å¯æ‚¨įš„é̘įē§į‰ˆčŽĸ阅。åĻ‚æžœæ‚¨åœ¨é‡å¯å‰įŧ–čž‘ä熿Ÿä¸Ē厞åŊ’æĄŖéĄšį›Žįš„č¯Ļįģ†äŋĄæ¯īŧŒåŽƒå°†čĸĢį§ģå›žæ‚¨įš„å¯†į åē“中。" + }, + "restartPremium": { + "message": "重启é̘įē§į‰ˆ" + }, "ppremiumSignUpReports": { "message": "坆᠁åĨåēˇã€č´ĻæˆˇäŊ“æŖ€äģĨåŠæ•°æŽæŗ„éœ˛æŠĨ告īŧŒäŋéšœæ‚¨įš„坆᠁åē“厉全。" }, @@ -1874,7 +1950,7 @@ "message": "čŋ‡æœŸåš´äģŊ" }, "monthly": { - "message": "month" + "message": "月" }, "expiration": { "message": "有效期" @@ -2313,7 +2389,7 @@ "message": "无效 PIN į ã€‚" }, "tooManyInvalidPinEntryAttemptsLoggingOut": { - "message": "æ— æ•ˆįš„ PIN 输å…Ĩå°č¯•æŦĄæ•°čŋ‡å¤šīŧŒæ­Ŗåœ¨æŗ¨é”€ã€‚" + "message": "æ— æ•ˆįš„ PIN 输å…Ĩå°č¯•æŦĄæ•°čŋ‡å¤šã€‚æ­Ŗåœ¨æŗ¨é”€ã€‚" }, "unlockWithBiometrics": { "message": "äŊŋį”¨į”Ÿį‰Šč¯†åˆĢ觪锁" @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "éĄšį›Žåˇ˛æ°¸äš…åˆ é™¤" }, + "archivedItemRestored": { + "message": "åŊ’æĄŖéĄšį›Žåˇ˛æĸ复" + }, "restoreItem": { "message": "æĸå¤éĄšį›Ž" }, @@ -2826,7 +2905,7 @@ "message": "æŽ’é™¤åŸŸåæ›´æ”šåˇ˛äŋå­˜" }, "limitSendViews": { - "message": "æŸĨįœ‹æŦĄæ•°é™åˆļ" + "message": "限åˆļæŸĨįœ‹æŦĄæ•°" }, "limitSendViewsHint": { "message": "čžžåˆ°é™éĸåŽīŧŒäģģäŊ•äēēæ— æŗ•æŸĨįœ‹æ­¤ Send。", @@ -2915,7 +2994,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendPermanentConfirmation": { - "message": "įĄŽåŽščĻæ°¸äš…åˆ é™¤čŋ™ä¸Ē Send 吗īŧŸ", + "message": "įĄŽåŽščĻæ°¸äš…åˆ é™¤æ­¤ Send 吗īŧŸ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editSend": { @@ -3079,10 +3158,10 @@ "message": "更新ä¸ģ坆᠁" }, "updateMasterPasswordWarning": { - "message": "æ‚¨įš„ä¸ģå¯†į æœ€čŋ‘čĸĢæ‚¨įģ„įģ‡įš„įŽĄį†å‘˜æ›´æ”ščŋ‡ã€‚čρčŽŋé—Žå¯†į åē“īŧŒæ‚¨åŋ…éĄģįĢ‹åŗæ›´æ–°åŽƒã€‚įģ§įģ­æ“äŊœå°†äŊŋ您退å‡ēåŊ“前äŧšč¯īŧŒåšļčĻæą‚æ‚¨é‡æ–°į™ģåŊ•。å…ļäģ–čŽžå¤‡ä¸Šįš„æ´ģ动äŧšč¯å¯čƒŊäŧšįģ§įģ­äŋæŒæ´ģ动įŠļ态é•ŋčžžä¸€å°æ—ļ。" + "message": "æ‚¨įš„ä¸ģå¯†į æœ€čŋ‘čĸĢæ‚¨įģ„įģ‡įš„įŽĄį†å‘˜æ›´æ”ščŋ‡ã€‚čρčŽŋé—Žå¯†į åē“īŧŒæ‚¨åŋ…éĄģįĢ‹åŗæ›´æ–°åŽƒã€‚įģ§įģ­æ“äŊœå°†äŊŋæ‚¨æŗ¨é”€åŊ“前äŧšč¯īŧŒåšļčĻæą‚æ‚¨é‡æ–°į™ģåŊ•。å…ļäģ–čŽžå¤‡ä¸Šįš„æ´ģ动äŧšč¯å¯čƒŊäŧšįģ§įģ­äŋæŒæ´ģ动įŠļ态é•ŋčžžä¸€å°æ—ļ。" }, "updateWeakMasterPasswordWarning": { - "message": "æ‚¨įš„ä¸ģå¯†į ä¸įŦĻåˆæŸä¸€éĄšæˆ–å¤šéĄšįģ„įģ‡į­–į•ĨčĻæą‚ã€‚čρčŽŋé—Žå¯†į åē“īŧŒåŋ…éĄģįĢ‹åŗæ›´æ–°æ‚¨įš„ä¸ģå¯†į ã€‚įģ§įģ­æ“äŊœå°†äŊŋ您退å‡ēåŊ“前äŧšč¯īŧŒåšļčĻæą‚æ‚¨é‡æ–°į™ģåŊ•。å…ļäģ–čŽžå¤‡ä¸Šįš„æ´ģ动äŧšč¯å¯čƒŊäŧšįģ§įģ­äŋæŒæ´ģ动įŠļ态é•ŋčžžä¸€å°æ—ļ。" + "message": "æ‚¨įš„ä¸ģå¯†į ä¸įŦĻåˆæŸä¸€éĄšæˆ–å¤šéĄšįģ„įģ‡į­–į•ĨčĻæą‚ã€‚čρčŽŋé—Žå¯†į åē“īŧŒåŋ…éĄģįĢ‹åŗæ›´æ–°æ‚¨įš„ä¸ģå¯†į ã€‚įģ§įģ­æ“äŊœå°†äŊŋæ‚¨æŗ¨é”€åŊ“前äŧšč¯īŧŒåšļčĻæą‚æ‚¨é‡æ–°į™ģåŊ•。å…ļäģ–čŽžå¤‡ä¸Šįš„æ´ģ动äŧšč¯å¯čƒŊäŧšįģ§įģ­äŋæŒæ´ģ动įŠļ态é•ŋčžžä¸€å°æ—ļ。" }, "tdeDisabledMasterPasswordRequired": { "message": "æ‚¨įš„įģ„įģ‡įρᔍäē†äŋĄäģģčŽžå¤‡åŠ å¯†ã€‚čρčŽŋé—Žæ‚¨įš„å¯†į åē“īŧŒč¯ˇčŽžįŊŽä¸€ä¸Ēä¸ģå¯†į ã€‚" @@ -3170,7 +3249,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "æ‚¨įš„įģ„įģ‡į­–į•Ĩæ­Ŗåœ¨åŊąå“æ‚¨įš„坆᠁åē“čļ…æ—ļã€‚æœ€å¤§å…čŽ¸įš„å¯†į åē“čļ…æ—ļä¸ē $HOURS$ 小æ—ļ $MINUTES$ åˆ†é’Ÿã€‚æ‚¨įš„å¯†į åē“čļ…æ—ļ动äŊœčĸĢ莞įŊŽä¸ē $ACTION$。", + "message": "æ‚¨įš„įģ„įģ‡į­–į•Ĩæ­Ŗåœ¨åŊąå“æ‚¨įš„坆᠁åē“čļ…æ—ļã€‚æœ€å¤§å…čŽ¸įš„å¯†į åē“čļ…æ—ļä¸ē $HOURS$ 小æ—ļ $MINUTES$ åˆ†é’Ÿã€‚æ‚¨įš„å¯†į åē“čļ…æ—ļ动äŊœčĸĢ莞įŊŽä¸ē「$ACTION$」。", "placeholders": { "hours": { "content": "$1", @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "æœĒæ‰žåˆ°å”¯ä¸€įš„æ ‡č¯†įŦĻ。" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "äģĨ下įģ„įģ‡įš„æˆå‘˜ä¸å†éœ€čρä¸ģå¯†į ã€‚č¯ˇä¸Žæ‚¨įš„įģ„įģ‡įŽĄį†å‘˜įĄŽčŽ¤ä¸‹éĸįš„åŸŸåã€‚" - }, "organizationName": { "message": "įģ„įģ‡åį§°" }, @@ -3294,6 +3370,12 @@ "error": { "message": "错蝝" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "觪坆错蝝" }, @@ -3308,7 +3390,7 @@ "description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'" }, "contactCSToAvoidDataLossPart2": { - "message": "äģĨéŋ免éĸå¤–įš„æ•°æŽä¸ĸå¤ąã€‚", + "message": "äģĨéŋ免čŋ›ä¸€æ­Ĩįš„æ•°æŽä¸ĸå¤ąã€‚", "description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'" }, "generateUsername": { @@ -3755,7 +3837,7 @@ "description": "Browser extension/addon" }, "desktop": { - "message": "æĄŒéĸ", + "message": "æĄŒéĸį̝", "description": "Desktop app" }, "webVault": { @@ -3865,10 +3947,10 @@ "message": "æŖ€æŸĨæ‚¨įš„į”ĩå­é‚ŽįŽą" }, "followTheLinkInTheEmailSentTo": { - "message": "į‚šå‡ģ发送到į”ĩ子邎äģļä¸­įš„é“žæŽĨ" + "message": "į‚šå‡ģ发送到" }, "andContinueCreatingYourAccount": { - "message": "į„ļ后įģ§įģ­åˆ›åģēæ‚¨įš„č´Ļæˆˇã€‚" + "message": "įš„į”ĩ子邎äģļä¸­įš„é“žæŽĨīŧŒį„ļ后įģ§įģ­åˆ›åģēæ‚¨įš„č´Ļæˆˇã€‚" }, "noEmail": { "message": "æ˛Ąæ”ļ到į”ĩ子邎äģļ吗īŧŸ" @@ -4176,10 +4258,6 @@ "ignore": { "message": "åŋŊį•Ĩ" }, - "importData": { - "message": "å¯ŧå…Ĩ数捎", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "å¯ŧå…Ĩå‡ē错" }, @@ -4365,7 +4443,7 @@ "message": "æ­¤įĢ™į‚šæ˛Ąæœ‰åŒšé…įš„į™ģåŊ•" }, "searchSavePasskeyNewLogin": { - "message": "搜į´ĸæˆ–å°†é€ščĄŒå¯†é’Ĩäŋå­˜ä¸ē一ä¸Ēæ–°įš„į™ģåŊ•" + "message": "搜į´ĸæˆ–å°†é€ščĄŒå¯†é’Ĩäŋå­˜ä¸ēæ–°įš„į™ģåŊ•" }, "confirm": { "message": "įĄŽčŽ¤" @@ -4374,7 +4452,7 @@ "message": "äŋå­˜é€ščĄŒå¯†é’Ĩ" }, "savePasskeyNewLogin": { - "message": "äŊœä¸ēæ–°įš„į™ģåŊ•éĄšį›Žäŋå­˜é€ščĄŒå¯†é’Ĩ" + "message": "å°†é€ščĄŒå¯†é’Ĩäŋå­˜ä¸ēæ–°įš„į™ģåŊ•" }, "chooseCipherForPasskeySave": { "message": "选拊一ä¸ĒᔍäēŽäŋå­˜æ­¤é€ščĄŒå¯†é’Ĩįš„į™ģåŊ•éĄšį›Ž" @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "æ›´å¤šé€‰éĄš" + }, "moreOptionsTitle": { "message": "æ›´å¤šé€‰éĄš - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "įŽĄį†æŽ§åˆļ台" }, + "admin": { + "message": "įŽĄį†å‘˜" + }, + "automaticUserConfirmation": { + "message": "č‡ĒåŠ¨į”¨æˆˇįĄŽčŽ¤" + }, + "automaticUserConfirmationHint": { + "message": "åŊ“æ­¤čŽžå¤‡åˇ˛č§Ŗé”æ—ļīŧŒč‡ĒåŠ¨įĄŽčŽ¤åž…å¤„į†įš„į”¨æˆˇ" + }, + "autoConfirmOnboardingCallout": { + "message": "通čŋ‡č‡ĒåŠ¨į”¨æˆˇįĄŽčŽ¤čŠ‚įœæ—ļ间" + }, + "autoConfirmWarning": { + "message": "čŋ™å¯čƒŊäŧšåŊąå“æ‚¨įģ„įģ‡įš„æ•°æŽåŽ‰å…¨ã€‚" + }, + "autoConfirmWarningLink": { + "message": "äē†č§Ŗæ­¤éŖŽé™Š" + }, + "autoConfirmSetup": { + "message": "č‡ĒåŠ¨įĄŽčŽ¤æ–°į”¨æˆˇ" + }, + "autoConfirmSetupDesc": { + "message": "åŊ“æ­¤čŽžå¤‡åˇ˛č§Ŗé”æ—ļīŧŒæ–°į”¨æˆˇå°†čĸĢč‡ĒåŠ¨įĄŽčŽ¤ã€‚" + }, + "autoConfirmSetupHint": { + "message": "æŊœåœ¨įš„åŽ‰å…¨éŖŽé™Šæœ‰å“Ēäē›īŧŸ" + }, + "autoConfirmEnabled": { + "message": "吝ᔍäē†č‡ĒåŠ¨įĄŽčŽ¤" + }, + "availableNow": { + "message": "į›Žå‰å¯į”¨" + }, "accountSecurity": { "message": "č´ĻæˆˇåŽ‰å…¨" }, + "phishingBlocker": { + "message": "įŊ‘įģœé’“éąŧæ‹ĻæˆĒ器" + }, + "enablePhishingDetection": { + "message": "įŊ‘įģœé’“éąŧæŖ€æĩ‹" + }, + "enablePhishingDetectionDesc": { + "message": "在čŽŋé—Žį–‘äŧŧ钓éąŧįŊ‘įĢ™å‰æ˜žį¤ēč­Ļ告" + }, "notifications": { "message": "通įŸĨ" }, @@ -4802,7 +4925,7 @@ "message": "新åĸž" }, "removeItem": { - "message": "删除 $NAME$", + "message": "į§ģ除 $NAME$", "description": "Remove a selected option, such as a folder or collection", "placeholders": { "name": { @@ -4894,7 +5017,7 @@ "message": "čŽˇå–æĄŒéĸ App" }, "getTheDesktopAppDesc": { - "message": "无需äŊŋᔍæĩč§ˆå™¨čŽŋé—Žæ‚¨įš„å¯†į åē“īŧŒį„ļåŽåœ¨æĄŒéĸ App 和æĩč§ˆå™¨æ‰Šåą•中同æ—ļ莞įŊŽį”Ÿį‰Šč¯†åˆĢ觪锁īŧŒåŗå¯åŽžįŽ°åŋĢé€Ÿč§Ŗé”ã€‚" + "message": "无需äŊŋᔍæĩč§ˆå™¨čŽŋé—Žæ‚¨įš„å¯†į åē“ã€‚åœ¨æĄŒéĸ App 和æĩč§ˆå™¨æ‰Šåą•中同æ—ļ莞įŊŽį”Ÿį‰Šč¯†åˆĢ觪锁īŧŒåŗå¯åŽžįŽ°åŋĢé€Ÿč§Ŗé”ã€‚" }, "downloadFromBitwardenNow": { "message": "įĢ‹åŗäģŽ bitwarden.com 下čŊŊ" @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "éšč—åŒšé…æŖ€æĩ‹ $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "昞į¤ēåŒšé…æŖ€æĩ‹" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "éšč—åŒšé…æŖ€æĩ‹" }, "autoFillOnPageLoad": { "message": "éĄĩéĸ加čŊŊæ—ļč‡Ē动åĄĢ充吗īŧŸ" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "čļ…åŽŊ" }, + "narrow": { + "message": "įĒ„" + }, "sshKeyWrongPassword": { "message": "æ‚¨čž“å…Ĩįš„å¯†į ä¸æ­ŖįĄŽã€‚" }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "æ­¤į™ģåŊ•å­˜åœ¨éŖŽé™Šä¸”įŧē少įŊ‘įĢ™ã€‚č¯ˇæˇģ加įŊ‘įĢ™åšļæ›´æ”šå¯†į äģĨåĸžåŧē厉全性。" }, + "vulnerablePassword": { + "message": "易受æ”ģå‡ģįš„å¯†į ã€‚" + }, + "changeNow": { + "message": "įĢ‹åŗæ›´æ”š" + }, "missingWebsite": { "message": "įŧē少įŊ‘įĢ™" }, @@ -5653,7 +5782,7 @@ "message": "å¯ŧå…ĨįŽ°æœ‰å¯†į " }, "emptyVaultNudgeBody": { - "message": "äŊŋᔍå¯ŧå…Ĩ器åŋĢ速将į™ģåŊ•äŧ čž“到 Bitwarden č€Œæ— éœ€æ‰‹åŠ¨æˇģ加。" + "message": "äŊŋᔍå¯ŧå…Ĩ器åŋĢ速将į™ģåŊ•čŊŦį§ģ到 Bitwarden č€Œæ— éœ€æ‰‹åŠ¨æˇģ加。" }, "emptyVaultNudgeButton": { "message": "įĢ‹åŗå¯ŧå…Ĩ" @@ -5682,7 +5811,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$ äŋæŠ¤", @@ -5772,7 +5901,7 @@ "message": "å…ŗäēŽæ­¤čŽžįŊŽ" }, "permitCipherDetailsDescription": { - "message": "Bitwarden 将äŊŋᔍ厞äŋå­˜įš„į™ģåŊ• URI æĨ蝆åˆĢåē”äŊŋᔍå“Ēä¸Ēå›žæ ‡æˆ–æ›´æ”šå¯†į įš„ URL æĨæ”šå–„æ‚¨įš„äŊ“éĒŒã€‚åŊ“您äŊŋį”¨æ­¤æœåŠĄæ—ļīŧŒä¸äŧšæ”ļ集或äŋå­˜äģģäŊ•äŋĄæ¯ã€‚" + "message": "Bitwarden 将äŊŋᔍ厞äŋå­˜įš„į™ģåŊ• URI æĨįĄŽåŽšåē”äŊŋį”¨įš„å›žæ ‡æˆ–æ›´æ”šå¯†į įš„ URLīŧŒäģĨæå‡æ‚¨įš„äŊŋᔍäŊ“éĒŒã€‚äŊŋį”¨æ­¤æœåŠĄæ—ļ不äŧšæ”ļ集或äŋå­˜äģģäŊ•äŋĄæ¯ã€‚" }, "noPermissionsViewPage": { "message": "æ‚¨æ˛Ąæœ‰æŸĨįœ‹æ­¤éĄĩéĸįš„æƒé™ã€‚č¯ˇå°č¯•äŊŋᔍå…ļäģ–č´Ļæˆˇį™ģåŊ•。" @@ -5818,8 +5947,8 @@ "andMoreFeatures": { "message": "äģĨ及更多īŧ" }, - "planDescPremium": { - "message": "全éĸįš„åœ¨įēŋåŽ‰å…¨é˜˛æŠ¤" + "advancedOnlineSecurity": { + "message": "é̘įē§åœ¨įēŋåŽ‰å…¨é˜˛æŠ¤" }, "upgradeToPremium": { "message": "升įē§ä¸ēé̘įē§į‰ˆ" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "åĄåˇ" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "æ‚¨įš„įģ„įģ‡åˇ˛ä¸å†äŊŋᔍä¸ģ坆᠁į™ģåŊ• Bitwarden。čρįģ§įģ­īŧŒč¯ˇénj蝁įģ„įģ‡å’ŒåŸŸåã€‚" + }, + "continueWithLogIn": { + "message": "įģ§įģ­į™ģåŊ•" + }, + "doNotContinue": { + "message": "不čρįģ§įģ­" + }, + "domain": { + "message": "域名" + }, + "keyConnectorDomainTooltip": { + "message": "æ­¤åŸŸåå°†å­˜å‚¨æ‚¨įš„č´ĻæˆˇåŠ å¯†å¯†é’ĨīŧŒæ‰€äģĨč¯ˇįĄŽäŋæ‚¨äŋĄäģģ厃。åĻ‚æžœæ‚¨ä¸įĄŽåŽšīŧŒč¯ˇä¸Žæ‚¨įš„įŽĄį†å‘˜č”įŗģ。" + }, + "verifyYourOrganization": { + "message": "éĒŒč¯æ‚¨įš„įģ„įģ‡äģĨį™ģåŊ•" + }, + "organizationVerified": { + "message": "įģ„įģ‡åˇ˛énj蝁" + }, + "domainVerified": { + "message": "åŸŸååˇ˛énj蝁" + }, + "leaveOrganizationContent": { + "message": "åĻ‚æžœä¸éĒŒč¯æ‚¨įš„įģ„įģ‡īŧŒæ‚¨å¯šįģ„įģ‡įš„čŽŋ闎权限将čĸĢæ’¤é”€ã€‚" + }, + "leaveNow": { + "message": "įĢ‹åŗé€€å‡ē" + }, + "verifyYourDomainToLogin": { + "message": "éĒŒč¯æ‚¨įš„åŸŸåäģĨį™ģåŊ•" + }, + "verifyYourDomainDescription": { + "message": "čρįģ§įģ­į™ģåŊ•īŧŒč¯ˇéĒŒč¯æ­¤åŸŸåã€‚" + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "čρįģ§įģ­į™ģåŊ•īŧŒč¯ˇénj蝁įģ„įģ‡å’ŒåŸŸåã€‚" + }, "sessionTimeoutSettingsAction": { "message": "čļ…æ—ļ动äŊœ" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "æ­¤čŽžįŊŽį”ąæ‚¨įš„įģ„įģ‡įŽĄį†ã€‚" + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "æ‚¨įš„įģ„įģ‡åˇ˛å°†æœ€å¤§äŧšč¯čļ…æ—ļ莞įŊŽä¸ē $HOURS$ 小æ—ļ $MINUTES$ 分钟。", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "æ‚¨įš„įģ„įģ‡åˇ˛å°†éģ˜čޤäŧšč¯čļ…æ—ļ莞įŊŽä¸ē「įĢ‹åŗã€ã€‚" + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "æ‚¨įš„įģ„įģ‡åˇ˛å°†éģ˜čޤäŧšč¯čļ…æ—ļ莞įŊŽä¸ē「įŗģįģŸé”åޚæ—ļ」。" + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "æ‚¨įš„įģ„įģ‡åˇ˛å°†éģ˜čޤäŧšč¯čļ…æ—ļ莞įŊŽä¸ē「æĩč§ˆå™¨é‡å¯æ—ļ」。" + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "最大čļ…æ—ļ不čƒŊčļ…čŋ‡ $HOURS$ 小æ—ļ $MINUTES$ 分钟", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "æĩč§ˆå™¨é‡å¯æ—ļ" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "莞įŊŽä¸€ä¸Ēč§Ŗé”æ–šåŧäģĨæ›´æ”šæ‚¨įš„čļ…æ—ļ动äŊœ" + }, + "upgrade": { + "message": "升įē§" + }, + "leaveConfirmationDialogTitle": { + "message": "įĄŽåŽščρ退å‡ē吗īŧŸ" + }, + "leaveConfirmationDialogContentOne": { + "message": "拒įģåŽīŧŒæ‚¨įš„ä¸ĒäēēéĄšį›Žå°†äŋį•™åœ¨æ‚¨įš„č´Ļæˆˇä¸­īŧŒäŊ†æ‚¨å°†å¤ąåŽģå¯šå…ąäēĢéĄšį›Žå’Œįģ„įģ‡åŠŸčƒŊįš„čŽŋ闎权限。" + }, + "leaveConfirmationDialogContentTwo": { + "message": "联įŗģæ‚¨įš„įŽĄį†å‘˜äģĨé‡æ–°čŽˇå–čŽŋ闎权限。" + }, + "leaveConfirmationDialogConfirmButton": { + "message": "退å‡ē $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "我č¯ĨåĻ‚äŊ•įŽĄį†æˆ‘įš„å¯†į åē“īŧŸ" + }, + "transferItemsToOrganizationTitle": { + "message": "čŊŦį§ģéĄšį›Žåˆ° $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "å‡ēäēŽåŽ‰å…¨å’Œåˆč§„č€ƒč™‘īŧŒ$ORGANIZATION$ čĻæą‚æ‰€æœ‰éĄšį›ŽåŊ’įģ„į쇿‰€æœ‰ã€‚į‚šå‡ģ「æŽĨ受」äģĨčŊŦį§ģæ‚¨įš„éĄšį›Žįš„æ‰€æœ‰æƒã€‚", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "æŽĨ受čŊŦį§ģ" + }, + "declineAndLeave": { + "message": "拒įģåšļ退å‡ē" + }, + "whyAmISeeingThis": { + "message": "ä¸ēäģ€äšˆæˆ‘äŧšįœ‹åˆ°čŋ™ä¸ĒīŧŸ" + }, + "resizeSideNavigation": { + "message": "č°ƒæ•´äž§čžšå¯ŧčˆĒ栏大小" } } diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index 38fb50d6c8b..540a4b053ff 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -28,6 +28,9 @@ "logInWithPasskey": { "message": "äŊŋᔍ坆įĸŧ金鑰į™ģå…Ĩ" }, + "unlockWithPasskey": { + "message": "Unlock with passkey" + }, "useSingleSignOn": { "message": "äŊŋį”¨å–Žä¸€į™ģå…Ĩ" }, @@ -436,8 +439,8 @@ "sync": { "message": "同æ­Ĩ" }, - "syncVaultNow": { - "message": "įĢ‹åŗåŒæ­Ĩ密įĸŧåēĢ" + "syncNow": { + "message": "įĢ‹åŗåŒæ­Ĩ" }, "lastSync": { "message": "上æŦĄåŒæ­Ĩæ–ŧīŧš" @@ -455,9 +458,6 @@ "bitWebVaultApp": { "message": "Bitwarden įļ˛é æ‡‰į”¨į¨‹åŧ" }, - "importItems": { - "message": "匯å…Ĩé …į›Ž" - }, "select": { "message": "選擇" }, @@ -576,17 +576,29 @@ "itemWasSentToArchive": { "message": "é …į›Žåˇ˛į§ģč‡ŗå°å­˜" }, + "itemWasUnarchived": { + "message": "åˇ˛å–æļˆå°å­˜é …į›Ž" + }, "itemUnarchived": { "message": "é …į›Žå–æļˆå°å­˜" }, "archiveItem": { "message": "å°å­˜é …į›Ž" }, - "archiveItemConfirmDesc": { - "message": "å°å­˜įš„é …į›Žå°‡ä¸æœƒå‡ēįžåœ¨ä¸€čˆŦ搜尋įĩæžœæˆ–č‡Ē動åĄĢå…Ĩåģēč­°ä¸­ã€‚įĸē厚čĻå°å­˜æ­¤é …į›Žå—ŽīŧŸ" + "archiveItemDialogContent": { + "message": "封存垌īŧŒæ­¤é …į›Žå°‡ä¸æœƒéĄ¯į¤ē在搜尋įĩæžœčˆ‡č‡Ē動åĄĢå…Ĩåģēč­°ä¸­ã€‚" + }, + "archived": { + "message": "厞封存" + }, + "unarchiveAndSave": { + "message": "取æļˆå°å­˜ä¸Ļå„˛å­˜" }, "upgradeToUseArchive": { - "message": "A premium membership is required to use Archive." + "message": "需čĻé€˛éšŽį‰ˆæœƒå“Ąæ‰čƒŊäŊŋį”¨å°å­˜åŠŸčƒŊ。" + }, + "itemRestored": { + "message": "åˇ˛é‚„åŽŸé …į›Ž" }, "edit": { "message": "ᎍčŧ¯" @@ -598,7 +610,7 @@ "message": "æĒĸčĻ–å…¨éƒ¨" }, "showAll": { - "message": "Show all" + "message": "éĄ¯į¤ē全部" }, "viewLess": { "message": "éĄ¯į¤ēčŧƒå°‘" @@ -1325,8 +1337,21 @@ "exportFrom": { "message": "匯å‡ēč‡Ē" }, - "exportVault": { - "message": "匯å‡ē密įĸŧåēĢ" + "exportVerb": { + "message": "匯å‡ē", + "description": "The verb form of the word Export" + }, + "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": "æĒ”æĄˆæ ŧåŧ" @@ -1406,6 +1431,27 @@ "learnMore": { "message": "æˇąå…Ĩäē†č§Ŗ" }, + "migrationsFailed": { + "message": "æ›´æ–°åŠ å¯†č¨­åŽšæ™‚į™ŧį”ŸéŒ¯čĒ¤ã€‚" + }, + "updateEncryptionSettingsTitle": { + "message": "æ›´æ–°æ‚¨įš„åŠ å¯†č¨­åŽš" + }, + "updateEncryptionSettingsDesc": { + "message": "æ–°įš„åģēč­°åŠ å¯†č¨­åŽšå°‡æå‡æ‚¨įš„å¸ŗæˆļ厉全性。čĢ‹čŧ¸å…Ĩä¸ģ密įĸŧäģĨįĢ‹åŗæ›´æ–°ã€‚" + }, + "confirmIdentityToContinue": { + "message": "čĢ‹å…ˆįĸēčĒčēĢ分垌再įšŧįēŒ" + }, + "enterYourMasterPassword": { + "message": "čŧ¸å…Ĩæ‚¨įš„ä¸ģ密įĸŧ" + }, + "updateSettings": { + "message": "æ›´æ–°č¨­åŽš" + }, + "later": { + "message": "äģĨ垌再čĒĒ" + }, "authenticatorKeyTotp": { "message": "éŠ—č­‰å™¨é‡‘é‘° (TOTP)" }, @@ -1436,6 +1482,15 @@ "attachmentSaved": { "message": "附äģļåˇ˛å„˛å­˜" }, + "fixEncryption": { + "message": "äŋŽæ­ŖåР坆" + }, + "fixEncryptionTooltip": { + "message": "æ­¤æĒ”æĄˆäŊŋᔍäē†éŽæ™‚įš„åŠ å¯†æ–šåŧã€‚" + }, + "attachmentUpdated": { + "message": "附äģļåˇ˛æ›´æ–°" + }, "file": { "message": "æĒ”æĄˆ" }, @@ -1445,6 +1500,9 @@ "selectFile": { "message": "選取æĒ”æĄˆ" }, + "itemsTransferred": { + "message": "é …į›Žåˇ˛čŊ‰į§ģ" + }, "maxFileSize": { "message": "æĒ”æĄˆæœ€å¤§į‚ē 500MB。" }, @@ -1475,12 +1533,30 @@ "ppremiumSignUpStorage": { "message": "ᔍæ–ŧæĒ”æĄˆé™„äģļįš„ 1 GB åŠ å¯†å„˛å­˜įŠē間。" }, + "premiumSignUpStorageV2": { + "message": "ᔍæ–ŧæĒ”æĄˆé™„äģļįš„ $SIZE$ åŠ å¯†å„˛å­˜įŠē間。", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "ᎊæ€Ĩ存取" }, "premiumSignUpTwoStepOptions": { "message": "å°ˆæœ‰įš„å…Šæ­Ĩ驟į™ģå…Ĩ選項īŧŒäž‹åĻ‚ YubiKey 和 Duo。" }, + "premiumSubscriptionEnded": { + "message": "æ‚¨įš„é€˛éšŽį‰ˆč¨‚é–ąåˇ˛åˆ°æœŸ" + }, + "archivePremiumRestart": { + "message": "č‹ĨčĻé‡æ–°å­˜å–æ‚¨įš„å°å­˜é …į›ŽīŧŒčĢ‹é‡æ–°å•Ÿį”¨é€˛éšŽį‰ˆč¨‚é–ąã€‚č‹Ĩæ‚¨åœ¨é‡æ–°å•Ÿį”¨å‰įˇ¨čŧ¯å°å­˜é …į›Žįš„čŠŗį´°čŗ‡æ–™īŧŒåŽƒå°‡æœƒčĸĢį§ģå›žæ‚¨įš„å¯†įĸŧåēĢ。" + }, + "restartPremium": { + "message": "é‡æ–°å•Ÿį”¨é€˛éšŽį‰ˆ" + }, "ppremiumSignUpReports": { "message": "密įĸŧåĨåēˇåēĻæĒĸæŸĨã€æäž›å¸ŗæˆļéĢ”æĒĸäģĨåŠčŗ‡æ–™å¤–æ´Šå ąå‘ŠīŧŒäģĨäŋéšœæ‚¨įš„密įĸŧåēĢ厉全。" }, @@ -1874,7 +1950,7 @@ "message": "逞期嚴äģŊ" }, "monthly": { - "message": "month" + "message": "月" }, "expiration": { "message": "逞期" @@ -2400,6 +2476,9 @@ "permanentlyDeletedItem": { "message": "é …į›Žåˇ˛æ°¸äš…åˆĒ除" }, + "archivedItemRestored": { + "message": "åˇ˛é‚„åŽŸå°å­˜é …į›Ž" + }, "restoreItem": { "message": "é‚„åŽŸé …į›Ž" }, @@ -3210,9 +3289,6 @@ "copyCustomFieldNameNotUnique": { "message": "æ‰žä¸åˆ°å”¯ä¸€č­˜åˆĨįĸŧ。" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "äģĨ下įĩ„įš”įš„æˆå“Ąåˇ˛ä¸å†éœ€čρä¸ģ密įĸŧ。čĢ‹čˆ‡äŊ įš„įĩ„įš”įŽĄį†å“ĄįĸēčĒä¸‹æ–šįš„įļ˛åŸŸã€‚" - }, "organizationName": { "message": "įĩ„įš”åį¨ą" }, @@ -3294,6 +3370,12 @@ "error": { "message": "錯čǤ" }, + "prfUnlockFailed": { + "message": "Failed to unlock with passkey. Please try again or use another unlock method." + }, + "noPrfCredentialsAvailable": { + "message": "No PRF-enabled passkeys are available for unlock. Please log in with a passkey first." + }, "decryptionError": { "message": "觪坆į™ŧį”ŸéŒ¯čǤ" }, @@ -4176,10 +4258,6 @@ "ignore": { "message": "åŋŊį•Ĩ" }, - "importData": { - "message": "匯å…Ĩčŗ‡æ–™", - "description": "Used for the header of the import dialog, the import button and within the file-password-prompt" - }, "importError": { "message": "匯å…Ĩ時į™ŧį”ŸéŒ¯čǤ" }, @@ -4673,6 +4751,9 @@ } } }, + "moreOptionsLabelNoPlaceholder": { + "message": "更多選項" + }, "moreOptionsTitle": { "message": "更多選項 - $ITEMNAME$", "description": "Title for a button that opens a menu with more options for an item.", @@ -4763,9 +4844,51 @@ "adminConsole": { "message": "įŽĄį†æŽ§åˆļ台" }, + "admin": { + "message": "įŽĄį†å“Ą" + }, + "automaticUserConfirmation": { + "message": "č‡Ē動äŊŋᔍ者įĸēčĒ" + }, + "automaticUserConfirmationHint": { + "message": "åœ¨æ­¤čŖįŊŽč§ŖéŽ–æ™‚č‡Ē動įĸēčĒåž…č™•į†įš„äŊŋᔍ者" + }, + "autoConfirmOnboardingCallout": { + "message": "透過č‡Ē動äŊŋᔍ者įĸēčĒį¯€įœæ™‚é–“" + }, + "autoConfirmWarning": { + "message": "可čƒŊåŊąéŸŋæ‚¨įš„įĩ„įš”čŗ‡æ–™åŽ‰å…¨æ€§ã€‚" + }, + "autoConfirmWarningLink": { + "message": "äē†č§Ŗéĸ¨éšĒ" + }, + "autoConfirmSetup": { + "message": "č‡Ē動įĸēčĒæ–°äŊŋᔍ者" + }, + "autoConfirmSetupDesc": { + "message": "į•ļæ­¤čŖįŊŽč™•æ–ŧč§ŖéŽ–į‹€æ…‹æ™‚īŧŒæ–°įš„äŊŋį”¨č€…å°‡æœƒč‡Ēå‹•į˛åž—įĸēčĒã€‚" + }, + "autoConfirmSetupHint": { + "message": "æŊ›åœ¨įš„厉全性éĸ¨éšĒ有å“Ēäē›īŧŸ" + }, + "autoConfirmEnabled": { + "message": "開啟č‡Ē動įĸēčĒ" + }, + "availableNow": { + "message": "įĢ‹åŗå¯į”¨" + }, "accountSecurity": { "message": "叺æˆļ厉全性" }, + "phishingBlocker": { + "message": "é‡Ŗé­šå°éŽ–å™¨" + }, + "enablePhishingDetection": { + "message": "é‡Ŗé­šåĩæ¸Ŧ" + }, + "enablePhishingDetectionDesc": { + "message": "åœ¨å­˜å–į–‘äŧŧé‡Ŗé­šįļ˛įĢ™å‰éĄ¯į¤ēč­Ļ告" + }, "notifications": { "message": "通įŸĨ" }, @@ -4912,7 +5035,7 @@ "message": "é€˛éšŽį‰ˆ" }, "unlockFeaturesWithPremium": { - "message": "Unlock reporting, emergency access, and more security features with Premium." + "message": "äŊŋį”¨é€˛éšŽį‰ˆč§ŖéŽ–å ąå‘Šã€įˇŠæ€Ĩ存取及更多厉全功čƒŊ。" }, "freeOrgsCannotUseAttachments": { "message": "免č˛ģįĩ„įš”į„Ąæŗ•äŊŋᔍ附æĒ”" @@ -5017,14 +5140,11 @@ } } }, - "hideMatchDetection": { - "message": "隱藏åĩæ¸Ŧåˆ°įš„åģ合 $WEBSITE$", - "placeholders": { - "website": { - "content": "$1", - "example": "https://example.com" - } - } + "showMatchDetectionNoPlaceholder": { + "message": "éĄ¯į¤ē比對åĩæ¸Ŧ" + }, + "hideMatchDetectionNoPlaceholder": { + "message": "éšąč—æ¯”å°åĩæ¸Ŧ" }, "autoFillOnPageLoad": { "message": "在頁éĸčŧ‰å…Ĩ時č‡Ē動åĄĢå¯ĢīŧŸ" @@ -5562,6 +5682,9 @@ "extraWide": { "message": "čļ…å¯Ŧ" }, + "narrow": { + "message": "į¸Žå°" + }, "sshKeyWrongPassword": { "message": "您čŧ¸å…Ĩįš„å¯†įĸŧ錯čĒ¤ã€‚" }, @@ -5610,6 +5733,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "æ­¤į™ģå…Ĩčŗ‡č¨Šå­˜åœ¨éĸ¨éšĒīŧŒä¸”įŧē少įļ˛įĢ™ã€‚čĢ‹æ–°åĸžįļ˛įĢ™ä¸ĻčŽŠæ›´å¯†įĸŧäģĨ提升厉全性。" }, + "vulnerablePassword": { + "message": "æœ‰åŽ‰å…¨į–‘æ…Žįš„å¯†įĸŧ。" + }, + "changeNow": { + "message": "įĢ‹åŗčŽŠæ›´" + }, "missingWebsite": { "message": "įŧē少įļ˛įĢ™" }, @@ -5818,17 +5947,17 @@ "andMoreFeatures": { "message": "äģĨ及å…ļäģ–功čƒŊ功čƒŊīŧ" }, - "planDescPremium": { - "message": "åŽŒæ•´įš„įˇšä¸ŠåŽ‰å…¨" + "advancedOnlineSecurity": { + "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": "é€˛éšŽį‰ˆč¨‚é–ąå¯į‚ē您提䞛更多åˇĨå…ˇīŧŒå”劊įļ­æŒåމ免ä¸ĻæŽŒæĄä¸ģ控æŦŠ" }, "explorePremium": { "message": "æŽĸį´ĸé€˛éšŽį‰ˆ" @@ -5849,7 +5978,144 @@ "cardNumberLabel": { "message": "支äģ˜åĄč™Ÿįĸŧ" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "æ‚¨įš„įĩ„įš”åˇ˛ä¸å†äŊŋᔍä¸ģ密įĸŧį™ģå…Ĩ Bitwarden。č‹ĨčρįšŧįēŒīŧŒčĢ‹éŠ—č­‰įĩ„įš”čˆ‡įļ˛åŸŸã€‚" + }, + "continueWithLogIn": { + "message": "įšŧįēŒį™ģå…Ĩ" + }, + "doNotContinue": { + "message": "不čρįšŧįēŒ" + }, + "domain": { + "message": "įļ˛åŸŸ" + }, + "keyConnectorDomainTooltip": { + "message": "æ­¤įļ˛åŸŸå°‡å„˛å­˜æ‚¨å¸ŗč™Ÿįš„加密金鑰īŧŒčĢ‹įĸēčĒæ‚¨äŋĄäģģ厃。č‹Ĩ不įĸē厚īŧŒčĢ‹æ´ŊčŠĸæ‚¨įš„įŽĄį†å“Ąã€‚" + }, + "verifyYourOrganization": { + "message": "éŠ—č­‰æ‚¨įš„įĩ„įš”äģĨį™ģå…Ĩ" + }, + "organizationVerified": { + "message": "įĩ„įš”åˇ˛éŠ—č­‰" + }, + "domainVerified": { + "message": "åˇ˛éŠ—č­‰įļ˛åŸŸ" + }, + "leaveOrganizationContent": { + "message": "č‹Ĩ您æœĒ驗證įĩ„įš”īŧŒå°‡æœƒčĸĢæ’¤éŠˇå°čОįĩ„įš”įš„å­˜å–æŦŠé™ã€‚" + }, + "leaveNow": { + "message": "įĢ‹åŗé›ĸ開" + }, + "verifyYourDomainToLogin": { + "message": "éŠ—č­‰æ‚¨įš„įļ˛åŸŸäģĨį™ģå…Ĩ" + }, + "verifyYourDomainDescription": { + "message": "č‹ĨčρįšŧįēŒį™ģå…ĨīŧŒčĢ‹éŠ—č­‰æ­¤įļ˛åŸŸã€‚" + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "č‹ĨčρįšŧįēŒį™ģå…ĨīŧŒčĢ‹éŠ—č­‰įĩ„įš”čˆ‡įļ˛åŸŸã€‚" + }, "sessionTimeoutSettingsAction": { "message": "逞時垌動äŊœ" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "æ­¤č¨­åŽšį”ąæ‚¨įš„įĩ„įš”įŽĄį†ã€‚" + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "æ‚¨įš„įĩ„įš”åˇ˛å°‡æœ€é•ˇåˇĨäŊœéšŽæŽĩé€žæ™‚č¨­į‚ē $HOURS$ å°æ™‚čˆ‡ $MINUTES$ 分鐘。", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToImmediately": { + "message": "æ‚¨įš„įĩ„įš”åˇ˛å°‡é č¨­åˇĨäŊœéšŽæŽĩé€žæ™‚č¨­åŽšį‚ē「įĢ‹åŗã€ã€‚" + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "æ‚¨įš„įĩ„įš”åˇ˛å°‡é č¨­åˇĨäŊœéšŽæŽĩé€žæ™‚č¨­åŽšį‚ē「在įŗģįĩąéŽ–åŽšæ™‚ã€ã€‚" + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "æ‚¨įš„įĩ„įš”åˇ˛å°‡é č¨­åˇĨäŊœéšŽæŽĩé€žæ™‚č¨­åŽšį‚ēã€Œåœ¨į€čĻŊ器重新啟動時」。" + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "æœ€é•ˇé€žæ™‚æ™‚é–“ä¸å¯čļ…過 $HOURS$ 小時 $MINUTES$ 分鐘", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "æ–ŧį€čĻŊ器重新啟動時" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "č¨­åŽšä¸€å€‹č§ŖéŽ–æ–šåŧäž†čŽŠæ›´æ‚¨įš„å¯†įĸŧåēĢ逞時動äŊœã€‚" + }, + "upgrade": { + "message": "å‡į´š" + }, + "leaveConfirmationDialogTitle": { + "message": "įĸē厚čρé›ĸ開嗎īŧŸ" + }, + "leaveConfirmationDialogContentOne": { + "message": "č‹Ĩ選擇拒įĩ•īŧŒæ‚¨įš„個äēēé …į›Žå°‡äŋį•™åœ¨å¸ŗč™Ÿä¸­īŧŒäŊ†æ‚¨å°‡å¤ąåŽģå°å…ąį”¨é …į›Žčˆ‡įĩ„įš”åŠŸčƒŊįš„å­˜å–æŦŠã€‚" + }, + "leaveConfirmationDialogContentTwo": { + "message": "č̋聝įĩĄæ‚¨įš„įŽĄį†å“ĄäģĨ重新取垗存取æŦŠé™ã€‚" + }, + "leaveConfirmationDialogConfirmButton": { + "message": "é›ĸ開 $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "我čρåĻ‚äŊ•įŽĄį†æˆ‘įš„å¯†įĸŧåēĢīŧŸ" + }, + "transferItemsToOrganizationTitle": { + "message": "å°‡é …į›ŽčŊ‰į§ģ臺 $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ į‚ēäē†åŽ‰å…¨æ€§čˆ‡åˆčĻæ€§īŧŒčĻæą‚æ‰€æœ‰é …į›Žįš†į”ąįĩ„į𔿓æœ‰ã€‚éģžæ“ŠæŽĨå—åŗå¯čŊ‰į§ģæ‚¨é …į›Žįš„æ“æœ‰æŦŠã€‚", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "同意čŊ‰į§ģ" + }, + "declineAndLeave": { + "message": "拒įĩ•ä¸Ļé›ĸ開" + }, + "whyAmISeeingThis": { + "message": "į‚ēäģ€éēŧæˆ‘æœƒįœ‹åˆ°æ­¤č¨Šæ¯īŧŸ" + }, + "resizeSideNavigation": { + "message": "čĒŋ整側邊æŦ„大小" } } diff --git a/apps/browser/src/auth/popup/account-switching/current-account.component.html b/apps/browser/src/auth/popup/account-switching/current-account.component.html index 2e2440f6258..7ab55f36753 100644 --- a/apps/browser/src/auth/popup/account-switching/current-account.component.html +++ b/apps/browser/src/auth/popup/account-switching/current-account.component.html @@ -2,7 +2,7 @@
`; - const shadowRoot = document.getElementById("shadow-root").attachShadow({ mode: "open" }); + const shadowRoot = document.getElementById("shadow-root")!.attachShadow({ mode: "open" }); shadowRoot.innerHTML = ` `; @@ -1668,7 +1668,7 @@ describe("AutofillOverlayContentService", () => { pageDetailsMock, ); await flushPromises(); - buttonElement.dispatchEvent(new KeyboardEvent("keyup", { code: "Enter" })); + buttonElement?.dispatchEvent(new KeyboardEvent("keyup", { code: "Enter" })); expect(sendExtensionMessageSpy).toHaveBeenCalledWith( "formFieldSubmitted", @@ -1716,6 +1716,85 @@ describe("AutofillOverlayContentService", () => { }); }); + describe("refreshMenuLayerPosition", () => { + it("calls refreshTopLayerPosition on the inline menu content service", () => { + autofillOverlayContentService.refreshMenuLayerPosition(); + + expect(inlineMenuContentService.refreshTopLayerPosition).toHaveBeenCalled(); + }); + + it("does not throw if inline menu content service is not available", () => { + const serviceWithoutInlineMenu = new AutofillOverlayContentService( + domQueryService, + domElementVisibilityService, + inlineMenuFieldQualificationService, + ); + + expect(() => serviceWithoutInlineMenu.refreshMenuLayerPosition()).not.toThrow(); + }); + }); + + describe("getOwnedInlineMenuTagNames", () => { + it("returns tag names from the inline menu content service", () => { + inlineMenuContentService.getOwnedTagNames.mockReturnValue(["div", "span"]); + + const result = autofillOverlayContentService.getOwnedInlineMenuTagNames(); + + expect(result).toEqual(["div", "span"]); + }); + + it("returns an empty array if inline menu content service is not available", () => { + const serviceWithoutInlineMenu = new AutofillOverlayContentService( + domQueryService, + domElementVisibilityService, + inlineMenuFieldQualificationService, + ); + + const result = serviceWithoutInlineMenu.getOwnedInlineMenuTagNames(); + + expect(result).toEqual([]); + }); + }); + + describe("getUnownedTopLayerItems", () => { + it("returns unowned top layer items from the inline menu content service", () => { + const mockElements = document.querySelectorAll("div"); + inlineMenuContentService.getUnownedTopLayerItems.mockReturnValue(mockElements); + + const result = autofillOverlayContentService.getUnownedTopLayerItems(true); + + expect(result).toEqual(mockElements); + expect(inlineMenuContentService.getUnownedTopLayerItems).toHaveBeenCalledWith(true); + }); + + it("returns undefined if inline menu content service is not available", () => { + const serviceWithoutInlineMenu = new AutofillOverlayContentService( + domQueryService, + domElementVisibilityService, + inlineMenuFieldQualificationService, + ); + + const result = serviceWithoutInlineMenu.getUnownedTopLayerItems(); + + expect(result).toBeUndefined(); + }); + }); + + describe("clearUserFilledFields", () => { + it("deletes all user filled fields", () => { + const mockElement1 = document.createElement("input") as FillableFormFieldElement; + const mockElement2 = document.createElement("input") as FillableFormFieldElement; + autofillOverlayContentService["userFilledFields"] = { + username: mockElement1, + password: mockElement2, + }; + + autofillOverlayContentService.clearUserFilledFields(); + + expect(autofillOverlayContentService["userFilledFields"]).toEqual({}); + }); + }); + describe("handleOverlayRepositionEvent", () => { const repositionEvents = [EVENTS.SCROLL, EVENTS.RESIZE]; repositionEvents.forEach((repositionEvent) => { @@ -2049,7 +2128,7 @@ describe("AutofillOverlayContentService", () => { }); it("skips focusing an element if no recently focused field exists", async () => { - autofillOverlayContentService["mostRecentlyFocusedField"] = undefined; + (autofillOverlayContentService as any)["mostRecentlyFocusedField"] = null; sendMockExtensionMessage({ command: "redirectAutofillInlineMenuFocusOut", @@ -2149,7 +2228,6 @@ describe("AutofillOverlayContentService", () => { }); it("returns null if the sub frame URL cannot be parsed correctly", async () => { - delete globalThis.location; globalThis.location = { href: "invalid-base" } as Location; sendMockExtensionMessage( { diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts index 817a7cca43c..7ea89e114ab 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts @@ -1086,15 +1086,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ pageDetails, ) ) { - const hasUsernameField = [...this.formFieldElements.values()].some((field) => - this.inlineMenuFieldQualificationService.isUsernameField(field), - ); - - if (hasUsernameField) { - void this.setQualifiedLoginFillType(autofillFieldData); - } else { - this.setQualifiedAccountCreationFillType(autofillFieldData); - } + this.setQualifiedAccountCreationFillType(autofillFieldData); return false; } @@ -1659,17 +1651,19 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ return false; }; const scrollHandler = this.useEventHandlersMemo( - throttle(async (event) => { + throttle(async (event: Event) => { + const scrollY = globalThis.scrollY; + const scrollX = globalThis.scrollX; if ( - currentScrollY !== globalThis.scrollY || - currentScrollX !== globalThis.scrollX || - eventTargetContainsFocusedField(event.target) + currentScrollY !== scrollY || + currentScrollX !== scrollX || + (event.target instanceof Element && eventTargetContainsFocusedField(event.target)) ) { repositionHandler(event); } - currentScrollY = globalThis.scrollY; - currentScrollX = globalThis.scrollX; + currentScrollY = scrollY; + currentScrollX = scrollX; }, 50), AUTOFILL_OVERLAY_HANDLE_SCROLL, ); 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 6f2c00a4dd4..117c7c5e2a4 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( @@ -87,7 +96,9 @@ export class CollectAutofillContentService implements CollectAutofillContentServ */ async getPageDetails(): Promise { // Set up listeners on top-layer candidates that predate Mutation Observer setup - this.setupInitialTopLayerListeners(); + if (this.autofillOverlayContentService) { + this.setupInitialTopLayerListeners(); + } if (!this.mutationObserver) { this.setupMutationObserver(); @@ -1063,17 +1074,21 @@ export class CollectAutofillContentService implements CollectAutofillContentServ } private setupTopLayerCandidateListener = (element: Element) => { - const ownedTags = this.autofillOverlayContentService.getOwnedInlineMenuTagNames() || []; - this.ownedExperienceTagNames = ownedTags; + if (this.autofillOverlayContentService) { + const ownedTags = this.autofillOverlayContentService.getOwnedInlineMenuTagNames() || []; + this.ownedExperienceTagNames = ownedTags; - if (!ownedTags.includes(element.tagName)) { - element.addEventListener("toggle", (event: ToggleEvent) => { - if (event.newState === "open") { - // Add a slight delay (but faster than a user's reaction), to ensure the layer - // positioning happens after any triggered toggle has completed. - setTimeout(this.autofillOverlayContentService.refreshMenuLayerPosition, 100); - } - }); + if (!ownedTags.includes(element.tagName)) { + element.addEventListener("toggle", (event: ToggleEvent) => { + if (event.newState === "open") { + // Add a slight delay (but faster than a user's reaction), to ensure the layer + // positioning happens after any triggered toggle has completed. + setTimeout(this.autofillOverlayContentService.refreshMenuLayerPosition, 100); + } + }); + + this.autofillOverlayContentService.refreshMenuLayerPosition(); + } } }; @@ -1400,7 +1415,7 @@ export class CollectAutofillContentService implements CollectAutofillContentServ this.intersectionObserver = new IntersectionObserver(this.handleFormElementIntersection, { root: null, rootMargin: "0px", - threshold: 1.0, + threshold: 0.9999, // Safari doesn't seem to function properly with a threshold of 1, }); } diff --git a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts index f7c46a9fa77..65f9eee1ecb 100644 --- a/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts +++ b/apps/browser/src/autofill/services/inline-menu-field-qualification.service.ts @@ -16,9 +16,7 @@ import { } from "./autofill-constants"; import AutofillService from "./autofill.service"; -export class InlineMenuFieldQualificationService - implements InlineMenuFieldQualificationServiceInterface -{ +export class InlineMenuFieldQualificationService implements InlineMenuFieldQualificationServiceInterface { private searchFieldNamesSet = new Set(AutoFillConstants.SearchFieldNames); private excludedAutofillFieldTypesSet = new Set(AutoFillConstants.ExcludedAutofillLoginTypes); private usernameFieldTypes = new Set(["text", "email", "number", "tel"]); @@ -945,7 +943,8 @@ export class InlineMenuFieldQualificationService !fieldType || !this.usernameFieldTypes.has(fieldType) || this.isExcludedFieldType(field, this.excludedAutofillFieldTypesSet) || - this.fieldHasDisqualifyingAttributeValue(field) + this.fieldHasDisqualifyingAttributeValue(field) || + this.isTotpField(field) ) { return false; } diff --git a/apps/browser/src/autofill/utils/index.ts b/apps/browser/src/autofill/utils/index.ts index a3d61c7f0b2..dc07ca1e258 100644 --- a/apps/browser/src/autofill/utils/index.ts +++ b/apps/browser/src/autofill/utils/index.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { FieldRect } from "../background/abstractions/overlay.background"; import { AutofillPort } from "../enums/autofill-port.enum"; import { FillableFormFieldElement, FormElementWithAttribute, FormFieldElement } from "../types"; @@ -144,11 +142,14 @@ export function setElementStyles( } for (const styleProperty in styles) { - element.style.setProperty( - styleProperty.replace(/([a-z])([A-Z])/g, "$1-$2"), // Convert camelCase to kebab-case - styles[styleProperty], - priority ? "important" : undefined, - ); + const styleValue = styles[styleProperty]; + if (styleValue !== undefined) { + element.style.setProperty( + styleProperty.replace(/([a-z])([A-Z])/g, "$1-$2"), // Convert camelCase to kebab-case + styleValue, + priority ? "important" : undefined, + ); + } } } @@ -175,12 +176,13 @@ export function setupExtensionDisconnectAction(callback: (port: chrome.runtime.P * @param windowContext - The global window context */ export function setupAutofillInitDisconnectAction(windowContext: Window) { - if (!windowContext.bitwardenAutofillInit) { + const bitwardenAutofillInit = windowContext.bitwardenAutofillInit; + if (!bitwardenAutofillInit) { return; } const onDisconnectCallback = () => { - windowContext.bitwardenAutofillInit.destroy(); + bitwardenAutofillInit.destroy(); delete windowContext.bitwardenAutofillInit; }; setupExtensionDisconnectAction(onDisconnectCallback); @@ -357,7 +359,7 @@ export function getAttributeBoolean( */ export function getPropertyOrAttribute(element: HTMLElement, attributeName: string): string | null { if (attributeName in element) { - return (element as FormElementWithAttribute)[attributeName]; + return (element as FormElementWithAttribute)[attributeName] ?? null; } return element.getAttribute(attributeName); @@ -369,9 +371,12 @@ export function getPropertyOrAttribute(element: HTMLElement, attributeName: stri * @param callback - The callback function to throttle. * @param limit - The time in milliseconds to throttle the callback. */ -export function throttle(callback: (_args: any) => any, limit: number) { +export function throttle unknown>( + callback: FunctionType, + limit: number, +): (this: ThisParameterType, ...args: Parameters) => void { let waitingDelay = false; - return function (...args: unknown[]) { + return function (this: ThisParameterType, ...args: Parameters) { if (!waitingDelay) { callback.apply(this, args); waitingDelay = true; @@ -387,9 +392,14 @@ export function throttle(callback: (_args: any) => any, limit: number) { * @param delay - The time in milliseconds to debounce the callback. * @param immediate - Determines whether the callback should run immediately. */ -export function debounce(callback: (_args: any) => any, delay: number, immediate?: boolean) { - let timeout: NodeJS.Timeout; - return function (...args: unknown[]) { +export function debounce unknown>( + callback: FunctionType, + delay: number, + immediate?: boolean, +): (this: ThisParameterType, ...args: Parameters) => void { + let timeout: ReturnType | null = null; + + return function (this: ThisParameterType, ...args: Parameters) { const callImmediately = !!immediate && !timeout; if (timeout) { @@ -430,16 +440,17 @@ export function getSubmitButtonKeywordsSet(element: HTMLElement): Set { const keywordsSet = new Set(); for (let i = 0; i < keywords.length; i++) { - if (typeof keywords[i] === "string") { + const keyword = keywords[i]; + if (typeof keyword === "string") { // Iterate over all keywords metadata and split them by non-letter characters. // This ensures we check against individual words and not the entire string. - keywords[i] + keyword .toLowerCase() .replace(/[-\s]/g, "") .split(/[^\p{L}]+/gu) - .forEach((keyword) => { - if (keyword) { - keywordsSet.add(keyword); + .forEach((splitKeyword) => { + if (splitKeyword) { + keywordsSet.add(splitKeyword); } }); } diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 78b5e323798..660fcb97bcf 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -40,7 +40,7 @@ import { DefaultPolicyService } from "@bitwarden/common/admin-console/services/p import { PolicyApiService } from "@bitwarden/common/admin-console/services/policy/policy-api.service"; import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service"; import { AccountService as AccountServiceAbstraction } from "@bitwarden/common/auth/abstractions/account.service"; -import { AuthRequestAnsweringServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction"; +import { AuthRequestAnsweringService } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction"; import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service"; import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service"; import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction"; @@ -52,7 +52,6 @@ import { UserVerificationService as UserVerificationServiceAbstraction } from "@ import { AuthServerNotificationTags } from "@bitwarden/common/auth/enums/auth-server-notification-tags"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service"; -import { AuthRequestAnsweringService } from "@bitwarden/common/auth/services/auth-request-answering/auth-request-answering.service"; import { PendingAuthRequestsStateService } from "@bitwarden/common/auth/services/auth-request-answering/pending-auth-requests.state"; import { AuthService } from "@bitwarden/common/auth/services/auth.service"; import { AvatarService } from "@bitwarden/common/auth/services/avatar.service"; @@ -82,9 +81,13 @@ import { import { isUrlInList } from "@bitwarden/common/autofill/utils"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service"; +import { PhishingDetectionSettingsServiceAbstraction } from "@bitwarden/common/dirt/services/abstractions/phishing-detection-settings.service.abstraction"; 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, @@ -122,6 +125,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"; @@ -160,6 +164,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"; @@ -189,6 +194,7 @@ import { SendService } from "@bitwarden/common/tools/send/services/send.service" import { InternalSendService as InternalSendServiceAbstraction } from "@bitwarden/common/tools/send/services/send.service.abstraction"; import { UserId } from "@bitwarden/common/types/guid"; import { CipherEncryptionService } from "@bitwarden/common/vault/abstractions/cipher-encryption.service"; +import { CipherSdkService } from "@bitwarden/common/vault/abstractions/cipher-sdk.service"; import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherFileUploadService as CipherFileUploadServiceAbstraction } from "@bitwarden/common/vault/abstractions/file-upload/cipher-file-upload.service"; import { FolderApiServiceAbstraction } from "@bitwarden/common/vault/abstractions/folder/folder-api.service.abstraction"; @@ -206,6 +212,7 @@ import { CipherAuthorizationService, DefaultCipherAuthorizationService, } from "@bitwarden/common/vault/services/cipher-authorization.service"; +import { DefaultCipherSdkService } from "@bitwarden/common/vault/services/cipher-sdk.service"; import { CipherService } from "@bitwarden/common/vault/services/cipher.service"; import { DefaultCipherEncryptionService } from "@bitwarden/common/vault/services/default-cipher-encryption.service"; import { CipherFileUploadService } from "@bitwarden/common/vault/services/file-upload/cipher-file-upload.service"; @@ -269,6 +276,7 @@ import { VaultExportServiceAbstraction, } from "@bitwarden/vault-export-core"; +import { ExtensionAuthRequestAnsweringService } from "../auth/services/auth-request-answering/extension-auth-request-answering.service"; import { AuthStatusBadgeUpdaterService } from "../auth/services/auth-status-badge-updater.service"; import { ExtensionLockService } from "../auth/services/extension-lock.service"; import { OverlayNotificationsBackground as OverlayNotificationsBackgroundInterface } from "../autofill/background/abstractions/overlay-notifications.background"; @@ -297,6 +305,7 @@ import { SafariApp } from "../browser/safariApp"; import { PhishingDataService } from "../dirt/phishing-detection/services/phishing-data.service"; import { PhishingDetectionService } from "../dirt/phishing-detection/services/phishing-detection.service"; import { BackgroundBrowserBiometricsService } from "../key-management/biometrics/background-browser-biometrics.service"; +import { BrowserSessionTimeoutTypeService } from "../key-management/session-timeout/services/browser-session-timeout-type.service"; import VaultTimeoutService from "../key-management/vault-timeout/vault-timeout.service"; import { BrowserActionsService } from "../platform/actions/browser-actions.service"; import { DefaultBadgeBrowserApi } from "../platform/badge/badge-browser-api"; @@ -360,6 +369,7 @@ export default class MainBackground { apiService: ApiServiceAbstraction; hibpApiService: HibpApiService; environmentService: BrowserEnvironmentService; + cipherSdkService: CipherSdkService; cipherService: CipherServiceAbstraction; folderService: InternalFolderServiceAbstraction; userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction; @@ -385,7 +395,7 @@ export default class MainBackground { serverNotificationsService: ServerNotificationsService; systemNotificationService: SystemNotificationsService; actionsService: ActionsService; - authRequestAnsweringService: AuthRequestAnsweringServiceAbstraction; + authRequestAnsweringService: AuthRequestAnsweringService; stateService: StateServiceAbstraction; userNotificationSettingsService: UserNotificationSettingsServiceAbstraction; autofillSettingsService: AutofillSettingsServiceAbstraction; @@ -452,11 +462,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; @@ -496,6 +508,7 @@ export default class MainBackground { // DIRT private phishingDataService: PhishingDataService; + private phishingDetectionSettingsService: PhishingDetectionSettingsServiceAbstraction; constructor() { const logoutCallback = async (logoutReason: LogoutReason, userId?: UserId) => @@ -571,7 +584,7 @@ export default class MainBackground { "ephemeral", "bitwarden-ephemeral", ); - await sessionStorage.save("session-key", derivedKey); + await sessionStorage.save("session-key", derivedKey.toJSON()); return derivedKey; }); @@ -732,7 +745,15 @@ export default class MainBackground { this.singleUserStateProvider, ); this.organizationService = new DefaultOrganizationService(this.stateProvider); - this.policyService = new DefaultPolicyService(this.stateProvider, this.organizationService); + this.policyService = new DefaultPolicyService( + this.stateProvider, + this.organizationService, + this.accountService, + ); + + const sessionTimeoutTypeService = new BrowserSessionTimeoutTypeService( + this.platformUtilsService, + ); this.vaultTimeoutSettingsService = new DefaultVaultTimeoutSettingsService( this.accountService, @@ -745,6 +766,7 @@ export default class MainBackground { this.stateProvider, this.logService, VaultTimeoutStringType.OnRestart, // default vault timeout + sessionTimeoutTypeService, ); this.apiService = new ApiService( @@ -781,18 +803,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, @@ -830,11 +840,39 @@ export default class MainBackground { this.configService, ); - this.pinService = new PinService( + this.registerSdkService = new DefaultRegisterSdkService( + sdkClientFactory, + this.environmentService, + this.platformUtilsService, this.accountService, - this.encryptService, - this.kdfConfigService, + 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, this.keyService, this.sdkService, @@ -938,6 +976,8 @@ export default class MainBackground { this.logService, ); + this.cipherSdkService = new DefaultCipherSdkService(this.sdkService, this.logService); + this.cipherService = new CipherService( this.keyService, this.domainSettingsService, @@ -953,6 +993,7 @@ export default class MainBackground { this.logService, this.cipherEncryptionService, this.messagingService, + this.cipherSdkService, ); this.folderService = new FolderService( this.keyService, @@ -1000,6 +1041,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, @@ -1027,6 +1069,7 @@ export default class MainBackground { this.stateProvider, this.securityStateService, this.kdfConfigService, + this.accountCryptographicStateService, ); this.syncServiceListener = new SyncServiceListener( @@ -1102,7 +1145,7 @@ export default class MainBackground { this.collectionService, this.keyService, this.encryptService, - this.pinService, + this.keyGenerationService, this.accountService, this.restrictedItemTypesService, ); @@ -1110,7 +1153,7 @@ export default class MainBackground { this.individualVaultExportService = new IndividualVaultExportService( this.folderService, this.cipherService, - this.pinService, + this.keyGenerationService, this.keyService, this.encryptService, this.cryptoFunctionService, @@ -1124,7 +1167,7 @@ export default class MainBackground { this.organizationVaultExportService = new OrganizationVaultExportService( this.cipherService, this.exportApiService, - this.pinService, + this.keyGenerationService, this.keyService, this.encryptService, this.cryptoFunctionService, @@ -1171,16 +1214,17 @@ export default class MainBackground { this.pendingAuthRequestStateService = new PendingAuthRequestsStateService(this.stateProvider); - this.authRequestAnsweringService = new AuthRequestAnsweringService( + this.authRequestAnsweringService = new ExtensionAuthRequestAnsweringService( this.accountService, - this.actionsService, this.authService, - this.i18nService, this.masterPasswordService, this.messagingService, this.pendingAuthRequestStateService, + this.actionsService, + this.i18nService, this.platformUtilsService, this.systemNotificationService, + this.logService, ); this.serverNotificationsService = new DefaultServerNotificationsService( @@ -1196,6 +1240,7 @@ export default class MainBackground { this.webPushConnectionService, this.authRequestAnsweringService, this.configService, + this.policyService, ); this.fido2UserInterfaceService = new BrowserFido2UserInterfaceService(this.authService); @@ -1467,12 +1512,20 @@ export default class MainBackground { this.platformUtilsService, ); - PhishingDetectionService.initialize( + this.phishingDetectionSettingsService = new PhishingDetectionSettingsService( this.accountService, this.billingAccountProfileStateService, this.configService, + this.logService, + this.organizationService, + this.platformUtilsService, + this.stateProvider, + ); + + PhishingDetectionService.initialize( this.logService, this.phishingDataService, + this.phishingDetectionSettingsService, messageListener, ); @@ -1518,7 +1571,6 @@ export default class MainBackground { await this.sdkLoadService.loadAndInit(); // Only the "true" background should run migrations await this.migrationRunner.run(); - this.encryptService.init(this.configService); // This is here instead of in the InitService b/c we don't plan for // side effects to run in the Browser InitService. diff --git a/apps/browser/src/background/runtime.background.ts b/apps/browser/src/background/runtime.background.ts index 597babdc777..eba6b01fe90 100644 --- a/apps/browser/src/background/runtime.background.ts +++ b/apps/browser/src/background/runtime.background.ts @@ -294,19 +294,11 @@ export default class RuntimeBackground { await this.openPopup(); break; case VaultMessages.OpenAtRiskPasswords: { - if (await this.shouldRejectManyOriginMessage(msg)) { - return; - } - await this.main.openAtRisksPasswordsPage(); this.announcePopupOpen(); break; } case VaultMessages.OpenBrowserExtensionToUrl: { - if (await this.shouldRejectManyOriginMessage(msg)) { - return; - } - await this.main.openTheExtensionToPage(msg.url); this.announcePopupOpen(); break; diff --git a/apps/browser/src/billing/popup/settings/premium-v2.component.html b/apps/browser/src/billing/popup/settings/premium-v2.component.html index 47d72751af3..fea3e558057 100644 --- a/apps/browser/src/billing/popup/settings/premium-v2.component.html +++ b/apps/browser/src/billing/popup/settings/premium-v2.component.html @@ -12,7 +12,7 @@
  • - {{ "ppremiumSignUpStorage" | i18n }} + {{ "premiumSignUpStorageV2" | i18n: `${storageProvidedGb} GB` }}
  • {{ "premiumSignUpTwoStepOptions" | i18n }} diff --git a/apps/browser/src/billing/popup/settings/premium-v2.component.ts b/apps/browser/src/billing/popup/settings/premium-v2.component.ts index b858b74242d..0c246d734e5 100644 --- a/apps/browser/src/billing/popup/settings/premium-v2.component.ts +++ b/apps/browser/src/billing/popup/settings/premium-v2.component.ts @@ -1,13 +1,14 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { CommonModule, CurrencyPipe, Location } from "@angular/common"; -import { Component } from "@angular/core"; +import { Component, OnInit } from "@angular/core"; import { RouterModule } from "@angular/router"; import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/billing/components/premium.component"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -44,7 +45,7 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co SectionComponent, ], }) -export class PremiumV2Component extends BasePremiumComponent { +export class PremiumV2Component extends BasePremiumComponent implements OnInit { priceString: string; constructor( @@ -59,6 +60,7 @@ export class PremiumV2Component extends BasePremiumComponent { billingAccountProfileStateService: BillingAccountProfileStateService, toastService: ToastService, accountService: AccountService, + billingApiService: BillingApiServiceAbstraction, ) { super( i18nService, @@ -70,15 +72,18 @@ export class PremiumV2Component extends BasePremiumComponent { billingAccountProfileStateService, toastService, accountService, + billingApiService, ); - + } + async ngOnInit() { + await super.ngOnInit(); // Support old price string. Can be removed in future once all translations are properly updated. const thePrice = this.currencyPipe.transform(this.price, "$"); // Safari extension crashes due to $1 appearing in the price string ($10.00). Escape the $ to fix. const formattedPrice = this.platformUtilsService.isSafari() ? thePrice.replace("$", "$$$") : thePrice; - this.priceString = i18nService.t("premiumPriceV2", formattedPrice); + this.priceString = this.i18nService.t("premiumPriceV2", formattedPrice); if (this.priceString.indexOf("%price%") > -1) { this.priceString = this.priceString.replace("%price%", thePrice); } 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..88068987dd7 --- /dev/null +++ b/apps/browser/src/dirt/phishing-detection/phishing-resources.ts @@ -0,0 +1,102 @@ +export type PhishingResource = { + name?: string; + remoteUrl: string; + /** Fallback URL to use if remoteUrl fails (e.g., due to SSL interception/cert issues) */ + fallbackUrl: 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://phish.co.za/latest/phishing-domains-ACTIVE.txt", + fallbackUrl: + "https://raw.githubusercontent.com/Phishing-Database/Phishing.Database/refs/heads/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://phish.co.za/latest/phishing-links-ACTIVE.txt", + fallbackUrl: + "https://raw.githubusercontent.com/Phishing-Database/Phishing.Database/refs/heads/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 query/hash only, NOT subpaths) + // e.g., entry "site.com/phish" matches "site.com/phish?id=1" or "site.com/phish#section" + // but NOT "site.com/phish/subpage" (different endpoint) + if ( + 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..d633c0612f5 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 @@ -1,4 +1,5 @@ import { MockProxy, mock } from "jest-mock-extended"; +import { firstValueFrom } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -9,7 +10,8 @@ import { import { FakeGlobalStateProvider } from "@bitwarden/common/spec"; import { LogService } from "@bitwarden/logging"; -import { PhishingDataService, PhishingData, PHISHING_DOMAINS_KEY } from "./phishing-data.service"; +import { PHISHING_DOMAINS_META_KEY, PhishingDataService } from "./phishing-data.service"; +import type { PhishingIndexedDbService } from "./phishing-indexeddb.service"; describe("PhishingDataService", () => { let service: PhishingDataService; @@ -17,20 +19,30 @@ describe("PhishingDataService", () => { let taskSchedulerService: TaskSchedulerService; let logService: MockProxy; let platformUtilsService: MockProxy; - const stateProvider: FakeGlobalStateProvider = new FakeGlobalStateProvider(); - - const setMockState = (state: PhishingData) => { - stateProvider.getFake(PHISHING_DOMAINS_KEY).stateSubject.next(state); - return state; - }; - + let mockIndexedDbService: MockProxy; + const fakeGlobalStateProvider: FakeGlobalStateProvider = new FakeGlobalStateProvider(); let fetchChecksumSpy: jest.SpyInstance; - let fetchDomainsSpy: jest.SpyInstance; - beforeEach(() => { - jest.useFakeTimers(); + beforeEach(async () => { + jest.clearAllMocks(); + + // Mock Request global if not available + if (typeof Request === "undefined") { + (global as any).Request = class { + constructor(public url: string) {} + }; + } + apiService = mock(); logService = mock(); + mockIndexedDbService = mock(); + + // Set default mock behaviors + mockIndexedDbService.hasUrl.mockResolvedValue(false); + mockIndexedDbService.loadAllUrls.mockResolvedValue([]); + mockIndexedDbService.saveUrls.mockResolvedValue(undefined); + mockIndexedDbService.addUrls.mockResolvedValue(undefined); + mockIndexedDbService.saveUrlsFromStream.mockResolvedValue(undefined); platformUtilsService = mock(); platformUtilsService.getApplicationVersion.mockResolvedValue("1.0.0"); @@ -40,119 +52,319 @@ describe("PhishingDataService", () => { service = new PhishingDataService( apiService, taskSchedulerService, - stateProvider, + fakeGlobalStateProvider, logService, platformUtilsService, ); - fetchChecksumSpy = jest.spyOn(service as any, "fetchPhishingDomainsChecksum"); - fetchDomainsSpy = jest.spyOn(service as any, "fetchPhishingDomains"); + // Replace the IndexedDB service with our mock + service["indexedDbService"] = mockIndexedDbService; + + fetchChecksumSpy = jest.spyOn(service as any, "fetchPhishingChecksum"); + fetchChecksumSpy.mockResolvedValue("new-checksum"); }); - describe("isPhishingDomains", () => { - it("should detect a phishing domain", async () => { - setMockState({ - domains: ["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); - expect(result).toBe(true); + describe("initialization", () => { + it("should initialize with IndexedDB service", () => { + expect(service["indexedDbService"]).toBeDefined(); }); - it("should not detect a safe domain", async () => { - setMockState({ - domains: ["phish.com", "badguy.net"], - timestamp: Date.now(), - checksum: "abc123", - applicationVersion: "1.0.0", - }); + it("should detect QA test addresses - http protocol", async () => { + const url = new URL("http://phishing.testcategory.com"); + expect(await service.isPhishingWebAddress(url)).toBe(true); + // IndexedDB should not be called for test addresses + expect(mockIndexedDbService.hasUrl).not.toHaveBeenCalled(); + }); + + it("should detect QA test addresses - https protocol", async () => { + const url = new URL("https://phishing.testcategory.com"); + expect(await service.isPhishingWebAddress(url)).toBe(true); + expect(mockIndexedDbService.hasUrl).not.toHaveBeenCalled(); + }); + + it("should detect QA test addresses - specific subpath /block", async () => { + const url = new URL("https://phishing.testcategory.com/block"); + expect(await service.isPhishingWebAddress(url)).toBe(true); + expect(mockIndexedDbService.hasUrl).not.toHaveBeenCalled(); + }); + + it("should NOT detect QA test addresses - different subpath", async () => { + mockIndexedDbService.hasUrl.mockResolvedValue(false); + mockIndexedDbService.loadAllUrls.mockResolvedValue([]); + + const url = new URL("https://phishing.testcategory.com/other"); + const result = await service.isPhishingWebAddress(url); + + // This should NOT be detected as a test address since only /block subpath is hardcoded + expect(result).toBe(false); + }); + + it("should detect QA test addresses - root path with trailing slash", async () => { + const url = new URL("https://phishing.testcategory.com/"); + const result = await service.isPhishingWebAddress(url); + + // This SHOULD be detected since URLs are normalized (trailing slash added to root URLs) + expect(result).toBe(true); + expect(mockIndexedDbService.hasUrl).not.toHaveBeenCalled(); + }); + }); + + describe("isPhishingWebAddress", () => { + it("should detect a phishing web address using quick hasUrl lookup", async () => { + // Mock hasUrl to return true for direct hostname match + mockIndexedDbService.hasUrl.mockResolvedValue(true); + + const url = new URL("http://phish.com/testing-param"); + const result = await service.isPhishingWebAddress(url); + + expect(result).toBe(true); + expect(mockIndexedDbService.hasUrl).toHaveBeenCalledWith("http://phish.com/testing-param"); + // Should not fall back to custom matcher when hasUrl returns true + expect(mockIndexedDbService.loadAllUrls).not.toHaveBeenCalled(); + }); + + it("should fall back to custom matcher when hasUrl returns false", async () => { + // Mock hasUrl to return false (no direct href match) + mockIndexedDbService.hasUrl.mockResolvedValue(false); + // Mock loadAllUrls to return phishing URLs for custom matcher + mockIndexedDbService.loadAllUrls.mockResolvedValue(["http://phish.com/path"]); + + const url = new URL("http://phish.com/path"); + const result = await service.isPhishingWebAddress(url); + + expect(result).toBe(true); + expect(mockIndexedDbService.hasUrl).toHaveBeenCalledWith("http://phish.com/path"); + expect(mockIndexedDbService.loadAllUrls).toHaveBeenCalled(); + }); + + it("should not detect a safe web address", async () => { + // Mock hasUrl to return false + mockIndexedDbService.hasUrl.mockResolvedValue(false); + // Mock loadAllUrls to return phishing URLs that don't match + mockIndexedDbService.loadAllUrls.mockResolvedValue(["http://phish.com", "http://badguy.net"]); + const url = new URL("http://safe.com"); - const result = await service.isPhishingDomain(url); + const result = await service.isPhishingWebAddress(url); + expect(result).toBe(false); + expect(mockIndexedDbService.hasUrl).toHaveBeenCalledWith("http://safe.com/"); + expect(mockIndexedDbService.loadAllUrls).toHaveBeenCalled(); }); - it("should match against root domain", async () => { - setMockState({ - domains: ["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); - expect(result).toBe(true); + it("should not match against root web address with subpaths using custom matcher", async () => { + // Mock hasUrl to return false (no direct href match) + mockIndexedDbService.hasUrl.mockResolvedValue(false); + // Mock loadAllUrls to return entry that matches with subpath + mockIndexedDbService.loadAllUrls.mockResolvedValue(["http://phish.com/login"]); + + const url = new URL("http://phish.com/login/page"); + const result = await service.isPhishingWebAddress(url); + + expect(result).toBe(false); + expect(mockIndexedDbService.hasUrl).toHaveBeenCalledWith("http://phish.com/login/page"); + expect(mockIndexedDbService.loadAllUrls).toHaveBeenCalled(); }); - 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); + it("should not match against root web address with different subpaths using custom matcher", async () => { + // Mock hasUrl to return false (no direct hostname match) + mockIndexedDbService.hasUrl.mockResolvedValue(false); + // Mock loadAllUrls to return entry that matches with subpath + mockIndexedDbService.loadAllUrls.mockResolvedValue(["http://phish.com/login/page1"]); + + const url = new URL("http://phish.com/login/page2"); + const result = await service.isPhishingWebAddress(url); + expect(result).toBe(false); + expect(mockIndexedDbService.hasUrl).toHaveBeenCalledWith("http://phish.com/login/page2"); + expect(mockIndexedDbService.loadAllUrls).toHaveBeenCalled(); + }); + + it("should handle IndexedDB errors gracefully", async () => { + // Mock hasUrl to throw error + mockIndexedDbService.hasUrl.mockRejectedValue(new Error("hasUrl error")); + // Mock loadAllUrls to also throw error + mockIndexedDbService.loadAllUrls.mockRejectedValue(new Error("IndexedDB error")); + + const url = new URL("http://phish.com/about"); + const result = await service.isPhishingWebAddress(url); + + expect(result).toBe(false); + expect(logService.error).toHaveBeenCalledWith( + "[PhishingDataService] IndexedDB lookup via hasUrl failed", + expect.any(Error), + ); + expect(logService.error).toHaveBeenCalledWith( + "[PhishingDataService] Error running custom matcher", + expect.any(Error), + ); }); }); - describe("getNextDomains", () => { - it("refetches all domains if applicationVersion has changed", async () => { - const prev: PhishingData = { - domains: ["a.com"], - timestamp: Date.now() - 60000, - checksum: "old", - applicationVersion: "1.0.0", - }; - fetchChecksumSpy.mockResolvedValue("new"); - fetchDomainsSpy.mockResolvedValue(["d.com", "e.com"]); + describe("data updates", () => { + it("should update full dataset via stream", async () => { + // Mock full dataset update + const mockResponse = { + ok: true, + body: {} as ReadableStream, + } as Response; + apiService.nativeFetch.mockResolvedValue(mockResponse); + + await firstValueFrom(service["_updateFullDataSet"]()); + + expect(mockIndexedDbService.saveUrlsFromStream).toHaveBeenCalled(); + }); + + it("should update daily dataset via addUrls", async () => { + // Mock daily update + const mockResponse = { + ok: true, + text: jest.fn().mockResolvedValue("newphish.com\nanotherbad.net"), + } as unknown as Response; + apiService.nativeFetch.mockResolvedValue(mockResponse); + + await firstValueFrom(service["_updateDailyDataSet"]()); + + expect(mockIndexedDbService.addUrls).toHaveBeenCalledWith(["newphish.com", "anotherbad.net"]); + }); + + it("should get updated meta information", async () => { + fetchChecksumSpy.mockResolvedValue("new-checksum"); platformUtilsService.getApplicationVersion.mockResolvedValue("2.0.0"); - const result = await service.getNextDomains(prev); + const meta = await firstValueFrom(service["_getUpdatedMeta"]()); - expect(result!.domains).toEqual(["d.com", "e.com"]); - expect(result!.checksum).toBe("new"); - expect(result!.applicationVersion).toBe("2.0.0"); + expect(meta).toBeDefined(); + expect(meta.checksum).toBe("new-checksum"); + expect(meta.applicationVersion).toBe("2.0.0"); + expect(meta.timestamp).toBeDefined(); }); + }); - it("only updates timestamp if checksum matches", async () => { - const prev: PhishingData = { - domains: ["a.com"], - timestamp: Date.now() - 60000, - checksum: "abc", + describe("phishing meta data updates", () => { + it("should not update metadata when no data updates occur", async () => { + // Set up existing metadata + const existingMeta = { + checksum: "existing-checksum", + timestamp: Date.now() - 1000, // 1 second ago (not expired) applicationVersion: "1.0.0", }; - fetchChecksumSpy.mockResolvedValue("abc"); - const result = await service.getNextDomains(prev); - expect(result!.domains).toEqual(prev.domains); - expect(result!.checksum).toBe("abc"); - expect(result!.timestamp).not.toBe(prev.timestamp); + await fakeGlobalStateProvider.get(PHISHING_DOMAINS_META_KEY).update(() => existingMeta); + + // Mock conditions where no update is needed + fetchChecksumSpy.mockResolvedValue("existing-checksum"); // Same checksum + platformUtilsService.getApplicationVersion.mockResolvedValue("1.0.0"); // Same version + const mockResponse = { + ok: true, + body: {} as ReadableStream, + } as Response; + apiService.nativeFetch.mockResolvedValue(mockResponse); + + // Trigger background update + const result = await firstValueFrom(service["_backgroundUpdate"](existingMeta)); + + // Verify metadata was NOT updated (same reference returned) + expect(result).toEqual(existingMeta); + expect(result?.timestamp).toBe(existingMeta.timestamp); + + // Verify no data updates were performed + expect(mockIndexedDbService.saveUrlsFromStream).not.toHaveBeenCalled(); + expect(mockIndexedDbService.addUrls).not.toHaveBeenCalled(); }); - it("patches daily domains if cache is fresh", async () => { - const prev: PhishingData = { - domains: ["a.com"], - timestamp: Date.now() - 60000, - checksum: "old", + it("should update metadata when full dataset update occurs due to checksum change", async () => { + // Set up existing metadata + const existingMeta = { + checksum: "old-checksum", + timestamp: Date.now() - 1000, 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"]); - expect(result!.checksum).toBe("new"); + await fakeGlobalStateProvider.get(PHISHING_DOMAINS_META_KEY).update(() => existingMeta); + + // Mock conditions for full update + fetchChecksumSpy.mockResolvedValue("new-checksum"); // Different checksum + platformUtilsService.getApplicationVersion.mockResolvedValue("1.0.0"); + const mockResponse = { + ok: true, + body: {} as ReadableStream, + } as Response; + apiService.nativeFetch.mockResolvedValue(mockResponse); + + // Trigger background update + const result = await firstValueFrom(service["_backgroundUpdate"](existingMeta)); + + // Verify metadata WAS updated with new values + expect(result?.checksum).toBe("new-checksum"); + expect(result?.timestamp).toBeGreaterThan(existingMeta.timestamp); + + // Verify full update was performed + expect(mockIndexedDbService.saveUrlsFromStream).toHaveBeenCalled(); + expect(mockIndexedDbService.addUrls).not.toHaveBeenCalled(); // Daily should not run }); - it("fetches all domains if cache is old", async () => { - const prev: PhishingData = { - domains: ["a.com"], - timestamp: Date.now() - 2 * 24 * 60 * 60 * 1000, - checksum: "old", + it("should update metadata when full dataset update occurs due to version change", async () => { + // Set up existing metadata + const existingMeta = { + checksum: "same-checksum", + timestamp: Date.now() - 1000, 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"]); - expect(result!.checksum).toBe("new"); + await fakeGlobalStateProvider.get(PHISHING_DOMAINS_META_KEY).update(() => existingMeta); + + // Mock conditions for full update + fetchChecksumSpy.mockResolvedValue("same-checksum"); + platformUtilsService.getApplicationVersion.mockResolvedValue("2.0.0"); // Different version + const mockResponse = { + ok: true, + body: {} as ReadableStream, + } as Response; + apiService.nativeFetch.mockResolvedValue(mockResponse); + + // Trigger background update + const result = await firstValueFrom(service["_backgroundUpdate"](existingMeta)); + + // Verify metadata WAS updated + expect(result?.applicationVersion).toBe("2.0.0"); + expect(result?.timestamp).toBeGreaterThan(existingMeta.timestamp); + + // Verify full update was performed + expect(mockIndexedDbService.saveUrlsFromStream).toHaveBeenCalled(); + expect(mockIndexedDbService.addUrls).not.toHaveBeenCalled(); + }); + + it("should update metadata when daily update occurs due to cache expiration", async () => { + // Set up existing metadata (expired cache) + const existingMeta = { + checksum: "same-checksum", + timestamp: Date.now() - 25 * 60 * 60 * 1000, // 25 hours ago (expired) + applicationVersion: "1.0.0", + }; + await fakeGlobalStateProvider.get(PHISHING_DOMAINS_META_KEY).update(() => existingMeta); + + // Mock conditions for daily update only + fetchChecksumSpy.mockResolvedValue("same-checksum"); // Same checksum (no full update) + platformUtilsService.getApplicationVersion.mockResolvedValue("1.0.0"); // Same version + const mockFullResponse = { + ok: true, + body: {} as ReadableStream, + } as Response; + const mockDailyResponse = { + ok: true, + text: jest.fn().mockResolvedValue("newdomain.com"), + } as unknown as Response; + apiService.nativeFetch + .mockResolvedValueOnce(mockFullResponse) + .mockResolvedValueOnce(mockDailyResponse); + + // Trigger background update + const result = await firstValueFrom(service["_backgroundUpdate"](existingMeta)); + + // Verify metadata WAS updated + expect(result?.timestamp).toBeGreaterThan(existingMeta.timestamp); + expect(result?.checksum).toBe("same-checksum"); + + // Verify only daily update was performed + expect(mockIndexedDbService.saveUrlsFromStream).not.toHaveBeenCalled(); + expect(mockIndexedDbService.addUrls).toHaveBeenCalledWith(["newdomain.com"]); }); }); }); 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..10268fa7f93 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 @@ -1,15 +1,24 @@ import { catchError, + concatMap, + defer, EMPTY, + exhaustMap, first, - firstValueFrom, + forkJoin, + from, + iif, map, + Observable, + of, retry, share, + takeUntil, startWith, Subject, switchMap, tap, + throwError, timer, } from "rxjs"; @@ -20,91 +29,87 @@ import { ScheduledTaskNames, TaskSchedulerService } from "@bitwarden/common/plat import { LogService } from "@bitwarden/logging"; import { GlobalStateProvider, KeyDefinition, PHISHING_DETECTION_DISK } from "@bitwarden/state"; -export type PhishingData = { - domains: string[]; - timestamp: number; - checksum: string; +import { getPhishingResources, PhishingResourceType } from "../phishing-resources"; +import { PhishingIndexedDbService } from "./phishing-indexeddb.service"; + +/** + * Metadata about the phishing data set + */ +export type PhishingDataMeta = { + /** The last known checksum of the phishing data set */ + checksum: string; + /** The last time the data set was updated */ + timestamp: number; /** * 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; }; -export const PHISHING_DOMAINS_KEY = new KeyDefinition( +/** + * The phishing data blob is a string representation of the phishing web addresses + */ +export type PhishingDataBlob = string; +export type PhishingData = { meta: PhishingDataMeta; blob: PhishingDataBlob }; + +export const PHISHING_DOMAINS_META_KEY = new KeyDefinition( PHISHING_DETECTION_DISK, - "phishingDomains", + "phishingDomainsMeta", { - deserializer: (value: PhishingData) => - value ?? { domains: [], timestamp: 0, checksum: "", applicationVersion: "" }, + deserializer: (value: PhishingDataMeta) => { + return { + checksum: value?.checksum ?? "", + timestamp: value?.timestamp ?? 0, + applicationVersion: value?.applicationVersion ?? "", + }; + }, }, ); -/** Coordinates fetching, caching, and patching of known phishing domains */ +export const PHISHING_DOMAINS_BLOB_KEY = new KeyDefinition( + PHISHING_DETECTION_DISK, + "phishingDomainsBlob", + { + deserializer: (value: string) => value ?? "", + }, +); + +/** 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"; + // While background scripts do not necessarily need destroying, + // processes in PhishingDataService are memory intensive. + // We are adding the destroy to guard against accidental leaks. + private _destroy$ = new Subject(); - private _testDomains = this.getTestDomains(); - private _cachedState = this.globalStateProvider.get(PHISHING_DOMAINS_KEY); - private _domains$ = this._cachedState.state$.pipe( - map( - (state) => - new Set( - (state?.domains?.filter((line) => line.trim().length > 0) ?? []).concat( - this._testDomains, - "phishing.testcategory.com", // Included for QA to test in prod - ), - ), - ), - ); + private _testWebAddresses = this.getTestWebAddresses(); + private _phishingMetaState = this.globalStateProvider.get(PHISHING_DOMAINS_META_KEY); - // How often are new domains added to the remote? + private indexedDbService: PhishingIndexedDbService; + + // How often are new web addresses added to the remote? readonly UPDATE_INTERVAL_DURATION = 24 * 60 * 60 * 1000; // 24 hours + private _backgroundUpdateTrigger$ = new Subject(); + private _triggerUpdate$ = new Subject(); update$ = this._triggerUpdate$.pipe( startWith(undefined), // Always emit once - tap(() => this.logService.info(`[PhishingDataService] Update triggered...`)), switchMap(() => - this._cachedState.state$.pipe( + this._phishingMetaState.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); - if (next) { - await this._cachedState.update(() => next); - this.logService.info(`[PhishingDataService] cache updated`); - } + tap((metaState) => { + // Perform any updates in the background + this._backgroundUpdateTrigger$.next(metaState); }), - retry({ - count: 3, - delay: (err, count) => { - this.logService.error( - `[PhishingDataService] Unable to update domains. Attempt ${count}.`, - err, - ); - return timer(5 * 60 * 1000); // 5 minutes - }, - resetOnSuccess: true, + catchError((err: unknown) => { + this.logService.error("[PhishingDataService] Background update failed to start.", err); + return EMPTY; }), - catchError( - ( - 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.", - err, - ); - return EMPTY; - }, - ), ), ), + takeUntil(this._destroy$), share(), ); @@ -114,7 +119,10 @@ export class PhishingDataService { private globalStateProvider: GlobalStateProvider, private logService: LogService, private platformUtilsService: PlatformUtilsService, + private resourceType: PhishingResourceType = PhishingResourceType.Links, ) { + this.logService.debug("[PhishingDataService] Initializing service..."); + this.indexedDbService = new PhishingIndexedDbService(this.logService); this.taskSchedulerService.registerTaskHandler(ScheduledTaskNames.phishingDomainUpdate, () => { this._triggerUpdate$.next(); }); @@ -122,102 +130,289 @@ export class PhishingDataService { ScheduledTaskNames.phishingDomainUpdate, this.UPDATE_INTERVAL_DURATION, ); + this._backgroundUpdateTrigger$ + .pipe( + exhaustMap((currentMeta) => { + return this._backgroundUpdate(currentMeta); + }), + takeUntil(this._destroy$), + ) + .subscribe(); + } + + dispose(): void { + // Signal all pipelines to stop and unsubscribe stored subscriptions + this._destroy$.next(); + this._destroy$.complete(); } /** - * 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) { + async isPhishingWebAddress(url: URL): Promise { + // Quick check for QA/dev test addresses + if (this._testWebAddresses.includes(url.href)) { return true; } + + const resource = getPhishingResources(this.resourceType); + + try { + // Quick lookup: check direct presence of href in IndexedDB + const hasUrl = await this.indexedDbService.hasUrl(url.href); + if (hasUrl) { + return true; + } + } catch (err) { + this.logService.error("[PhishingDataService] IndexedDB lookup via hasUrl failed", err); + } + + // If a custom matcher is provided, iterate stored entries and apply the matcher. + if (resource && resource.match) { + try { + const entries = await this.indexedDbService.loadAllUrls(); + for (const entry of entries) { + if (resource.match(url, entry)) { + return true; + } + } + } catch (err) { + this.logService.error("[PhishingDataService] Error running custom matcher", err); + } + return false; + } return false; } - async getNextDomains(prev: PhishingData | null): Promise { - prev = prev ?? { domains: [], timestamp: 0, checksum: "", applicationVersion: "" }; - const timestamp = Date.now(); - const prevAge = timestamp - prev.timestamp; - this.logService.info(`[PhishingDataService] Cache age: ${prevAge}`); + // [FIXME] Pull fetches into api service + private async fetchPhishingChecksum(type: PhishingResourceType = PhishingResourceType.Domains) { + const checksumUrl = getPhishingResources(type)!.checksumUrl; + this.logService.debug(`[PhishingDataService] Fetching checksum from: ${checksumUrl}`); - const applicationVersion = await this.platformUtilsService.getApplicationVersion(); + try { + const response = await this.apiService.nativeFetch(new Request(checksumUrl)); + if (!response.ok) { + throw new Error( + `[PhishingDataService] Failed to fetch checksum: ${response.status} ${response.statusText}`, + ); + } - // If checksum matches, return existing data with new timestamp & version - const remoteChecksum = await this.fetchPhishingDomainsChecksum(); - if (remoteChecksum && prev.checksum === remoteChecksum) { - this.logService.info( - `[PhishingDataService] Remote checksum matches local checksum, updating timestamp only.`, + return await response.text(); + } catch (error) { + this.logService.error( + `[PhishingDataService] Checksum fetch failed from ${checksumUrl}`, + error, ); - return { ...prev, timestamp, applicationVersion }; + throw error; } - // Checksum is different, data needs to be updated. - - // Approach 1: Fetch only new domains and append - const isOneDayOldMax = prevAge <= this.UPDATE_INTERVAL_DURATION; - if (isOneDayOldMax && applicationVersion === prev.applicationVersion) { - const dailyDomains: string[] = await this.fetchPhishingDomains( - PhishingDataService.RemotePhishingDatabaseTodayUrl, - ); - this.logService.info( - `[PhishingDataService] ${dailyDomains.length} new phishing domains added`, - ); - return { - domains: prev.domains.concat(dailyDomains), - checksum: remoteChecksum, - timestamp, - applicationVersion, - }; - } - - // Approach 2: Fetch all domains - const domains = await this.fetchPhishingDomains(PhishingDataService.RemotePhishingDatabaseUrl); - return { - domains, - timestamp, - checksum: remoteChecksum, - applicationVersion, - }; } - private async fetchPhishingDomainsChecksum() { - const response = await this.apiService.nativeFetch( - new Request(PhishingDataService.RemotePhishingDatabaseChecksumUrl), - ); - if (!response.ok) { - throw new Error(`[PhishingDataService] Failed to fetch checksum: ${response.status}`); - } - return response.text(); - } - - private async fetchPhishingDomains(url: string) { + // [FIXME] Pull fetches into api service + private async fetchToday(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"); + // Normalize URLs by converting to URL object and back to ensure consistent format (e.g., trailing slashes) + const testWebAddresses: string[] = [ + new URL("http://phishing.testcategory.com").href, + new URL("https://phishing.testcategory.com").href, + new URL("https://phishing.testcategory.com/block").href, + ]; if (!flag) { - return []; + return testWebAddresses; } - 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, + "[PhishingDataService] Dev flag enabled for testing phishing detection. Adding test phishing web addresses:", + webAddresses, ); - return domains as string[]; + // Normalize dev flag URLs as well, filtering out invalid ones + const normalizedDevAddresses = (webAddresses as string[]) + .filter((addr) => { + try { + new URL(addr); + return true; + } catch { + this.logService.warning( + `[PhishingDataService] Invalid test URL in dev flag, skipping: ${addr}`, + ); + return false; + } + }) + .map((addr) => new URL(addr).href); + return testWebAddresses.concat(normalizedDevAddresses); } - return []; + return testWebAddresses; + } + + private _getUpdatedMeta(): Observable { + return defer(() => { + const now = Date.now(); + + return forkJoin({ + applicationVersion: from(this.platformUtilsService.getApplicationVersion()), + remoteChecksum: from(this.fetchPhishingChecksum(this.resourceType)), + }).pipe( + map(({ applicationVersion, remoteChecksum }) => { + return { + checksum: remoteChecksum, + timestamp: now, + applicationVersion, + }; + }), + ); + }); + } + + // Streams the full phishing data set and saves it to IndexedDB + private _updateFullDataSet() { + const resource = getPhishingResources(this.resourceType); + + if (!resource?.remoteUrl) { + return throwError(() => new Error("Invalid resource URL")); + } + + this.logService.info(`[PhishingDataService] Starting FULL update using ${resource.remoteUrl}`); + return from(this.apiService.nativeFetch(new Request(resource.remoteUrl))).pipe( + switchMap((response) => { + if (!response.ok || !response.body) { + return throwError( + () => + new Error( + `[PhishingDataService] Full fetch failed: ${response.status}, ${response.statusText}`, + ), + ); + } + + return from(this.indexedDbService.saveUrlsFromStream(response.body)); + }), + catchError((err: unknown) => { + this.logService.error( + `[PhishingDataService] Full dataset update failed using primary source ${err}`, + ); + this.logService.warning( + `[PhishingDataService] Falling back to: ${resource.fallbackUrl} (Note: Fallback data may be less up-to-date)`, + ); + // Try fallback URL + return from(this.apiService.nativeFetch(new Request(resource.fallbackUrl))).pipe( + switchMap((fallbackResponse) => { + if (!fallbackResponse.ok || !fallbackResponse.body) { + return throwError( + () => + new Error( + `[PhishingDataService] Fallback fetch failed: ${fallbackResponse.status}, ${fallbackResponse.statusText}`, + ), + ); + } + + return from(this.indexedDbService.saveUrlsFromStream(fallbackResponse.body)); + }), + catchError((fallbackError: unknown) => { + this.logService.error(`[PhishingDataService] Fallback source failed`); + return throwError(() => fallbackError); + }), + ); + }), + ); + } + + private _updateDailyDataSet() { + this.logService.info("[PhishingDataService] Starting DAILY update..."); + + const todayUrl = getPhishingResources(this.resourceType)?.todayUrl; + if (!todayUrl) { + return throwError(() => new Error("Today URL missing")); + } + + return from(this.fetchToday(todayUrl)).pipe( + switchMap((lines) => from(this.indexedDbService.addUrls(lines))), + ); + } + + private _backgroundUpdate( + previous: PhishingDataMeta | null, + ): Observable { + // Use defer to restart timer if retry is activated + return defer(() => { + const startTime = Date.now(); + this.logService.info(`[PhishingDataService] Update triggered...`); + + // Get updated meta info + return this._getUpdatedMeta().pipe( + // Update full data set if application version or checksum changed + concatMap((newMeta) => + iif( + () => { + const appVersionChanged = newMeta.applicationVersion !== previous?.applicationVersion; + const checksumChanged = newMeta.checksum !== previous?.checksum; + + this.logService.info( + `[PhishingDataService] Checking if full update is needed: appVersionChanged=${appVersionChanged}, checksumChanged=${checksumChanged}`, + ); + return appVersionChanged || checksumChanged; + }, + this._updateFullDataSet().pipe(map(() => ({ meta: newMeta, updated: true }))), + of({ meta: newMeta, updated: false }), + ), + ), + // Update daily data set if last update was more than UPDATE_INTERVAL_DURATION ago + concatMap((result) => + iif( + () => { + const isCacheExpired = + Date.now() - (previous?.timestamp ?? 0) > this.UPDATE_INTERVAL_DURATION; + return isCacheExpired; + }, + this._updateDailyDataSet().pipe(map(() => ({ meta: result.meta, updated: true }))), + of(result), + ), + ), + concatMap((result) => { + if (!result.updated) { + this.logService.debug(`[PhishingDataService] No update needed, metadata unchanged`); + return of(previous); + } + + this.logService.debug(`[PhishingDataService] Updated phishing meta data:`, result.meta); + return from(this._phishingMetaState.update(() => result.meta)).pipe( + tap(() => { + const elapsed = Date.now() - startTime; + this.logService.info(`[PhishingDataService] Updated data set in ${elapsed}ms`); + }), + ); + }), + retry({ + count: 2, // Total 3 attempts (initial + 2 retries) + delay: (error, retryCount) => { + this.logService.error( + `[PhishingDataService] Attempt ${retryCount} failed. Retrying in 5m...`, + error, + ); + return timer(5 * 60 * 1000); // Wait 5 mins before next attempt + }, + }), + catchError((err: unknown) => { + const elapsed = Date.now() - startTime; + this.logService.error( + `[PhishingDataService] Retries unsuccessful after ${elapsed}ms.`, + err, + ); + return of(previous); + }), + ); + }); } } 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 e33b4b1b4f1..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 @@ -1,9 +1,7 @@ import { mock, MockProxy } from "jest-mock-extended"; import { Observable, of } from "rxjs"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { PhishingDetectionSettingsServiceAbstraction } from "@bitwarden/common/dirt/services/abstractions/phishing-detection-settings.service.abstraction"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessageListener } from "@bitwarden/messaging"; @@ -11,17 +9,12 @@ import { PhishingDataService } from "./phishing-data.service"; import { PhishingDetectionService } from "./phishing-detection.service"; describe("PhishingDetectionService", () => { - let accountService: AccountService; - let billingAccountProfileStateService: BillingAccountProfileStateService; - let configService: ConfigService; let logService: LogService; let phishingDataService: MockProxy; let messageListener: MockProxy; + let phishingDetectionSettingsService: MockProxy; beforeEach(() => { - accountService = { getAccount$: jest.fn(() => of(null)) } as any; - billingAccountProfileStateService = {} as any; - configService = { getFeatureFlag$: jest.fn(() => of(false)) } as any; logService = { info: jest.fn(), debug: jest.fn(), warning: jest.fn(), error: jest.fn() } as any; phishingDataService = mock(); messageListener = mock({ @@ -29,16 +22,17 @@ describe("PhishingDetectionService", () => { return new Observable(); }, }); + phishingDetectionSettingsService = mock({ + on$: of(true), + }); }); it("should initialize without errors", () => { expect(() => { PhishingDetectionService.initialize( - accountService, - billingAccountProfileStateService, - configService, logService, phishingDataService, + phishingDetectionSettingsService, messageListener, ); }).not.toThrow(); @@ -61,6 +55,7 @@ describe("PhishingDetectionService", () => { // logService, // phishingDataService, // messageListener, + // phishingDetectionSettingsService, // ); // }); @@ -81,6 +76,12 @@ describe("PhishingDetectionService", () => { // logService, // phishingDataService, // messageListener, + // 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 4917e740be8..815007e1d4c 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 @@ -1,21 +1,16 @@ import { - combineLatest, concatMap, distinctUntilChanged, EMPTY, filter, map, merge, - of, Subject, switchMap, tap, } from "rxjs"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { PhishingDetectionSettingsServiceAbstraction } from "@bitwarden/common/dirt/services/abstractions/phishing-detection-settings.service.abstraction"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { CommandDefinition, MessageListener } from "@bitwarden/messaging"; @@ -50,11 +45,9 @@ export class PhishingDetectionService { private static _didInit = false; static initialize( - accountService: AccountService, - billingAccountProfileStateService: BillingAccountProfileStateService, - configService: ConfigService, logService: LogService, phishingDataService: PhishingDataService, + phishingDetectionSettingsService: PhishingDetectionSettingsServiceAbstraction, messageListener: MessageListener, ) { if (this._didInit) { @@ -101,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; } @@ -118,22 +111,9 @@ export class PhishingDetectionService { .messages$(PHISHING_DETECTION_CANCEL_COMMAND) .pipe(switchMap((message) => BrowserApi.closeTab(message.tabId))); - const activeAccountHasAccess$ = combineLatest([ - accountService.activeAccount$, - configService.getFeatureFlag$(FeatureFlag.PhishingDetection), - ]).pipe( - switchMap(([account, featureEnabled]) => { - if (!account) { - logService.debug("[PhishingDetectionService] No active account."); - return of(false); - } - return billingAccountProfileStateService - .hasPremiumFromAnySource$(account.id) - .pipe(map((hasPremium) => hasPremium && featureEnabled)); - }), - ); + const phishingDetectionActive$ = phishingDetectionSettingsService.on$; - const initSub = activeAccountHasAccess$ + const initSub = phishingDetectionActive$ .pipe( distinctUntilChanged(), switchMap((activeUserHasAccess) => { @@ -157,6 +137,9 @@ export class PhishingDetectionService { this._didInit = true; return () => { + // Dispose phishing data service resources + phishingDataService.dispose(); + initSub.unsubscribe(); this._didInit = false; diff --git a/apps/browser/src/dirt/phishing-detection/services/phishing-indexeddb.service.spec.ts b/apps/browser/src/dirt/phishing-detection/services/phishing-indexeddb.service.spec.ts new file mode 100644 index 00000000000..99e101cc199 --- /dev/null +++ b/apps/browser/src/dirt/phishing-detection/services/phishing-indexeddb.service.spec.ts @@ -0,0 +1,455 @@ +import { ReadableStream as NodeReadableStream } from "stream/web"; + +import { mock, MockProxy } from "jest-mock-extended"; + +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; + +import { PhishingIndexedDbService } from "./phishing-indexeddb.service"; + +describe("PhishingIndexedDbService", () => { + let service: PhishingIndexedDbService; + let logService: MockProxy; + + // Mock IndexedDB storage (keyed by URL for row-per-URL storage) + let mockStore: Map; + let mockObjectStore: any; + let mockTransaction: any; + let mockDb: any; + let mockOpenRequest: any; + + beforeEach(() => { + logService = mock(); + mockStore = new Map(); + + // Mock IDBObjectStore + mockObjectStore = { + put: jest.fn().mockImplementation((record: { url: string }) => { + const request = { + error: null as DOMException | null, + result: undefined as undefined, + onsuccess: null as (() => void) | null, + onerror: null as (() => void) | null, + }; + setTimeout(() => { + mockStore.set(record.url, record); + request.onsuccess?.(); + }, 0); + return request; + }), + get: jest.fn().mockImplementation((key: string) => { + const request = { + error: null as DOMException | null, + result: mockStore.get(key), + onsuccess: null as (() => void) | null, + onerror: null as (() => void) | null, + }; + setTimeout(() => { + request.result = mockStore.get(key); + request.onsuccess?.(); + }, 0); + return request; + }), + clear: jest.fn().mockImplementation(() => { + const request = { + error: null as DOMException | null, + result: undefined as undefined, + onsuccess: null as (() => void) | null, + onerror: null as (() => void) | null, + }; + setTimeout(() => { + mockStore.clear(); + request.onsuccess?.(); + }, 0); + return request; + }), + openCursor: jest.fn().mockImplementation(() => { + const entries = Array.from(mockStore.entries()); + let index = 0; + const request = { + error: null as DOMException | null, + result: null as any, + onsuccess: null as ((e: any) => void) | null, + onerror: null as (() => void) | null, + }; + const advanceCursor = () => { + if (index < entries.length) { + const [, value] = entries[index]; + index++; + request.result = { + value, + continue: () => setTimeout(advanceCursor, 0), + }; + } else { + request.result = null; + } + request.onsuccess?.({ target: request }); + }; + setTimeout(advanceCursor, 0); + return request; + }), + }; + + // Mock IDBTransaction + mockTransaction = { + objectStore: jest.fn().mockReturnValue(mockObjectStore), + oncomplete: null as (() => void) | null, + onerror: null as (() => void) | null, + }; + + // Trigger oncomplete after a tick + const originalObjectStore = mockTransaction.objectStore; + mockTransaction.objectStore = jest.fn().mockImplementation((...args: any[]) => { + setTimeout(() => mockTransaction.oncomplete?.(), 0); + return originalObjectStore(...args); + }); + + // Mock IDBDatabase + mockDb = { + transaction: jest.fn().mockReturnValue(mockTransaction), + close: jest.fn(), + objectStoreNames: { + contains: jest.fn().mockReturnValue(true), + }, + createObjectStore: jest.fn(), + }; + + // Mock IDBOpenDBRequest + mockOpenRequest = { + error: null as DOMException | null, + result: mockDb, + onsuccess: null as (() => void) | null, + onerror: null as (() => void) | null, + onupgradeneeded: null as ((event: any) => void) | null, + }; + + // Mock indexedDB.open + const mockIndexedDB = { + open: jest.fn().mockImplementation(() => { + setTimeout(() => { + mockOpenRequest.onsuccess?.(); + }, 0); + return mockOpenRequest; + }), + }; + + global.indexedDB = mockIndexedDB as any; + + service = new PhishingIndexedDbService(logService); + }); + + afterEach(() => { + jest.clearAllMocks(); + delete (global as any).indexedDB; + }); + + describe("saveUrls", () => { + it("stores URLs in IndexedDB and returns true", async () => { + const urls = ["https://phishing.com", "https://malware.net"]; + + const result = await service.saveUrls(urls); + + expect(result).toBe(true); + expect(mockDb.transaction).toHaveBeenCalledWith("phishing-urls", "readwrite"); + expect(mockObjectStore.clear).toHaveBeenCalled(); + expect(mockObjectStore.put).toHaveBeenCalledTimes(2); + expect(mockDb.close).toHaveBeenCalled(); + }); + + it("handles empty array", async () => { + const result = await service.saveUrls([]); + + expect(result).toBe(true); + expect(mockObjectStore.clear).toHaveBeenCalled(); + }); + + it("trims whitespace from URLs", async () => { + const urls = [" https://example.com ", "\nhttps://test.org\n"]; + + await service.saveUrls(urls); + + expect(mockObjectStore.put).toHaveBeenCalledWith({ url: "https://example.com" }); + expect(mockObjectStore.put).toHaveBeenCalledWith({ url: "https://test.org" }); + }); + + it("skips empty lines", async () => { + const urls = ["https://example.com", "", " ", "https://test.org"]; + + await service.saveUrls(urls); + + expect(mockObjectStore.put).toHaveBeenCalledTimes(2); + }); + + it("handles duplicate URLs via upsert (keyPath deduplication)", async () => { + const urls = [ + "https://example.com", + "https://example.com", // duplicate + "https://test.org", + ]; + + const result = await service.saveUrls(urls); + + expect(result).toBe(true); + // put() is called 3 times, but mockStore (using Map with URL as key) + // only stores 2 unique entries - demonstrating upsert behavior + expect(mockObjectStore.put).toHaveBeenCalledTimes(3); + expect(mockStore.size).toBe(2); + }); + + it("logs error and returns false on failure", async () => { + const error = new Error("IndexedDB error"); + mockOpenRequest.error = error; + (global.indexedDB.open as jest.Mock).mockImplementation(() => { + setTimeout(() => { + mockOpenRequest.onerror?.(); + }, 0); + return mockOpenRequest; + }); + + const result = await service.saveUrls(["https://test.com"]); + + expect(result).toBe(false); + expect(logService.error).toHaveBeenCalledWith( + "[PhishingIndexedDbService] Save failed", + expect.any(Error), + ); + }); + }); + + describe("addUrls", () => { + it("appends URLs to IndexedDB without clearing", async () => { + // Pre-populate store with existing data + mockStore.set("https://existing.com", { url: "https://existing.com" }); + + const urls = ["https://phishing.com", "https://malware.net"]; + const result = await service.addUrls(urls); + + expect(result).toBe(true); + expect(mockDb.transaction).toHaveBeenCalledWith("phishing-urls", "readwrite"); + expect(mockObjectStore.clear).not.toHaveBeenCalled(); + expect(mockObjectStore.put).toHaveBeenCalledTimes(2); + // Existing data should still be present + expect(mockStore.has("https://existing.com")).toBe(true); + expect(mockStore.size).toBe(3); + expect(mockDb.close).toHaveBeenCalled(); + }); + + it("handles empty array without clearing", async () => { + mockStore.set("https://existing.com", { url: "https://existing.com" }); + + const result = await service.addUrls([]); + + expect(result).toBe(true); + expect(mockObjectStore.clear).not.toHaveBeenCalled(); + expect(mockStore.has("https://existing.com")).toBe(true); + }); + + it("trims whitespace from URLs", async () => { + const urls = [" https://example.com ", "\nhttps://test.org\n"]; + + await service.addUrls(urls); + + expect(mockObjectStore.put).toHaveBeenCalledWith({ url: "https://example.com" }); + expect(mockObjectStore.put).toHaveBeenCalledWith({ url: "https://test.org" }); + }); + + it("skips empty lines", async () => { + const urls = ["https://example.com", "", " ", "https://test.org"]; + + await service.addUrls(urls); + + expect(mockObjectStore.put).toHaveBeenCalledTimes(2); + }); + + it("handles duplicate URLs via upsert", async () => { + mockStore.set("https://example.com", { url: "https://example.com" }); + + const urls = [ + "https://example.com", // Already exists + "https://test.org", + ]; + + const result = await service.addUrls(urls); + + expect(result).toBe(true); + expect(mockObjectStore.put).toHaveBeenCalledTimes(2); + expect(mockStore.size).toBe(2); + }); + + it("logs error and returns false on failure", async () => { + const error = new Error("IndexedDB error"); + mockOpenRequest.error = error; + (global.indexedDB.open as jest.Mock).mockImplementation(() => { + setTimeout(() => { + mockOpenRequest.onerror?.(); + }, 0); + return mockOpenRequest; + }); + + const result = await service.addUrls(["https://test.com"]); + + expect(result).toBe(false); + expect(logService.error).toHaveBeenCalledWith( + "[PhishingIndexedDbService] Add failed", + expect.any(Error), + ); + }); + }); + + describe("hasUrl", () => { + it("returns true for existing URL", async () => { + mockStore.set("https://example.com", { url: "https://example.com" }); + + const result = await service.hasUrl("https://example.com"); + + expect(result).toBe(true); + expect(mockDb.transaction).toHaveBeenCalledWith("phishing-urls", "readonly"); + expect(mockObjectStore.get).toHaveBeenCalledWith("https://example.com"); + }); + + it("returns false for non-existing URL", async () => { + const result = await service.hasUrl("https://notfound.com"); + + expect(result).toBe(false); + }); + + it("returns false on error", async () => { + const error = new Error("IndexedDB error"); + mockOpenRequest.error = error; + (global.indexedDB.open as jest.Mock).mockImplementation(() => { + setTimeout(() => { + mockOpenRequest.onerror?.(); + }, 0); + return mockOpenRequest; + }); + + const result = await service.hasUrl("https://example.com"); + + expect(result).toBe(false); + expect(logService.error).toHaveBeenCalledWith( + "[PhishingIndexedDbService] Check failed", + expect.any(Error), + ); + }); + }); + + describe("loadAllUrls", () => { + it("loads all URLs using cursor", async () => { + mockStore.set("https://example.com", { url: "https://example.com" }); + mockStore.set("https://test.org", { url: "https://test.org" }); + + const result = await service.loadAllUrls(); + + expect(result).toContain("https://example.com"); + expect(result).toContain("https://test.org"); + expect(result.length).toBe(2); + }); + + it("returns empty array when no data exists", async () => { + const result = await service.loadAllUrls(); + + expect(result).toEqual([]); + }); + + it("returns empty array on error", async () => { + const error = new Error("IndexedDB error"); + mockOpenRequest.error = error; + (global.indexedDB.open as jest.Mock).mockImplementation(() => { + setTimeout(() => { + mockOpenRequest.onerror?.(); + }, 0); + return mockOpenRequest; + }); + + const result = await service.loadAllUrls(); + + expect(result).toEqual([]); + expect(logService.error).toHaveBeenCalledWith( + "[PhishingIndexedDbService] Load failed", + expect.any(Error), + ); + }); + }); + + describe("saveUrlsFromStream", () => { + it("saves URLs from stream", async () => { + const content = "https://example.com\nhttps://test.org\nhttps://phishing.net"; + const stream = new NodeReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode(content)); + controller.close(); + }, + }) as unknown as ReadableStream; + + const result = await service.saveUrlsFromStream(stream); + + expect(result).toBe(true); + expect(mockObjectStore.clear).toHaveBeenCalled(); + expect(mockObjectStore.put).toHaveBeenCalledTimes(3); + }); + + it("handles chunked stream data", async () => { + const content = "https://url1.com\nhttps://url2.com"; + const encoder = new TextEncoder(); + const encoded = encoder.encode(content); + + // Split into multiple small chunks + const stream = new NodeReadableStream({ + start(controller) { + controller.enqueue(encoded.slice(0, 5)); + controller.enqueue(encoded.slice(5, 10)); + controller.enqueue(encoded.slice(10)); + controller.close(); + }, + }) as unknown as ReadableStream; + + const result = await service.saveUrlsFromStream(stream); + + expect(result).toBe(true); + expect(mockObjectStore.put).toHaveBeenCalledTimes(2); + }); + + it("returns false on error", async () => { + const error = new Error("IndexedDB error"); + mockOpenRequest.error = error; + (global.indexedDB.open as jest.Mock).mockImplementation(() => { + setTimeout(() => { + mockOpenRequest.onerror?.(); + }, 0); + return mockOpenRequest; + }); + + const stream = new NodeReadableStream({ + start(controller) { + controller.enqueue(new TextEncoder().encode("https://test.com")); + controller.close(); + }, + }) as unknown as ReadableStream; + + const result = await service.saveUrlsFromStream(stream); + + expect(result).toBe(false); + expect(logService.error).toHaveBeenCalledWith( + "[PhishingIndexedDbService] Stream save failed", + expect.any(Error), + ); + }); + }); + + describe("database initialization", () => { + it("creates object store with keyPath on upgrade", async () => { + mockDb.objectStoreNames.contains.mockReturnValue(false); + + (global.indexedDB.open as jest.Mock).mockImplementation(() => { + setTimeout(() => { + mockOpenRequest.onupgradeneeded?.({ target: mockOpenRequest }); + mockOpenRequest.onsuccess?.(); + }, 0); + return mockOpenRequest; + }); + + await service.hasUrl("https://test.com"); + + expect(mockDb.createObjectStore).toHaveBeenCalledWith("phishing-urls", { keyPath: "url" }); + }); + }); +}); diff --git a/apps/browser/src/dirt/phishing-detection/services/phishing-indexeddb.service.ts b/apps/browser/src/dirt/phishing-detection/services/phishing-indexeddb.service.ts new file mode 100644 index 00000000000..fe0f10da221 --- /dev/null +++ b/apps/browser/src/dirt/phishing-detection/services/phishing-indexeddb.service.ts @@ -0,0 +1,276 @@ +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; + +/** + * Record type for phishing URL storage in IndexedDB. + */ +type PhishingUrlRecord = { url: string }; + +/** + * IndexedDB storage service for phishing URLs. + * Stores URLs as individual rows. + */ +export class PhishingIndexedDbService { + private readonly DB_NAME = "bitwarden-phishing"; + private readonly STORE_NAME = "phishing-urls"; + private readonly DB_VERSION = 1; + private readonly CHUNK_SIZE = 50000; + + constructor(private logService: LogService) {} + + /** + * Opens the IndexedDB database, creating the object store if needed. + */ + private openDatabase(): Promise { + return new Promise((resolve, reject) => { + const req = indexedDB.open(this.DB_NAME, this.DB_VERSION); + req.onerror = () => reject(req.error); + req.onsuccess = () => resolve(req.result); + req.onupgradeneeded = (e) => { + const db = (e.target as IDBOpenDBRequest).result; + if (!db.objectStoreNames.contains(this.STORE_NAME)) { + db.createObjectStore(this.STORE_NAME, { keyPath: "url" }); + } + }; + }); + } + + /** + * Clears all records from the phishing URLs store. + */ + private clearStore(db: IDBDatabase): Promise { + return new Promise((resolve, reject) => { + const req = db.transaction(this.STORE_NAME, "readwrite").objectStore(this.STORE_NAME).clear(); + req.onerror = () => reject(req.error); + req.onsuccess = () => resolve(); + }); + } + + /** + * Saves an array of phishing URLs to IndexedDB. + * Atomically replaces all existing data. + * + * @param urls - Array of phishing URLs to save + * @returns `true` if save succeeded, `false` on error + */ + async saveUrls(urls: string[]): Promise { + this.logService.debug( + `[PhishingIndexedDbService] Clearing and saving ${urls.length} to the store...`, + ); + let db: IDBDatabase | null = null; + try { + db = await this.openDatabase(); + await this.clearStore(db); + await this.saveChunked(db, urls); + return true; + } catch (error) { + this.logService.error("[PhishingIndexedDbService] Save failed", error); + return false; + } finally { + db?.close(); + } + } + + /** + * Adds an array of phishing URLs to IndexedDB. + * Appends to existing data without clearing. + * + * @param urls - Array of phishing URLs to add + * @returns `true` if add succeeded, `false` on error + */ + async addUrls(urls: string[]): Promise { + this.logService.debug(`[PhishingIndexedDbService] Adding ${urls.length} to the store...`); + + let db: IDBDatabase | null = null; + try { + db = await this.openDatabase(); + await this.saveChunked(db, urls); + return true; + } catch (error) { + this.logService.error("[PhishingIndexedDbService] Add failed", error); + return false; + } finally { + db?.close(); + } + } + + /** + * Saves URLs in chunks to prevent transaction timeouts and UI freezes. + */ + private async saveChunked(db: IDBDatabase, urls: string[]): Promise { + const cleaned = urls.map((u) => u.trim()).filter(Boolean); + for (let i = 0; i < cleaned.length; i += this.CHUNK_SIZE) { + await this.saveChunk(db, cleaned.slice(i, i + this.CHUNK_SIZE)); + await new Promise((r) => setTimeout(r, 0)); // Yield to event loop + } + } + + /** + * Saves a single chunk of URLs in one transaction. + */ + private saveChunk(db: IDBDatabase, urls: string[]): Promise { + return new Promise((resolve, reject) => { + const tx = db.transaction(this.STORE_NAME, "readwrite"); + const store = tx.objectStore(this.STORE_NAME); + for (const url of urls) { + store.put({ url } as PhishingUrlRecord); + } + tx.oncomplete = () => resolve(); + tx.onerror = () => reject(tx.error); + }); + } + + /** + * Checks if a URL exists in the phishing database. + * + * @param url - The URL to check + * @returns `true` if URL exists, `false` if not found or on error + */ + async hasUrl(url: string): Promise { + this.logService.debug(`[PhishingIndexedDbService] Checking if store contains ${url}...`); + + let db: IDBDatabase | null = null; + try { + db = await this.openDatabase(); + return await this.checkUrlExists(db, url); + } catch (error) { + this.logService.error("[PhishingIndexedDbService] Check failed", error); + return false; + } finally { + db?.close(); + } + } + + /** + * Performs the actual URL existence check using index lookup. + */ + private checkUrlExists(db: IDBDatabase, url: string): Promise { + return new Promise((resolve, reject) => { + const tx = db.transaction(this.STORE_NAME, "readonly"); + const req = tx.objectStore(this.STORE_NAME).get(url); + req.onerror = () => reject(req.error); + req.onsuccess = () => resolve(req.result !== undefined); + }); + } + + /** + * Loads all phishing URLs from IndexedDB. + * + * @returns Array of all stored URLs, or empty array on error + */ + async loadAllUrls(): Promise { + this.logService.debug("[PhishingIndexedDbService] Loading all urls from store..."); + + let db: IDBDatabase | null = null; + try { + db = await this.openDatabase(); + return await this.getAllUrls(db); + } catch (error) { + this.logService.error("[PhishingIndexedDbService] Load failed", error); + return []; + } finally { + db?.close(); + } + } + + /** + * Iterates all records using a cursor. + */ + private getAllUrls(db: IDBDatabase): Promise { + return new Promise((resolve, reject) => { + const urls: string[] = []; + const req = db + .transaction(this.STORE_NAME, "readonly") + .objectStore(this.STORE_NAME) + .openCursor(); + req.onerror = () => reject(req.error); + req.onsuccess = (e) => { + const cursor = (e.target as IDBRequest).result; + if (cursor) { + urls.push((cursor.value as PhishingUrlRecord).url); + cursor.continue(); + } else { + resolve(urls); + } + }; + }); + } + + /** + * Saves phishing URLs directly from a stream. + * Processes data incrementally to minimize memory usage. + * + * @param stream - ReadableStream of newline-delimited URLs + * @returns `true` if save succeeded, `false` on error + */ + async saveUrlsFromStream(stream: ReadableStream): Promise { + this.logService.debug("[PhishingIndexedDbService] Saving urls to the store from stream..."); + + let db: IDBDatabase | null = null; + try { + db = await this.openDatabase(); + await this.clearStore(db); + await this.processStream(db, stream); + this.logService.info( + "[PhishingIndexedDbService] Finished saving urls to the store from stream.", + ); + return true; + } catch (error) { + this.logService.error("[PhishingIndexedDbService] Stream save failed", error); + return false; + } finally { + db?.close(); + } + } + + /** + * Processes a stream of URL data, parsing lines and saving in chunks. + */ + private async processStream(db: IDBDatabase, stream: ReadableStream): Promise { + const reader = stream.getReader(); + const decoder = new TextDecoder(); + let buffer = ""; + let urls: string[] = []; + + try { + while (true) { + const { done, value } = await reader.read(); + + // Decode BEFORE done check; stream: !done flushes on final call + buffer += decoder.decode(value, { stream: !done }); + + if (done) { + // Split remaining buffer by newlines in case it contains multiple URLs + const lines = buffer.split("\n"); + for (const line of lines) { + const trimmed = line.trim(); + if (trimmed) { + urls.push(trimmed); + } + } + if (urls.length > 0) { + await this.saveChunk(db, urls); + } + break; + } + + const lines = buffer.split("\n"); + buffer = lines.pop() ?? ""; + + for (const line of lines) { + const trimmed = line.trim(); + if (trimmed) { + urls.push(trimmed); + } + + if (urls.length >= this.CHUNK_SIZE) { + await this.saveChunk(db, urls); + urls = []; + await new Promise((r) => setTimeout(r, 0)); + } + } + } + } finally { + reader.releaseLock(); + } + } +} diff --git a/apps/browser/src/key-management/biometrics/foreground-browser-biometrics.ts b/apps/browser/src/key-management/biometrics/foreground-browser-biometrics.ts index b6e84fee31a..d803a457a81 100644 --- a/apps/browser/src/key-management/biometrics/foreground-browser-biometrics.ts +++ b/apps/browser/src/key-management/biometrics/foreground-browser-biometrics.ts @@ -48,7 +48,11 @@ export class ForegroundBrowserBiometricsService extends BiometricsService { result: BiometricsStatus; error: string; }>(BiometricsCommands.GetBiometricsStatusForUser, { userId: id }); - return response.result; + if (response != null) { + return response.result; + } else { + return BiometricsStatus.DesktopDisconnected; + } } async getShouldAutopromptNow(): Promise { diff --git a/apps/browser/src/key-management/key-connector/remove-password.component.html b/apps/browser/src/key-management/key-connector/remove-password.component.html deleted file mode 100644 index 427065e83f3..00000000000 --- a/apps/browser/src/key-management/key-connector/remove-password.component.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - @if (loading) { -
    - - {{ "loading" | i18n }} -
    - } @else { -

    {{ "removeMasterPasswordForOrganizationUserKeyConnector" | i18n }}

    -

    {{ "organizationName" | i18n }}:

    -

    {{ organization.name }}

    -

    {{ "keyConnectorDomain" | i18n }}:

    -

    {{ organization.keyConnectorUrl }}

    - - - - } -
    diff --git a/apps/browser/src/key-management/key-connector/remove-password.component.ts b/apps/browser/src/key-management/key-connector/remove-password.component.ts deleted file mode 100644 index c4077a1eca9..00000000000 --- a/apps/browser/src/key-management/key-connector/remove-password.component.ts +++ /dev/null @@ -1,14 +0,0 @@ -// FIXME (PM-22628): angular imports are forbidden in background -// eslint-disable-next-line no-restricted-imports -import { Component } from "@angular/core"; - -import { RemovePasswordComponent as BaseRemovePasswordComponent } from "@bitwarden/key-management-ui"; - -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -@Component({ - selector: "app-remove-password", - templateUrl: "remove-password.component.html", - standalone: false, -}) -export class RemovePasswordComponent extends BaseRemovePasswordComponent {} diff --git a/apps/browser/src/key-management/lock/services/extension-lock-component.service.spec.ts b/apps/browser/src/key-management/lock/services/extension-lock-component.service.spec.ts index 7678b65d29e..ecdb899b9a7 100644 --- a/apps/browser/src/key-management/lock/services/extension-lock-component.service.spec.ts +++ b/apps/browser/src/key-management/lock/services/extension-lock-component.service.spec.ts @@ -14,7 +14,7 @@ import { BiometricsStatus, BiometricStateService, } from "@bitwarden/key-management"; -import { UnlockOptions } from "@bitwarden/key-management-ui"; +import { UnlockOptions, WebAuthnPrfUnlockService } from "@bitwarden/key-management-ui"; import { BrowserApi } from "../../../platform/browser/browser-api"; import BrowserPopupUtils from "../../../platform/browser/browser-popup-utils"; @@ -34,6 +34,7 @@ describe("ExtensionLockComponentService", () => { let vaultTimeoutSettingsService: MockProxy; let routerService: MockProxy; let biometricStateService: MockProxy; + let webAuthnPrfUnlockService: MockProxy; beforeEach(() => { userDecryptionOptionsService = mock(); @@ -43,37 +44,21 @@ describe("ExtensionLockComponentService", () => { vaultTimeoutSettingsService = mock(); routerService = mock(); biometricStateService = mock(); + webAuthnPrfUnlockService = mock(); TestBed.configureTestingModule({ providers: [ - ExtensionLockComponentService, { - provide: UserDecryptionOptionsServiceAbstraction, - useValue: userDecryptionOptionsService, - }, - { - provide: PlatformUtilsService, - useValue: platformUtilsService, - }, - { - provide: BiometricsService, - useValue: biometricsService, - }, - { - provide: PinServiceAbstraction, - useValue: pinService, - }, - { - provide: VaultTimeoutSettingsService, - useValue: vaultTimeoutSettingsService, - }, - { - provide: BrowserRouterService, - useValue: routerService, - }, - { - provide: BiometricStateService, - useValue: biometricStateService, + provide: ExtensionLockComponentService, + useFactory: () => + new ExtensionLockComponentService( + userDecryptionOptionsService, + biometricsService, + pinService, + biometricStateService, + routerService, + webAuthnPrfUnlockService, + ), }, ], }); @@ -212,6 +197,9 @@ describe("ExtensionLockComponentService", () => { enabled: true, biometricsStatus: BiometricsStatus.Available, }, + prf: { + enabled: false, + }, }, ], [ @@ -234,6 +222,9 @@ describe("ExtensionLockComponentService", () => { enabled: true, biometricsStatus: BiometricsStatus.Available, }, + prf: { + enabled: false, + }, }, ], [ @@ -256,6 +247,9 @@ describe("ExtensionLockComponentService", () => { enabled: true, biometricsStatus: BiometricsStatus.Available, }, + prf: { + enabled: false, + }, }, ], [ @@ -278,6 +272,9 @@ describe("ExtensionLockComponentService", () => { enabled: true, biometricsStatus: BiometricsStatus.Available, }, + prf: { + enabled: false, + }, }, ], [ @@ -300,6 +297,9 @@ describe("ExtensionLockComponentService", () => { enabled: false, biometricsStatus: BiometricsStatus.UnlockNeeded, }, + prf: { + enabled: false, + }, }, ], [ @@ -322,6 +322,9 @@ describe("ExtensionLockComponentService", () => { enabled: false, biometricsStatus: BiometricsStatus.NotEnabledInConnectedDesktopApp, }, + prf: { + enabled: false, + }, }, ], [ @@ -344,6 +347,9 @@ describe("ExtensionLockComponentService", () => { enabled: false, biometricsStatus: BiometricsStatus.HardwareUnavailable, }, + prf: { + enabled: false, + }, }, ], ]; @@ -374,6 +380,9 @@ describe("ExtensionLockComponentService", () => { // PIN pinService.isPinDecryptionAvailable.mockResolvedValue(mockInputs.pinDecryptionAvailable); + // PRF + webAuthnPrfUnlockService.isPrfUnlockAvailable.mockResolvedValue(false); + const unlockOptions = await firstValueFrom(service.getAvailableUnlockOptions$(userId)); expect(unlockOptions).toEqual(expectedOutput); diff --git a/apps/browser/src/key-management/lock/services/extension-lock-component.service.ts b/apps/browser/src/key-management/lock/services/extension-lock-component.service.ts index 9f137d694a9..5e6e564bbc2 100644 --- a/apps/browser/src/key-management/lock/services/extension-lock-component.service.ts +++ b/apps/browser/src/key-management/lock/services/extension-lock-component.service.ts @@ -1,6 +1,3 @@ -// FIXME (PM-22628): angular imports are forbidden in background -// eslint-disable-next-line no-restricted-imports -import { inject } from "@angular/core"; import { combineLatest, defer, firstValueFrom, map, Observable } from "rxjs"; import { UserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth/common"; @@ -11,7 +8,11 @@ import { BiometricsStatus, BiometricStateService, } from "@bitwarden/key-management"; -import { LockComponentService, UnlockOptions } from "@bitwarden/key-management-ui"; +import { + LockComponentService, + UnlockOptions, + WebAuthnPrfUnlockService, +} from "@bitwarden/key-management-ui"; import { BiometricErrors, BiometricErrorTypes } from "../../../models/biometricErrors"; import { BrowserApi } from "../../../platform/browser/browser-api"; @@ -21,11 +22,14 @@ import BrowserPopupUtils from "../../../platform/browser/browser-popup-utils"; import { BrowserRouterService } from "../../../platform/popup/services/browser-router.service"; export class ExtensionLockComponentService implements LockComponentService { - private readonly userDecryptionOptionsService = inject(UserDecryptionOptionsServiceAbstraction); - private readonly biometricsService = inject(BiometricsService); - private readonly pinService = inject(PinServiceAbstraction); - private readonly routerService = inject(BrowserRouterService); - private readonly biometricStateService = inject(BiometricStateService); + constructor( + private readonly userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction, + private readonly biometricsService: BiometricsService, + private readonly pinService: PinServiceAbstraction, + private readonly biometricStateService: BiometricStateService, + private readonly routerService: BrowserRouterService, + private readonly webAuthnPrfUnlockService: WebAuthnPrfUnlockService, + ) {} getPreviousUrl(): string | null { return this.routerService.getPreviousUrl() ?? null; @@ -81,8 +85,12 @@ export class ExtensionLockComponentService implements LockComponentService { }), this.userDecryptionOptionsService.userDecryptionOptionsById$(userId), defer(() => this.pinService.isPinDecryptionAvailable(userId)), + defer(async () => { + const available = await this.webAuthnPrfUnlockService.isPrfUnlockAvailable(userId); + return { available }; + }), ]).pipe( - map(([biometricsStatus, userDecryptionOptions, pinDecryptionAvailable]) => { + map(([biometricsStatus, userDecryptionOptions, pinDecryptionAvailable, prfUnlockInfo]) => { const unlockOpts: UnlockOptions = { masterPassword: { enabled: userDecryptionOptions?.hasMasterPassword, @@ -94,6 +102,9 @@ export class ExtensionLockComponentService implements LockComponentService { enabled: biometricsStatus === BiometricsStatus.Available, biometricsStatus: biometricsStatus, }, + prf: { + enabled: prfUnlockInfo.available, + }, }; return unlockOpts; }), diff --git a/apps/browser/src/key-management/session-timeout/services/browser-session-timeout-settings-component.service.spec.ts b/apps/browser/src/key-management/session-timeout/services/browser-session-timeout-settings-component.service.spec.ts new file mode 100644 index 00000000000..cf5d556a553 --- /dev/null +++ b/apps/browser/src/key-management/session-timeout/services/browser-session-timeout-settings-component.service.spec.ts @@ -0,0 +1,57 @@ +import { mock } from "jest-mock-extended"; + +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { SessionTimeoutTypeService } from "@bitwarden/common/key-management/session-timeout"; +import { + VaultTimeoutNumberType, + VaultTimeoutStringType, +} from "@bitwarden/common/key-management/vault-timeout"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; + +import { BrowserSessionTimeoutSettingsComponentService } from "./browser-session-timeout-settings-component.service"; + +describe("BrowserSessionTimeoutSettingsComponentService", () => { + let service: BrowserSessionTimeoutSettingsComponentService; + let mockI18nService: jest.Mocked; + let mockSessionTimeoutTypeService: jest.Mocked; + let mockPolicyService: jest.Mocked; + let mockMessagingService: jest.Mocked; + + beforeEach(() => { + mockI18nService = mock(); + mockSessionTimeoutTypeService = mock(); + mockPolicyService = mock(); + mockMessagingService = mock(); + + service = new BrowserSessionTimeoutSettingsComponentService( + mockI18nService, + mockSessionTimeoutTypeService, + mockPolicyService, + mockMessagingService, + ); + }); + + describe("onTimeoutSave", () => { + it("should call messagingService.send with 'bgReseedStorage' when timeout is Never", () => { + service.onTimeoutSave(VaultTimeoutStringType.Never); + + expect(mockMessagingService.send).toHaveBeenCalledWith("bgReseedStorage"); + }); + + it.each([ + VaultTimeoutNumberType.Immediately, + VaultTimeoutNumberType.OnMinute, + VaultTimeoutNumberType.EightHours, + VaultTimeoutStringType.OnIdle, + VaultTimeoutStringType.OnSleep, + VaultTimeoutStringType.OnLocked, + VaultTimeoutStringType.OnRestart, + VaultTimeoutStringType.Custom, + ])("should not call messagingService.send when timeout is %s", (timeoutValue) => { + service.onTimeoutSave(timeoutValue); + + expect(mockMessagingService.send).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/browser/src/key-management/session-timeout/services/browser-session-timeout-settings-component.service.ts b/apps/browser/src/key-management/session-timeout/services/browser-session-timeout-settings-component.service.ts index 297718687eb..24925e25e24 100644 --- a/apps/browser/src/key-management/session-timeout/services/browser-session-timeout-settings-component.service.ts +++ b/apps/browser/src/key-management/session-timeout/services/browser-session-timeout-settings-component.service.ts @@ -1,56 +1,24 @@ -import { defer, Observable, of } from "rxjs"; - +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { SessionTimeoutTypeService } from "@bitwarden/common/key-management/session-timeout"; import { VaultTimeout, - VaultTimeoutOption, VaultTimeoutStringType, } from "@bitwarden/common/key-management/vault-timeout"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SessionTimeoutSettingsComponentService } from "@bitwarden/key-management-ui"; -export class BrowserSessionTimeoutSettingsComponentService - implements SessionTimeoutSettingsComponentService -{ - availableTimeoutOptions$: Observable = defer(() => { - const options: VaultTimeoutOption[] = [ - { name: this.i18nService.t("immediately"), value: 0 }, - { name: this.i18nService.t("oneMinute"), value: 1 }, - { name: this.i18nService.t("fiveMinutes"), value: 5 }, - { name: this.i18nService.t("fifteenMinutes"), value: 15 }, - { name: this.i18nService.t("thirtyMinutes"), value: 30 }, - { name: this.i18nService.t("oneHour"), value: 60 }, - { name: this.i18nService.t("fourHours"), value: 240 }, - ]; - - const showOnLocked = - !this.platformUtilsService.isFirefox() && - !this.platformUtilsService.isSafari() && - !(this.platformUtilsService.isOpera() && navigator.platform === "MacIntel"); - - if (showOnLocked) { - options.push({ - name: this.i18nService.t("onLocked"), - value: VaultTimeoutStringType.OnLocked, - }); - } - - options.push( - { name: this.i18nService.t("onRestart"), value: VaultTimeoutStringType.OnRestart }, - { name: this.i18nService.t("never"), value: VaultTimeoutStringType.Never }, - ); - - return of(options); - }); - +export class BrowserSessionTimeoutSettingsComponentService extends SessionTimeoutSettingsComponentService { constructor( - private readonly i18nService: I18nService, - private readonly platformUtilsService: PlatformUtilsService, + i18nService: I18nService, + sessionTimeoutTypeService: SessionTimeoutTypeService, + policyService: PolicyService, private readonly messagingService: MessagingService, - ) {} + ) { + super(i18nService, sessionTimeoutTypeService, policyService); + } - onTimeoutSave(timeout: VaultTimeout): void { + override onTimeoutSave(timeout: VaultTimeout): void { if (timeout === VaultTimeoutStringType.Never) { this.messagingService.send("bgReseedStorage"); } diff --git a/apps/browser/src/key-management/session-timeout/services/browser-session-timeout-type.service.spec.ts b/apps/browser/src/key-management/session-timeout/services/browser-session-timeout-type.service.spec.ts new file mode 100644 index 00000000000..83de5c51a4a --- /dev/null +++ b/apps/browser/src/key-management/session-timeout/services/browser-session-timeout-type.service.spec.ts @@ -0,0 +1,139 @@ +import { mock } from "jest-mock-extended"; + +import { + VaultTimeoutNumberType, + VaultTimeoutStringType, +} from "@bitwarden/common/key-management/vault-timeout"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; + +import { BrowserSessionTimeoutTypeService } from "./browser-session-timeout-type.service"; + +describe("BrowserSessionTimeoutTypeService", () => { + let service: BrowserSessionTimeoutTypeService; + let mockPlatformUtilsService: jest.Mocked; + + beforeEach(() => { + mockPlatformUtilsService = mock(); + service = new BrowserSessionTimeoutTypeService(mockPlatformUtilsService); + }); + + describe("isAvailable", () => { + it.each([ + VaultTimeoutNumberType.Immediately, + VaultTimeoutStringType.OnRestart, + VaultTimeoutStringType.Never, + VaultTimeoutStringType.Custom, + ])("should return true for always available type: %s", async (timeoutType) => { + const result = await service.isAvailable(timeoutType); + + expect(result).toBe(true); + }); + + it.each([VaultTimeoutNumberType.OnMinute, VaultTimeoutNumberType.EightHours])( + "should return true for numeric timeout type: %s", + async (timeoutType) => { + const result = await service.isAvailable(timeoutType); + + expect(result).toBe(true); + }, + ); + + describe("OnLocked availability", () => { + const mockNavigatorPlatform = (platform: string) => { + Object.defineProperty(navigator, "platform", { + value: platform, + writable: true, + configurable: true, + }); + }; + + beforeEach(() => { + mockNavigatorPlatform("Linux x86_64"); + mockPlatformUtilsService.isFirefox.mockReturnValue(false); + mockPlatformUtilsService.isSafari.mockReturnValue(false); + mockPlatformUtilsService.isOpera.mockReturnValue(false); + }); + + it("should return true when not Firefox, Safari, or Opera on Mac", async () => { + const result = await service.isAvailable(VaultTimeoutStringType.OnLocked); + + expect(result).toBe(true); + }); + + it("should return true when Opera on non-Mac platform", async () => { + mockNavigatorPlatform("Win32"); + mockPlatformUtilsService.isOpera.mockReturnValue(true); + + const result = await service.isAvailable(VaultTimeoutStringType.OnLocked); + + expect(result).toBe(true); + }); + + it("should return false when Opera on Mac", async () => { + mockNavigatorPlatform("MacIntel"); + mockPlatformUtilsService.isOpera.mockReturnValue(true); + + const result = await service.isAvailable(VaultTimeoutStringType.OnLocked); + + expect(result).toBe(false); + }); + + it("should return false when Firefox", async () => { + mockPlatformUtilsService.isFirefox.mockReturnValue(true); + + const result = await service.isAvailable(VaultTimeoutStringType.OnLocked); + + expect(result).toBe(false); + }); + + it("should return false when Safari", async () => { + mockPlatformUtilsService.isSafari.mockReturnValue(true); + + const result = await service.isAvailable(VaultTimeoutStringType.OnLocked); + + expect(result).toBe(false); + }); + }); + + it.each([VaultTimeoutStringType.OnIdle, VaultTimeoutStringType.OnSleep])( + "should return false for unavailable timeout type: %s", + async (timeoutType) => { + const result = await service.isAvailable(timeoutType); + + expect(result).toBe(false); + }, + ); + }); + + describe("getOrPromoteToAvailable", () => { + it.each([ + VaultTimeoutNumberType.Immediately, + VaultTimeoutNumberType.OnMinute, + VaultTimeoutStringType.Never, + VaultTimeoutStringType.OnRestart, + VaultTimeoutStringType.OnLocked, + VaultTimeoutStringType.Custom, + ])("should return the original type when it is available: %s", async (timeoutType) => { + jest.spyOn(service, "isAvailable").mockResolvedValue(true); + + const result = await service.getOrPromoteToAvailable(timeoutType); + + expect(result).toBe(timeoutType); + expect(service.isAvailable).toHaveBeenCalledWith(timeoutType); + }); + + it.each([ + VaultTimeoutStringType.OnIdle, + VaultTimeoutStringType.OnSleep, + VaultTimeoutStringType.OnLocked, + 5, + ])("should return OnRestart when type is not available: %s", async (timeoutType) => { + jest.spyOn(service, "isAvailable").mockResolvedValue(false); + + const result = await service.getOrPromoteToAvailable(timeoutType); + + expect(result).toBe(VaultTimeoutStringType.OnRestart); + expect(service.isAvailable).toHaveBeenCalledWith(timeoutType); + }); + }); +}); diff --git a/apps/browser/src/key-management/session-timeout/services/browser-session-timeout-type.service.ts b/apps/browser/src/key-management/session-timeout/services/browser-session-timeout-type.service.ts new file mode 100644 index 00000000000..33ac3e356d4 --- /dev/null +++ b/apps/browser/src/key-management/session-timeout/services/browser-session-timeout-type.service.ts @@ -0,0 +1,43 @@ +import { SessionTimeoutTypeService } from "@bitwarden/common/key-management/session-timeout"; +import { + isVaultTimeoutTypeNumeric, + VaultTimeout, + VaultTimeoutNumberType, + VaultTimeoutStringType, +} from "@bitwarden/common/key-management/vault-timeout"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; + +export class BrowserSessionTimeoutTypeService implements SessionTimeoutTypeService { + constructor(private readonly platformUtilsService: PlatformUtilsService) {} + + async isAvailable(type: VaultTimeout): Promise { + switch (type) { + case VaultTimeoutNumberType.Immediately: + case VaultTimeoutStringType.OnRestart: + case VaultTimeoutStringType.Never: + case VaultTimeoutStringType.Custom: + return true; + case VaultTimeoutStringType.OnLocked: + return ( + !this.platformUtilsService.isFirefox() && + !this.platformUtilsService.isSafari() && + !(this.platformUtilsService.isOpera() && navigator.platform === "MacIntel") + ); + default: + if (isVaultTimeoutTypeNumeric(type)) { + return true; + } + break; + } + + return false; + } + + async getOrPromoteToAvailable(type: VaultTimeout): Promise { + const available = await this.isAvailable(type); + if (!available) { + return VaultTimeoutStringType.OnRestart; + } + return type; + } +} diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index 1651f616e03..ce5311f848a 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": "2026.1.0", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index 67399192b64..9cb77aa3040 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": "2026.1.0", "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/platform/browser/browser-popup-utils.spec.ts b/apps/browser/src/platform/browser/browser-popup-utils.spec.ts index 6e2175e3a79..cb04f30b589 100644 --- a/apps/browser/src/platform/browser/browser-popup-utils.spec.ts +++ b/apps/browser/src/platform/browser/browser-popup-utils.spec.ts @@ -1,7 +1,7 @@ import { createChromeTabMock } from "../../autofill/spec/autofill-mocks"; import { BrowserApi } from "./browser-api"; -import BrowserPopupUtils from "./browser-popup-utils"; +import BrowserPopupUtils, { PopupWidthOptions } from "./browser-popup-utils"; describe("BrowserPopupUtils", () => { afterEach(() => { @@ -152,7 +152,7 @@ describe("BrowserPopupUtils", () => { focused: false, alwaysOnTop: false, incognito: false, - width: 380, + width: PopupWidthOptions.default, }); jest.spyOn(BrowserApi, "createWindow").mockImplementation(); jest.spyOn(BrowserApi, "updateWindowProperties").mockImplementation(); @@ -168,7 +168,7 @@ describe("BrowserPopupUtils", () => { expect(BrowserApi.createWindow).toHaveBeenCalledWith({ type: "popup", focused: true, - width: 380, + width: PopupWidthOptions.default, height: 630, left: 85, top: 190, @@ -197,7 +197,7 @@ describe("BrowserPopupUtils", () => { expect(BrowserApi.createWindow).toHaveBeenCalledWith({ type: "popup", focused: true, - width: 380, + width: PopupWidthOptions.default, height: 630, left: 85, top: 190, @@ -214,7 +214,7 @@ describe("BrowserPopupUtils", () => { expect(BrowserApi.createWindow).toHaveBeenCalledWith({ type: "popup", focused: true, - width: 380, + width: PopupWidthOptions.default, height: 630, left: 85, top: 190, @@ -267,7 +267,7 @@ describe("BrowserPopupUtils", () => { expect(BrowserApi.createWindow).toHaveBeenCalledWith({ type: "popup", focused: true, - width: 380, + width: PopupWidthOptions.default, height: 630, left: 85, top: 190, @@ -290,7 +290,7 @@ describe("BrowserPopupUtils", () => { focused: false, alwaysOnTop: false, incognito: false, - width: 380, + width: PopupWidthOptions.default, state: "fullscreen", }); jest @@ -321,7 +321,7 @@ describe("BrowserPopupUtils", () => { focused: false, alwaysOnTop: false, incognito: false, - width: 380, + width: PopupWidthOptions.default, state: "fullscreen", }); diff --git a/apps/browser/src/platform/browser/browser-popup-utils.ts b/apps/browser/src/platform/browser/browser-popup-utils.ts index 8343799d0eb..c8dba57e708 100644 --- a/apps/browser/src/platform/browser/browser-popup-utils.ts +++ b/apps/browser/src/platform/browser/browser-popup-utils.ts @@ -10,9 +10,9 @@ import { BrowserApi } from "./browser-api"; * Value represents width in pixels */ export const PopupWidthOptions = Object.freeze({ - default: 380, - wide: 480, - "extra-wide": 600, + default: 480, + wide: 600, + narrow: 380, }); type PopupWidthOptions = typeof PopupWidthOptions; diff --git a/apps/browser/src/platform/popup/layout/popup-layout.mdx b/apps/browser/src/platform/popup/layout/popup-layout.mdx index a2725350a8f..0a1c80b13f6 100644 --- a/apps/browser/src/platform/popup/layout/popup-layout.mdx +++ b/apps/browser/src/platform/popup/layout/popup-layout.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Canvas } from "@storybook/addon-docs"; +import { Meta, Story, Canvas } from "@storybook/addon-docs/blocks"; import * as stories from "./popup-layout.stories"; diff --git a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts index c6ffe1a6414..2e088b8161e 100644 --- a/apps/browser/src/platform/popup/layout/popup-layout.stories.ts +++ b/apps/browser/src/platform/popup/layout/popup-layout.stories.ts @@ -44,7 +44,7 @@ import { PopupTabNavigationComponent } from "./popup-tab-navigation.component"; @Component({ selector: "extension-container", template: ` -
    +
    `, @@ -678,7 +678,7 @@ export const WidthOptions: Story = { template: /* HTML */ `
    Default:
    -
    +
    Wide:
    diff --git a/apps/browser/src/platform/popup/layout/popup-page.component.html b/apps/browser/src/platform/popup/layout/popup-page.component.html index 828d9947373..bb24fb800aa 100644 --- a/apps/browser/src/platform/popup/layout/popup-page.component.html +++ b/apps/browser/src/platform/popup/layout/popup-page.component.html @@ -25,7 +25,6 @@
    (false); @@ -33,10 +39,21 @@ export class PopupPageComponent { protected readonly scrolled = signal(false); isScrolled = this.scrolled.asReadonly(); + constructor() { + this.scrollLayout.scrollableRef$ + .pipe( + filter((ref): ref is ElementRef => ref != null), + switchMap((ref) => + fromEvent(ref.nativeElement, "scroll").pipe( + startWith(null), + map(() => ref.nativeElement.scrollTop !== 0), + ), + ), + takeUntilDestroyed(this.destroyRef), + ) + .subscribe((isScrolled) => this.scrolled.set(isScrolled)); + } + /** Accessible loading label for the spinner. Defaults to "loading" */ readonly loadingText = input(this.i18nService.t("loading")); - - handleScroll(event: Event) { - this.scrolled.set((event.currentTarget as HTMLElement).scrollTop !== 0); - } } diff --git a/apps/browser/src/platform/popup/layout/popup-size.service.ts b/apps/browser/src/platform/popup/layout/popup-size.service.ts index 0e4aacb9a97..4c0c901270e 100644 --- a/apps/browser/src/platform/popup/layout/popup-size.service.ts +++ b/apps/browser/src/platform/popup/layout/popup-size.service.ts @@ -37,7 +37,7 @@ export class PopupSizeService { /** Begin listening for state changes */ async init() { this.width$.subscribe((width: PopupWidthOption) => { - PopupSizeService.setStyle(width); + void PopupSizeService.setStyle(width); localStorage.setItem(PopupSizeService.LocalStorageKey, width); }); } @@ -77,13 +77,14 @@ export class PopupSizeService { } } - private static setStyle(width: PopupWidthOption) { - if (!BrowserPopupUtils.inPopup(window)) { + private static async setStyle(width: PopupWidthOption) { + const isInTab = await BrowserPopupUtils.isInTab(); + if (!BrowserPopupUtils.inPopup(window) || isInTab) { return; } const pxWidth = PopupWidthOptions[width] ?? PopupWidthOptions.default; - document.body.style.minWidth = `${pxWidth}px`; + document.body.style.width = `${pxWidth}px`; } /** @@ -91,6 +92,6 @@ export class PopupSizeService { **/ static initBodyWidthFromLocalStorage() { const storedValue = localStorage.getItem(PopupSizeService.LocalStorageKey); - this.setStyle(storedValue as any); + void this.setStyle(storedValue as any); } } diff --git a/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.ts b/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.ts index 4abd9cd4803..26138d57954 100644 --- a/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.ts +++ b/apps/browser/src/platform/popup/layout/popup-tab-navigation.component.ts @@ -22,7 +22,7 @@ export type NavButton = { templateUrl: "popup-tab-navigation.component.html", imports: [CommonModule, LinkModule, RouterModule, JslibModule, IconModule], host: { - class: "tw-block tw-h-full tw-w-full tw-flex tw-flex-col", + class: "tw-block tw-size-full tw-flex tw-flex-col", }, }) export class PopupTabNavigationComponent { diff --git a/apps/browser/src/platform/popup/view-cache/popup-router-cache.service.ts b/apps/browser/src/platform/popup/view-cache/popup-router-cache.service.ts index abb7c6405c2..6e5218c9f27 100644 --- a/apps/browser/src/platform/popup/view-cache/popup-router-cache.service.ts +++ b/apps/browser/src/platform/popup/view-cache/popup-router-cache.service.ts @@ -120,8 +120,8 @@ export class PopupRouterCacheService { /** * Navigate back in history */ - async back() { - if (!BrowserPopupUtils.inPopup(window)) { + async back(updateCache = false) { + if (!updateCache && !BrowserPopupUtils.inPopup(window)) { this.location.back(); return; } diff --git a/apps/browser/src/platform/storage/memory-storage-service-interactions.spec.ts b/apps/browser/src/platform/storage/memory-storage-service-interactions.spec.ts index 8004559f57c..c223e4c4925 100644 --- a/apps/browser/src/platform/storage/memory-storage-service-interactions.spec.ts +++ b/apps/browser/src/platform/storage/memory-storage-service-interactions.spec.ts @@ -13,8 +13,7 @@ import { mockPorts } from "../../../spec/mock-port.spec-util"; import { BackgroundMemoryStorageService } from "./background-memory-storage.service"; import { ForegroundMemoryStorageService } from "./foreground-memory-storage.service"; -// These are succeeding individually but failing in a batch run - skipping for now -describe.skip("foreground background memory storage interaction", () => { +describe("foreground background memory storage interaction", () => { let foreground: ForegroundMemoryStorageService; let background: BackgroundMemoryStorageService; let logService: MockProxy; @@ -28,7 +27,7 @@ describe.skip("foreground background memory storage interaction", () => { }); afterEach(() => { - jest.resetAllMocks(); + jest.clearAllMocks(); }); test.each(["has", "get"])( diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index a36396afa1a..1f1d4d25b40 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -42,12 +42,18 @@ import { TwoFactorAuthComponent, TwoFactorAuthGuard, } from "@bitwarden/auth/angular"; +import { canAccessAutoConfirmSettings } from "@bitwarden/auto-confirm"; import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components"; -import { LockComponent, ConfirmKeyConnectorDomainComponent } from "@bitwarden/key-management-ui"; +import { + LockComponent, + ConfirmKeyConnectorDomainComponent, + RemovePasswordComponent, +} from "@bitwarden/key-management-ui"; import { AccountSwitcherComponent } from "../auth/popup/account-switching/account-switcher.component"; import { AuthExtensionRoute } from "../auth/popup/constants/auth-extension-route.constant"; import { fido2AuthGuard } from "../auth/popup/guards/fido2-auth.guard"; +import { platformPopoutGuard } from "../auth/popup/guards/platform-popout.guard"; import { AccountSecurityComponent } from "../auth/popup/settings/account-security.component"; import { ExtensionDeviceManagementComponent } from "../auth/popup/settings/extension-device-management.component"; import { Fido2Component } from "../autofill/popup/fido2/fido2.component"; @@ -58,12 +64,12 @@ import { NotificationsSettingsComponent } from "../autofill/popup/settings/notif import { PremiumV2Component } from "../billing/popup/settings/premium-v2.component"; import { PhishingWarning } from "../dirt/phishing-detection/popup/phishing-warning.component"; import { ProtectedByComponent } from "../dirt/phishing-detection/popup/protected-by-component"; -import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component"; import BrowserPopupUtils from "../platform/browser/browser-popup-utils"; import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service"; 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"; @@ -80,11 +86,13 @@ import { PasswordHistoryV2Component } from "../vault/popup/components/vault-v2/v import { VaultV2Component } from "../vault/popup/components/vault-v2/vault-v2.component"; import { ViewV2Component } from "../vault/popup/components/vault-v2/view-v2/view-v2.component"; import { + atRiskPasswordAuthGuard, canAccessAtRiskPasswords, hasAtRiskPasswords, } from "../vault/popup/guards/at-risk-passwords.guard"; import { clearVaultStateGuard } from "../vault/popup/guards/clear-vault-state.guard"; import { IntroCarouselGuard } from "../vault/popup/guards/intro-carousel.guard"; +import { AdminSettingsComponent } from "../vault/popup/settings/admin-settings.component"; import { AppearanceV2Component } from "../vault/popup/settings/appearance-v2.component"; import { ArchiveComponent } from "../vault/popup/settings/archive.component"; import { DownloadBitwardenComponent } from "../vault/popup/settings/download-bitwarden.component"; @@ -187,9 +195,22 @@ const routes: Routes = [ }, { path: "remove-password", - component: RemovePasswordComponent, + component: ExtensionAnonLayoutWrapperComponent, canActivate: [authGuard], data: { elevation: 1 } satisfies RouteDataProperties, + children: [ + { + path: "", + component: RemovePasswordComponent, + data: { + pageTitle: { + key: "verifyYourOrganization", + }, + showBackButton: false, + pageIcon: LockIcon, + } satisfies ExtensionAnonLayoutWrapperData, + }, + ], }, { path: "view-cipher", @@ -245,7 +266,7 @@ const routes: Routes = [ { path: "import", component: ImportBrowserV2Component, - canActivate: [authGuard], + canActivate: [authGuard, firefoxPopoutGuard()], data: { elevation: 1 } satisfies RouteDataProperties, }, { @@ -314,6 +335,12 @@ const routes: Routes = [ canActivate: [authGuard], data: { elevation: 1 } satisfies RouteDataProperties, }, + { + path: "admin", + component: AdminSettingsComponent, + canActivate: [authGuard, canAccessAutoConfirmSettings], + data: { elevation: 1 } satisfies RouteDataProperties, + }, { path: "clone-cipher", component: AddEditV2Component, @@ -323,13 +350,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, }, { @@ -414,7 +441,7 @@ const routes: Routes = [ }, { path: AuthRoute.LoginWithPasskey, - canActivate: [unauthGuardFn(unauthRouteOverrides)], + canActivate: [unauthGuardFn(unauthRouteOverrides), platformPopoutGuard(["linux"])], data: { pageIcon: TwoFactorAuthSecurityKeyIcon, pageTitle: { @@ -645,7 +672,7 @@ const routes: Routes = [ component: ConfirmKeyConnectorDomainComponent, data: { pageTitle: { - key: "confirmKeyConnectorDomain", + key: "verifyYourOrganization", }, showBackButton: true, pageIcon: DomainIcon, @@ -697,7 +724,7 @@ const routes: Routes = [ { path: "at-risk-passwords", component: AtRiskPasswordsComponent, - canActivate: [authGuard, canAccessAtRiskPasswords, hasAtRiskPasswords], + canActivate: [atRiskPasswordAuthGuard, canAccessAtRiskPasswords, hasAtRiskPasswords], }, { path: AuthExtensionRoute.AccountSwitcher, diff --git a/apps/browser/src/popup/app.component.html b/apps/browser/src/popup/app.component.html index 3d81354ca35..3a5c8021e17 100644 --- a/apps/browser/src/popup/app.component.html +++ b/apps/browser/src/popup/app.component.html @@ -13,8 +13,11 @@
    } @else { -
    - + +
    +
    + +
    +
    - } diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index 8f00569b720..e4cb8a654c4 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -14,16 +14,11 @@ import { NavigationEnd, Router, RouterOutlet } from "@angular/router"; import { catchError, concatMap, - distinctUntilChanged, filter, firstValueFrom, map, of, - pairwise, - startWith, Subject, - switchMap, - take, takeUntil, tap, } from "rxjs"; @@ -38,7 +33,7 @@ import { } from "@bitwarden/auth/common"; import { BrowserApi } from "@bitwarden/browser/platform/browser/browser-api"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { AuthRequestAnsweringServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction"; +import { AuthRequestAnsweringService } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; @@ -83,7 +78,8 @@ export class AppComponent implements OnInit, OnDestroy { private lastActivity: Date; private activeUserId: UserId; private routerAnimations = false; - private processingPendingAuth = false; + private processingPendingAuthRequests = false; + private shouldRerunAuthRequestProcessing = false; private destroy$ = new Subject(); @@ -118,7 +114,7 @@ export class AppComponent implements OnInit, OnDestroy { private logService: LogService, private authRequestService: AuthRequestServiceAbstraction, private pendingAuthRequestsState: PendingAuthRequestsStateService, - private authRequestAnsweringService: AuthRequestAnsweringServiceAbstraction, + private authRequestAnsweringService: AuthRequestAnsweringService, ) { this.deviceTrustToastService.setupListeners$.pipe(takeUntilDestroyed()).subscribe(); @@ -136,22 +132,7 @@ export class AppComponent implements OnInit, OnDestroy { this.activeUserId = account?.id; }); - // Trigger processing auth requests when the active user is in an unlocked state. Runs once when - // the popup is open. - this.accountService.activeAccount$ - .pipe( - map((a) => a?.id), // Extract active userId - distinctUntilChanged(), // Only when userId actually changes - filter((userId) => userId != null), // Require a valid userId - switchMap((userId) => this.authService.authStatusFor$(userId).pipe(take(1))), // Get current auth status once for new user - filter((status) => status === AuthenticationStatus.Unlocked), // Only when the new user is Unlocked - tap(() => { - // Trigger processing when switching users while popup is open - void this.authRequestAnsweringService.processPendingAuthRequests(); - }), - takeUntil(this.destroy$), - ) - .subscribe(); + this.authRequestAnsweringService.setupUnlockListenersForProcessingAuthRequests(this.destroy$); this.authService.activeAccountStatus$ .pipe( @@ -163,23 +144,6 @@ export class AppComponent implements OnInit, OnDestroy { ) .subscribe(); - // When the popup is already open and the active account transitions to Unlocked, - // process any pending auth requests for the active user. The above subscription does not handle - // this case. - this.authService.activeAccountStatus$ - .pipe( - startWith(null as unknown as AuthenticationStatus), // Seed previous value to handle initial emission - pairwise(), // Compare previous and current statuses - filter( - ([prev, curr]) => - prev !== AuthenticationStatus.Unlocked && curr === AuthenticationStatus.Unlocked, // Fire on transitions into Unlocked (incl. initial) - ), - takeUntil(this.destroy$), - ) - .subscribe(() => { - void this.authRequestAnsweringService.processPendingAuthRequests(); - }); - this.ngZone.runOutsideAngular(() => { window.onmousedown = () => this.recordActivity(); window.ontouchstart = () => this.recordActivity(); @@ -234,38 +198,31 @@ export class AppComponent implements OnInit, OnDestroy { await this.router.navigate(["lock"]); } else if (msg.command === "openLoginApproval") { - if (this.processingPendingAuth) { + if (this.processingPendingAuthRequests) { + // If an "openLoginApproval" message is received while we are currently processing other + // auth requests, then set a flag so we remember to process that new auth request + this.shouldRerunAuthRequestProcessing = true; return; } - this.processingPendingAuth = true; - try { - // Always query server for all pending requests and open a dialog for each - const pendingList = await firstValueFrom( - this.authRequestService.getPendingAuthRequests$(), - ); - if (Array.isArray(pendingList) && pendingList.length > 0) { - const respondedIds = new Set(); - for (const req of pendingList) { - if (req?.id == null) { - continue; - } - const dialogRef = LoginApprovalDialogComponent.open(this.dialogService, { - notificationId: req.id, - }); - const result = await firstValueFrom(dialogRef.closed); + /** + * This do/while loop allows us to: + * - a) call processPendingAuthRequests() once on "openLoginApproval" + * - b) remember to re-call processPendingAuthRequests() if another "openLoginApproval" was + * received while we were processing the original auth requests + */ + do { + this.shouldRerunAuthRequestProcessing = false; - if (result !== undefined && typeof result === "boolean") { - respondedIds.add(req.id); - if (respondedIds.size === pendingList.length && this.activeUserId != null) { - await this.pendingAuthRequestsState.clear(this.activeUserId); - } - } - } + try { + await this.processPendingAuthRequests(); + } catch (error) { + this.logService.error(`Error processing pending auth requests: ${error}`); + this.shouldRerunAuthRequestProcessing = false; // Reset flag to prevent infinite loop on persistent errors } - } finally { - this.processingPendingAuth = false; - } + // If an "openLoginApproval" message was received while processPendingAuthRequests() was running, then + // shouldRerunAuthRequestProcessing will have been set to true + } while (this.shouldRerunAuthRequestProcessing); } else if (msg.command === "showDialog") { // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises @@ -403,4 +360,39 @@ export class AppComponent implements OnInit, OnDestroy { this.toastService.showToast(toastOptions); } + + private async processPendingAuthRequests() { + this.processingPendingAuthRequests = true; + + try { + // Always query server for all pending requests and open a dialog for each + const pendingList = await firstValueFrom(this.authRequestService.getPendingAuthRequests$()); + + if (Array.isArray(pendingList) && pendingList.length > 0) { + const respondedIds = new Set(); + + for (const req of pendingList) { + if (req?.id == null) { + continue; + } + + const dialogRef = LoginApprovalDialogComponent.open(this.dialogService, { + notificationId: req.id, + }); + + const result = await firstValueFrom(dialogRef.closed); + + if (result !== undefined && typeof result === "boolean") { + respondedIds.add(req.id); + + if (respondedIds.size === pendingList.length && this.activeUserId != null) { + await this.pendingAuthRequestsState.clear(this.activeUserId); + } + } + } + } + } finally { + this.processingPendingAuthRequests = false; + } + } } diff --git a/apps/browser/src/popup/app.module.ts b/apps/browser/src/popup/app.module.ts index 71846cc6444..d178cee2fc3 100644 --- a/apps/browser/src/popup/app.module.ts +++ b/apps/browser/src/popup/app.module.ts @@ -28,7 +28,6 @@ import { CurrentAccountComponent } from "../auth/popup/account-switching/current import { AccountSecurityComponent } from "../auth/popup/settings/account-security.component"; import { AutofillComponent } from "../autofill/popup/settings/autofill.component"; import { NotificationsSettingsComponent } from "../autofill/popup/settings/notifications.component"; -import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component"; import { PopOutComponent } from "../platform/popup/components/pop-out.component"; import { PopupFooterComponent } from "../platform/popup/layout/popup-footer.component"; import { PopupHeaderComponent } from "../platform/popup/layout/popup-header.component"; @@ -85,13 +84,7 @@ import "../platform/popup/locales"; CalloutModule, LinkModule, ], - declarations: [ - AppComponent, - ColorPasswordPipe, - ColorPasswordCountPipe, - TabsV2Component, - RemovePasswordComponent, - ], + declarations: [AppComponent, ColorPasswordPipe, ColorPasswordCountPipe, TabsV2Component], exports: [CalloutModule], providers: [CurrencyPipe, DatePipe], bootstrap: [AppComponent], 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 dcd0496ed30..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 @@ -14,7 +14,6 @@ - div { - height: 100%; - width: 100%; -} - -main::-webkit-scrollbar, -cdk-virtual-scroll-viewport::-webkit-scrollbar, -.vault-select::-webkit-scrollbar { - width: 10px; - height: 10px; -} - -main::-webkit-scrollbar-track, -.vault-select::-webkit-scrollbar-track { - background-color: transparent; -} - -cdk-virtual-scroll-viewport::-webkit-scrollbar-track { - @include themify($themes) { - background-color: themed("backgroundColor"); - } -} - -main::-webkit-scrollbar-thumb, -cdk-virtual-scroll-viewport::-webkit-scrollbar-thumb, -.vault-select::-webkit-scrollbar-thumb { - border-radius: 10px; - margin-right: 1px; - - @include themify($themes) { - background-color: themed("scrollbarColor"); - } - - &:hover { - @include themify($themes) { - background-color: themed("scrollbarHoverColor"); - } - } -} - -header:not(bit-callout header, bit-dialog header, popup-page header) { - height: 44px; - display: flex; - - &:not(.no-theme) { - border-bottom: 1px solid #000000; - - @include themify($themes) { - color: themed("headerColor"); - background-color: themed("headerBackgroundColor"); - border-bottom-color: themed("headerBorderColor"); - } - } - - .header-content { - display: flex; - flex: 1 1 auto; - } - - .header-content > .right, - .header-content > .right > .right { - height: 100%; - } - - .left, - .right { - flex: 1; - display: flex; - min-width: -webkit-min-content; /* Workaround to Chrome bug */ - .header-icon { - margin-right: 5px; - } - } - - .right { - justify-content: flex-end; - align-items: center; - app-avatar { - max-height: 30px; - margin-right: 5px; - } - } - - .center { - display: flex; - align-items: center; - text-align: center; - min-width: 0; - } - - .login-center { - margin: auto; - } - - app-pop-out > button, - div > button:not(app-current-account button):not(.home-acc-switcher-btn), - div > a { - border: none; - padding: 0 10px; - text-decoration: none; - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - height: 100%; - white-space: pre; - - &:not(.home-acc-switcher-btn):hover, - &:not(.home-acc-switcher-btn):focus { - @include themify($themes) { - background-color: themed("headerBackgroundHoverColor"); - color: themed("headerColor"); - } - } - - &[disabled] { - opacity: 0.65; - cursor: default !important; - background-color: inherit !important; - } - - i + span { - margin-left: 5px; - } - } - - app-pop-out { - display: flex; - padding-right: 0.5em; - } - - .title { - font-weight: bold; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .search { - padding: 7px 10px; - width: 100%; - text-align: left; - position: relative; - display: flex; - - .bwi { - position: absolute; - top: 15px; - left: 20px; - - @include themify($themes) { - color: themed("headerInputPlaceholderColor"); - } - } - - input:not(bit-form-field input) { - width: 100%; - margin: 0; - border: none; - padding: 5px 10px 5px 30px; - border-radius: $border-radius; - - @include themify($themes) { - background-color: themed("headerInputBackgroundColor"); - color: themed("headerInputColor"); - } - - &::selection { - @include themify($themes) { - // explicitly set text selection to invert foreground/background - background-color: themed("headerInputColor"); - color: themed("headerInputBackgroundColor"); - } - } - - &:focus { - border-radius: $border-radius; - outline: none; - - @include themify($themes) { - background-color: themed("headerInputBackgroundFocusColor"); - } - } - - &::-webkit-input-placeholder { - @include themify($themes) { - color: themed("headerInputPlaceholderColor"); - } - } - /** make the cancel button visible in both dark/light themes **/ - &[type="search"]::-webkit-search-cancel-button { - -webkit-appearance: none; - appearance: none; - height: 15px; - width: 15px; - background-repeat: no-repeat; - mask-image: url("../images/close-button-white.svg"); - -webkit-mask-image: url("../images/close-button-white.svg"); - @include themify($themes) { - background-color: themed("headerInputColor"); - } - } - } - } - - .left + .search, - .left + .sr-only + .search { - padding-left: 0; - - .bwi { - left: 10px; - } - } - - .search + .right { - margin-left: -10px; - } -} - -.content { - padding: 15px 5px; -} - -app-root { - width: 100%; - height: 100vh; - display: flex; - - @include themify($themes) { - background-color: themed("backgroundColor"); - } -} - -main:not(popup-page main):not(auth-anon-layout main) { - position: absolute; - top: 44px; - bottom: 0; - left: 0; - right: 0; - overflow-y: auto; - overflow-x: hidden; - - @include themify($themes) { - background-color: themed("backgroundColor"); - } - - &.no-header { - top: 0; - } - - &.flex { - display: flex; - flex-flow: column; - height: calc(100% - 44px); - } -} - -.center-content, -.no-items, -.full-loading-spinner { - display: flex; - justify-content: center; - align-items: center; - height: 100%; - flex-direction: column; - flex-grow: 1; -} - -.no-items, -.full-loading-spinner { - text-align: center; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - - .no-items-image { - @include themify($themes) { - content: url("../images/search-desktop" + themed("svgSuffix")); - } - } - - .bwi { - margin-bottom: 10px; - - @include themify($themes) { - color: themed("disabledIconColor"); - } - } -} - -// cdk-virtual-scroll -.cdk-virtual-scroll-viewport { - width: 100%; - height: 100%; - overflow-y: auto; - overflow-x: hidden; -} - -.cdk-virtual-scroll-content-wrapper { - width: 100%; -} diff --git a/apps/browser/src/popup/scss/box.scss b/apps/browser/src/popup/scss/box.scss deleted file mode 100644 index 763f73a15cb..00000000000 --- a/apps/browser/src/popup/scss/box.scss +++ /dev/null @@ -1,620 +0,0 @@ -@import "variables.scss"; - -.box { - position: relative; - width: 100%; - - &.first { - margin-top: 0; - } - - .box-header { - margin: 0 10px 5px 10px; - text-transform: uppercase; - display: flex; - - @include themify($themes) { - color: themed("headingColor"); - } - } - - .box-content { - @include themify($themes) { - background-color: themed("backgroundColor"); - border-color: themed("borderColor"); - } - - &.box-content-padded { - padding: 10px 15px; - } - - &.condensed .box-content-row, - .box-content-row.condensed { - padding-top: 5px; - padding-bottom: 5px; - } - - &.no-hover .box-content-row, - .box-content-row.no-hover { - &:hover, - &:focus { - @include themify($themes) { - background-color: themed("boxBackgroundColor") !important; - } - } - } - - &.single-line .box-content-row, - .box-content-row.single-line { - padding-top: 10px; - padding-bottom: 10px; - margin: 5px; - } - - &.row-top-padding { - padding-top: 10px; - } - } - - .box-footer { - margin: 0 5px 5px 5px; - padding: 0 10px 5px 10px; - font-size: $font-size-small; - - button.btn { - font-size: $font-size-small; - padding: 0; - } - - button.btn.primary { - font-size: $font-size-base; - padding: 7px 15px; - width: 100%; - - &:hover { - @include themify($themes) { - border-color: themed("borderHoverColor") !important; - } - } - } - - @include themify($themes) { - color: themed("mutedColor"); - } - } - - &.list { - margin: 10px 0 20px 0; - .box-content { - .virtual-scroll-item { - display: inline-block; - width: 100%; - } - - .box-content-row { - text-decoration: none; - border-radius: $border-radius; - // background-color: $background-color; - - @include themify($themes) { - color: themed("textColor"); - background-color: themed("boxBackgroundColor"); - } - - &.padded { - padding-top: 10px; - padding-bottom: 10px; - } - - &.no-hover { - &:hover { - @include themify($themes) { - background-color: themed("boxBackgroundColor") !important; - } - } - } - - &:hover, - &:focus, - &.active { - @include themify($themes) { - background-color: themed("listItemBackgroundHoverColor"); - } - } - - &:focus { - border-left: 5px solid #000000; - padding-left: 5px; - - @include themify($themes) { - border-left-color: themed("mutedColor"); - } - } - - .action-buttons { - .row-btn { - padding-left: 5px; - padding-right: 5px; - } - } - - .text:not(.no-ellipsis), - .detail { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .row-main { - display: flex; - min-width: 0; - align-items: normal; - - .row-main-content { - min-width: 0; - } - } - } - - &.single-line { - .box-content-row { - display: flex; - padding-top: 10px; - padding-bottom: 10px; - margin: 5px; - border-radius: $border-radius; - } - } - } - } -} - -.box-content-row { - display: block; - padding: 5px 10px; - position: relative; - z-index: 1; - border-radius: $border-radius; - margin: 3px 5px; - - @include themify($themes) { - background-color: themed("boxBackgroundColor"); - } - - &:last-child { - &:before { - border: none; - height: 0; - } - } - - &.override-last:last-child:before { - border-bottom: 1px solid #000000; - @include themify($themes) { - border-bottom-color: themed("boxBorderColor"); - } - } - - &.last:last-child:before { - border-bottom: 1px solid #000000; - @include themify($themes) { - border-bottom-color: themed("boxBorderColor"); - } - } - - &:after { - content: ""; - display: table; - clear: both; - } - - &:hover, - &:focus, - &.active { - @include themify($themes) { - background-color: themed("boxBackgroundHoverColor"); - } - } - - &.pre { - white-space: pre; - overflow-x: auto; - } - - &.pre-wrap { - white-space: pre-wrap; - overflow-x: auto; - } - - .row-label, - label { - font-size: $font-size-small; - display: block; - width: 100%; - margin-bottom: 5px; - - @include themify($themes) { - color: themed("mutedColor"); - } - - .sub-label { - margin-left: 10px; - } - } - - .flex-label { - font-size: $font-size-small; - display: flex; - flex-grow: 1; - margin-bottom: 5px; - - @include themify($themes) { - color: themed("mutedColor"); - } - - > a { - flex-grow: 0; - } - } - - .text, - .detail { - display: block; - text-align: left; - - @include themify($themes) { - color: themed("textColor"); - } - } - - .detail { - font-size: $font-size-small; - - @include themify($themes) { - color: themed("mutedColor"); - } - } - - .img-right, - .txt-right { - float: right; - margin-left: 10px; - } - - .row-main { - flex-grow: 1; - min-width: 0; - } - - &.box-content-row-flex, - .box-content-row-flex, - &.box-content-row-checkbox, - &.box-content-row-link, - &.box-content-row-input, - &.box-content-row-slider, - &.box-content-row-multi { - display: flex; - align-items: center; - word-break: break-all; - - &.box-content-row-word-break { - word-break: normal; - } - } - - &.box-content-row-multi { - input:not([type="checkbox"]) { - width: 100%; - } - - input + label.sr-only + select { - margin-top: 5px; - } - - > a, - > button { - padding: 8px 8px 8px 4px; - margin: 0; - - @include themify($themes) { - color: themed("dangerColor"); - } - } - } - - &.box-content-row-multi, - &.box-content-row-newmulti { - padding-left: 10px; - } - - &.box-content-row-newmulti { - @include themify($themes) { - color: themed("primaryColor"); - } - } - - &.box-content-row-checkbox, - &.box-content-row-link, - &.box-content-row-input, - &.box-content-row-slider { - padding-top: 10px; - padding-bottom: 10px; - margin: 5px; - - label, - .row-label { - font-size: $font-size-base; - display: block; - width: initial; - margin-bottom: 0; - - @include themify($themes) { - color: themed("textColor"); - } - } - - > span { - @include themify($themes) { - color: themed("mutedColor"); - } - } - - > input { - margin: 0 0 0 auto; - padding: 0; - } - - > * { - margin-right: 15px; - - &:last-child { - margin-right: 0; - } - } - } - - &.box-content-row-checkbox-left { - justify-content: flex-start; - - > input { - margin: 0 15px 0 0; - } - } - - &.box-content-row-input { - label { - white-space: nowrap; - } - - input { - text-align: right; - - &[type="number"] { - max-width: 50px; - } - } - } - - &.box-content-row-slider { - input[type="range"] { - height: 10px; - } - - input[type="number"] { - width: 45px; - } - - label { - white-space: nowrap; - } - } - - input:not([type="checkbox"]):not([type="radio"]), - textarea { - border: none; - width: 100%; - background-color: transparent !important; - - &::-webkit-input-placeholder { - @include themify($themes) { - color: themed("inputPlaceholderColor"); - } - } - - &:not([type="file"]):focus { - outline: none; - } - } - - select { - width: 100%; - border: 1px solid #000000; - border-radius: $border-radius; - padding: 7px 4px; - - @include themify($themes) { - border-color: themed("inputBorderColor"); - } - } - - .action-buttons { - display: flex; - margin-left: 5px; - - &.action-buttons-fixed { - align-self: start; - margin-top: 2px; - } - - .row-btn { - cursor: pointer; - padding: 10px 8px; - background: none; - border: none; - - @include themify($themes) { - color: themed("boxRowButtonColor"); - } - - &:hover, - &:focus { - @include themify($themes) { - color: themed("boxRowButtonHoverColor"); - } - } - - &.disabled, - &[disabled] { - @include themify($themes) { - color: themed("disabledIconColor"); - opacity: themed("disabledBoxOpacity"); - } - - &:hover { - @include themify($themes) { - color: themed("disabledIconColor"); - opacity: themed("disabledBoxOpacity"); - } - } - cursor: default !important; - } - } - - &.no-pad .row-btn { - padding-top: 0; - padding-bottom: 0; - } - } - - &:not(.box-draggable-row) { - .action-buttons .row-btn:last-child { - margin-right: -3px; - } - } - - &.box-draggable-row { - &.box-content-row-checkbox { - input[type="checkbox"] + .drag-handle { - margin-left: 10px; - } - } - } - - .drag-handle { - cursor: move; - padding: 10px 2px 10px 8px; - user-select: none; - - @include themify($themes) { - color: themed("mutedColor"); - } - } - - &.cdk-drag-preview { - position: relative; - display: flex; - align-items: center; - opacity: 0.8; - - @include themify($themes) { - background-color: themed("boxBackgroundColor"); - } - } - - select.field-type { - margin: 5px 0 0 25px; - width: calc(100% - 25px); - } - - .icon { - display: flex; - justify-content: center; - align-items: center; - min-width: 34px; - margin-left: -5px; - - @include themify($themes) { - color: themed("mutedColor"); - } - - &.icon-small { - min-width: 25px; - } - - img { - border-radius: $border-radius; - max-height: 20px; - max-width: 20px; - } - } - - .progress { - display: flex; - height: 5px; - overflow: hidden; - margin: 5px -15px -10px; - - .progress-bar { - display: flex; - flex-direction: column; - justify-content: center; - white-space: nowrap; - background-color: $brand-primary; - } - } - - .radio-group { - display: flex; - justify-content: flex-start; - align-items: center; - margin-bottom: 5px; - - input { - flex-grow: 0; - } - - label { - margin: 0 0 0 5px; - flex-grow: 1; - font-size: $font-size-base; - display: block; - width: 100%; - - @include themify($themes) { - color: themed("textColor"); - } - } - - &.align-start { - align-items: start; - margin-top: 10px; - - label { - margin-top: -4px; - } - } - } -} - -.truncate { - display: inline-block; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -form { - .box { - .box-content { - .box-content-row { - &.no-hover { - &:hover { - @include themify($themes) { - background-color: themed("transparentColor") !important; - } - } - } - } - } - } -} diff --git a/apps/browser/src/popup/scss/buttons.scss b/apps/browser/src/popup/scss/buttons.scss deleted file mode 100644 index e9af536dc3d..00000000000 --- a/apps/browser/src/popup/scss/buttons.scss +++ /dev/null @@ -1,118 +0,0 @@ -@import "variables.scss"; - -.btn { - border-radius: $border-radius; - padding: 7px 15px; - border: 1px solid #000000; - font-size: $font-size-base; - text-align: center; - cursor: pointer; - - @include themify($themes) { - background-color: themed("buttonBackgroundColor"); - border-color: themed("buttonBorderColor"); - color: themed("buttonColor"); - } - - &.primary { - @include themify($themes) { - color: themed("buttonPrimaryColor"); - } - } - - &.danger { - @include themify($themes) { - color: themed("buttonDangerColor"); - } - } - - &.callout-half { - font-weight: bold; - max-width: 50%; - } - - &:hover:not([disabled]) { - cursor: pointer; - - @include themify($themes) { - background-color: darken(themed("buttonBackgroundColor"), 1.5%); - border-color: darken(themed("buttonBorderColor"), 17%); - color: darken(themed("buttonColor"), 10%); - } - - &.primary { - @include themify($themes) { - color: darken(themed("buttonPrimaryColor"), 6%); - } - } - - &.danger { - @include themify($themes) { - color: darken(themed("buttonDangerColor"), 6%); - } - } - } - - &:focus:not([disabled]) { - cursor: pointer; - outline: 0; - - @include themify($themes) { - background-color: darken(themed("buttonBackgroundColor"), 6%); - border-color: darken(themed("buttonBorderColor"), 25%); - } - } - - &[disabled] { - opacity: 0.65; - cursor: default !important; - } - - &.block { - display: block; - width: calc(100% - 10px); - margin: 0 auto; - } - - &.link, - &.neutral { - border: none !important; - background: none !important; - - &:focus { - text-decoration: underline; - } - } -} - -.action-buttons { - .btn { - &:focus { - outline: auto; - } - } -} - -button.box-content-row { - display: block; - width: calc(100% - 10px); - text-align: left; - border-color: none; - - @include themify($themes) { - background-color: themed("boxBackgroundColor"); - } -} - -button { - border: none; - background: transparent; - color: inherit; -} - -.login-buttons { - .btn.block { - width: 100%; - margin-bottom: 10px; - } -} diff --git a/apps/browser/src/popup/scss/environment.scss b/apps/browser/src/popup/scss/environment.scss deleted file mode 100644 index cd8f6379e2c..00000000000 --- a/apps/browser/src/popup/scss/environment.scss +++ /dev/null @@ -1,43 +0,0 @@ -@import "variables.scss"; - -html.browser_safari { - &.safari_height_fix { - body { - height: 360px !important; - - &.body-xs { - height: 300px !important; - } - - &.body-full { - height: 100% !important; - } - } - } - - header { - .search .bwi { - left: 20px; - } - - .left + .search .bwi { - left: 10px; - } - } - - .content { - &.login-page { - padding-top: 100px; - } - } - - app-root { - border-width: 1px; - border-style: solid; - border-color: #000000; - } - - &.theme_light app-root { - border-color: #777777; - } -} diff --git a/apps/browser/src/popup/scss/grid.scss b/apps/browser/src/popup/scss/grid.scss deleted file mode 100644 index 8cdb29bb52c..00000000000 --- a/apps/browser/src/popup/scss/grid.scss +++ /dev/null @@ -1,11 +0,0 @@ -.row { - display: flex; - margin: 0 -15px; - width: 100%; -} - -.col { - flex-basis: 0; - flex-grow: 1; - padding: 0 15px; -} diff --git a/apps/browser/src/popup/scss/misc.scss b/apps/browser/src/popup/scss/misc.scss deleted file mode 100644 index 006e1d35f6a..00000000000 --- a/apps/browser/src/popup/scss/misc.scss +++ /dev/null @@ -1,348 +0,0 @@ -@import "variables.scss"; - -small, -.small { - font-size: $font-size-small; -} - -.bg-primary { - @include themify($themes) { - background-color: themed("primaryColor") !important; - } -} - -.bg-success { - @include themify($themes) { - background-color: themed("successColor") !important; - } -} - -.bg-danger { - @include themify($themes) { - background-color: themed("dangerColor") !important; - } -} - -.bg-info { - @include themify($themes) { - background-color: themed("infoColor") !important; - } -} - -.bg-warning { - @include themify($themes) { - background-color: themed("warningColor") !important; - } -} - -.text-primary { - @include themify($themes) { - color: themed("primaryColor") !important; - } -} - -.text-success { - @include themify($themes) { - color: themed("successColor") !important; - } -} - -.text-muted { - @include themify($themes) { - color: themed("mutedColor") !important; - } -} - -.text-default { - @include themify($themes) { - color: themed("textColor") !important; - } -} - -.text-danger { - @include themify($themes) { - color: themed("dangerColor") !important; - } -} - -.text-info { - @include themify($themes) { - color: themed("infoColor") !important; - } -} - -.text-warning { - @include themify($themes) { - color: themed("warningColor") !important; - } -} - -.text-center { - text-align: center; -} - -.font-weight-semibold { - font-weight: 600; -} - -p.lead { - font-size: $font-size-large; - margin-bottom: 20px; - font-weight: normal; -} - -.flex-right { - margin-left: auto; -} - -.flex-bottom { - margin-top: auto; -} - -.no-margin { - margin: 0 !important; -} - -.display-block { - display: block !important; -} - -.monospaced { - font-family: $font-family-monospace; -} - -.show-whitespace { - white-space: pre-wrap; -} - -.img-responsive { - display: block; - max-width: 100%; - height: auto; -} - -.img-rounded { - border-radius: $border-radius; -} - -.select-index-top { - position: relative; - z-index: 100; -} - -.sr-only { - position: absolute !important; - width: 1px !important; - height: 1px !important; - padding: 0 !important; - margin: -1px !important; - overflow: hidden !important; - clip: rect(0, 0, 0, 0) !important; - border: 0 !important; -} - -:not(:focus) > .exists-only-on-parent-focus { - display: none; -} - -.password-wrapper { - overflow-wrap: break-word; - white-space: pre-wrap; - min-width: 0; -} - -.password-number { - @include themify($themes) { - color: themed("passwordNumberColor"); - } -} - -.password-special { - @include themify($themes) { - color: themed("passwordSpecialColor"); - } -} - -.password-character { - display: inline-flex; - flex-direction: column; - align-items: center; - width: 30px; - height: 36px; - font-weight: 600; - - &:nth-child(odd) { - @include themify($themes) { - background-color: themed("backgroundColor"); - } - } -} - -.password-count { - white-space: nowrap; - font-size: 8px; - - @include themify($themes) { - color: themed("passwordCountText") !important; - } -} - -#duo-frame { - background: url("../images/loading.svg") 0 0 no-repeat; - width: 100%; - height: 470px; - margin-bottom: -10px; - - iframe { - width: 100%; - height: 100%; - border: none; - } -} - -#web-authn-frame { - width: 100%; - height: 40px; - - iframe { - border: none; - height: 100%; - width: 100%; - } -} - -body.linux-webauthn { - width: 485px !important; - #web-authn-frame { - iframe { - width: 375px; - margin: 0 55px; - } - } -} - -app-root > #loading { - display: flex; - text-align: center; - justify-content: center; - align-items: center; - height: 100%; - width: 100%; - color: $text-muted; - - @include themify($themes) { - color: themed("mutedColor"); - } -} - -app-vault-icon, -.app-vault-icon { - display: flex; -} - -.logo-image { - margin: 0 auto; - width: 142px; - height: 21px; - background-size: 142px 21px; - background-repeat: no-repeat; - @include themify($themes) { - background-image: url("../images/logo-" + themed("logoSuffix") + "@2x.png"); - } - @media (min-width: 219px) { - width: 189px; - height: 28px; - background-size: 189px 28px; - } - @media (min-width: 314px) { - width: 284px; - height: 43px; - background-size: 284px 43px; - } -} - -[hidden] { - display: none !important; -} - -.draggable { - cursor: move; -} - -input[type="password"]::-ms-reveal { - display: none; -} - -.flex { - display: flex; - - &.flex-grow { - > * { - flex: 1; - } - } -} - -// Text selection styles -// Set explicit selection styles (assumes primary accent color has sufficient -// contrast against the background, so its inversion is also still readable) -// and suppress user selection for most elements (to make it more app-like) - -:not(bit-form-field input)::selection { - @include themify($themes) { - color: themed("backgroundColor"); - background-color: themed("primaryAccentColor"); - } -} - -h1, -h2, -h3, -label, -a, -button, -p, -img, -.box-header, -.box-footer, -.callout, -.row-label, -.modal-title, -.overlay-container { - user-select: none; - - &.user-select { - user-select: auto; - } -} - -/* tweak for inconsistent line heights in cipher view */ -.box-footer button, -.box-footer a { - line-height: 1; -} - -// Workaround for slow performance on external monitors on Chrome + MacOS -// See: https://bugs.chromium.org/p/chromium/issues/detail?id=971701#c64 -@keyframes redraw { - 0% { - opacity: 0.99; - } - 100% { - opacity: 1; - } -} -html.force_redraw { - animation: redraw 1s linear infinite; -} - -/* override for vault icon in browser (pre extension refresh) */ -app-vault-icon:not(app-vault-list-items-container app-vault-icon) > div { - display: flex; - justify-content: center; - align-items: center; - float: left; - height: 36px; - width: 34px; - margin-left: -5px; -} diff --git a/apps/browser/src/popup/scss/pages.scss b/apps/browser/src/popup/scss/pages.scss deleted file mode 100644 index 56c5f80c86c..00000000000 --- a/apps/browser/src/popup/scss/pages.scss +++ /dev/null @@ -1,144 +0,0 @@ -@import "variables.scss"; - -app-home { - position: fixed; - height: 100%; - width: 100%; - - .center-content { - margin-top: -50px; - height: calc(100% + 50px); - } - - img { - width: 284px; - margin: 0 auto; - } - - p.lead { - margin: 30px 0; - } - - .btn + .btn { - margin-top: 10px; - } - - button.settings-icon { - position: absolute; - top: 10px; - left: 10px; - - @include themify($themes) { - color: themed("mutedColor"); - } - - &:not(:hover):not(:focus) { - span { - clip: rect(0 0 0 0); - clip-path: inset(50%); - height: 1px; - overflow: hidden; - position: absolute; - white-space: nowrap; - width: 1px; - } - } - - &:hover, - &:focus { - text-decoration: none; - - @include themify($themes) { - color: themed("primaryColor"); - } - } - } -} - -body.body-sm, -body.body-xs { - app-home { - .center-content { - margin-top: 0; - height: 100%; - } - - p.lead { - margin: 15px 0; - } - } -} - -body.body-full { - app-home { - .center-content { - margin-top: -80px; - height: calc(100% + 80px); - } - } -} - -.createAccountLink { - padding: 30px 10px 0 10px; -} - -.remember-email-check { - padding-top: 18px; - padding-left: 10px; - padding-bottom: 18px; -} - -.login-buttons > button { - margin: 15px 0 15px 0; -} - -.useBrowserlink { - margin-left: 5px; - margin-top: 20px; - - span { - font-weight: 700; - font-size: $font-size-small; - } -} - -.fido2-browser-selector-dropdown { - @include themify($themes) { - background-color: themed("boxBackgroundColor"); - } - padding: 8px; - width: 100%; - box-shadow: - 0 2px 2px 0 rgba(0, 0, 0, 0.14), - 0 3px 1px -2px rgba(0, 0, 0, 0.12), - 0 1px 5px 0 rgba(0, 0, 0, 0.2); - border-radius: $border-radius; -} - -.fido2-browser-selector-dropdown-item { - @include themify($themes) { - color: themed("textColor") !important; - } - width: 100%; - text-align: left; - padding: 0px 15px 0px 5px; - margin-bottom: 5px; - border-radius: 3px; - border: 1px solid transparent; - transition: all 0.2s ease-in-out; - - &:hover { - @include themify($themes) { - background-color: themed("listItemBackgroundHoverColor") !important; - } - } - - &:last-child { - margin-bottom: 0; - } -} - -/** Temporary fix for avatar, will not be required once we migrate to tailwind preflight **/ -bit-avatar svg { - display: block; -} diff --git a/apps/browser/src/popup/scss/plugins.scss b/apps/browser/src/popup/scss/plugins.scss deleted file mode 100644 index 591e8a1bd0c..00000000000 --- a/apps/browser/src/popup/scss/plugins.scss +++ /dev/null @@ -1,23 +0,0 @@ -@import "variables.scss"; - -@each $mfaType in $mfaTypes { - .mfaType#{$mfaType} { - content: url("../images/two-factor/" + $mfaType + ".png"); - max-width: 100px; - } -} - -.mfaType1 { - @include themify($themes) { - content: url("../images/two-factor/1" + themed("mfaLogoSuffix")); - max-width: 100px; - max-height: 45px; - } -} - -.mfaType7 { - @include themify($themes) { - content: url("../images/two-factor/7" + themed("mfaLogoSuffix")); - max-width: 100px; - } -} diff --git a/apps/browser/src/popup/scss/popup.scss b/apps/browser/src/popup/scss/popup.scss index b150de2c75d..59b4d472f23 100644 --- a/apps/browser/src/popup/scss/popup.scss +++ b/apps/browser/src/popup/scss/popup.scss @@ -1,13 +1,50 @@ @import "../../../../../libs/angular/src/scss/bwicons/styles/style.scss"; @import "variables.scss"; @import "../../../../../libs/angular/src/scss/icons.scss"; -@import "base.scss"; -@import "grid.scss"; -@import "box.scss"; -@import "buttons.scss"; -@import "misc.scss"; -@import "environment.scss"; -@import "pages.scss"; -@import "plugins.scss"; @import "@angular/cdk/overlay-prebuilt.css"; @import "../../../../../libs/components/src/multi-select/scss/bw.theme"; + +.cdk-virtual-scroll-content-wrapper { + width: 100%; +} + +// MFA Types for logo styling with no dark theme alternative +$mfaTypes: 0, 2, 3, 4, 6; + +@each $mfaType in $mfaTypes { + .mfaType#{$mfaType} { + content: url("../images/two-factor/" + $mfaType + ".png"); + max-width: 100px; + } +} + +.mfaType0 { + content: url("../images/two-factor/0.png"); + max-width: 100px; + max-height: 45px; +} + +.mfaType1 { + max-width: 100px; + max-height: 45px; + + &:is(.theme_light *) { + content: url("../images/two-factor/1.png"); + } + + &:is(.theme_dark *) { + content: url("../images/two-factor/1-w.png"); + } +} + +.mfaType7 { + max-width: 100px; + + &:is(.theme_light *) { + content: url("../images/two-factor/7.png"); + } + + &:is(.theme_dark *) { + content: url("../images/two-factor/7-w.png"); + } +} diff --git a/apps/browser/src/popup/scss/tailwind.css b/apps/browser/src/popup/scss/tailwind.css index 54139990356..0ef7b82bfed 100644 --- a/apps/browser/src/popup/scss/tailwind.css +++ b/apps/browser/src/popup/scss/tailwind.css @@ -1,4 +1,104 @@ -@import "../../../../../libs/components/src/tw-theme.css"; +@import "../../../../../libs/components/src/tw-theme-preflight.css"; + +@layer base { + html { + overflow: hidden; + min-height: 600px; + height: 100%; + + &.body-sm { + min-height: 500px; + } + + &.body-xs { + min-height: 400px; + } + + &.body-xxs { + min-height: 300px; + } + + &.body-3xs { + min-height: 240px; + } + + &.body-full { + min-height: unset; + width: 100%; + height: 100%; + + & body { + width: 100%; + } + } + } + + html.browser_safari { + &.safari_height_fix { + body { + height: 360px !important; + + &.body-xs { + height: 300px !important; + } + + &.body-full { + height: 100% !important; + } + } + } + + app-root { + border-width: 1px; + border-style: solid; + border-color: #000000; + } + + &.theme_light app-root { + border-color: #777777; + } + } + + body { + width: 480px; + height: 100%; + position: relative; + min-height: inherit; + overflow: hidden; + @apply tw-bg-background-alt; + } + + /** + * Workaround for slow performance on external monitors on Chrome + MacOS + * See: https://bugs.chromium.org/p/chromium/issues/detail?id=971701#c64 + */ + @keyframes redraw { + 0% { + opacity: 0.99; + } + 100% { + opacity: 1; + } + } + html.force_redraw { + animation: redraw 1s linear infinite; + } + + /** + * Text selection style: + * suppress user selection for most elements (to make it more app-like) + */ + h1, + h2, + h3, + label, + a, + button, + p, + img { + user-select: none; + } +} @layer components { /** Safari Support */ @@ -19,4 +119,59 @@ html:not(.browser_safari) .tw-styled-scrollbar { scrollbar-color: rgb(var(--color-secondary-500)) rgb(var(--color-background-alt)); } + + #duo-frame { + background: url("../images/loading.svg") 0 0 no-repeat; + width: 100%; + height: 470px; + margin-bottom: -10px; + + iframe { + width: 100%; + height: 100%; + border: none; + } + } + + #web-authn-frame { + width: 100%; + height: 40px; + + iframe { + border: none; + height: 100%; + width: 100%; + } + } + + body.linux-webauthn { + width: 485px !important; + #web-authn-frame { + iframe { + width: 375px; + margin: 0 55px; + } + } + } + + app-root > #loading { + display: flex; + text-align: center; + justify-content: center; + align-items: center; + height: 100%; + width: 100%; + + @apply tw-text-muted; + } + + /** + * Text selection style: + * Set explicit selection styles (assumes primary accent color has sufficient + * contrast against the background, so its inversion is also still readable) + */ + :not(bit-form-field input)::selection { + @apply tw-text-contrast; + @apply tw-bg-primary-700; + } } diff --git a/apps/browser/src/popup/scss/variables.scss b/apps/browser/src/popup/scss/variables.scss index e57e98fd0cc..02a10521bca 100644 --- a/apps/browser/src/popup/scss/variables.scss +++ b/apps/browser/src/popup/scss/variables.scss @@ -1,178 +1,42 @@ -īģŋ$dark-icon-themes: "theme_dark"; +īģŋ/** + * DEPRECATED: DO NOT MODIFY OR USE! + */ + +$dark-icon-themes: "theme_dark"; $font-family-sans-serif: Inter, "Helvetica Neue", Helvetica, Arial, sans-serif; $font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace; -$font-size-base: 16px; -$font-size-large: 18px; -$font-size-xlarge: 22px; -$font-size-xxlarge: 28px; -$font-size-small: 12px; $text-color: #000000; -$border-color: #f0f0f0; $border-color-dark: #ddd; -$list-item-hover: #fbfbfb; -$list-icon-color: #767679; -$disabled-box-opacity: 1; -$border-radius: 6px; -$line-height-base: 1.42857143; -$icon-hover-color: lighten($text-color, 50%); - -$mfaTypes: 0, 2, 3, 4, 6; - -$gray: #555; -$gray-light: #777; -$text-muted: $gray-light; - $brand-primary: #175ddc; -$brand-danger: #c83522; $brand-success: #017e45; -$brand-info: #555555; -$brand-warning: #8b6609; -$brand-primary-accent: #1252a3; - $background-color: #f0f0f0; - -$box-background-color: white; -$box-background-hover-color: $list-item-hover; -$box-border-color: $border-color; -$border-color-alt: #c3c5c7; - -$button-border-color: darken($border-color-dark, 12%); -$button-background-color: white; -$button-color: lighten($text-color, 40%); $button-color-primary: darken($brand-primary, 8%); -$button-color-danger: darken($brand-danger, 10%); - -$code-color: #c01176; -$code-color-dark: #f08dc7; $themes: ( light: ( textColor: $text-color, - hoverColorTransparent: rgba($text-color, 0.15), borderColor: $border-color-dark, backgroundColor: $background-color, - borderColorAlt: $border-color-alt, - backgroundColorAlt: #ffffff, - scrollbarColor: rgba(100, 100, 100, 0.2), - scrollbarHoverColor: rgba(100, 100, 100, 0.4), - boxBackgroundColor: $box-background-color, - boxBackgroundHoverColor: $box-background-hover-color, - boxBorderColor: $box-border-color, - tabBackgroundColor: #ffffff, - tabBackgroundHoverColor: $list-item-hover, - headerColor: #ffffff, - headerBackgroundColor: $brand-primary, - headerBackgroundHoverColor: rgba(255, 255, 255, 0.1), - headerBorderColor: $brand-primary, - headerInputBackgroundColor: darken($brand-primary, 8%), - headerInputBackgroundFocusColor: darken($brand-primary, 10%), - headerInputColor: #ffffff, - headerInputPlaceholderColor: lighten($brand-primary, 35%), - listItemBackgroundHoverColor: $list-item-hover, - disabledIconColor: $list-icon-color, - disabledBoxOpacity: $disabled-box-opacity, - headingColor: $gray-light, - labelColor: $gray-light, - mutedColor: $text-muted, - totpStrokeColor: $brand-primary, - boxRowButtonColor: $brand-primary, - boxRowButtonHoverColor: darken($brand-primary, 10%), inputBorderColor: darken($border-color-dark, 7%), inputBackgroundColor: #ffffff, - inputPlaceholderColor: lighten($gray-light, 35%), - buttonBackgroundColor: $button-background-color, - buttonBorderColor: $button-border-color, - buttonColor: $button-color, buttonPrimaryColor: $button-color-primary, - buttonDangerColor: $button-color-danger, primaryColor: $brand-primary, - primaryAccentColor: $brand-primary-accent, - dangerColor: $brand-danger, successColor: $brand-success, - infoColor: $brand-info, - warningColor: $brand-warning, - logoSuffix: "dark", - mfaLogoSuffix: ".png", passwordNumberColor: #007fde, passwordSpecialColor: #c40800, - passwordCountText: #212529, - calloutBorderColor: $border-color-dark, - calloutBackgroundColor: $box-background-color, - toastTextColor: #ffffff, - svgSuffix: "-light.svg", - transparentColor: rgba(0, 0, 0, 0), - dateInputColorScheme: light, - // https://stackoverflow.com/a/53336754 - webkitCalendarPickerFilter: invert(46%) sepia(69%) saturate(6397%) hue-rotate(211deg) - brightness(85%) contrast(103%), - // light has no hover so use same color - webkitCalendarPickerHoverFilter: invert(46%) sepia(69%) saturate(6397%) hue-rotate(211deg) - brightness(85%) contrast(103%), - codeColor: $code-color, ), dark: ( textColor: #ffffff, - hoverColorTransparent: rgba($text-color, 0.15), borderColor: #161c26, backgroundColor: #161c26, - borderColorAlt: #6e788a, - backgroundColorAlt: #2f343d, - scrollbarColor: #6e788a, - scrollbarHoverColor: #8d94a5, - boxBackgroundColor: #2f343d, - boxBackgroundHoverColor: #3c424e, - boxBorderColor: #4c525f, - tabBackgroundColor: #2f343d, - tabBackgroundHoverColor: #3c424e, - headerColor: #ffffff, - headerBackgroundColor: #2f343d, - headerBackgroundHoverColor: #3c424e, - headerBorderColor: #161c26, - headerInputBackgroundColor: #3c424e, - headerInputBackgroundFocusColor: #4c525f, - headerInputColor: #ffffff, - headerInputPlaceholderColor: #bac0ce, - listItemBackgroundHoverColor: #3c424e, - disabledIconColor: #bac0ce, - disabledBoxOpacity: 0.5, - headingColor: #bac0ce, - labelColor: #bac0ce, - mutedColor: #bac0ce, - totpStrokeColor: #4c525f, - boxRowButtonColor: #bac0ce, - boxRowButtonHoverColor: #ffffff, inputBorderColor: #4c525f, inputBackgroundColor: #2f343d, - inputPlaceholderColor: #bac0ce, - buttonBackgroundColor: #3c424e, - buttonBorderColor: #4c525f, - buttonColor: #bac0ce, buttonPrimaryColor: #6f9df1, - buttonDangerColor: #ff8d85, primaryColor: #6f9df1, - primaryAccentColor: #6f9df1, - dangerColor: #ff8d85, successColor: #52e07c, - infoColor: #a4b0c6, - warningColor: #ffeb66, - logoSuffix: "white", - mfaLogoSuffix: "-w.png", passwordNumberColor: #6f9df1, passwordSpecialColor: #ff8d85, - passwordCountText: #ffffff, - calloutBorderColor: #4c525f, - calloutBackgroundColor: #3c424e, - toastTextColor: #1f242e, - svgSuffix: "-dark.svg", - transparentColor: rgba(0, 0, 0, 0), - dateInputColorScheme: dark, - // https://stackoverflow.com/a/53336754 - must prepend brightness(0) saturate(100%) to dark themed date inputs - webkitCalendarPickerFilter: brightness(0) saturate(100%) invert(86%) sepia(19%) saturate(152%) - hue-rotate(184deg) brightness(87%) contrast(93%), - webkitCalendarPickerHoverFilter: brightness(0) saturate(100%) invert(100%) sepia(0%) - saturate(0%) hue-rotate(93deg) brightness(103%) contrast(103%), - codeColor: $code-color-dark, ), ); diff --git a/apps/browser/src/popup/services/init.service.ts b/apps/browser/src/popup/services/init.service.ts index 91b6f0ff105..24ff637c29b 100644 --- a/apps/browser/src/popup/services/init.service.ts +++ b/apps/browser/src/popup/services/init.service.ts @@ -1,10 +1,7 @@ -import { DOCUMENT } from "@angular/common"; -import { inject, Inject, Injectable } from "@angular/core"; +import { inject, Inject, Injectable, DOCUMENT } from "@angular/core"; import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction"; import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; -import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; @@ -31,8 +28,6 @@ export class InitService { private sdkLoadService: SdkLoadService, private viewCacheService: PopupViewCacheService, private readonly migrationRunner: MigrationRunner, - private configService: ConfigService, - private encryptService: EncryptService, @Inject(DOCUMENT) private document: Document, ) {} @@ -44,7 +39,6 @@ export class InitService { this.twoFactorService.init(); await this.viewCacheService.init(); await this.sizeService.init(); - this.encryptService.init(this.configService); const htmlEl = window.document.documentElement; this.themingService.applyThemeChangesTo(this.document); diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index c462319dc2e..a8bfb23d83f 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -3,7 +3,11 @@ import { APP_INITIALIZER, NgModule, NgZone } from "@angular/core"; import { merge, of, Subject } from "rxjs"; -import { CollectionService } from "@bitwarden/admin-console/common"; +import { + CollectionService, + OrganizationUserApiService, + OrganizationUserService, +} from "@bitwarden/admin-console/common"; import { DeviceManagementComponentServiceAbstraction } from "@bitwarden/angular/auth/device-management/device-management-component.service.abstraction"; import { ChangePasswordService } from "@bitwarden/angular/auth/password-management/change-password"; import { AngularThemingService } from "@bitwarden/angular/platform/services/theming/angular-theming.service"; @@ -23,6 +27,12 @@ import { WINDOW, } from "@bitwarden/angular/services/injection-tokens"; import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module"; +import { + AUTOFILL_NUDGE_SERVICE, + AUTO_CONFIRM_NUDGE_SERVICE, + AutoConfirmNudgeService, +} from "@bitwarden/angular/vault"; +import { VaultProfileService } from "@bitwarden/angular/vault/services/vault-profile.service"; import { LoginComponentService, TwoFactorAuthComponentService, @@ -38,21 +48,31 @@ import { LogoutService, UserDecryptionOptionsServiceAbstraction, } from "@bitwarden/auth/common"; +import { + AutomaticUserConfirmationService, + DefaultAutomaticUserConfirmationService, +} from "@bitwarden/auto-confirm"; +import { ExtensionAuthRequestAnsweringService } from "@bitwarden/browser/auth/services/auth-request-answering/extension-auth-request-answering.service"; import { ExtensionNewDeviceVerificationComponentService } from "@bitwarden/browser/auth/services/new-device-verification/extension-new-device-verification-component.service"; +import { BrowserRouterService } from "@bitwarden/browser/platform/popup/services/browser-router.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { + InternalOrganizationServiceAbstraction, + OrganizationService, +} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService, AccountService as AccountServiceAbstraction, } from "@bitwarden/common/auth/abstractions/account.service"; -import { AuthRequestAnsweringServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction"; +import { AuthRequestAnsweringService } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; -import { AuthRequestAnsweringService } from "@bitwarden/common/auth/services/auth-request-answering/auth-request-answering.service"; +import { WebAuthnLoginPrfKeyServiceAbstraction } from "@bitwarden/common/auth/abstractions/webauthn/webauthn-login-prf-key.service.abstraction"; import { PendingAuthRequestsStateService } from "@bitwarden/common/auth/services/auth-request-answering/pending-auth-requests.state"; import { AutofillSettingsService, @@ -67,6 +87,8 @@ import { UserNotificationSettingsServiceAbstraction, } from "@bitwarden/common/autofill/services/user-notification-settings.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +import { PhishingDetectionSettingsServiceAbstraction } from "@bitwarden/common/dirt/services/abstractions/phishing-detection-settings.service.abstraction"; +import { PhishingDetectionSettingsService } from "@bitwarden/common/dirt/services/phishing-detection/phishing-detection-settings.service"; import { ClientType } from "@bitwarden/common/enums"; import { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; @@ -76,6 +98,8 @@ import { InternalMasterPasswordServiceAbstraction, MasterPasswordServiceAbstraction, } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; +import { SessionTimeoutTypeService } from "@bitwarden/common/key-management/session-timeout"; import { VaultTimeoutService, VaultTimeoutStringType, @@ -135,15 +159,19 @@ import { DialogService, ToastService, } from "@bitwarden/components"; +import { GeneratorServicesModule } from "@bitwarden/generator-components"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { BiometricsService, + BiometricStateService, DefaultKeyService, KdfConfigService, KeyService, } from "@bitwarden/key-management"; import { LockComponentService, + WebAuthnPrfUnlockService, + DefaultWebAuthnPrfUnlockService, SessionTimeoutSettingsComponentService, } from "@bitwarden/key-management-ui"; import { DerivedStateProvider, GlobalStateProvider, StateProvider } from "@bitwarden/state"; @@ -170,6 +198,7 @@ import { InlineMenuFieldQualificationService } from "../../autofill/services/inl import { ForegroundBrowserBiometricsService } from "../../key-management/biometrics/foreground-browser-biometrics"; import { ExtensionLockComponentService } from "../../key-management/lock/services/extension-lock-component.service"; import { BrowserSessionTimeoutSettingsComponentService } from "../../key-management/session-timeout/services/browser-session-timeout-settings-component.service"; +import { BrowserSessionTimeoutTypeService } from "../../key-management/session-timeout/services/browser-session-timeout-type.service"; import { ForegroundVaultTimeoutService } from "../../key-management/vault-timeout/foreground-vault-timeout.service"; import { BrowserActionsService } from "../../platform/actions/browser-actions.service"; import { BrowserApi } from "../../platform/browser/browser-api"; @@ -202,6 +231,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"; @@ -485,18 +515,19 @@ const safeProviders: SafeProvider[] = [ deps: [], }), safeProvider({ - provide: AuthRequestAnsweringServiceAbstraction, - useClass: AuthRequestAnsweringService, + provide: AuthRequestAnsweringService, + useClass: ExtensionAuthRequestAnsweringService, deps: [ AccountServiceAbstraction, - ActionsService, AuthService, - I18nServiceAbstraction, MasterPasswordServiceAbstraction, MessagingService, PendingAuthRequestsStateService, + ActionsService, + I18nServiceAbstraction, PlatformUtilsService, SystemNotificationsService, + LogService, ], }), safeProvider({ @@ -509,6 +540,19 @@ const safeProviders: SafeProvider[] = [ useClass: UserNotificationSettingsService, deps: [StateProvider], }), + safeProvider({ + provide: PhishingDetectionSettingsServiceAbstraction, + useClass: PhishingDetectionSettingsService, + deps: [ + AccountService, + BillingAccountProfileStateService, + ConfigService, + LogService, + OrganizationService, + PlatformUtilsService, + StateProvider, + ], + }), safeProvider({ provide: MessageListener, useFactory: (subject: Subject>>, ngZone: NgZone) => @@ -534,15 +578,6 @@ const safeProviders: SafeProvider[] = [ useFactory: () => new Subject>>(), deps: [], }), - safeProvider({ - provide: MessageSender, - useFactory: (subject: Subject>>, logService: LogService) => - MessageSender.combine( - new SubjectMessageSender(subject), // For sending messages in the same context - new ChromeMessageSender(logService), // For sending messages to different contexts - ), - deps: [INTRAPROCESS_MESSAGING_SUBJECT, LogService], - }), safeProvider({ provide: DISK_BACKUP_LOCAL_STORAGE, useFactory: (diskStorage: AbstractStorageService & ObservableStorageService) => @@ -566,7 +601,14 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: LockComponentService, useClass: ExtensionLockComponentService, - deps: [], + deps: [ + UserDecryptionOptionsServiceAbstraction, + BiometricsService, + PinServiceAbstraction, + BiometricStateService, + BrowserRouterService, + WebAuthnPrfUnlockService, + ], }), // TODO: PM-18182 - Refactor component services into lazy loaded modules safeProvider({ @@ -615,6 +657,21 @@ const safeProviders: SafeProvider[] = [ AccountServiceAbstraction, ], }), + safeProvider({ + provide: WebAuthnPrfUnlockService, + useClass: DefaultWebAuthnPrfUnlockService, + deps: [ + WebAuthnLoginPrfKeyServiceAbstraction, + KeyService, + UserDecryptionOptionsServiceAbstraction, + EncryptService, + EnvironmentService, + PlatformUtilsService, + WINDOW, + LogService, + ConfigService, + ], + }), safeProvider({ provide: AnimationControlService, useClass: DefaultAnimationControlService, @@ -723,15 +780,48 @@ const safeProviders: SafeProvider[] = [ useClass: ExtensionNewDeviceVerificationComponentService, deps: [], }), + safeProvider({ + provide: AutomaticUserConfirmationService, + useClass: DefaultAutomaticUserConfirmationService, + deps: [ + ConfigService, + ApiService, + OrganizationUserService, + StateProvider, + InternalOrganizationServiceAbstraction, + OrganizationUserApiService, + PolicyService, + ], + }), + safeProvider({ + provide: SessionTimeoutTypeService, + useClass: BrowserSessionTimeoutTypeService, + deps: [PlatformUtilsService], + }), safeProvider({ provide: SessionTimeoutSettingsComponentService, useClass: BrowserSessionTimeoutSettingsComponentService, - deps: [I18nServiceAbstraction, PlatformUtilsService, MessagingServiceAbstraction], + deps: [ + I18nServiceAbstraction, + SessionTimeoutTypeService, + PolicyService, + MessagingServiceAbstraction, + ], + }), + safeProvider({ + provide: AUTOFILL_NUDGE_SERVICE as SafeInjectionToken, + useClass: BrowserAutofillNudgeService, + deps: [StateProvider, VaultProfileService, LogService], + }), + safeProvider({ + provide: AUTO_CONFIRM_NUDGE_SERVICE as SafeInjectionToken, + useClass: AutoConfirmNudgeService, + deps: [StateProvider, AutomaticUserConfirmationService], }), ]; @NgModule({ - imports: [JslibServicesModule], + imports: [JslibServicesModule, GeneratorServicesModule], declarations: [], // Do not register your dependency here! Add it to the typesafeProviders array using the helper function providers: safeProviders, 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/add-edit/send-add-edit.component.ts b/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.ts index 8f30d00cc31..f180564b912 100644 --- a/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.ts +++ b/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.ts @@ -9,9 +9,9 @@ import { map, switchMap } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; +import { SendType } from "@bitwarden/common/tools/send/types/send-type"; import { SendId } from "@bitwarden/common/types/guid"; import { AsyncActionsModule, 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/send-v2/send-created/send-created.component.spec.ts b/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.spec.ts index 1a3df238543..521d72bba0c 100644 --- a/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.spec.ts +++ b/apps/browser/src/tools/popup/send-v2/send-created/send-created.component.spec.ts @@ -11,9 +11,9 @@ import { EnvironmentService } from "@bitwarden/common/platform/abstractions/envi import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SelfHostedEnvironment } from "@bitwarden/common/platform/services/default-environment.service"; -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { SendType } from "@bitwarden/common/tools/send/types/send-type"; import { ButtonModule, I18nMockService, IconModule, ToastService } from "@bitwarden/components"; import { PopOutComponent } from "../../../../platform/popup/components/pop-out.component"; diff --git a/apps/browser/src/tools/popup/send-v2/send-file-popout-dialog/send-file-popout-dialog-container.component.ts b/apps/browser/src/tools/popup/send-v2/send-file-popout-dialog/send-file-popout-dialog-container.component.ts index 1f0d9f2a0c9..ddf50eb39bf 100644 --- a/apps/browser/src/tools/popup/send-v2/send-file-popout-dialog/send-file-popout-dialog-container.component.ts +++ b/apps/browser/src/tools/popup/send-v2/send-file-popout-dialog/send-file-popout-dialog-container.component.ts @@ -2,7 +2,7 @@ import { CommonModule } from "@angular/common"; import { Component, input, OnInit } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; +import { SendType } from "@bitwarden/common/tools/send/types/send-type"; import { CenterPositionStrategy, DialogService } from "@bitwarden/components"; import { SendFormConfig } from "@bitwarden/send-ui"; diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.html b/apps/browser/src/tools/popup/send-v2/send-v2.component.html index 47ecd7564dc..48295fda35d 100644 --- a/apps/browser/src/tools/popup/send-v2/send-v2.component.html +++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.html @@ -1,4 +1,4 @@ - + diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts b/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts index 6d79f430a37..dc4b935c6c8 100644 --- a/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts +++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.spec.ts @@ -11,15 +11,15 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; +import { mockAccountInfoWith } from "@bitwarden/common/spec"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { SendType } from "@bitwarden/common/tools/send/types/send-type"; import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { ButtonModule, NoItemsModule } from "@bitwarden/components"; import { @@ -96,9 +96,10 @@ describe("SendV2Component", () => { useValue: { activeAccount$: of({ id: "123", - email: "test@email.com", - emailVerified: true, - name: "Test User", + ...mockAccountInfoWith({ + email: "test@email.com", + name: "Test User", + }), }), }, }, @@ -108,7 +109,6 @@ describe("SendV2Component", () => { provide: BillingAccountProfileStateService, useValue: { hasPremiumFromAnySource$: of(false) }, }, - { provide: ConfigService, useValue: mock() }, { provide: EnvironmentService, useValue: mock() }, { provide: LogService, useValue: mock() }, { provide: PlatformUtilsService, useValue: mock() }, diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts index 89769bdd1ce..8c1edee79dc 100644 --- a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts +++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts @@ -11,9 +11,7 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; +import { SendType } from "@bitwarden/common/tools/send/types/send-type"; import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { skeletonLoadingDelay } from "@bitwarden/common/vault/utils/skeleton-loading.operator"; @@ -84,30 +82,17 @@ export class SendV2Component implements OnDestroy { protected listState: SendState | null = null; protected sends$ = this.sendItemsService.filteredAndSortedSends$; - private skeletonFeatureFlag$ = this.configService.getFeatureFlag$( - FeatureFlag.VaultLoadingSkeletons, - ); protected sendsLoading$ = this.sendItemsService.loading$.pipe( distinctUntilChanged(), shareReplay({ bufferSize: 1, refCount: true }), ); - /** Spinner Loading State */ - protected showSpinnerLoaders$ = combineLatest([ - this.sendsLoading$, - this.skeletonFeatureFlag$, - ]).pipe(map(([loading, skeletonsEnabled]) => loading && !skeletonsEnabled)); - /** Skeleton Loading State */ protected showSkeletonsLoaders$ = combineLatest([ this.sendsLoading$, this.searchService.isSendSearching$, - this.skeletonFeatureFlag$, ]).pipe( - map( - ([loading, cipherSearching, skeletonsEnabled]) => - (loading || cipherSearching) && skeletonsEnabled, - ), + map(([loading, cipherSearching]) => loading || cipherSearching), distinctUntilChanged(), skeletonLoadingDelay(), ); @@ -128,7 +113,6 @@ export class SendV2Component implements OnDestroy { protected sendListFiltersService: SendListFiltersService, private policyService: PolicyService, private accountService: AccountService, - private configService: ConfigService, private searchService: SearchService, ) { combineLatest([ @@ -139,7 +123,7 @@ export class SendV2Component implements OnDestroy { .pipe(takeUntilDestroyed()) .subscribe(([emptyList, noFilteredResults, currentFilter]) => { if (currentFilter?.sendType !== null) { - this.title = this.sendTypeTitles[currentFilter.sendType] ?? "allSends"; + this.title = this.sendTypeTitles[currentFilter.sendType as SendType] ?? "allSends"; } else { this.title = "allSends"; } 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 d6bf3a3a253..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" > - {{ "exportVault" | 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..c6f1c9dbc3b 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 + }
    @@ -84,6 +82,24 @@ + + @if (showAdminSettingsLink$ | async) { + + + +
    +

    {{ "admin" | i18n }}

    + @if (showAdminBadge$ | 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..a05fa45753e 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 @@ -6,6 +6,7 @@ import { BehaviorSubject, firstValueFrom, of, Subject } from "rxjs"; import { PremiumUpgradeDialogComponent } from "@bitwarden/angular/billing/components"; import { NudgesService, NudgeType } from "@bitwarden/angular/vault"; +import { AutomaticUserConfirmationService } from "@bitwarden/auto-confirm"; import { AutofillBrowserSettingsService } from "@bitwarden/browser/autofill/services/autofill-browser-settings.service"; import { BrowserApi } from "@bitwarden/browser/platform/browser/browser-api"; import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -42,6 +43,9 @@ describe("SettingsV2Component", () => { defaultBrowserAutofillDisabled$: Subject; isBrowserAutofillSettingOverridden: jest.Mock>; }; + let mockAutoConfirmService: { + canManageAutoConfirm$: jest.Mock; + }; let dialogService: MockProxy; let openSpy: jest.SpyInstance; @@ -66,6 +70,10 @@ describe("SettingsV2Component", () => { isBrowserAutofillSettingOverridden: jest.fn().mockResolvedValue(false), }; + mockAutoConfirmService = { + canManageAutoConfirm$: jest.fn().mockReturnValue(of(false)), + }; + jest.spyOn(BrowserApi, "getBrowserClientVendor").mockReturnValue("Chrome"); const cfg = TestBed.configureTestingModule({ @@ -75,6 +83,7 @@ describe("SettingsV2Component", () => { { provide: BillingAccountProfileStateService, useValue: mockBillingState }, { provide: NudgesService, useValue: mockNudges }, { provide: AutofillBrowserSettingsService, useValue: mockAutofillSettings }, + { provide: AutomaticUserConfirmationService, useValue: mockAutoConfirmService }, { provide: DialogService, useValue: dialogService }, { provide: I18nService, useValue: { t: jest.fn((key: string) => key) } }, { provide: GlobalStateProvider, useValue: new FakeGlobalStateProvider() }, @@ -148,31 +157,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 +169,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..2c9f893c99c 100644 --- a/apps/browser/src/tools/popup/settings/settings-v2.component.ts +++ b/apps/browser/src/tools/popup/settings/settings-v2.component.ts @@ -1,22 +1,15 @@ 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"; import { NudgesService, NudgeType } from "@bitwarden/angular/vault"; import { SpotlightComponent } from "@bitwarden/angular/vault/components/spotlight/spotlight.component"; +import { AutomaticUserConfirmationService } from "@bitwarden/auto-confirm"; import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; import { UserId } from "@bitwarden/common/types/guid"; import { @@ -28,8 +21,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 +46,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 +67,25 @@ 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; - }), - ), + showAdminBadge$: Observable = this.authenticatedAccount$.pipe( + switchMap((account) => + this.nudgesService.showNudgeBadge$(NudgeType.AutoConfirmNudge, account.id), ), ); + showAutofillBadge$: Observable = this.authenticatedAccount$.pipe( + switchMap((account) => this.nudgesService.showNudgeBadge$(NudgeType.AutofillNudge, account.id)), + ); + + showAdminSettingsLink$: Observable = this.accountService.activeAccount$.pipe( + getUserId, + switchMap((userId) => this.autoConfirmService.canManageAutoConfirm$(userId)), + ); + constructor( private readonly nudgesService: NudgesService, private readonly accountService: AccountService, - private readonly autofillBrowserSettingsService: AutofillBrowserSettingsService, + private readonly autoConfirmService: AutomaticUserConfirmationService, private readonly accountProfileStateService: BillingAccountProfileStateService, private readonly dialogService: DialogService, ) {} diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.html index 8f184c6a0c1..8b4d2d21b8b 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.html @@ -1,3 +1,5 @@ +@let previouslyCouldArchive = !(userCanArchive$ | async) && config?.originalCipher?.archivedDate; + + @if (config?.originalCipher?.archivedDate) { + + + {{ "archived" | i18n }} + + + } @@ -24,21 +33,41 @@ - + + @if (isEditMode) { + @if ((archiveFlagEnabled$ | async) && isCipherArchived) { + + } + @if ((userCanArchive$ | async) && canCipherBeArchived) { + + } + } + @if (canDeleteCipher$ | async) { + + } + diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts index 1bffcd9ad51..8ea23e7e2b9 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts @@ -1,10 +1,15 @@ +import { Location } from "@angular/common"; import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; +import { provideNoopAnimations } from "@angular/platform-browser/animations"; import { ActivatedRoute, Router } from "@angular/router"; import { mock, MockProxy } from "jest-mock-extended"; -import { BehaviorSubject } from "rxjs"; +import { BehaviorSubject, of } from "rxjs"; +import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; import { EventType } from "@bitwarden/common/enums"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -12,13 +17,17 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { mockAccountServiceWith } from "@bitwarden/common/spec"; import { UserId } from "@bitwarden/common/types/guid"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; +import { TaskService } from "@bitwarden/common/vault/tasks"; import { AddEditCipherInfo } from "@bitwarden/common/vault/types/add-edit-cipher-info"; +import { DialogService } from "@bitwarden/components"; import { + ArchiveCipherUtilitiesService, CipherFormConfig, CipherFormConfigService, CipherFormMode, @@ -45,15 +54,17 @@ describe("AddEditV2Component", () => { let cipherServiceMock: MockProxy; const buildConfigResponse = { originalCipher: {} } as CipherFormConfig; - const buildConfig = jest.fn((mode: CipherFormMode) => - Promise.resolve({ ...buildConfigResponse, mode }), - ); + const buildConfig = jest.fn((mode) => Promise.resolve({ ...buildConfigResponse, mode })); const queryParams$ = new BehaviorSubject({}); const disable = jest.fn(); const navigate = jest.fn(); const back = jest.fn().mockResolvedValue(null); const setHistory = jest.fn(); const collect = jest.fn().mockResolvedValue(null); + const history$ = jest.fn(); + const historyGo = jest.fn().mockResolvedValue(null); + const openSimpleDialog = jest.fn().mockResolvedValue(true); + const cipherArchiveService = mock(); beforeEach(async () => { buildConfig.mockClear(); @@ -61,6 +72,12 @@ describe("AddEditV2Component", () => { navigate.mockClear(); back.mockClear(); collect.mockClear(); + history$.mockClear(); + historyGo.mockClear(); + openSimpleDialog.mockClear(); + + cipherArchiveService.hasArchiveFlagEnabled$ = of(true); + cipherArchiveService.userCanArchive$.mockReturnValue(of(false)); addEditCipherInfo$ = new BehaviorSubject(null); cipherServiceMock = mock({ @@ -70,11 +87,13 @@ describe("AddEditV2Component", () => { await TestBed.configureTestingModule({ imports: [AddEditV2Component], providers: [ + provideNoopAnimations(), { provide: PlatformUtilsService, useValue: mock() }, { provide: ConfigService, useValue: mock() }, - { provide: PopupRouterCacheService, useValue: { back, setHistory } }, + { provide: PopupRouterCacheService, useValue: { back, setHistory, history$ } }, { provide: PopupCloseWarningService, useValue: { disable } }, { provide: Router, useValue: { navigate } }, + { provide: Location, useValue: { historyGo } }, { provide: ActivatedRoute, useValue: { queryParams: queryParams$ } }, { provide: I18nService, useValue: { t: (key: string) => key } }, { provide: CipherService, useValue: cipherServiceMock }, @@ -83,10 +102,33 @@ describe("AddEditV2Component", () => { { provide: CipherAuthorizationService, useValue: { - canDeleteCipher$: jest.fn().mockReturnValue(true), + canDeleteCipher$: jest.fn().mockReturnValue(of(true)), }, }, { provide: AccountService, useValue: mockAccountServiceWith("UserId" as UserId) }, + { + provide: TaskService, + useValue: mock(), + }, + { + provide: ViewCacheService, + useValue: { signal: jest.fn(() => (): any => null) }, + }, + { + provide: BillingAccountProfileStateService, + useValue: mock(), + }, + { + provide: CipherArchiveService, + useValue: cipherArchiveService, + }, + { + provide: ArchiveCipherUtilitiesService, + useValue: { + archiveCipher: jest.fn().mockResolvedValue(null), + unarchiveCipher: jest.fn().mockResolvedValue(null), + }, + }, ], }) .overrideProvider(CipherFormConfigService, { @@ -94,6 +136,11 @@ describe("AddEditV2Component", () => { buildConfig, }, }) + .overrideProvider(DialogService, { + useValue: { + openSimpleDialog, + }, + }) .compileComponents(); fixture = TestBed.createComponent(AddEditV2Component); @@ -356,6 +403,151 @@ describe("AddEditV2Component", () => { }); }); + describe("submit button text", () => { + beforeEach(() => { + // prevent form from rendering + jest.spyOn(component as any, "loading", "get").mockReturnValue(true); + }); + + it("sets it to 'save' by default", fakeAsync(() => { + buildConfigResponse.originalCipher = {} as Cipher; + + queryParams$.next({}); + + tick(); + + const submitBtn = fixture.debugElement.query(By.css("button[type=submit]")); + expect(submitBtn.nativeElement.textContent.trim()).toBe("save"); + })); + + it("sets it to 'save' when the user is able to archive the item", fakeAsync(() => { + buildConfigResponse.originalCipher = { isArchived: false } as any; + + queryParams$.next({}); + + tick(); + + const submitBtn = fixture.debugElement.query(By.css("button[type=submit]")); + expect(submitBtn.nativeElement.textContent.trim()).toBe("save"); + })); + + it("sets it to 'unarchiveAndSave' when the user cannot archive and the item is archived", fakeAsync(() => { + cipherArchiveService.userCanArchive$.mockReturnValue(of(false)); + buildConfigResponse.originalCipher = { isArchived: true } as any; + + queryParams$.next({}); + tick(); + + const submitBtn = fixture.debugElement.query(By.css("button[type=submit]")); + expect(submitBtn.nativeElement.textContent.trim()).toBe("save"); + })); + }); + + describe("archive", () => { + it("calls archiveCipherUtilsService service to archive the cipher", async () => { + buildConfigResponse.originalCipher = { id: "222-333-444-5555", edit: true } as Cipher; + queryParams$.next({ cipherId: "222-333-444-5555" }); + + await fixture.whenStable(); + await component.archive(); + + expect(component["archiveCipherUtilsService"].archiveCipher).toHaveBeenCalledWith( + expect.objectContaining({ id: "222-333-444-5555" }), + true, + ); + }); + }); + + describe("unarchive", () => { + it("calls archiveCipherUtilsService service to unarchive the cipher", async () => { + buildConfigResponse.originalCipher = { + id: "222-333-444-5555", + archivedDate: new Date(), + edit: true, + } as Cipher; + queryParams$.next({ cipherId: "222-333-444-5555" }); + + await component.unarchive(); + + expect(component["archiveCipherUtilsService"].unarchiveCipher).toHaveBeenCalledWith( + expect.objectContaining({ id: "222-333-444-5555" }), + ); + }); + }); + + describe("archive button", () => { + beforeEach(() => { + // prevent form from rendering + jest.spyOn(component as any, "loading", "get").mockReturnValue(true); + buildConfigResponse.originalCipher = { archivedDate: undefined, edit: true } as Cipher; + }); + + it("shows the archive button when the user can archive and the cipher can be archived", fakeAsync(() => { + cipherArchiveService.userCanArchive$.mockReturnValue(of(true)); + queryParams$.next({ cipherId: "222-333-444-5555" }); + tick(); + fixture.detectChanges(); + + const archiveBtn = fixture.debugElement.query(By.css("button[biticonbutton='bwi-archive']")); + expect(archiveBtn).toBeTruthy(); + })); + + it("does not show the archive button when the user cannot archive", fakeAsync(() => { + cipherArchiveService.userCanArchive$.mockReturnValue(of(false)); + queryParams$.next({ cipherId: "222-333-444-5555" }); + + tick(); + fixture.detectChanges(); + + const archiveBtn = fixture.debugElement.query(By.css("button[biticonbutton='bwi-archive']")); + expect(archiveBtn).toBeFalsy(); + })); + + it("does not show the archive button when the cipher cannot be archived", fakeAsync(() => { + cipherArchiveService.userCanArchive$.mockReturnValue(of(true)); + buildConfigResponse.originalCipher = { archivedDate: new Date(), edit: true } as Cipher; + queryParams$.next({ cipherId: "222-333-444-5555" }); + + tick(); + fixture.detectChanges(); + + const archiveBtn = fixture.debugElement.query(By.css("button[biticonbutton='bwi-archive']")); + expect(archiveBtn).toBeFalsy(); + })); + }); + + describe("unarchive button", () => { + beforeEach(() => { + // prevent form from rendering + jest.spyOn(component as any, "loading", "get").mockReturnValue(true); + buildConfigResponse.originalCipher = { edit: true } as Cipher; + }); + + it("shows the unarchive button when the cipher is archived", fakeAsync(() => { + buildConfigResponse.originalCipher = { archivedDate: new Date(), edit: true } as Cipher; + + tick(); + fixture.detectChanges(); + + const unarchiveBtn = fixture.debugElement.query( + By.css("button[biticonbutton='bwi-unarchive']"), + ); + expect(unarchiveBtn).toBeTruthy(); + })); + + it("does not show the unarchive button when the cipher is not archived", fakeAsync(() => { + queryParams$.next({ cipherId: "222-333-444-5555" }); + + tick(); + fixture.detectChanges(); + + const unarchiveBtn = fixture.debugElement.query( + By.css("button[biticonbutton='bwi-unarchive']"), + ); + expect(unarchiveBtn).toBeFalsy(); + })); + }); + describe("delete", () => { it("dialogService openSimpleDialog called when deleteBtn is hit", async () => { const dialogSpy = jest @@ -374,11 +566,187 @@ describe("AddEditV2Component", () => { expect(deleteCipherSpy).toHaveBeenCalled(); }); - it("navigates to vault tab after deletion", async () => { + it("navigates to vault tab after deletion by default", async () => { jest.spyOn(component["dialogService"], "openSimpleDialog").mockResolvedValue(true); await component.delete(); expect(navigate).toHaveBeenCalledWith(["/tabs/vault"]); }); + + it("navigates to custom route when not in history", fakeAsync(() => { + buildConfigResponse.originalCipher = { edit: true, id: "123" } as Cipher; + queryParams$.next({ + cipherId: "123", + routeAfterDeletion: "/archive", + }); + + tick(); + + // Mock history without the target route + history$.mockReturnValue( + of([ + { url: "/tabs/vault" }, + { url: "/view-cipher?cipherId=123" }, + { url: "/add-edit?cipherId=123" }, + ]), + ); + + jest.spyOn(component["dialogService"], "openSimpleDialog").mockResolvedValue(true); + + void component.delete(); + tick(); + + expect(history$).toHaveBeenCalled(); + expect(historyGo).not.toHaveBeenCalled(); + expect(navigate).toHaveBeenCalledWith(["/archive"]); + })); + + it("uses historyGo when custom route exists in history", fakeAsync(() => { + buildConfigResponse.originalCipher = { edit: true, id: "123" } as Cipher; + queryParams$.next({ + cipherId: "123", + routeAfterDeletion: "/archive", + }); + + tick(); + + history$.mockReturnValue( + of([ + { url: "/tabs/vault" }, + { url: "/archive" }, + { url: "/view-cipher?cipherId=123" }, + { url: "/add-edit?cipherId=123" }, + ]), + ); + + jest.spyOn(component["dialogService"], "openSimpleDialog").mockResolvedValue(true); + + void component.delete(); + tick(); + + expect(history$).toHaveBeenCalled(); + expect(historyGo).toHaveBeenCalledWith(-2); + expect(navigate).not.toHaveBeenCalled(); + })); + + it("uses router.navigate for default /tabs/vault route", fakeAsync(() => { + buildConfigResponse.originalCipher = { edit: true, id: "456" } as Cipher; + component.routeAfterDeletion = "/tabs/vault"; + + queryParams$.next({ + cipherId: "456", + }); + + tick(); + + jest.spyOn(component["dialogService"], "openSimpleDialog").mockResolvedValue(true); + + void component.delete(); + tick(); + + expect(history$).not.toHaveBeenCalled(); + expect(historyGo).not.toHaveBeenCalled(); + expect(navigate).toHaveBeenCalledWith(["/tabs/vault"]); + })); + + it("ignores invalid routeAfterDeletion query param and uses default route", fakeAsync(() => { + // Reset the component's routeAfterDeletion to default before this test + component.routeAfterDeletion = "/tabs/vault"; + + buildConfigResponse.originalCipher = { edit: true, id: "456" } as Cipher; + queryParams$.next({ + cipherId: "456", + routeAfterDeletion: "/invalid/route", + }); + + tick(); + + // The invalid route should be ignored, routeAfterDeletion should remain default + expect(component.routeAfterDeletion).toBe("/tabs/vault"); + })); + }); + + describe("reloadAddEditCipherData", () => { + beforeEach(fakeAsync(() => { + addEditCipherInfo$.next({ + cipher: { + name: "InitialName", + type: CipherType.Login, + login: { + password: "initialPassword", + username: "initialUsername", + uris: [{ uri: "https://initial.com" }], + }, + }, + } as AddEditCipherInfo); + queryParams$.next({}); + tick(); + + cipherServiceMock.setAddEditCipherInfo.mockClear(); + })); + + it("replaces all initialValues with new data, clearing stale fields", fakeAsync(() => { + const newCipherInfo = { + cipher: { + name: "UpdatedName", + type: CipherType.Login, + login: { + password: "updatedPassword", + uris: [{ uri: "https://updated.com" }], + }, + }, + } as AddEditCipherInfo; + + addEditCipherInfo$.next(newCipherInfo); + + const messageListener = component["messageListener"]; + messageListener({ command: "reloadAddEditCipherData" }); + tick(); + + expect(component.config.initialValues).toEqual({ + name: "UpdatedName", + password: "updatedPassword", + loginUri: "https://updated.com", + } as OptionalInitialValues); + + expect(cipherServiceMock.setAddEditCipherInfo).toHaveBeenCalledWith(null, "UserId"); + })); + + it("does not reload data if config is not set", fakeAsync(() => { + component.config = null; + + const messageListener = component["messageListener"]; + messageListener({ command: "reloadAddEditCipherData" }); + tick(); + + expect(cipherServiceMock.setAddEditCipherInfo).not.toHaveBeenCalled(); + })); + + it("does not reload data if latestCipherInfo is null", fakeAsync(() => { + addEditCipherInfo$.next(null); + + const messageListener = component["messageListener"]; + messageListener({ command: "reloadAddEditCipherData" }); + tick(); + + expect(component.config.initialValues).toEqual({ + name: "InitialName", + password: "initialPassword", + username: "initialUsername", + loginUri: "https://initial.com", + } as OptionalInitialValues); + + expect(cipherServiceMock.setAddEditCipherInfo).not.toHaveBeenCalled(); + })); + + it("ignores messages with different commands", fakeAsync(() => { + const initialValues = component.config.initialValues; + + const messageListener = component["messageListener"]; + messageListener({ command: "someOtherCommand" }); + tick(); + + expect(component.config.initialValues).toBe(initialValues); + })); }); }); diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts index 60e44cefbdf..895a5fe0cce 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { CommonModule } from "@angular/common"; -import { Component, OnInit } from "@angular/core"; +import { CommonModule, Location } from "@angular/common"; +import { Component, OnInit, OnDestroy, viewChild } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormsModule } from "@angular/forms"; import { ActivatedRoute, Params, Router } from "@angular/router"; @@ -16,6 +16,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { CipherType, toCipherType } from "@bitwarden/common/vault/enums"; @@ -29,8 +30,11 @@ import { IconButtonModule, DialogService, ToastService, + BadgeModule, } from "@bitwarden/components"; import { + ArchiveCipherUtilitiesService, + CipherFormComponent, CipherFormConfig, CipherFormConfigService, CipherFormGenerationService, @@ -60,6 +64,18 @@ import { import { VaultPopoutType } from "../../../utils/vault-popout-window"; import { OpenAttachmentsComponent } from "../attachments/open-attachments/open-attachments.component"; +/** + * Available routes to navigate to after editing a cipher. + * Useful when the user could be coming from a different view other than the main vault (e.g., archive). + */ +export const ROUTES_AFTER_EDIT_DELETION = Object.freeze({ + tabsVault: "/tabs/vault", + archive: "/archive", +} as const); + +export type ROUTES_AFTER_EDIT_DELETION = + (typeof ROUTES_AFTER_EDIT_DELETION)[keyof typeof ROUTES_AFTER_EDIT_DELETION]; + /** * Helper class to parse query parameters for the AddEdit route. */ @@ -75,6 +91,7 @@ class QueryParams { this.username = params.username; this.name = params.name; this.prefillNameAndURIFromTab = params.prefillNameAndURIFromTab; + this.routeAfterDeletion = params.routeAfterDeletion ?? ROUTES_AFTER_EDIT_DELETION.tabsVault; } /** @@ -127,6 +144,12 @@ class QueryParams { * NOTE: This will override the `uri` and `name` query parameters if set to true. */ prefillNameAndURIFromTab?: true; + + /** + * The view that will be navigated to after deleting the cipher. + * @default "/tabs/vault" + */ + routeAfterDeletion?: ROUTES_AFTER_EDIT_DELETION; } export type AddEditQueryParams = Partial>; @@ -156,12 +179,15 @@ export type AddEditQueryParams = Partial>; AsyncActionsModule, PopOutComponent, IconButtonModule, + BadgeModule, ], }) -export class AddEditV2Component implements OnInit { +export class AddEditV2Component implements OnInit, OnDestroy { + readonly cipherFormComponent = viewChild(CipherFormComponent); headerText: string; config: CipherFormConfig; canDeleteCipher$: Observable; + routeAfterDeletion: ROUTES_AFTER_EDIT_DELETION = "/tabs/vault"; get loading() { return this.config == null; @@ -171,9 +197,26 @@ export class AddEditV2Component implements OnInit { return this.config?.originalCipher?.id as CipherId; } + get cipher(): CipherView { + return new CipherView(this.config?.originalCipher); + } + + get canCipherBeArchived(): boolean { + return this.cipher?.canBeArchived; + } + + get isCipherArchived(): boolean { + return this.cipher?.isArchived; + } + private fido2PopoutSessionData$ = fido2PopoutSessionData$(); private fido2PopoutSessionData: Fido2SessionData; + protected userId$ = this.accountService.activeAccount$.pipe(getUserId); + protected userCanArchive$ = this.userId$.pipe( + switchMap((userId) => this.archiveService.userCanArchive$(userId)), + ); + private get inFido2PopoutWindow() { return BrowserPopupUtils.inPopout(window) && this.fido2PopoutSessionData.isFido2Session; } @@ -182,6 +225,8 @@ export class AddEditV2Component implements OnInit { return BrowserPopupUtils.inSingleActionPopout(window, VaultPopoutType.addEditVaultItem); } + protected archiveFlagEnabled$ = this.archiveService.hasArchiveFlagEnabled$; + constructor( private route: ActivatedRoute, private i18nService: I18nService, @@ -196,16 +241,65 @@ export class AddEditV2Component implements OnInit { private dialogService: DialogService, protected cipherAuthorizationService: CipherAuthorizationService, private accountService: AccountService, + private location: Location, + private archiveService: CipherArchiveService, + private archiveCipherUtilsService: ArchiveCipherUtilitiesService, ) { this.subscribeToParams(); } + private messageListener: (message: any) => void; + async ngOnInit() { this.fido2PopoutSessionData = await firstValueFrom(this.fido2PopoutSessionData$); if (BrowserPopupUtils.inPopout(window)) { this.popupCloseWarningService.enable(); } + + // Listen for messages to reload cipher data when the pop up is already open + this.messageListener = async (message: any) => { + if (message?.command === "reloadAddEditCipherData") { + try { + await this.reloadCipherData(); + } catch (error) { + this.logService.error("Failed to reload cipher data", error); + } + } + }; + BrowserApi.addListener(chrome.runtime.onMessage, this.messageListener); + } + + ngOnDestroy() { + if (this.messageListener) { + BrowserApi.removeListener(chrome.runtime.onMessage, this.messageListener); + } + } + + /** + * Reloads the cipher data when the popup is already open and new form data is submitted. + * This completely replaces the initialValues to clear any stale data from the previous submission. + */ + private async reloadCipherData() { + if (!this.config) { + return; + } + + const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + + const latestCipherInfo = await firstValueFrom( + this.cipherService.addEditCipherInfo$(activeUserId), + ); + + if (latestCipherInfo != null) { + this.config = { + ...this.config, + initialValues: mapAddEditCipherInfoToInitialValues(latestCipherInfo), + }; + + // Be sure to clear the "cached" cipher info, so it doesn't get used again + await this.cipherService.setAddEditCipherInfo(null, activeUserId); + } } /** @@ -276,6 +370,10 @@ export class AddEditV2Component implements OnInit { await BrowserApi.sendMessage("addEditCipherSubmitted"); } + get isEditMode(): boolean { + return ["edit", "partial-edit"].includes(this.config?.mode); + } + subscribeToParams(): void { this.route.queryParams .pipe( @@ -299,9 +397,7 @@ export class AddEditV2Component implements OnInit { } config.initialValues = await this.setInitialValuesFromParams(params); - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(getUserId), - ); + const activeUserId = await firstValueFrom(this.userId$); // The browser notification bar and overlay use addEditCipherInfo$ to pass modified cipher details to the form // Attempt to fetch them here and overwrite the initialValues if present @@ -332,6 +428,13 @@ export class AddEditV2Component implements OnInit { ); } + if ( + params.routeAfterDeletion && + Object.values(ROUTES_AFTER_EDIT_DELETION).includes(params.routeAfterDeletion) + ) { + this.routeAfterDeletion = params.routeAfterDeletion; + } + return config; }), ) @@ -384,6 +487,40 @@ export class AddEditV2Component implements OnInit { return this.i18nService.t(translation[type]); } + /** + * Update the cipher in the form after archiving/unarchiving. + * @param revisionDate The new revision date. + * @param archivedDate The new archived date (null if unarchived). + **/ + updateCipherFromArchive = (revisionDate: Date, archivedDate: Date | null) => { + this.cipherFormComponent().patchCipher((current) => { + current.revisionDate = revisionDate; + current.archivedDate = archivedDate; + return current; + }); + }; + + archive = async () => { + const cipherResponse = await this.archiveCipherUtilsService.archiveCipher(this.cipher, true); + + if (!cipherResponse) { + return; + } + this.updateCipherFromArchive( + new Date(cipherResponse.revisionDate), + new Date(cipherResponse.archivedDate), + ); + }; + + unarchive = async () => { + const cipherResponse = await this.archiveCipherUtilsService.unarchiveCipher(this.cipher); + + if (!cipherResponse) { + return; + } + this.updateCipherFromArchive(new Date(cipherResponse.revisionDate), null); + }; + delete = async () => { const confirmed = await this.dialogService.openSimpleDialog({ title: { key: "deleteItem" }, @@ -398,14 +535,28 @@ export class AddEditV2Component implements OnInit { } try { - const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + const activeUserId = await firstValueFrom(this.userId$); await this.deleteCipher(activeUserId); } catch (e) { this.logService.error(e); return false; } - await this.router.navigate(["/tabs/vault"]); + if (this.routeAfterDeletion !== ROUTES_AFTER_EDIT_DELETION.tabsVault) { + const history = await firstValueFrom(this.popupRouterCacheService.history$()); + const targetIndex = history.map((h) => h.url).lastIndexOf(this.routeAfterDeletion); + + if (targetIndex !== -1) { + const stepsBack = targetIndex - (history.length - 1); + // Use historyGo to navigate back to the target route in history + // This allows downstream calls to `back()` to continue working as expected + await this.location.historyGo(stepsBack); + } else { + await this.router.navigate([this.routeAfterDeletion]); + } + } else { + await this.router.navigate([this.routeAfterDeletion]); + } this.toastService.showToast({ variant: "success", diff --git a/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.spec.ts index 871163ac80b..1da2d352c14 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.spec.ts @@ -1,4 +1,4 @@ -import { Component, Input } from "@angular/core"; +import { Component, input, ChangeDetectionStrategy } from "@angular/core"; import { ComponentFixture, TestBed, fakeAsync, tick } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; import { ActivatedRoute, Router } from "@angular/router"; @@ -25,31 +25,23 @@ import { PopupRouterCacheService } from "../../../../../platform/popup/view-cach import { AttachmentsV2Component } from "./attachments-v2.component"; -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "popup-header", template: ``, + changeDetection: ChangeDetectionStrategy.OnPush, }) class MockPopupHeaderComponent { - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input() pageTitle: string; - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input() backAction: () => void; + readonly pageTitle = input(); + readonly backAction = input<() => void>(); } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "popup-footer", template: ``, + changeDetection: ChangeDetectionStrategy.OnPush, }) class MockPopupFooterComponent { - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input() pageTitle: string; + readonly pageTitle = input(); } describe("AttachmentsV2Component", () => { @@ -120,7 +112,7 @@ describe("AttachmentsV2Component", () => { const submitBtn = fixture.debugElement.queryAll(By.directive(ButtonComponent))[1] .componentInstance; - expect(cipherAttachment.submitBtn).toEqual(submitBtn); + expect(cipherAttachment.submitBtn()).toEqual(submitBtn); }); it("navigates the user to the edit view `onUploadSuccess`", fakeAsync(() => { diff --git a/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.ts index 295496c701f..29282d293de 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/attachments/attachments-v2.component.ts @@ -51,6 +51,6 @@ export class AttachmentsV2Component { /** Navigate the user back to the edit screen after uploading an attachment */ async navigateBack() { - await this.popupRouterCacheService.back(); + await this.popupRouterCacheService.back(true); } } diff --git a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts index 459b328c44e..e9636e09873 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts @@ -11,6 +11,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs import { ProductTierType } from "@bitwarden/common/billing/enums"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { mockAccountInfoWith } from "@bitwarden/common/spec"; import { CipherId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; @@ -60,9 +61,10 @@ describe("OpenAttachmentsComponent", () => { const accountService = { activeAccount$: of({ id: mockUserId, - email: "test@email.com", - emailVerified: true, - name: "Test User", + ...mockAccountInfoWith({ + email: "test@email.com", + name: "Test User", + }), }), }; const formStatusChange$ = new BehaviorSubject<"enabled" | "disabled">("enabled"); 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)) { + + + @if (canEdit) { + + } + +
    + {{ "clone" | i18n }} + + + {{ "assignToCollections" | i18n }} + + + @if (showArchive$ | async) { + @if (canArchive$ | async) { + + } @else { } - - - - - - - @if (canEdit) { - - } - - - {{ "clone" | i18n }} - - - {{ "assignToCollections" | i18n }} - - - @if (showArchive$ | async) { - @if (canArchive$ | async) { - - } @else { - } } @if (canDelete$ | 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 577b7d96771..b999d8db35a 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 }, @@ -108,7 +102,7 @@ describe("ItemMoreOptionsComponent", () => { { provide: RestrictedItemTypesService, useValue: { restricted$: of([]) } }, { provide: CipherArchiveService, - useValue: { userCanArchive$: () => of(true), hasArchiveFlagEnabled$: () => of(true) }, + useValue: { userCanArchive$: () => of(true), hasArchiveFlagEnabled$: of(true) }, }, { provide: ToastService, useValue: { showToast: () => {} } }, { provide: Router, useValue: { navigate: () => Promise.resolve(true) } }, @@ -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" }); @@ -180,19 +158,8 @@ describe("ItemMoreOptionsComponent", () => { expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); }); - 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(); - - expect(dialogService.openSimpleDialog).not.toHaveBeenCalled(); - }); - 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 +173,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); @@ -261,22 +228,30 @@ describe("ItemMoreOptionsComponent", () => { }); describe("URI match strategy handling", () => { + it("calls the passwordService to passwordRepromptCheck", async () => { + autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); + mockConfirmDialogResult(AutofillConfirmationDialogResult.AutofilledOnly); + + await component.doAutofill(); + + expect(passwordRepromptService.passwordRepromptCheck).toHaveBeenCalledWith(baseCipher); + }); + describe("when the default URI match strategy is Exact", () => { beforeEach(() => { uriMatchStrategy$.next(UriMatchStrategy.Exact); }); - it("calls the passwordService to passwordRepromptCheck", async () => { - autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); - mockConfirmDialogResult(AutofillConfirmationDialogResult.AutofilledOnly); - - await component.doAutofill(); - - expect(passwordRepromptService.passwordRepromptCheck).toHaveBeenCalledWith(baseCipher); - }); - - it("shows the exact match dialog", async () => { + it("shows the exact match dialog when the cipher has no saved URIs", async () => { autofillSvc.currentAutofillTab$.next({ url: "https://no-match.example.com" }); + cipherService.getFullCipherView.mockImplementation(async (c) => ({ + ...baseCipher, + ...c, + login: { + ...baseCipher.login, + uris: [], + }, + })); await component.doAutofill(); @@ -291,6 +266,53 @@ describe("ItemMoreOptionsComponent", () => { expect(autofillSvc.doAutofill).not.toHaveBeenCalled(); expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); }); + + it("does not show the exact match dialog when the cipher has at least one non-exact match uri", async () => { + mockConfirmDialogResult(AutofillConfirmationDialogResult.AutofilledOnly); + cipherService.getFullCipherView.mockImplementation(async (c) => ({ + ...baseCipher, + ...c, + login: { + ...baseCipher.login, + uris: [ + { uri: "https://one.example.com", match: UriMatchStrategy.Exact }, + { uri: "https://two.example.com", match: UriMatchStrategy.Domain }, + ], + }, + })); + + autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com/path" }); + await component.doAutofill(); + + expect(dialogService.openSimpleDialog).not.toHaveBeenCalled(); + }); + + it("shows the exact match dialog when the cipher uris all have a match strategy of Exact", async () => { + cipherService.getFullCipherView.mockImplementation(async (c) => ({ + ...baseCipher, + ...c, + login: { + ...baseCipher.login, + uris: [ + { uri: "https://one.example.com", match: UriMatchStrategy.Exact }, + { uri: "https://two.example.com/a", match: UriMatchStrategy.Exact }, + ], + }, + })); + + autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com/path" }); + await component.doAutofill(); + + expect(dialogService.openSimpleDialog).toHaveBeenCalledWith( + expect.objectContaining({ + title: expect.objectContaining({ key: "cannotAutofill" }), + content: expect.objectContaining({ key: "cannotAutofillExactMatch" }), + type: "info", + }), + ); + expect(autofillSvc.doAutofill).not.toHaveBeenCalled(); + expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); + }); }); describe("when the default URI match strategy is not Exact", () => { @@ -298,7 +320,45 @@ describe("ItemMoreOptionsComponent", () => { mockConfirmDialogResult(AutofillConfirmationDialogResult.Canceled); uriMatchStrategy$.next(UriMatchStrategy.Domain); }); - it("does not show the exact match dialog", async () => { + + it("does not show the exact match dialog when the cipher has no saved URIs", async () => { + autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); + + await component.doAutofill(); + + expect(dialogService.openSimpleDialog).not.toHaveBeenCalled(); + }); + + it("shows the exact match dialog when the cipher has only exact match saved URIs", async () => { + cipherService.getFullCipherView.mockImplementation(async (c) => ({ + ...baseCipher, + ...c, + login: { + ...baseCipher.login, + uris: [ + { uri: "https://one.example.com", match: UriMatchStrategy.Exact }, + { uri: "https://two.example.com/a", match: UriMatchStrategy.Exact }, + ], + }, + })); + + autofillSvc.currentAutofillTab$.next({ url: "https://no-match.example.com" }); + + await component.doAutofill(); + + expect(dialogService.openSimpleDialog).toHaveBeenCalledWith( + expect.objectContaining({ + title: expect.objectContaining({ key: "cannotAutofill" }), + content: expect.objectContaining({ key: "cannotAutofillExactMatch" }), + type: "info", + }), + ); + expect(autofillSvc.doAutofill).not.toHaveBeenCalled(); + expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); + }); + + it("does not show the exact match dialog when the cipher has at least one uri without a match strategy of Exact", async () => { + mockConfirmDialogResult(AutofillConfirmationDialogResult.Canceled); cipherService.getFullCipherView.mockImplementation(async (c) => ({ ...baseCipher, ...c, @@ -317,70 +377,6 @@ describe("ItemMoreOptionsComponent", () => { expect(dialogService.openSimpleDialog).not.toHaveBeenCalled(); }); - - it("shows the exact match dialog when the cipher has a single uri with a match strategy of Exact", async () => { - cipherService.getFullCipherView.mockImplementation(async (c) => ({ - ...baseCipher, - ...c, - login: { - ...baseCipher.login, - uris: [{ uri: "https://one.example.com", match: UriMatchStrategy.Exact }], - }, - })); - - autofillSvc.currentAutofillTab$.next({ url: "https://no-match.example.com" }); - - await component.doAutofill(); - - expect(dialogService.openSimpleDialog).toHaveBeenCalledWith( - expect.objectContaining({ - title: expect.objectContaining({ key: "cannotAutofill" }), - content: expect.objectContaining({ key: "cannotAutofillExactMatch" }), - type: "info", - }), - ); - expect(autofillSvc.doAutofill).not.toHaveBeenCalled(); - expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); - }); - }); - - it("does not show the exact match dialog when the cipher has no uris", async () => { - mockConfirmDialogResult(AutofillConfirmationDialogResult.Canceled); - cipherService.getFullCipherView.mockImplementation(async (c) => ({ - ...baseCipher, - ...c, - login: { - ...baseCipher.login, - uris: [], - }, - })); - - autofillSvc.currentAutofillTab$.next({ url: "https://no-match.example.com" }); - - await component.doAutofill(); - - expect(dialogService.openSimpleDialog).not.toHaveBeenCalled(); - }); - - it("does not show the exact match dialog when the cipher has a uri with a match strategy of Exact and a uri with a match strategy of Domain", async () => { - mockConfirmDialogResult(AutofillConfirmationDialogResult.Canceled); - cipherService.getFullCipherView.mockImplementation(async (c) => ({ - ...baseCipher, - ...c, - login: { - ...baseCipher.login, - uris: [ - { uri: "https://one.example.com", match: UriMatchStrategy.Exact }, - { uri: "https://page.example.com", match: UriMatchStrategy.Domain }, - ], - }, - })); - - autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); - - await component.doAutofill(); - - expect(dialogService.openSimpleDialog).not.toHaveBeenCalled(); }); }); @@ -409,4 +405,42 @@ describe("ItemMoreOptionsComponent", () => { }); }); }); + + describe("canAssignCollections$", () => { + it("emits true when user has organizations and editable collections", (done) => { + jest.spyOn(component["organizationService"], "hasOrganizations").mockReturnValue(of(true)); + jest + .spyOn(component["collectionService"], "decryptedCollections$") + .mockReturnValue(of([{ id: "col-1", readOnly: false }] as any)); + + component["canAssignCollections$"].subscribe((result) => { + expect(result).toBe(true); + done(); + }); + }); + + it("emits false when user has no organizations", (done) => { + jest.spyOn(component["organizationService"], "hasOrganizations").mockReturnValue(of(false)); + jest + .spyOn(component["collectionService"], "decryptedCollections$") + .mockReturnValue(of([{ id: "col-1", readOnly: false }] as any)); + + component["canAssignCollections$"].subscribe((result) => { + expect(result).toBe(false); + done(); + }); + }); + + it("emits false when all collections are read-only", (done) => { + jest.spyOn(component["organizationService"], "hasOrganizations").mockReturnValue(of(true)); + jest + .spyOn(component["collectionService"], "decryptedCollections$") + .mockReturnValue(of([{ id: "col-1", readOnly: true }] as any)); + + component["canAssignCollections$"].subscribe((result) => { + expect(result).toBe(false); + done(); + }); + }); + }); }); 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 4dfaf7bc66f..d7de51ad20f 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$; /** @@ -141,7 +134,7 @@ export class ItemMoreOptionsComponent { }), ); - protected showArchive$: Observable = this.cipherArchiveService.hasArchiveFlagEnabled$(); + protected showArchive$: Observable = this.cipherArchiveService.hasArchiveFlagEnabled$; protected canArchive$: Observable = this.accountService.activeAccount$.pipe( getUserId, @@ -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, ) {} @@ -213,16 +204,15 @@ export class ItemMoreOptionsComponent { } const uris = cipher.login?.uris ?? []; - 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) - ) { + const showExactMatchDialog = + uris.length === 0 + ? uriMatchStrategy === UriMatchStrategy.Exact + : // all saved URIs are exact match + uris.every((u) => (u.match ?? uriMatchStrategy) === UriMatchStrategy.Exact); + + if (showExactMatchDialog) { await this.dialogService.openSimpleDialog({ title: { key: "cannotAutofill" }, content: { key: "cannotAutofillExactMatch" }, @@ -233,11 +223,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) { @@ -292,8 +277,7 @@ export class ItemMoreOptionsComponent { this.accountService.activeAccount$.pipe(map((a) => a?.id)), )) as UserId; - const encryptedCipher = await this.cipherService.encrypt(cipher, activeUserId); - await this.cipherService.updateWithServer(encryptedCipher); + await this.cipherService.updateWithServer(cipher, activeUserId); this.toastService.showToast({ variant: "success", message: this.i18nService.t( @@ -391,7 +375,8 @@ export class ItemMoreOptionsComponent { const confirmed = await this.dialogService.openSimpleDialog({ title: { key: "archiveItem" }, - content: { key: "archiveItemConfirmDesc" }, + content: { key: "archiveItemDialogContent" }, + acceptButtonText: { key: "archiveVerb" }, type: "info", }); diff --git a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.html index 7dd0a5a3bc7..fa8683c12dc 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/new-item-dropdown/new-item-dropdown-v2.component.html @@ -1,5 +1,5 @@ - diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.spec.ts index 37c4804e600..ca73a7332ee 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.spec.ts @@ -4,7 +4,6 @@ import { FormsModule } from "@angular/forms"; import { BehaviorSubject } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SearchTextDebounceInterval } from "@bitwarden/common/vault/services/search.service"; import { SearchModule } from "@bitwarden/components"; @@ -20,7 +19,6 @@ describe("VaultV2SearchComponent", () => { const searchText$ = new BehaviorSubject(""); const loading$ = new BehaviorSubject(false); - const featureFlag$ = new BehaviorSubject(true); const applyFilter = jest.fn(); const createComponent = () => { @@ -31,7 +29,6 @@ describe("VaultV2SearchComponent", () => { beforeEach(async () => { applyFilter.mockClear(); - featureFlag$.next(true); await TestBed.configureTestingModule({ imports: [VaultV2SearchComponent, CommonModule, SearchModule, JslibModule, FormsModule], @@ -49,12 +46,6 @@ describe("VaultV2SearchComponent", () => { loading$, }, }, - { - provide: ConfigService, - useValue: { - getFeatureFlag$: jest.fn(() => featureFlag$), - }, - }, { provide: I18nService, useValue: { t: (key: string) => key } }, ], }).compileComponents(); @@ -70,91 +61,55 @@ describe("VaultV2SearchComponent", () => { }); describe("debouncing behavior", () => { - describe("when feature flag is enabled", () => { - beforeEach(() => { - featureFlag$.next(true); - createComponent(); - }); - - it("debounces search text changes when not loading", fakeAsync(() => { - loading$.next(false); - - component.searchText = "test"; - component.onSearchTextChanged(); - - expect(applyFilter).not.toHaveBeenCalled(); - - tick(SearchTextDebounceInterval); - - expect(applyFilter).toHaveBeenCalledWith("test"); - expect(applyFilter).toHaveBeenCalledTimes(1); - })); - - it("should not debounce search text changes when loading", fakeAsync(() => { - loading$.next(true); - - component.searchText = "test"; - component.onSearchTextChanged(); - - tick(0); - - expect(applyFilter).toHaveBeenCalledWith("test"); - expect(applyFilter).toHaveBeenCalledTimes(1); - })); - - it("cancels previous debounce when new text is entered", fakeAsync(() => { - loading$.next(false); - - component.searchText = "test"; - component.onSearchTextChanged(); - - tick(SearchTextDebounceInterval / 2); - - component.searchText = "test2"; - component.onSearchTextChanged(); - - tick(SearchTextDebounceInterval / 2); - - expect(applyFilter).not.toHaveBeenCalled(); - - tick(SearchTextDebounceInterval / 2); - - expect(applyFilter).toHaveBeenCalledWith("test2"); - expect(applyFilter).toHaveBeenCalledTimes(1); - })); + beforeEach(() => { + createComponent(); }); - describe("when feature flag is disabled", () => { - beforeEach(() => { - featureFlag$.next(false); - createComponent(); - }); + it("debounces search text changes when not loading", fakeAsync(() => { + loading$.next(false); - it("debounces search text changes", fakeAsync(() => { - component.searchText = "test"; - component.onSearchTextChanged(); + component.searchText = "test"; + component.onSearchTextChanged(); - expect(applyFilter).not.toHaveBeenCalled(); + expect(applyFilter).not.toHaveBeenCalled(); - tick(SearchTextDebounceInterval); + tick(SearchTextDebounceInterval); - expect(applyFilter).toHaveBeenCalledWith("test"); - expect(applyFilter).toHaveBeenCalledTimes(1); - })); + expect(applyFilter).toHaveBeenCalledWith("test"); + expect(applyFilter).toHaveBeenCalledTimes(1); + })); - it("ignores loading state and always debounces", fakeAsync(() => { - loading$.next(true); + it("should not debounce search text changes when loading", fakeAsync(() => { + loading$.next(true); - component.searchText = "test"; - component.onSearchTextChanged(); + component.searchText = "test"; + component.onSearchTextChanged(); - expect(applyFilter).not.toHaveBeenCalled(); + tick(0); - tick(SearchTextDebounceInterval); + expect(applyFilter).toHaveBeenCalledWith("test"); + expect(applyFilter).toHaveBeenCalledTimes(1); + })); - expect(applyFilter).toHaveBeenCalledWith("test"); - expect(applyFilter).toHaveBeenCalledTimes(1); - })); - }); + it("cancels previous debounce when new text is entered", fakeAsync(() => { + loading$.next(false); + + component.searchText = "test"; + component.onSearchTextChanged(); + + tick(SearchTextDebounceInterval / 2); + + component.searchText = "test2"; + component.onSearchTextChanged(); + + tick(SearchTextDebounceInterval / 2); + + expect(applyFilter).not.toHaveBeenCalled(); + + tick(SearchTextDebounceInterval / 2); + + expect(applyFilter).toHaveBeenCalledWith("test2"); + expect(applyFilter).toHaveBeenCalledTimes(1); + })); }); }); diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.ts index 154cd49c5a3..3419bd30ea0 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-search/vault-v2-search.component.ts @@ -7,17 +7,13 @@ import { Subscription, combineLatest, debounce, - debounceTime, distinctUntilChanged, filter, map, - switchMap, timer, } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { SearchTextDebounceInterval } from "@bitwarden/common/vault/services/search.service"; import { SearchModule } from "@bitwarden/components"; @@ -40,7 +36,6 @@ export class VaultV2SearchComponent { constructor( private vaultPopupItemsService: VaultPopupItemsService, private vaultPopupLoadingService: VaultPopupLoadingService, - private configService: ConfigService, private ngZone: NgZone, ) { this.subscribeToLatestSearchText(); @@ -63,31 +58,19 @@ export class VaultV2SearchComponent { } subscribeToApplyFilter(): void { - this.configService - .getFeatureFlag$(FeatureFlag.VaultLoadingSkeletons) + combineLatest([this.searchText$, this.loading$]) .pipe( - switchMap((enabled) => { - if (!enabled) { - return this.searchText$.pipe( - debounceTime(SearchTextDebounceInterval), - distinctUntilChanged(), - ); - } - - return combineLatest([this.searchText$, this.loading$]).pipe( - debounce(([_, isLoading]) => { - // If loading apply immediately to avoid stale searches. - // After loading completes, debounce to avoid excessive searches. - const delayTime = isLoading ? 0 : SearchTextDebounceInterval; - return timer(delayTime); - }), - distinctUntilChanged( - ([prevText, prevLoading], [newText, newLoading]) => - prevText === newText && prevLoading === newLoading, - ), - map(([text, _]) => text), - ); + debounce(([_, isLoading]) => { + // If loading apply immediately to avoid stale searches. + // After loading completes, debounce to avoid excessive searches. + const delayTime = isLoading ? 0 : SearchTextDebounceInterval; + return timer(delayTime); }), + distinctUntilChanged( + ([prevText, prevLoading], [newText, newLoading]) => + prevText === newText && prevLoading === newLoading, + ), + map(([text, _]) => text), takeUntilDestroyed(), ) .subscribe((text) => { 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..20871b4b134 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 @@ -1,4 +1,4 @@ - + @@ -8,31 +8,28 @@ - -
    - - {{ "yourVaultIsEmpty" | i18n }} - -

    - {{ "emptyVaultDescription" | i18n }} -

    -
    - - {{ "newLogin" | i18n }} - -
    -
    -
    - - @if (skeletonFeatureFlag$ | async) { - - + @if (vaultState === VaultStateEnum.Empty) { + +
    + + {{ "yourVaultIsEmpty" | i18n }} + +

    + {{ "emptyVaultDescription" | i18n }} +

    +
    + + {{ "newLogin" | i18n }} + +
    +
    - } @else { - }
    - - - - - - - - - @if (skeletonFeatureFlag$ | async) { - - + @if (vaultState === null) { + + @if (!(loading$ | async)) { + + + + } - } @else { - }
    diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.spec.ts index 5563cd3033b..e3b72c3319f 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.spec.ts @@ -1,7 +1,7 @@ -import { CdkVirtualScrollableElement } from "@angular/cdk/scrolling"; import { ChangeDetectionStrategy, Component, input, NO_ERRORS_SCHEMA } from "@angular/core"; import { TestBed, fakeAsync, flush, tick } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; +import { provideNoopAnimations } from "@angular/platform-browser/animations"; import { ActivatedRoute, Router } from "@angular/router"; import { RouterTestingModule } from "@angular/router/testing"; import { mock } from "jest-mock-extended"; @@ -10,6 +10,10 @@ import { BehaviorSubject, Observable, Subject, of } from "rxjs"; import { PremiumUpgradeDialogComponent } from "@bitwarden/angular/billing/components"; import { NudgeType, NudgesService } from "@bitwarden/angular/vault"; import { VaultProfileService } from "@bitwarden/angular/vault/services/vault-profile.service"; +import { + AutoConfirmExtensionSetupDialogComponent, + AutomaticUserConfirmationService, +} from "@bitwarden/auto-confirm"; import { CurrentAccountComponent } from "@bitwarden/browser/auth/popup/account-switching/current-account.component"; import AutofillService from "@bitwarden/browser/autofill/services/autofill.service"; import { PopOutComponent } from "@bitwarden/browser/platform/popup/components/pop-out.component"; @@ -28,7 +32,11 @@ import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/res import { TaskService } from "@bitwarden/common/vault/tasks"; import { DialogService } from "@bitwarden/components"; import { StateProvider } from "@bitwarden/state"; -import { DecryptionFailureDialogComponent } from "@bitwarden/vault"; +import { + DecryptionFailureDialogComponent, + VaultItemsTransferService, + DefaultVaultItemsTransferService, +} from "@bitwarden/vault"; import { BrowserApi } from "../../../../platform/browser/browser-api"; import BrowserPopupUtils from "../../../../platform/browser/browser-popup-utils"; @@ -132,6 +140,7 @@ class VaultListItemsContainerStubComponent { const mockDialogRef = { close: jest.fn(), afterClosed: jest.fn().mockReturnValue(of(undefined)), + closed: of(undefined), } as unknown as import("@bitwarden/components").DialogRef; jest @@ -141,6 +150,11 @@ jest jest .spyOn(DecryptionFailureDialogComponent, "open") .mockImplementation((_: DialogService, _params: any) => mockDialogRef as any); + +const autoConfirmDialogSpy = jest + .spyOn(AutoConfirmExtensionSetupDialogComponent, "open") + .mockImplementation((_: DialogService) => mockDialogRef as any); + jest.spyOn(BrowserApi, "isPopupOpen").mockResolvedValue(false); jest.spyOn(BrowserPopupUtils, "openCurrentPagePopout").mockResolvedValue(); @@ -180,7 +194,7 @@ describe("VaultV2Component", () => { const nudgesSvc = { showNudgeSpotlight$: jest.fn().mockImplementation((_type: NudgeType) => of(false)), dismissNudge: jest.fn().mockResolvedValue(undefined), - } as Partial; + }; const dialogSvc = {} as Partial; @@ -193,6 +207,11 @@ describe("VaultV2Component", () => { 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; } @@ -209,11 +228,23 @@ describe("VaultV2Component", () => { .mockResolvedValue(new Date(Date.now() - 8 * 24 * 60 * 60 * 1000)), // 8 days ago }; + const configSvc = { + getFeatureFlag$: jest.fn().mockImplementation((_flag: string) => of(false)), + }; + + const autoConfirmSvc = { + configuration$: jest.fn().mockReturnValue(of({})), + canManageAutoConfirm$: jest.fn().mockReturnValue(of(false)), + upsert: jest.fn().mockResolvedValue(undefined), + autoConfirmUser: jest.fn().mockResolvedValue(undefined), + }; + beforeEach(async () => { jest.clearAllMocks(); await TestBed.configureTestingModule({ imports: [VaultV2Component, RouterTestingModule], providers: [ + provideNoopAnimations(), { provide: VaultPopupItemsService, useValue: itemsSvc }, { provide: VaultPopupListFiltersService, useValue: filtersSvc }, { provide: VaultPopupScrollPositionService, useValue: scrollSvc }, @@ -256,14 +287,16 @@ describe("VaultV2Component", () => { { provide: StateProvider, useValue: mock() }, { provide: ConfigService, - useValue: { - getFeatureFlag$: (_: string) => of(false), - }, + useValue: configSvc, }, { provide: SearchService, useValue: { isCipherSearching$: of(false) }, }, + { + provide: AutomaticUserConfirmationService, + useValue: autoConfirmSvc, + }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); @@ -281,6 +314,9 @@ describe("VaultV2Component", () => { AutofillVaultListItemsComponent, VaultListItemsContainerComponent, ], + providers: [ + { provide: VaultItemsTransferService, useValue: DefaultVaultItemsTransferService }, + ], }, add: { imports: [ @@ -294,6 +330,7 @@ describe("VaultV2Component", () => { AutofillVaultListItemsStubComponent, VaultListItemsContainerStubComponent, ], + providers: [{ provide: VaultItemsTransferService, useValue: vaultItemsTransferSvc }], }, }); @@ -342,6 +379,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)); @@ -352,24 +390,33 @@ describe("VaultV2Component", () => { itemsLoading$.next(false); + readySubject$.next(true); + expect(values[values.length - 1]).toBe(false); }); - it("ngAfterViewInit waits for allFilters$ then starts scroll position service", fakeAsync(() => { + it("passes popup-page scroll region element to scroll position service", fakeAsync(() => { + const fixture = TestBed.createComponent(VaultV2Component); + const component = fixture.componentInstance; + + const readySubject$ = component["readySubject"] as unknown as BehaviorSubject; + const itemsLoading$ = itemsSvc.loading$ as unknown as BehaviorSubject; const allFilters$ = filtersSvc.allFilters$ as unknown as Subject; - (component as any).virtualScrollElement = {} as CdkVirtualScrollableElement; - - component.ngAfterViewInit(); - expect(scrollSvc.start).not.toHaveBeenCalled(); - - allFilters$.next({ any: true }); + fixture.detectChanges(); tick(); - expect(scrollSvc.start).toHaveBeenCalledTimes(1); - expect(scrollSvc.start).toHaveBeenCalledWith((component as any).virtualScrollElement); + const scrollRegion = fixture.nativeElement.querySelector( + '[data-testid="popup-layout-scroll-region"]', + ) as HTMLElement; - flush(); + // Unblock loading + itemsLoading$.next(false); + readySubject$.next(true); + allFilters$.next({}); + tick(); + + expect(scrollSvc.start).toHaveBeenCalledWith(scrollRegion); })); it("showPremiumDialog opens PremiumUpgradeDialogComponent", () => { @@ -453,7 +500,9 @@ describe("VaultV2Component", () => { hasPremiumFromAnySource$.next(false); - (nudgesSvc.showNudgeSpotlight$ as jest.Mock).mockImplementation((type: NudgeType) => + configSvc.getFeatureFlag$.mockImplementation((_flag: string) => of(true)); + + nudgesSvc.showNudgeSpotlight$.mockImplementation((type: NudgeType) => of(type === NudgeType.PremiumUpgrade), ); @@ -482,9 +531,11 @@ describe("VaultV2Component", () => { })); it("renders Empty-Vault spotlight when vaultState is Empty and nudge is on", fakeAsync(() => { + configSvc.getFeatureFlag$.mockImplementation((_flag: string) => of(false)); + itemsSvc.emptyVault$.next(true); - (nudgesSvc.showNudgeSpotlight$ as jest.Mock).mockImplementation((type: NudgeType) => { + nudgesSvc.showNudgeSpotlight$.mockImplementation((type: NudgeType) => { return of(type === NudgeType.EmptyVaultNudge); }); @@ -566,4 +617,86 @@ describe("VaultV2Component", () => { const spotlights = queryAllSpotlights(fixture); expect(spotlights.length).toBe(0); })); + + describe("AutoConfirmExtensionSetupDialog", () => { + beforeEach(() => { + autoConfirmDialogSpy.mockClear(); + }); + + it("opens dialog when canManage is true and showBrowserNotification is undefined", fakeAsync(() => { + autoConfirmSvc.canManageAutoConfirm$.mockReturnValue(of(true)); + autoConfirmSvc.configuration$.mockReturnValue( + of({ + enabled: false, + showSetupDialog: true, + showBrowserNotification: undefined, + }), + ); + + const fixture = TestBed.createComponent(VaultV2Component); + const component = fixture.componentInstance; + + void component.ngOnInit(); + tick(); + + expect(autoConfirmDialogSpy).toHaveBeenCalledWith(expect.any(Object)); + })); + + it("does not open dialog when showBrowserNotification is false", fakeAsync(() => { + autoConfirmSvc.canManageAutoConfirm$.mockReturnValue(of(true)); + autoConfirmSvc.configuration$.mockReturnValue( + of({ + enabled: false, + showSetupDialog: true, + showBrowserNotification: false, + }), + ); + + const fixture = TestBed.createComponent(VaultV2Component); + const component = fixture.componentInstance; + + void component.ngOnInit(); + tick(); + + expect(autoConfirmDialogSpy).not.toHaveBeenCalled(); + })); + + it("does not open dialog when showBrowserNotification is true", fakeAsync(() => { + autoConfirmSvc.canManageAutoConfirm$.mockReturnValue(of(true)); + autoConfirmSvc.configuration$.mockReturnValue( + of({ + enabled: true, + showSetupDialog: true, + showBrowserNotification: true, + }), + ); + + const fixture = TestBed.createComponent(VaultV2Component); + const component = fixture.componentInstance; + + void component.ngOnInit(); + tick(); + + expect(autoConfirmDialogSpy).not.toHaveBeenCalled(); + })); + + it("does not open dialog when canManage is false even if showBrowserNotification is undefined", fakeAsync(() => { + autoConfirmSvc.canManageAutoConfirm$.mockReturnValue(of(false)); + autoConfirmSvc.configuration$.mockReturnValue( + of({ + enabled: false, + showSetupDialog: true, + showBrowserNotification: undefined, + }), + ); + + const fixture = TestBed.createComponent(VaultV2Component); + const component = fixture.componentInstance; + + void component.ngOnInit(); + tick(); + + expect(autoConfirmDialogSpy).not.toHaveBeenCalled(); + })); + }); }); 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 9cee4f66b67..c58b7b20d2f 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 @@ -1,7 +1,7 @@ import { LiveAnnouncer } from "@angular/cdk/a11y"; -import { CdkVirtualScrollableElement, ScrollingModule } from "@angular/cdk/scrolling"; +import { ScrollingModule } from "@angular/cdk/scrolling"; import { CommonModule } from "@angular/common"; -import { AfterViewInit, Component, DestroyRef, OnDestroy, OnInit, ViewChild } from "@angular/core"; +import { Component, DestroyRef, effect, inject, OnDestroy, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { Router, RouterModule } from "@angular/router"; import { @@ -15,7 +15,9 @@ import { shareReplay, switchMap, take, + withLatestFrom, tap, + BehaviorSubject, } from "rxjs"; import { PremiumUpgradeDialogComponent } from "@bitwarden/angular/billing/components"; @@ -24,6 +26,11 @@ import { NudgesService, NudgeType } from "@bitwarden/angular/vault"; import { SpotlightComponent } from "@bitwarden/angular/vault/components/spotlight/spotlight.component"; import { VaultProfileService } from "@bitwarden/angular/vault/services/vault-profile.service"; import { DeactivatedOrg, NoResults, VaultOpen } from "@bitwarden/assets/svg"; +import { + AutoConfirmExtensionSetupDialogComponent, + AutoConfirmState, + AutomaticUserConfirmationService, +} from "@bitwarden/auto-confirm"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; @@ -40,9 +47,15 @@ import { ButtonModule, DialogService, NoItemsModule, + ScrollLayoutService, + ToastService, 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,12 +118,9 @@ 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 - // eslint-disable-next-line @angular-eslint/prefer-signals - @ViewChild(CdkVirtualScrollableElement) virtualScrollElement?: CdkVirtualScrollableElement; - +export class VaultV2Component implements OnInit, OnDestroy { NudgeType = NudgeType; cipherType = CipherType; private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId); @@ -125,7 +135,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"; @@ -133,8 +158,8 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { }), ); - protected skeletonFeatureFlag$ = this.configService.getFeatureFlag$( - FeatureFlag.VaultLoadingSkeletons, + protected premiumSpotlightFeatureFlag$ = this.configService.getFeatureFlag$( + FeatureFlag.BrowserPremiumSpotlight, ); private showPremiumNudgeSpotlight$ = this.activeUserId$.pipe( @@ -164,6 +189,7 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { ); protected showPremiumSpotlight$ = combineLatest([ + this.premiumSpotlightFeatureFlag$, this.showPremiumNudgeSpotlight$, this.showHasItemsVaultSpotlight$, this.hasPremium$, @@ -171,8 +197,13 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { this.accountAgeInDays$, ]).pipe( map( - ([showPremiumNudge, showHasItemsNudge, hasPremium, count, age]) => - showPremiumNudge && !showHasItemsNudge && !hasPremium && count >= 5 && age >= 7, + ([featureFlagEnabled, showPremiumNudge, showHasItemsNudge, hasPremium, count, age]) => + featureFlagEnabled && + showPremiumNudge && + !showHasItemsNudge && + !hasPremium && + count >= 5 && + age >= 7, ), shareReplay({ bufferSize: 1, refCount: true }), ); @@ -181,23 +212,18 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { PremiumUpgradeDialogComponent.open(this.dialogService); } - /** When true, show spinner loading state */ - protected showSpinnerLoaders$ = combineLatest([this.loading$, this.skeletonFeatureFlag$]).pipe( - map(([loading, skeletonsEnabled]) => loading && !skeletonsEnabled), - ); - /** When true, show skeleton loading state with debouncing to prevent flicker */ protected showSkeletonsLoaders$ = combineLatest([ this.loading$, this.searchService.isCipherSearching$, - this.skeletonFeatureFlag$, + this.vaultItemsTransferService.transferInProgress$, ]).pipe( - map( - ([loading, cipherSearching, skeletonsEnabled]) => - (loading || cipherSearching) && skeletonsEnabled, - ), + map(([loading, cipherSearching, transferInProgress]) => { + return loading || cipherSearching || transferInProgress; + }), distinctUntilChanged(), skeletonLoadingDelay(), + shareReplay({ bufferSize: 1, refCount: true }), ); protected newItemItemValues$: Observable = @@ -235,12 +261,15 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { private introCarouselService: IntroCarouselService, private nudgesService: NudgesService, private router: Router, + private autoConfirmService: AutomaticUserConfirmationService, + private toastService: ToastService, private vaultProfileService: VaultProfileService, private billingAccountService: BillingAccountProfileStateService, private liveAnnouncer: LiveAnnouncer, private i18nService: I18nService, private configService: ConfigService, private searchService: SearchService, + private vaultItemsTransferService: VaultItemsTransferService, ) { combineLatest([ this.vaultPopupItemsService.emptyVault$, @@ -266,16 +295,21 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { }); } - ngAfterViewInit(): void { - if (this.virtualScrollElement) { - // The filters component can cause the size of the virtual scroll element to change, - // which can cause the scroll position to be land in the wrong spot. To fix this, - // wait until all filters are populated before restoring the scroll position. - this.allFilters$.pipe(take(1), takeUntilDestroyed(this.destroyRef)).subscribe(() => { - this.vaultScrollPositionService.start(this.virtualScrollElement!); + private readonly scrollLayout = inject(ScrollLayoutService); + + private readonly _scrollPositionEffect = effect((onCleanup) => { + const sub = combineLatest([this.scrollLayout.scrollableRef$, this.allFilters$, this.loading$]) + .pipe( + filter(([ref, _filters, loading]) => !!ref && !loading), + take(1), + takeUntilDestroyed(this.destroyRef), + ) + .subscribe(([ref]) => { + this.vaultScrollPositionService.start(ref!.nativeElement); }); - } - } + + onCleanup(() => sub.unsubscribe()); + }); async ngOnInit() { this.activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); @@ -295,6 +329,40 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { cipherIds: ciphers.map((c) => c.id as CipherId), }); }); + + const autoConfirmState$ = this.autoConfirmService.configuration$(this.activeUserId); + + combineLatest([ + this.autoConfirmService.canManageAutoConfirm$(this.activeUserId), + autoConfirmState$, + ]) + .pipe( + filter(([canManage, state]) => canManage && state.showBrowserNotification === undefined), + take(1), + switchMap(() => AutoConfirmExtensionSetupDialogComponent.open(this.dialogService).closed), + withLatestFrom(autoConfirmState$, this.accountService.activeAccount$.pipe(getUserId)), + switchMap(([result, state, userId]) => { + const newState: AutoConfirmState = { + ...state, + enabled: result ?? false, + showBrowserNotification: !result, + }; + + if (result) { + this.toastService.showToast({ + message: this.i18nService.t("autoConfirmEnabled"), + variant: "success", + }); + } + + return this.autoConfirmService.upsert(userId, newState); + }), + takeUntilDestroyed(this.destroyRef), + ) + .subscribe(); + await this.vaultItemsTransferService.enforceOrganizationDataOwnership(this.activeUserId); + + this.readySubject.next(true); } ngOnDestroy() { diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html index 9b8380a4214..03eb701704f 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.html @@ -1,39 +1,56 @@ - + + @if (cipher?.isArchived) { + + {{ "archived" | i18n }} + + } + + - + @if (cipher) { + + } - - - - - + @if (!cipher.isDeleted) { + + } + @if (cipher.isDeleted && cipher.permissions.restore) { + + } + + @if ((archiveFlagEnabled$ | async) && cipher.isArchived) { + + } + @if ((userCanArchive$ | async) && cipher.canBeArchived) { + + } + @if (canDeleteCipher$ | async) { + + } + diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.spec.ts index 3d4fdb2e9f9..dd2c3e0252c 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.spec.ts @@ -1,9 +1,13 @@ -import { ComponentFixture, fakeAsync, flush, TestBed } from "@angular/core/testing"; +import { ComponentFixture, fakeAsync, flush, TestBed, tick } from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; import { ActivatedRoute, Router } from "@angular/router"; import { mock } from "jest-mock-extended"; import { of, Subject } from "rxjs"; +import { CollectionService } from "@bitwarden/admin-console/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AUTOFILL_ID, @@ -11,20 +15,32 @@ import { COPY_USERNAME_ID, COPY_VERIFICATION_CODE_ID, } from "@bitwarden/common/autofill/constants"; +import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; import { EventType } from "@bitwarden/common/enums"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; import { UserId } from "@bitwarden/common/types/guid"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; +import { CipherRiskService } from "@bitwarden/common/vault/abstractions/cipher-risk.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums"; +import { CipherData } from "@bitwarden/common/vault/models/data/cipher.data"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; +import { TaskService } from "@bitwarden/common/vault/tasks"; import { DialogService, ToastService } from "@bitwarden/components"; -import { CopyCipherFieldService, PasswordRepromptService } from "@bitwarden/vault"; +import { + ArchiveCipherUtilitiesService, + CopyCipherFieldService, + PasswordRepromptService, +} from "@bitwarden/vault"; import { BrowserApi } from "../../../../../platform/browser/browser-api"; import BrowserPopupUtils from "../../../../../platform/browser/browser-popup-utils"; @@ -62,7 +78,10 @@ describe("ViewV2Component", () => { username: "test-username", password: "test-password", totp: "123", + uris: ["https://example.com"], }, + permissions: {}, + card: {}, } as unknown as CipherView; const mockPasswordRepromptService = { @@ -84,6 +103,8 @@ describe("ViewV2Component", () => { softDeleteWithServer: jest.fn().mockResolvedValue(undefined), }; + const cipherArchiveService = mock(); + beforeEach(async () => { mockCipherService.cipherViews$.mockClear(); mockCipherService.deleteWithServer.mockClear(); @@ -97,6 +118,10 @@ describe("ViewV2Component", () => { back.mockClear(); showToast.mockClear(); showPasswordPrompt.mockClear(); + cipherArchiveService.hasArchiveFlagEnabled$ = of(true); + cipherArchiveService.userCanArchive$.mockReturnValue(of(false)); + cipherArchiveService.archiveWithServer.mockResolvedValue({ id: "122-333-444" } as CipherData); + cipherArchiveService.unarchiveWithServer.mockResolvedValue({ id: "122-333-444" } as CipherData); await TestBed.configureTestingModule({ imports: [ViewV2Component], @@ -131,7 +156,7 @@ describe("ViewV2Component", () => { { provide: CipherAuthorizationService, useValue: { - canDeleteCipher$: jest.fn().mockReturnValue(true), + canDeleteCipher$: jest.fn().mockReturnValue(of(true)), }, }, { @@ -142,6 +167,61 @@ describe("ViewV2Component", () => { provide: PasswordRepromptService, useValue: mockPasswordRepromptService, }, + { + provide: CipherArchiveService, + useValue: cipherArchiveService, + }, + { + provide: OrganizationService, + useValue: mock(), + }, + { + provide: CollectionService, + useValue: mock(), + }, + { + provide: FolderService, + useValue: mock(), + }, + { + provide: TaskService, + useValue: mock(), + }, + { + provide: ApiService, + useValue: mock(), + }, + { + provide: EnvironmentService, + useValue: { + environment$: of({ + getIconsUrl: () => "https://example.com", + }), + }, + }, + { + provide: DomainSettingsService, + useValue: { + showFavicons$: of(true), + }, + }, + { + provide: BillingAccountProfileStateService, + useValue: { + hasPremiumFromAnySource$: jest.fn().mockReturnValue(of(false)), + }, + }, + { + provide: ArchiveCipherUtilitiesService, + useValue: { + archiveCipher: jest.fn().mockResolvedValue(null), + unarchiveCipher: jest.fn().mockResolvedValue(null), + }, + }, + { + provide: CipherRiskService, + useValue: mock(), + }, ], }) .overrideProvider(DialogService, { @@ -154,6 +234,7 @@ describe("ViewV2Component", () => { fixture = TestBed.createComponent(ViewV2Component); component = fixture.componentInstance; fixture.detectChanges(); + (component as any).showFooter$ = of(true); }); describe("queryParams", () => { @@ -352,6 +433,93 @@ describe("ViewV2Component", () => { })); }); + describe("archive button", () => { + it("shows the archive button when the user can archive and the cipher can be archived", fakeAsync(() => { + jest.spyOn(component["archiveService"], "userCanArchive$").mockReturnValueOnce(of(true)); + component.cipher = { ...mockCipher, canBeArchived: true } as CipherView; + tick(); + fixture.detectChanges(); + + const archiveBtn = fixture.debugElement.query(By.css("button[biticonbutton='bwi-archive']")); + expect(archiveBtn).toBeTruthy(); + })); + + it("does not show the archive button when the user cannot archive", fakeAsync(() => { + jest.spyOn(component["archiveService"], "userCanArchive$").mockReturnValueOnce(of(false)); + component.cipher = { ...mockCipher, canBeArchived: true, isDeleted: false } as CipherView; + + tick(); + fixture.detectChanges(); + + const archiveBtn = fixture.debugElement.query(By.css("button[biticonbutton='bwi-archive']")); + expect(archiveBtn).toBeFalsy(); + })); + + it("does not show the archive button when the cipher cannot be archived", fakeAsync(() => { + jest.spyOn(component["archiveService"], "userCanArchive$").mockReturnValueOnce(of(true)); + component.cipher = { ...mockCipher, archivedDate: new Date(), edit: true } as CipherView; + + tick(); + fixture.detectChanges(); + + const archiveBtn = fixture.debugElement.query(By.css("button[biticonbutton='bwi-archive']")); + expect(archiveBtn).toBeFalsy(); + })); + }); + + describe("unarchive button", () => { + it("shows the unarchive button when the cipher is archived", fakeAsync(() => { + component.cipher = { ...mockCipher, isArchived: true } as CipherView; + + tick(); + fixture.detectChanges(); + + const unarchiveBtn = fixture.debugElement.query( + By.css("button[biticonbutton='bwi-unarchive']"), + ); + expect(unarchiveBtn).toBeTruthy(); + })); + + it("does not show the unarchive button when the cipher is not archived", fakeAsync(() => { + component.cipher = { ...mockCipher, archivedDate: undefined } as CipherView; + + tick(); + fixture.detectChanges(); + + const unarchiveBtn = fixture.debugElement.query( + By.css("button[biticonbutton='bwi-unarchive']"), + ); + expect(unarchiveBtn).toBeFalsy(); + })); + }); + + describe("archive", () => { + beforeEach(() => { + component.cipher = { ...mockCipher, canBeArchived: true } as CipherView; + }); + + it("calls archive service to archive the cipher", async () => { + await component.archive(); + + expect(component["archiveCipherUtilsService"].archiveCipher).toHaveBeenCalledWith( + expect.objectContaining({ id: "122-333-444" }), + true, + ); + }); + }); + + describe("unarchive", () => { + it("calls archive service to unarchive the cipher", async () => { + component.cipher = { ...mockCipher, isArchived: true } as CipherView; + + await component.unarchive(); + + expect(component["archiveCipherUtilsService"].unarchiveCipher).toHaveBeenCalledWith( + expect.objectContaining({ id: "122-333-444" }), + ); + }); + }); + describe("delete", () => { beforeEach(() => { component.cipher = mockCipher; @@ -477,4 +645,44 @@ describe("ViewV2Component", () => { }); }); }); + + describe("archived badge", () => { + it("shows archived badge if the cipher is archived", fakeAsync(() => { + component.cipher = { ...mockCipher, isArchived: true } as CipherView; + mockCipherService.cipherViews$.mockImplementationOnce(() => + of([ + { + ...mockCipher, + isArchived: true, + }, + ]), + ); + + params$.next({ action: "view", cipherId: mockCipher.id }); + + flush(); + + fixture.detectChanges(); + + const badge = fixture.nativeElement.querySelector("span[bitBadge]"); + expect(badge).toBeTruthy(); + })); + + it("does not show archived badge if the cipher is not archived", () => { + component.cipher = { ...mockCipher, isArchived: false } as CipherView; + mockCipherService.cipherViews$.mockImplementationOnce(() => + of([ + { + ...mockCipher, + archivedDate: new Date(), + }, + ]), + ); + + fixture.detectChanges(); + + const badge = fixture.nativeElement.querySelector("span[bitBadge]"); + expect(badge).toBeFalsy(); + }); + }); }); diff --git a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts index 1dea91c0b9f..f57b3e2d7f1 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/view-v2/view-v2.component.ts @@ -7,9 +7,9 @@ import { FormsModule } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; import { firstValueFrom, Observable, switchMap, of, map } from "rxjs"; -import { CollectionView } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; @@ -25,6 +25,7 @@ import { EventType } from "@bitwarden/common/enums"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { UserId } from "@bitwarden/common/types/guid"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions/view-password-history.service"; @@ -34,6 +35,7 @@ import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cip import { filterOutNullish } from "@bitwarden/common/vault/utils/observable-utilities"; import { AsyncActionsModule, + BadgeModule, ButtonModule, CalloutModule, DialogService, @@ -42,6 +44,7 @@ import { ToastService, } from "@bitwarden/components"; import { + ArchiveCipherUtilitiesService, ChangeLoginPasswordService, CipherViewComponent, CopyCipherFieldService, @@ -58,6 +61,7 @@ import { BrowserPremiumUpgradePromptService } from "../../../services/browser-pr import { BrowserViewPasswordHistoryService } from "../../../services/browser-view-password-history.service"; import { VaultPopupScrollPositionService } from "../../../services/vault-popup-scroll-position.service"; import { closeViewVaultItemPopout, VaultPopoutType } from "../../../utils/vault-popout-window"; +import { ROUTES_AFTER_EDIT_DELETION } from "../add-edit/add-edit-v2.component"; import { PopupFooterComponent } from "./../../../../../platform/popup/layout/popup-footer.component"; import { PopupHeaderComponent } from "./../../../../../platform/popup/layout/popup-header.component"; @@ -95,6 +99,7 @@ type LoadAction = AsyncActionsModule, PopOutComponent, CalloutModule, + BadgeModule, ], providers: [ { provide: ViewPasswordHistoryService, useClass: BrowserViewPasswordHistoryService }, @@ -112,8 +117,13 @@ export class ViewV2Component { collections$: Observable; loadAction: LoadAction; senderTabId?: number; + routeAfterDeletion?: ROUTES_AFTER_EDIT_DELETION; protected showFooter$: Observable; + protected userCanArchive$ = this.accountService.activeAccount$ + .pipe(getUserId) + .pipe(switchMap((userId) => this.archiveService.userCanArchive$(userId))); + protected archiveFlagEnabled$ = this.archiveService.hasArchiveFlagEnabled$; constructor( private passwordRepromptService: PasswordRepromptService, @@ -131,6 +141,8 @@ export class ViewV2Component { protected cipherAuthorizationService: CipherAuthorizationService, private copyCipherFieldService: CopyCipherFieldService, private popupScrollPositionService: VaultPopupScrollPositionService, + private archiveService: CipherArchiveService, + private archiveCipherUtilsService: ArchiveCipherUtilitiesService, ) { this.subscribeToParams(); } @@ -141,6 +153,9 @@ export class ViewV2Component { switchMap(async (params) => { this.loadAction = params.action; this.senderTabId = params.senderTabId ? parseInt(params.senderTabId, 10) : undefined; + this.routeAfterDeletion = params.routeAfterDeletion + ? params.routeAfterDeletion + : undefined; this.activeUserId = await firstValueFrom( this.accountService.activeAccount$.pipe(getUserId), @@ -220,7 +235,12 @@ export class ViewV2Component { return false; } void this.router.navigate(["/edit-cipher"], { - queryParams: { cipherId: this.cipher.id, type: this.cipher.type, isNew: false }, + queryParams: { + cipherId: this.cipher.id, + type: this.cipher.type, + isNew: false, + routeAfterDeletion: this.routeAfterDeletion, + }, }); return true; } @@ -272,6 +292,24 @@ export class ViewV2Component { }); }; + archive = async () => { + const cipherResponse = await this.archiveCipherUtilsService.archiveCipher(this.cipher, true); + + if (!cipherResponse) { + return; + } + this.cipher.archivedDate = new Date(cipherResponse.archivedDate); + }; + + unarchive = async () => { + const cipherResponse = await this.archiveCipherUtilsService.unarchiveCipher(this.cipher); + + if (!cipherResponse) { + return; + } + this.cipher.archivedDate = null; + }; + protected deleteCipher() { return this.cipher.isDeleted ? this.cipherService.deleteWithServer(this.cipher.id, this.activeUserId) diff --git a/apps/browser/src/vault/popup/guards/at-risk-passwords.guard.ts b/apps/browser/src/vault/popup/guards/at-risk-passwords.guard.ts index 03111859165..1b279e1078d 100644 --- a/apps/browser/src/vault/popup/guards/at-risk-passwords.guard.ts +++ b/apps/browser/src/vault/popup/guards/at-risk-passwords.guard.ts @@ -1,7 +1,13 @@ import { inject } from "@angular/core"; -import { CanActivateFn, Router } from "@angular/router"; +import { + ActivatedRouteSnapshot, + CanActivateFn, + Router, + RouterStateSnapshot, +} from "@angular/router"; import { combineLatest, map, switchMap } from "rxjs"; +import { authGuard } from "@bitwarden/angular/auth/guards"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; @@ -9,6 +15,24 @@ import { SecurityTaskType, TaskService } from "@bitwarden/common/vault/tasks"; import { filterOutNullish } from "@bitwarden/common/vault/utils/observable-utilities"; import { ToastService } from "@bitwarden/components"; +/** + * Wrapper around the main auth guard to redirect to login if not authenticated. + * This is necessary because the main auth guard returns false when not authenticated, + * which in a browser context may result in a blank extension page rather than a redirect. + */ +export const atRiskPasswordAuthGuard: CanActivateFn = async ( + route: ActivatedRouteSnapshot, + routerState: RouterStateSnapshot, +) => { + const router = inject(Router); + + const authGuardResponse = await authGuard(route, routerState); + if (authGuardResponse === true) { + return authGuardResponse; + } + return router.createUrlTree(["/login"]); +}; + export const canAccessAtRiskPasswords: CanActivateFn = () => { const accountService = inject(AccountService); const taskService = inject(TaskService); 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/services/vault-popup-autofill.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-autofill.service.spec.ts index 5818c6e32ff..94542009a89 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-autofill.service.spec.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-autofill.service.spec.ts @@ -378,8 +378,7 @@ describe("VaultPopupAutofillService", () => { expect(result).toBe(true); expect(mockCipher.login.uris).toHaveLength(1); expect(mockCipher.login.uris[0].uri).toBe(mockCurrentTab.url); - expect(mockCipherService.encrypt).toHaveBeenCalledWith(mockCipher, mockUserId); - expect(mockCipherService.updateWithServer).toHaveBeenCalledWith(mockEncryptedCipher); + expect(mockCipherService.updateWithServer).toHaveBeenCalledWith(mockCipher, mockUserId); }); it("should add a URI to the cipher when there are no existing URIs", async () => { diff --git a/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts b/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts index 6feeec29efc..025088e029e 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts @@ -426,8 +426,7 @@ export class VaultPopupAutofillService { const activeUserId = await firstValueFrom( this.accountService.activeAccount$.pipe(map((a) => a?.id)), ); - const encCipher = await this.cipherService.encrypt(cipher, activeUserId); - await this.cipherService.updateWithServer(encCipher); + await this.cipherService.updateWithServer(cipher, activeUserId); this.messagingService.send("editedCipher"); return true; } catch { diff --git a/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts index 513e159f7aa..7cd73279c3d 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-items.service.spec.ts @@ -3,8 +3,9 @@ import { TestBed } from "@angular/core/testing"; import { mock } from "jest-mock-extended"; import { BehaviorSubject, firstValueFrom, of, take, timeout } from "rxjs"; -import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { ProductTierType } from "@bitwarden/common/billing/enums"; diff --git a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts index 692e21d0084..1358c5faebe 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.spec.ts @@ -3,12 +3,13 @@ import { TestBed, discardPeriodicTasks, fakeAsync, tick } from "@angular/core/te import { FormBuilder } from "@angular/forms"; import { BehaviorSubject, skipWhile } from "rxjs"; -import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import * as vaultFilterSvc from "@bitwarden/angular/vault/vault-filter/services/vault-filter.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { ProductTierType } from "@bitwarden/common/billing/enums"; @@ -822,7 +823,6 @@ function createSeededVaultPopupListFiltersService( accountServiceMock, viewCacheServiceMock, restrictedItemTypesServiceMock, - configService, ); }); diff --git a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts index 08db7d5d4ab..85c415d01fe 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-list-filters.service.ts @@ -14,23 +14,21 @@ import { take, } from "rxjs"; -import { - CollectionService, - CollectionTypes, - CollectionView, -} from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { ViewCacheService } from "@bitwarden/angular/platform/view-cache"; import { DynamicTreeNode } from "@bitwarden/angular/vault/vault-filter/models/dynamic-tree-node.model"; import { sortDefaultCollections } from "@bitwarden/angular/vault/vault-filter/services/vault-filter.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { + CollectionView, + CollectionTypes, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { ProductTierType } from "@bitwarden/common/billing/enums"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { asUuid } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -191,7 +189,6 @@ export class VaultPopupListFiltersService { private accountService: AccountService, private viewCacheService: ViewCacheService, private restrictedItemTypesService: RestrictedItemTypesService, - private configService: ConfigService, ) { this.filterForm.controls.organization.valueChanges .pipe(takeUntilDestroyed()) @@ -455,19 +452,15 @@ export class VaultPopupListFiltersService { ), this.collectionService.decryptedCollections$(userId), this.organizationService.memberOrganizations$(userId), - this.configService.getFeatureFlag$(FeatureFlag.CreateDefaultLocation), ]), ), - map(([filters, allCollections, orgs, defaultVaultEnabled]) => { + map(([filters, allCollections, orgs]) => { const orgFilterId = filters.organization?.id ?? null; // When the organization filter is selected, filter out collections that do not belong to the selected organization const filtered = orgFilterId ? allCollections.filter((c) => c.organizationId === orgFilterId) : allCollections; - if (!defaultVaultEnabled) { - return filtered; - } return sortDefaultCollections(filtered, orgs, this.i18nService.collator); }), map((fullList) => { diff --git a/apps/browser/src/vault/popup/services/vault-popup-scroll-position.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-scroll-position.service.spec.ts index 562375f8f85..af21f664f2d 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-scroll-position.service.spec.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-scroll-position.service.spec.ts @@ -1,4 +1,3 @@ -import { CdkVirtualScrollableElement } from "@angular/cdk/scrolling"; import { fakeAsync, TestBed, tick } from "@angular/core/testing"; import { NavigationEnd, Router } from "@angular/router"; import { Subject, Subscription } from "rxjs"; @@ -66,21 +65,18 @@ describe("VaultPopupScrollPositionService", () => { }); describe("start", () => { - const elementScrolled$ = new Subject(); - const focus = jest.fn(); - const nativeElement = { - scrollTop: 0, - querySelector: jest.fn(() => ({ focus })), - addEventListener: jest.fn(), - style: { - visibility: "", - }, - }; - const virtualElement = { - elementScrolled: () => elementScrolled$, - getElementRef: () => ({ nativeElement }), - scrollTo: jest.fn(), - } as unknown as CdkVirtualScrollableElement; + let scrollElement: HTMLElement; + + beforeEach(() => { + scrollElement = document.createElement("div"); + + (scrollElement as any).scrollTo = jest.fn(function scrollTo(opts: { top?: number }) { + if (opts?.top != null) { + (scrollElement as any).scrollTop = opts.top; + } + }); + (scrollElement as any).scrollTop = 0; + }); afterEach(() => { // remove the actual subscription created by `.subscribe` @@ -89,47 +85,55 @@ describe("VaultPopupScrollPositionService", () => { describe("initial scroll position", () => { beforeEach(() => { - (virtualElement.scrollTo as jest.Mock).mockClear(); - nativeElement.querySelector.mockClear(); + ((scrollElement as any).scrollTo as jest.Mock).mockClear(); }); it("does not scroll when `scrollPosition` is null", () => { service["scrollPosition"] = null; - service.start(virtualElement); + service.start(scrollElement); - expect(virtualElement.scrollTo).not.toHaveBeenCalled(); + expect((scrollElement as any).scrollTo).not.toHaveBeenCalled(); }); - it("scrolls the virtual element to `scrollPosition`", fakeAsync(() => { + it("scrolls the element to `scrollPosition` (async via setTimeout)", fakeAsync(() => { service["scrollPosition"] = 500; - nativeElement.scrollTop = 500; - service.start(virtualElement); + service.start(scrollElement); tick(); - expect(virtualElement.scrollTo).toHaveBeenCalledWith({ behavior: "instant", top: 500 }); + expect((scrollElement as any).scrollTo).toHaveBeenCalledWith({ + behavior: "instant", + top: 500, + }); + expect((scrollElement as any).scrollTop).toBe(500); })); }); describe("scroll listener", () => { it("unsubscribes from any existing subscription", () => { - service.start(virtualElement); + service.start(scrollElement); expect(unsubscribe).toHaveBeenCalled(); }); - it("subscribes to `elementScrolled`", fakeAsync(() => { - virtualElement.measureScrollOffset = jest.fn(() => 455); + it("stores scrollTop on subsequent scroll events (skips first)", fakeAsync(() => { + service["scrollPosition"] = null; - service.start(virtualElement); + service.start(scrollElement); - elementScrolled$.next(null); // first subscription is skipped by `skip(1)` - elementScrolled$.next(null); + // First scroll event is intentionally ignored (equivalent to old skip(1)). + (scrollElement as any).scrollTop = 111; + scrollElement.dispatchEvent(new Event("scroll")); + tick(); + + expect(service["scrollPosition"]).toBeNull(); + + // Second scroll event should persist. + (scrollElement as any).scrollTop = 455; + scrollElement.dispatchEvent(new Event("scroll")); tick(); - expect(virtualElement.measureScrollOffset).toHaveBeenCalledTimes(1); - expect(virtualElement.measureScrollOffset).toHaveBeenCalledWith("top"); expect(service["scrollPosition"]).toBe(455); })); }); diff --git a/apps/browser/src/vault/popup/services/vault-popup-scroll-position.service.ts b/apps/browser/src/vault/popup/services/vault-popup-scroll-position.service.ts index 5bfe0ec9331..7261fdd6633 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-scroll-position.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-scroll-position.service.ts @@ -1,8 +1,7 @@ -import { CdkVirtualScrollableElement } from "@angular/cdk/scrolling"; import { inject, Injectable } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { NavigationEnd, Router } from "@angular/router"; -import { filter, skip, Subscription } from "rxjs"; +import { filter, fromEvent, Subscription } from "rxjs"; @Injectable({ providedIn: "root", @@ -31,24 +30,25 @@ export class VaultPopupScrollPositionService { } /** Scrolls the user to the stored scroll position and starts tracking scroll of the page. */ - start(virtualScrollElement: CdkVirtualScrollableElement) { + start(scrollElement: HTMLElement) { if (this.hasScrollPosition()) { // Use `setTimeout` to scroll after rendering is complete setTimeout(() => { - virtualScrollElement.scrollTo({ top: this.scrollPosition!, behavior: "instant" }); + scrollElement.scrollTo({ top: this.scrollPosition!, behavior: "instant" }); }); } this.scrollSubscription?.unsubscribe(); // Skip the first scroll event to avoid settings the scroll from the above `scrollTo` call - this.scrollSubscription = virtualScrollElement - ?.elementScrolled() - .pipe(skip(1)) - .subscribe(() => { - const offset = virtualScrollElement.measureScrollOffset("top"); - this.scrollPosition = offset; - }); + let skipped = false; + this.scrollSubscription = fromEvent(scrollElement, "scroll").subscribe(() => { + if (!skipped) { + skipped = true; + return; + } + this.scrollPosition = scrollElement.scrollTop; + }); } /** Stops the scroll listener from updating the stored location. */ diff --git a/apps/browser/src/vault/popup/settings/admin-settings.component.html b/apps/browser/src/vault/popup/settings/admin-settings.component.html new file mode 100644 index 00000000000..5e67750278f --- /dev/null +++ b/apps/browser/src/vault/popup/settings/admin-settings.component.html @@ -0,0 +1,41 @@ + + + + + + + +
    + @if (showAutoConfirmSpotlight$ | async) { + +
    + + {{ "autoConfirmOnboardingCallout" | i18n }} + + + +
    +
    + } + +
    + + + + + {{ "automaticUserConfirmation" | i18n }} + + + {{ "automaticUserConfirmationHint" | i18n }} + + +
    +
    +
    diff --git a/apps/browser/src/vault/popup/settings/admin-settings.component.spec.ts b/apps/browser/src/vault/popup/settings/admin-settings.component.spec.ts new file mode 100644 index 00000000000..f7b4e7b473a --- /dev/null +++ b/apps/browser/src/vault/popup/settings/admin-settings.component.spec.ts @@ -0,0 +1,199 @@ +import { ChangeDetectionStrategy, Component, input } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { provideNoopAnimations } from "@angular/platform-browser/animations"; +import { mock, MockProxy } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { NudgesService, NudgeType } from "@bitwarden/angular/vault"; +import { AutoConfirmState, AutomaticUserConfirmationService } from "@bitwarden/auto-confirm"; +import { PopOutComponent } from "@bitwarden/browser/platform/popup/components/pop-out.component"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { mockAccountServiceWith } from "@bitwarden/common/spec"; +import { UserId } from "@bitwarden/common/types/guid"; +import { DialogService } from "@bitwarden/components"; + +import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; +import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component"; + +import { AdminSettingsComponent } from "./admin-settings.component"; + +@Component({ + selector: "popup-header", + template: ``, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class MockPopupHeaderComponent { + readonly pageTitle = input(); + readonly backAction = input<() => void>(); +} + +@Component({ + selector: "popup-page", + template: ``, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class MockPopupPageComponent { + readonly loading = input(); +} + +@Component({ + selector: "app-pop-out", + template: ``, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class MockPopOutComponent { + readonly show = input(true); +} + +describe("AdminSettingsComponent", () => { + let component: AdminSettingsComponent; + let fixture: ComponentFixture; + let autoConfirmService: MockProxy; + let nudgesService: MockProxy; + let mockDialogService: MockProxy; + + const userId = "test-user-id" as UserId; + const mockAutoConfirmState: AutoConfirmState = { + enabled: false, + showSetupDialog: true, + showBrowserNotification: false, + }; + + beforeEach(async () => { + autoConfirmService = mock(); + nudgesService = mock(); + mockDialogService = mock(); + + autoConfirmService.configuration$.mockReturnValue(of(mockAutoConfirmState)); + autoConfirmService.upsert.mockResolvedValue(undefined); + nudgesService.showNudgeSpotlight$.mockReturnValue(of(false)); + + await TestBed.configureTestingModule({ + imports: [AdminSettingsComponent], + providers: [ + provideNoopAnimations(), + { provide: AccountService, useValue: mockAccountServiceWith(userId) }, + { provide: AutomaticUserConfirmationService, useValue: autoConfirmService }, + { provide: DialogService, useValue: mockDialogService }, + { provide: NudgesService, useValue: nudgesService }, + { provide: I18nService, useValue: { t: (key: string) => key } }, + ], + }) + .overrideComponent(AdminSettingsComponent, { + remove: { + imports: [PopupHeaderComponent, PopupPageComponent, PopOutComponent], + }, + add: { + imports: [MockPopupHeaderComponent, MockPopupPageComponent, MockPopOutComponent], + }, + }) + .compileComponents(); + + fixture = TestBed.createComponent(AdminSettingsComponent); + component = fixture.componentInstance; + }); + + describe("initialization", () => { + it("should populate form with current auto-confirm state", async () => { + const mockState: AutoConfirmState = { + enabled: true, + showSetupDialog: false, + showBrowserNotification: true, + }; + autoConfirmService.configuration$.mockReturnValue(of(mockState)); + + await component.ngOnInit(); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component["adminForm"].value).toEqual({ + autoConfirm: true, + }); + }); + + it("should populate form with disabled auto-confirm state", async () => { + await component.ngOnInit(); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component["adminForm"].value).toEqual({ + autoConfirm: false, + }); + }); + }); + + describe("spotlight", () => { + beforeEach(async () => { + await component.ngOnInit(); + fixture.detectChanges(); + }); + + it("should expose showAutoConfirmSpotlight$ observable", (done) => { + nudgesService.showNudgeSpotlight$.mockReturnValue(of(true)); + + const newFixture = TestBed.createComponent(AdminSettingsComponent); + const newComponent = newFixture.componentInstance; + + newComponent["showAutoConfirmSpotlight$"].subscribe((show) => { + expect(show).toBe(true); + expect(nudgesService.showNudgeSpotlight$).toHaveBeenCalledWith( + NudgeType.AutoConfirmNudge, + userId, + ); + done(); + }); + }); + + it("should dismiss spotlight and update state", async () => { + autoConfirmService.upsert.mockResolvedValue(); + + await component.dismissSpotlight(); + + expect(autoConfirmService.upsert).toHaveBeenCalledWith(userId, { + ...mockAutoConfirmState, + showBrowserNotification: false, + }); + }); + + it("should use current userId when dismissing spotlight", async () => { + autoConfirmService.upsert.mockResolvedValue(); + + await component.dismissSpotlight(); + + expect(autoConfirmService.upsert).toHaveBeenCalledWith(userId, expect.any(Object)); + }); + + it("should preserve existing state when dismissing spotlight", async () => { + const customState: AutoConfirmState = { + enabled: true, + showSetupDialog: false, + showBrowserNotification: true, + }; + autoConfirmService.configuration$.mockReturnValue(of(customState)); + autoConfirmService.upsert.mockResolvedValue(); + + await component.dismissSpotlight(); + + expect(autoConfirmService.upsert).toHaveBeenCalledWith(userId, { + ...customState, + showBrowserNotification: false, + }); + }); + }); + + describe("form validation", () => { + beforeEach(async () => { + await component.ngOnInit(); + fixture.detectChanges(); + }); + + it("should have a valid form", () => { + expect(component["adminForm"].valid).toBe(true); + }); + + it("should have autoConfirm control", () => { + expect(component["adminForm"].controls.autoConfirm).toBeDefined(); + }); + }); +}); diff --git a/apps/browser/src/vault/popup/settings/admin-settings.component.ts b/apps/browser/src/vault/popup/settings/admin-settings.component.ts new file mode 100644 index 00000000000..e4b676525ed --- /dev/null +++ b/apps/browser/src/vault/popup/settings/admin-settings.component.ts @@ -0,0 +1,121 @@ +import { CommonModule } from "@angular/common"; +import { + ChangeDetectionStrategy, + Component, + DestroyRef, + OnInit, + signal, + WritableSignal, +} from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { FormBuilder, ReactiveFormsModule } from "@angular/forms"; +import { firstValueFrom, map, Observable, of, switchMap, tap, withLatestFrom } from "rxjs"; + +import { NudgesService, NudgeType } from "@bitwarden/angular/vault"; +import { SpotlightComponent } from "@bitwarden/angular/vault/components/spotlight/spotlight.component"; +import { + AutoConfirmWarningDialogComponent, + AutomaticUserConfirmationService, +} from "@bitwarden/auto-confirm"; +import { PopOutComponent } from "@bitwarden/browser/platform/popup/components/pop-out.component"; +import { PopupHeaderComponent } from "@bitwarden/browser/platform/popup/layout/popup-header.component"; +import { PopupPageComponent } from "@bitwarden/browser/platform/popup/layout/popup-page.component"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { + BitIconButtonComponent, + CardComponent, + DialogService, + FormFieldModule, + SwitchComponent, +} from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; +import { UserId } from "@bitwarden/user-core"; + +@Component({ + templateUrl: "./admin-settings.component.html", + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + CommonModule, + PopupPageComponent, + PopupHeaderComponent, + PopOutComponent, + FormFieldModule, + ReactiveFormsModule, + SwitchComponent, + CardComponent, + SpotlightComponent, + BitIconButtonComponent, + I18nPipe, + ], +}) +export class AdminSettingsComponent implements OnInit { + private userId$: Observable = this.accountService.activeAccount$.pipe(getUserId); + + protected readonly formLoading: WritableSignal = signal(true); + protected adminForm = this.formBuilder.group({ + autoConfirm: false, + }); + protected showAutoConfirmSpotlight$: Observable = this.userId$.pipe( + switchMap((userId) => + this.nudgesService.showNudgeSpotlight$(NudgeType.AutoConfirmNudge, userId), + ), + ); + + constructor( + private formBuilder: FormBuilder, + private accountService: AccountService, + private autoConfirmService: AutomaticUserConfirmationService, + private destroyRef: DestroyRef, + private dialogService: DialogService, + private nudgesService: NudgesService, + ) {} + + async ngOnInit() { + const userId = await firstValueFrom(this.userId$); + const autoConfirmEnabled = ( + await firstValueFrom(this.autoConfirmService.configuration$(userId)) + ).enabled; + this.adminForm.setValue({ autoConfirm: autoConfirmEnabled }); + + this.formLoading.set(false); + + this.adminForm.controls.autoConfirm.valueChanges + .pipe( + switchMap((newValue) => { + if (newValue) { + return this.confirm(); + } + return of(false); + }), + withLatestFrom(this.autoConfirmService.configuration$(userId)), + switchMap(([newValue, existingState]) => + this.autoConfirmService.upsert(userId, { + ...existingState, + enabled: newValue, + showBrowserNotification: false, + }), + ), + takeUntilDestroyed(this.destroyRef), + ) + .subscribe(); + } + + private confirm(): Observable { + return AutoConfirmWarningDialogComponent.open(this.dialogService).closed.pipe( + map((result) => result ?? false), + tap((result) => { + if (!result) { + this.adminForm.setValue({ autoConfirm: false }, { emitEvent: false }); + } + }), + ); + } + + async dismissSpotlight() { + const userId = await firstValueFrom(this.userId$); + const state = await firstValueFrom(this.autoConfirmService.configuration$(userId)); + + await this.autoConfirmService.upsert(userId, { ...state, showBrowserNotification: false }); + } +} diff --git a/apps/browser/src/vault/popup/settings/appearance-v2.component.ts b/apps/browser/src/vault/popup/settings/appearance-v2.component.ts index e6515ae7461..e02ccf25f3e 100644 --- a/apps/browser/src/vault/popup/settings/appearance-v2.component.ts +++ b/apps/browser/src/vault/popup/settings/appearance-v2.component.ts @@ -79,7 +79,7 @@ export class AppearanceV2Component implements OnInit { protected readonly widthOptions: Option[] = [ { label: this.i18nService.t("default"), value: "default" }, { label: this.i18nService.t("wide"), value: "wide" }, - { label: this.i18nService.t("extraWide"), value: "extra-wide" }, + { label: this.i18nService.t("narrow"), value: "narrow" }, ]; constructor( diff --git a/apps/browser/src/vault/popup/settings/archive.component.html b/apps/browser/src/vault/popup/settings/archive.component.html index a7b23dc5122..01ac799ba29 100644 --- a/apps/browser/src/vault/popup/settings/archive.component.html +++ b/apps/browser/src/vault/popup/settings/archive.component.html @@ -5,6 +5,21 @@
    + @if (showSubscriptionEndedMessaging$ | async) { + +
    + +

    {{ "premiumSubscriptionEnded" | i18n }}

    +
    +

    + {{ "archivePremiumRestart" | i18n }} +

    + +
    + } + @if (archivedCiphers$ | async; as archivedItems) { @if (archivedItems.length) { @@ -27,9 +42,23 @@
    {{ cipher.name }} - @if (CipherViewLikeUtils.hasAttachments(cipher)) { - - } +
    + @if (cipher.organizationId) { + + } + @if (CipherViewLikeUtils.hasAttachments(cipher)) { + + } +
    {{ CipherViewLikeUtils.subtitle(cipher) }} @@ -48,6 +77,15 @@ + @if (canAssignCollections$ | async) { + + } diff --git a/apps/browser/src/vault/popup/settings/archive.component.spec.ts b/apps/browser/src/vault/popup/settings/archive.component.spec.ts new file mode 100644 index 00000000000..2f5cfb8d824 --- /dev/null +++ b/apps/browser/src/vault/popup/settings/archive.component.spec.ts @@ -0,0 +1,140 @@ +import { TestBed } from "@angular/core/testing"; +import { Router } from "@angular/router"; +import { mock } from "jest-mock-extended"; +import { BehaviorSubject, of } from "rxjs"; + +import { CollectionService } from "@bitwarden/admin-console/common"; +import { PopupRouterCacheService } from "@bitwarden/browser/platform/popup/view-cache/popup-router-cache.service"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CipherViewLike } from "@bitwarden/common/vault/utils/cipher-view-like-utils"; +import { DialogService, ToastService } from "@bitwarden/components"; +import { LogService } from "@bitwarden/logging"; +import { PasswordRepromptService } from "@bitwarden/vault"; + +import { ArchiveComponent } from "./archive.component"; + +// 'qrcode-parser' is used by `BrowserTotpCaptureService` but is an es6 module that jest can't compile. +// Mock the entire module here to prevent jest from throwing an error. I wasn't able to find a way to mock the +// `BrowserTotpCaptureService` where jest would not load the file in the first place. +jest.mock("qrcode-parser", () => {}); + +describe("ArchiveComponent", () => { + let component: ArchiveComponent; + + let hasOrganizations: jest.Mock; + let decryptedCollections$: jest.Mock; + let navigate: jest.Mock; + let showPasswordPrompt: jest.Mock; + + beforeAll(async () => { + navigate = jest.fn(); + showPasswordPrompt = jest.fn().mockResolvedValue(true); + hasOrganizations = jest.fn(); + decryptedCollections$ = jest.fn(); + + await TestBed.configureTestingModule({ + providers: [ + { provide: Router, useValue: { navigate } }, + { + provide: AccountService, + useValue: { activeAccount$: new BehaviorSubject({ id: "user-id" }) }, + }, + { provide: PasswordRepromptService, useValue: { showPasswordPrompt } }, + { provide: OrganizationService, useValue: { hasOrganizations } }, + { provide: CollectionService, useValue: { decryptedCollections$ } }, + { provide: DialogService, useValue: mock() }, + { provide: CipherService, useValue: mock() }, + { provide: CipherArchiveService, useValue: mock() }, + { provide: ToastService, useValue: mock() }, + { provide: PopupRouterCacheService, useValue: mock() }, + { provide: PlatformUtilsService, useValue: mock() }, + { provide: LogService, useValue: mock() }, + { provide: I18nService, useValue: { t: (key: string) => key } }, + ], + }).compileComponents(); + + const fixture = TestBed.createComponent(ArchiveComponent); + component = fixture.componentInstance; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe("canAssignCollections$", () => { + it("emits true when user has organizations and editable collections", (done) => { + hasOrganizations.mockReturnValue(of(true)); + decryptedCollections$.mockReturnValue(of([{ id: "col-1", readOnly: false }] as any)); + + component["canAssignCollections$"].subscribe((result) => { + expect(result).toBe(true); + done(); + }); + }); + + it("emits false when user has no organizations", (done) => { + hasOrganizations.mockReturnValue(of(false)); + decryptedCollections$.mockReturnValue(of([{ id: "col-1", readOnly: false }] as any)); + + component["canAssignCollections$"].subscribe((result) => { + expect(result).toBe(false); + done(); + }); + }); + + it("emits false when all collections are read-only", (done) => { + hasOrganizations.mockReturnValue(of(true)); + decryptedCollections$.mockReturnValue(of([{ id: "col-1", readOnly: true }] as any)); + + component["canAssignCollections$"].subscribe((result) => { + expect(result).toBe(false); + done(); + }); + }); + }); + + describe("conditionallyNavigateToAssignCollections", () => { + const mockCipher = { + id: "cipher-1", + reprompt: 0, + } as CipherViewLike; + + it("navigates to assign-collections when reprompt is not required", async () => { + await component.conditionallyNavigateToAssignCollections(mockCipher); + + expect(navigate).toHaveBeenCalledWith(["/assign-collections"], { + queryParams: { cipherId: "cipher-1" }, + }); + }); + + it("prompts for password when reprompt is required", async () => { + const cipherWithReprompt = { ...mockCipher, reprompt: 1 }; + + await component.conditionallyNavigateToAssignCollections( + cipherWithReprompt as CipherViewLike, + ); + + expect(showPasswordPrompt).toHaveBeenCalled(); + expect(navigate).toHaveBeenCalledWith(["/assign-collections"], { + queryParams: { cipherId: "cipher-1" }, + }); + }); + + it("does not navigate when password prompt is cancelled", async () => { + const cipherWithReprompt = { ...mockCipher, reprompt: 1 }; + showPasswordPrompt.mockResolvedValueOnce(false); + + await component.conditionallyNavigateToAssignCollections( + cipherWithReprompt as CipherViewLike, + ); + + expect(showPasswordPrompt).toHaveBeenCalled(); + expect(navigate).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/browser/src/vault/popup/settings/archive.component.ts b/apps/browser/src/vault/popup/settings/archive.component.ts index b1c78444a3f..8a81d733039 100644 --- a/apps/browser/src/vault/popup/settings/archive.component.ts +++ b/apps/browser/src/vault/popup/settings/archive.component.ts @@ -1,9 +1,13 @@ import { CommonModule } from "@angular/common"; import { Component, inject } from "@angular/core"; +import { toSignal } from "@angular/core/rxjs-interop"; import { Router } from "@angular/router"; -import { firstValueFrom, map, Observable, startWith, switchMap } from "rxjs"; +import { combineLatest, firstValueFrom, map, Observable, startWith, switchMap } from "rxjs"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -25,16 +29,20 @@ import { SectionHeaderComponent, ToastService, TypographyModule, + CardComponent, + ButtonComponent, } from "@bitwarden/components"; import { CanDeleteCipherDirective, DecryptionFailureDialogComponent, + OrgIconDirective, PasswordRepromptService, } from "@bitwarden/vault"; 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"; +import { ROUTES_AFTER_EDIT_DELETION } from "../components/vault-v2/add-edit/add-edit-v2.component"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @@ -55,6 +63,9 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co SectionComponent, SectionHeaderComponent, TypographyModule, + OrgIconDirective, + CardComponent, + ButtonComponent, ], }) export class ArchiveComponent { @@ -67,13 +78,38 @@ export class ArchiveComponent { private i18nService = inject(I18nService); private cipherArchiveService = inject(CipherArchiveService); private passwordRepromptService = inject(PasswordRepromptService); + private organizationService = inject(OrganizationService); + private collectionService = inject(CollectionService); private userId$: Observable = this.accountService.activeAccount$.pipe(getUserId); + private readonly orgMap = toSignal( + this.userId$.pipe( + switchMap((userId) => + this.organizationService.organizations$(userId).pipe( + map((orgs) => { + const map = new Map(); + for (const org of orgs) { + map.set(org.id, org); + } + return map; + }), + ), + ), + ), + ); + + private readonly collections = toSignal( + this.userId$.pipe(switchMap((userId) => this.collectionService.decryptedCollections$(userId))), + ); + protected archivedCiphers$ = this.userId$.pipe( switchMap((userId) => this.cipherArchiveService.archivedCiphers$(userId)), ); + protected userCanArchive$ = this.userId$.pipe( + switchMap((userId) => this.cipherArchiveService.userCanArchive$(userId)), + ); protected CipherViewLikeUtils = CipherViewLikeUtils; protected loading$ = this.archivedCiphers$.pipe( @@ -81,13 +117,39 @@ export class ArchiveComponent { startWith(true), ); + protected canAssignCollections$ = this.userId$.pipe( + switchMap((userId) => { + return combineLatest([ + this.organizationService.hasOrganizations(userId), + this.collectionService.decryptedCollections$(userId), + ]).pipe( + map(([hasOrgs, collections]) => { + const canEditCollections = collections.some((c) => !c.readOnly); + return hasOrgs && canEditCollections; + }), + ); + }), + ); + + protected showSubscriptionEndedMessaging$ = this.userId$.pipe( + switchMap((userId) => this.cipherArchiveService.showSubscriptionEndedMessaging$(userId)), + ); + + async navigateToPremium() { + await this.router.navigate(["/premium"]); + } + async view(cipher: CipherViewLike) { if (!(await this.canInteract(cipher))) { return; } await this.router.navigate(["/view-cipher"], { - queryParams: { cipherId: cipher.id, type: cipher.type }, + queryParams: { + cipherId: cipher.id, + type: cipher.type, + routeAfterDeletion: ROUTES_AFTER_EDIT_DELETION.archive, + }, }); } @@ -97,7 +159,11 @@ export class ArchiveComponent { } await this.router.navigate(["/edit-cipher"], { - queryParams: { cipherId: cipher.id, type: cipher.type }, + queryParams: { + cipherId: cipher.id, + type: cipher.type, + routeAfterDeletion: ROUTES_AFTER_EDIT_DELETION.archive, + }, }); } @@ -173,6 +239,17 @@ export class ArchiveComponent { }); } + /** Prompts for password when necessary then navigates to the assign collections route */ + async conditionallyNavigateToAssignCollections(cipher: CipherViewLike) { + if (cipher.reprompt && !(await this.passwordRepromptService.showPasswordPrompt())) { + return; + } + + await this.router.navigate(["/assign-collections"], { + queryParams: { cipherId: cipher.id }, + }); + } + /** * Check if the user is able to interact with the cipher * (password re-prompt / decryption failure checks). @@ -189,4 +266,22 @@ export class ArchiveComponent { return this.passwordRepromptService.passwordRepromptCheck(cipher); } + + /** + * Get the organization tier type for the given cipher. + */ + orgTierType({ organizationId }: CipherViewLike) { + return this.orgMap()?.get(organizationId as string)?.productTierType; + } + + /** + * Get the organization icon tooltip for the given cipher. + */ + orgIconTooltip({ collectionIds }: CipherViewLike) { + if (collectionIds.length !== 1) { + return this.i18nService.t("nCollections", collectionIds.length); + } + + return this.collections()?.find((c) => c.id === collectionIds[0])?.name; + } } diff --git a/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.ts b/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.ts index bad6011b2d8..edebdab062f 100644 --- a/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.ts +++ b/apps/browser/src/vault/popup/settings/trash-list-items-container/trash-list-items-container.component.ts @@ -115,15 +115,22 @@ export class TrashListItemsContainerComponent { } async restore(cipher: PopupCipherViewLike) { + let toastMessage; try { const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); await this.cipherService.restoreWithServer(cipher.id as string, activeUserId); + if (cipher.archivedDate) { + toastMessage = this.i18nService.t("archivedItemRestored"); + } else { + toastMessage = this.i18nService.t("restoredItem"); + } + await this.router.navigate(["/trash"]); this.toastService.showToast({ variant: "success", title: null, - message: this.i18nService.t("restoredItem"), + message: toastMessage, }); } catch (e) { this.logService.error(e); 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 225640137e8..ad009c7a60b 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 @@ diff --git a/apps/browser/src/vault/popup/settings/vault-settings-v2.component.spec.ts b/apps/browser/src/vault/popup/settings/vault-settings-v2.component.spec.ts new file mode 100644 index 00000000000..554570de7f9 --- /dev/null +++ b/apps/browser/src/vault/popup/settings/vault-settings-v2.component.spec.ts @@ -0,0 +1,205 @@ +import { ChangeDetectionStrategy, Component, DebugElement, input } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; +import { provideRouter, Router } from "@angular/router"; +import { mock } from "jest-mock-extended"; +import { BehaviorSubject } from "rxjs"; + +import { NudgesService } from "@bitwarden/angular/vault"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; +import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { DialogService, ToastService } from "@bitwarden/components"; + +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"; + +import { VaultSettingsV2Component } from "./vault-settings-v2.component"; + +@Component({ + selector: "popup-header", + template: ``, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class MockPopupHeaderComponent { + readonly pageTitle = input(); + readonly showBackButton = input(); +} + +@Component({ + selector: "popup-page", + template: ``, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class MockPopupPageComponent {} + +@Component({ + selector: "app-pop-out", + template: ``, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class MockPopOutComponent { + readonly show = input(true); +} + +describe("VaultSettingsV2Component", () => { + let component: VaultSettingsV2Component; + let fixture: ComponentFixture; + let router: Router; + let mockCipherArchiveService: jest.Mocked; + + const mockActiveAccount$ = new BehaviorSubject<{ id: string }>({ + id: "user-id", + }); + const mockUserCanArchive$ = new BehaviorSubject(false); + const mockHasArchiveFlagEnabled$ = new BehaviorSubject(true); + const mockArchivedCiphers$ = new BehaviorSubject([]); + const mockShowNudgeBadge$ = new BehaviorSubject(false); + + const queryByTestId = (testId: string): DebugElement | null => { + return fixture.debugElement.query(By.css(`[data-test-id="${testId}"]`)); + }; + + const setArchiveState = ( + canArchive: boolean, + archivedItems: CipherView[] = [], + flagEnabled = true, + ) => { + mockUserCanArchive$.next(canArchive); + mockArchivedCiphers$.next(archivedItems); + mockHasArchiveFlagEnabled$.next(flagEnabled); + fixture.detectChanges(); + }; + + beforeEach(async () => { + // Reset BehaviorSubjects to initial values + mockUserCanArchive$.next(false); + mockHasArchiveFlagEnabled$.next(true); + mockArchivedCiphers$.next([]); + mockShowNudgeBadge$.next(false); + + mockCipherArchiveService = mock({ + userCanArchive$: jest.fn().mockReturnValue(mockUserCanArchive$), + archivedCiphers$: jest.fn().mockReturnValue(mockArchivedCiphers$), + }); + mockCipherArchiveService.hasArchiveFlagEnabled$ = mockHasArchiveFlagEnabled$.asObservable(); + + await TestBed.configureTestingModule({ + imports: [VaultSettingsV2Component], + providers: [ + provideRouter([ + { path: "archive", component: VaultSettingsV2Component }, + { path: "premium", component: VaultSettingsV2Component }, + ]), + { provide: SyncService, useValue: mock() }, + { provide: ToastService, useValue: mock() }, + { provide: ConfigService, useValue: mock() }, + { provide: DialogService, useValue: mock() }, + { provide: I18nService, useValue: { t: (key: string) => key } }, + { provide: CipherArchiveService, useValue: mockCipherArchiveService }, + { + provide: NudgesService, + useValue: { showNudgeBadge$: jest.fn().mockReturnValue(mockShowNudgeBadge$) }, + }, + + { + provide: BillingAccountProfileStateService, + useValue: mock(), + }, + { + provide: AccountService, + useValue: { activeAccount$: mockActiveAccount$ }, + }, + ], + }) + .overrideComponent(VaultSettingsV2Component, { + remove: { + imports: [PopupHeaderComponent, PopupPageComponent, PopOutComponent], + }, + add: { + imports: [MockPopupHeaderComponent, MockPopupPageComponent, MockPopOutComponent], + }, + }) + .compileComponents(); + + fixture = TestBed.createComponent(VaultSettingsV2Component); + component = fixture.componentInstance; + router = TestBed.inject(Router); + jest.spyOn(router, "navigate"); + }); + + describe("archive link", () => { + it("shows direct archive link when user can archive", () => { + setArchiveState(true); + + const archiveLink = queryByTestId("archive-link"); + + expect(archiveLink?.nativeElement.getAttribute("routerLink")).toBe("/archive"); + }); + + it("routes to archive when user has archived items but cannot archive", async () => { + setArchiveState(false, [{ id: "cipher1" } as CipherView]); + + const premiumArchiveLink = queryByTestId("premium-archive-link"); + + premiumArchiveLink?.nativeElement.click(); + await fixture.whenStable(); + + expect(router.navigate).toHaveBeenCalledWith(["/archive"]); + }); + + it("prompts for premium when user cannot archive and has no archived items", async () => { + setArchiveState(false, []); + const badge = component["premiumBadgeComponent"](); + jest.spyOn(badge!, "promptForPremium"); + + const premiumArchiveLink = queryByTestId("premium-archive-link"); + + premiumArchiveLink?.nativeElement.click(); + await fixture.whenStable(); + + expect(badge!.promptForPremium).toHaveBeenCalled(); + }); + }); + + describe("archive visibility", () => { + it("displays archive link when user can archive", () => { + setArchiveState(true); + + const archiveLink = queryByTestId("archive-link"); + + expect(archiveLink).toBeTruthy(); + expect(component["userCanArchive"]()).toBe(true); + }); + + it("hides archive link when feature flag is disabled", () => { + setArchiveState(false, [], false); + + const archiveLink = queryByTestId("archive-link"); + const premiumArchiveLink = queryByTestId("premium-archive-link"); + + expect(archiveLink).toBeNull(); + expect(premiumArchiveLink).toBeNull(); + expect(component["showArchiveItem"]()).toBe(false); + }); + + it("shows premium badge when user has no archived items and cannot archive", () => { + setArchiveState(false, []); + + expect(component["premiumBadgeComponent"]()).toBeTruthy(); + expect(component["userHasArchivedItems"]()).toBe(false); + }); + + it("shows premium badge when user has archived items but cannot archive", () => { + setArchiveState(false, [{ id: "cipher1" } as CipherView]); + + expect(component["premiumBadgeComponent"]()).toBeTruthy(); + expect(component["userHasArchivedItems"]()).toBe(true); + }); + }); +}); diff --git a/apps/browser/src/vault/popup/settings/vault-settings-v2.component.ts b/apps/browser/src/vault/popup/settings/vault-settings-v2.component.ts index c6db820c232..c1d90d678cb 100644 --- a/apps/browser/src/vault/popup/settings/vault-settings-v2.component.ts +++ b/apps/browser/src/vault/popup/settings/vault-settings-v2.component.ts @@ -1,5 +1,5 @@ import { CommonModule } from "@angular/common"; -import { Component, OnDestroy, OnInit } from "@angular/core"; +import { Component, OnDestroy, OnInit, viewChild } from "@angular/core"; import { toSignal } from "@angular/core/rxjs-interop"; import { Router, RouterModule } from "@angular/router"; import { firstValueFrom, map, switchMap } from "rxjs"; @@ -42,6 +42,8 @@ import { BrowserPremiumUpgradePromptService } from "../services/browser-premium- ], }) export class VaultSettingsV2Component implements OnInit, OnDestroy { + private readonly premiumBadgeComponent = viewChild(PremiumBadgeComponent); + lastSync = "--"; private userId$ = this.accountService.activeAccount$.pipe(getUserId); @@ -49,7 +51,7 @@ export class VaultSettingsV2Component implements OnInit, OnDestroy { this.userId$.pipe(switchMap((userId) => this.cipherArchiveService.userCanArchive$(userId))), ); - protected readonly showArchiveItem = toSignal(this.cipherArchiveService.hasArchiveFlagEnabled$()); + protected readonly showArchiveItem = toSignal(this.cipherArchiveService.hasArchiveFlagEnabled$); protected readonly userHasArchivedItems = toSignal( this.userId$.pipe( @@ -117,4 +119,18 @@ export class VaultSettingsV2Component implements OnInit, OnDestroy { this.lastSync = this.i18nService.t("never"); } } + + /** + * When a user can archive or has previously archived items, route them to + * the archive page. Otherwise, prompt them to upgrade to premium. + */ + async conditionallyRouteToArchive(event: Event) { + event.preventDefault(); + const premiumBadge = this.premiumBadgeComponent(); + if (this.userCanArchive() || this.userHasArchivedItems()) { + await this.router.navigate(["/archive"]); + } else if (premiumBadge) { + await premiumBadge.promptForPremium(event); + } + } } diff --git a/apps/browser/src/vault/popup/utils/vault-popout-window.spec.ts b/apps/browser/src/vault/popup/utils/vault-popout-window.spec.ts index 4597c004290..3389228dda4 100644 --- a/apps/browser/src/vault/popup/utils/vault-popout-window.spec.ts +++ b/apps/browser/src/vault/popup/utils/vault-popout-window.spec.ts @@ -2,6 +2,7 @@ import { mock } from "jest-mock-extended"; import { CipherType } from "@bitwarden/common/vault/enums"; +import { BrowserApi } from "../../../platform/browser/browser-api"; import BrowserPopupUtils from "../../../platform/browser/browser-popup-utils"; import { @@ -23,6 +24,19 @@ describe("VaultPopoutWindow", () => { .spyOn(BrowserPopupUtils, "closeSingleActionPopout") .mockImplementation(); + beforeEach(() => { + jest.spyOn(BrowserApi, "tabsQuery").mockResolvedValue([]); + jest.spyOn(BrowserApi, "updateWindowProperties").mockResolvedValue(); + global.chrome = { + ...global.chrome, + runtime: { + ...global.chrome?.runtime, + sendMessage: jest.fn().mockResolvedValue(undefined), + getURL: jest.fn((path) => `chrome-extension://extension-id/${path}`), + }, + }; + }); + afterEach(() => { jest.clearAllMocks(); }); @@ -123,6 +137,32 @@ describe("VaultPopoutWindow", () => { }, ); }); + + it("sends a message to refresh data when the popup is already open", async () => { + const existingPopupTab = { + id: 123, + windowId: 456, + url: `chrome-extension://extension-id/popup/index.html#/edit-cipher?singleActionPopout=${VaultPopoutType.addEditVaultItem}_${CipherType.Login}`, + } as chrome.tabs.Tab; + + jest.spyOn(BrowserApi, "tabsQuery").mockResolvedValue([existingPopupTab]); + const sendMessageSpy = jest.spyOn(chrome.runtime, "sendMessage"); + const updateWindowSpy = jest.spyOn(BrowserApi, "updateWindowProperties"); + + await openAddEditVaultItemPopout( + mock({ windowId: 1, url: "https://jest-testing-website.com" }), + { + cipherType: CipherType.Login, + }, + ); + + expect(openPopoutSpy).not.toHaveBeenCalled(); + expect(sendMessageSpy).toHaveBeenCalledWith({ + command: "reloadAddEditCipherData", + data: { cipherId: undefined, cipherType: CipherType.Login }, + }); + expect(updateWindowSpy).toHaveBeenCalledWith(456, { focused: true }); + }); }); describe("closeAddEditVaultItemPopout", () => { diff --git a/apps/browser/src/vault/popup/utils/vault-popout-window.ts b/apps/browser/src/vault/popup/utils/vault-popout-window.ts index 3dae96b6cc7..cccf005cd2e 100644 --- a/apps/browser/src/vault/popup/utils/vault-popout-window.ts +++ b/apps/browser/src/vault/popup/utils/vault-popout-window.ts @@ -115,10 +115,26 @@ async function openAddEditVaultItemPopout( addEditCipherUrl += formatQueryString("uri", url); } - await BrowserPopupUtils.openPopout(addEditCipherUrl, { - singleActionKey, - senderWindowId: windowId, - }); + const extensionUrl = chrome.runtime.getURL("popup/index.html"); + const existingPopupTabs = await BrowserApi.tabsQuery({ url: `${extensionUrl}*` }); + const existingPopup = existingPopupTabs.find((tab) => + tab.url?.includes(`singleActionPopout=${singleActionKey}`), + ); + // Check if the an existing popup is already open + try { + await chrome.runtime.sendMessage({ + command: "reloadAddEditCipherData", + data: { cipherId, cipherType }, + }); + await BrowserApi.updateWindowProperties(existingPopup.windowId, { + focused: true, + }); + } catch { + await BrowserPopupUtils.openPopout(addEditCipherUrl, { + singleActionKey, + senderWindowId: windowId, + }); + } } /** diff --git a/apps/browser/src/vault/popup/views/popup-cipher.view.ts b/apps/browser/src/vault/popup/views/popup-cipher.view.ts index 6f85e7b6eb4..7d035ceb6df 100644 --- a/apps/browser/src/vault/popup/views/popup-cipher.view.ts +++ b/apps/browser/src/vault/popup/views/popup-cipher.view.ts @@ -1,4 +1,4 @@ -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherListView } from "@bitwarden/sdk-internal"; diff --git a/apps/browser/store/locales/th/copy.resx b/apps/browser/store/locales/th/copy.resx index 1473e24d92e..5ffab9e41fe 100644 --- a/apps/browser/store/locales/th/copy.resx +++ b/apps/browser/store/locales/th/copy.resx @@ -118,58 +118,57 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden - ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ + Bitwarden Password Manager - At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. + āš„ā¸Ąāšˆā¸§āšˆā¸˛ā¸ˆā¸°ā¸—ā¸ĩāšˆā¸šāš‰ā¸˛ā¸™ ⏗ā¸ĩāšˆā¸—ā¸ŗā¸‡ā¸˛ā¸™ ā¸Ģ⏪⏎⏭⏪⏰ā¸Ģā¸§āšˆā¸˛ā¸‡āš€ā¸”ā¸´ā¸™ā¸—ā¸˛ā¸‡ Bitwarden ā¸Šāšˆā¸§ā¸ĸā¸›ā¸ā¸›āš‰ā¸­ā¸‡ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ ā¸žā¸˛ā¸Ē⏄ā¸ĩā¸ĸāšŒ āšā¸Ĩā¸°ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸Ēā¸ŗā¸„ā¸ąā¸ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš„ā¸”āš‰ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸‡āšˆā¸˛ā¸ĸ⏔⏞ā¸ĸ - Recognized as the best password manager by PCMag, WIRED, The Verge, CNET, G2, and more! + āš„ā¸”āš‰ā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗā¸ĸā¸­ā¸Ąā¸Ŗā¸ąā¸šā¸§āšˆā¸˛āš€ā¸›āš‡ā¸™ā¸•ā¸ąā¸§ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸—ā¸ĩāšˆā¸”ā¸ĩ⏗ā¸ĩāšˆā¸Ēā¸¸ā¸”āš‚ā¸”ā¸ĸ PCMag, WIRED, The Verge, CNET, G2 āšā¸Ĩ⏰⏭ā¸ĩā¸ā¸Ąā¸˛ā¸ā¸Ąā¸˛ā¸ĸ! -SECURE YOUR DIGITAL LIFE -Secure your digital life and protect against data breaches by generating and saving unique, strong passwords for every account. Maintain everything in an end-to-end encrypted password vault that only you can access. +ā¸›ā¸ā¸›āš‰ā¸­ā¸‡ā¸Šā¸ĩā¸§ā¸´ā¸•ā¸”ā¸´ā¸ˆā¸´ā¸—ā¸ąā¸Ĩ⏂⏭⏇⏄⏏⏓ +ā¸›ā¸ā¸›āš‰ā¸­ā¸‡ā¸Šā¸ĩā¸§ā¸´ā¸•ā¸”ā¸´ā¸ˆā¸´ā¸—ā¸ąā¸Ĩāšā¸Ĩā¸°ā¸›āš‰ā¸­ā¸‡ā¸ā¸ąā¸™ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸Ŗā¸ąāšˆā¸§āš„ā¸Ģā¸Ĩāš‚ā¸”ā¸ĸ⏁⏞⏪ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āšā¸Ĩā¸°ā¸šā¸ąā¸™ā¸—ā¸ļ⏁⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸—ā¸ĩāšˆā¸Ŗā¸ąā¸”ā¸ā¸¸ā¸Ąāšā¸Ĩā¸°āš„ā¸Ąāšˆā¸‹āš‰ā¸ŗā¸ā¸ąā¸™ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸—ā¸¸ā¸ā¸šā¸ąā¸ā¸Šā¸ĩ ā¸”ā¸šāšā¸Ĩā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸—ā¸¸ā¸ā¸­ā¸ĸāšˆā¸˛ā¸‡āšƒā¸™ā¸•ā¸šāš‰ā¸™ā¸´ā¸Ŗā¸ ā¸ąā¸ĸ⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸—ā¸ĩāšˆāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸Ģā¸ąā¸Ēāšā¸šā¸šā¸•āš‰ā¸™ā¸—ā¸˛ā¸‡ā¸–ā¸ļ⏇⏛ā¸Ĩ⏞ā¸ĸ⏗⏞⏇ ⏋ā¸ļāšˆā¸‡ā¸Ąā¸ĩāš€ā¸žā¸ĩā¸ĸā¸‡ā¸„ā¸¸ā¸“āš€ā¸—āšˆā¸˛ā¸™ā¸ąāš‰ā¸™ā¸—ā¸ĩāšˆāš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡āš„ā¸”āš‰ -ACCESS YOUR DATA, ANYWHERE, ANYTIME, ON ANY DEVICE -Easily manage, store, secure, and share unlimited passwords across unlimited devices without restrictions. +āš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš„ā¸”āš‰ā¸—ā¸¸ā¸ā¸—ā¸ĩāšˆ ā¸—ā¸¸ā¸āš€ā¸§ā¸Ĩ⏞ ā¸šā¸™ā¸—ā¸¸ā¸ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒ +ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗ ā¸ˆā¸ąā¸”āš€ā¸āš‡ā¸š ā¸›ā¸ā¸›āš‰ā¸­ā¸‡ āšā¸Ĩā¸°āšā¸Šā¸ŖāšŒā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āš„ā¸”āš‰āš„ā¸Ąāšˆā¸ˆā¸ŗā¸ā¸ąā¸”ā¸šā¸™ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒāš„ā¸Ąāšˆā¸ˆā¸ŗā¸ā¸ąā¸”ā¸ˆā¸ŗā¸™ā¸§ā¸™āš„ā¸”āš‰ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸‡āšˆā¸˛ā¸ĸ⏔⏞ā¸ĸāš‚ā¸”ā¸ĸāš„ā¸Ąāšˆā¸Ąā¸ĩā¸‚āš‰ā¸­ā¸ˆā¸ŗā¸ā¸ąā¸” -EVERYONE SHOULD HAVE THE TOOLS TO STAY SAFE ONLINE -Utilize Bitwarden for free with no ads or selling data. Bitwarden believes everyone should have the ability to stay safe online. Premium plans offer access to advanced features. +ā¸—ā¸¸ā¸ā¸„ā¸™ā¸„ā¸§ā¸Ŗā¸Ąā¸ĩāš€ā¸„ā¸Ŗā¸ˇāšˆā¸­ā¸‡ā¸Ąā¸ˇā¸­āš€ā¸žā¸ˇāšˆā¸­ā¸„ā¸§ā¸˛ā¸Ąā¸›ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸā¸šā¸™āš‚ā¸Ĩā¸ā¸­ā¸­ā¸™āš„ā¸Ĩā¸™āšŒ +āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™ Bitwarden āš„ā¸”āš‰ā¸Ÿā¸Ŗā¸ĩāš‚ā¸”ā¸ĸāš„ā¸Ąāšˆā¸Ąā¸ĩāš‚ā¸†ā¸Šā¸“ā¸˛ā¸Ģ⏪⏎⏭⏁⏞⏪⏂⏞ā¸ĸā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ Bitwarden āš€ā¸Šā¸ˇāšˆā¸­ā¸§āšˆā¸˛ā¸—ā¸¸ā¸ā¸„ā¸™ā¸„ā¸§ā¸Ŗā¸Ąā¸ĩā¸„ā¸§ā¸˛ā¸Ąā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āšƒā¸™ā¸ā¸˛ā¸Ŗā¸­ā¸ĸā¸šāšˆā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸›ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸā¸šā¸™āš‚ā¸Ĩā¸ā¸­ā¸­ā¸™āš„ā¸Ĩā¸™āšŒ āšā¸œā¸™ā¸žā¸Ŗā¸ĩāš€ā¸Ąā¸ĩā¸ĸā¸Ąā¸ˆā¸°ā¸Ąā¸­ā¸šā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡ā¸Ÿā¸ĩāš€ā¸ˆā¸­ā¸ŖāšŒā¸‚ā¸ąāš‰ā¸™ā¸Ēā¸šā¸‡ -EMPOWER YOUR TEAMS WITH BITWARDEN -Plans for Teams and Enterprise come with professional business features. Some examples include SSO integration, self-hosting, directory integration and SCIM provisioning, global policies, API access, event logs, and more. +āš€ā¸Ēā¸Ŗā¸´ā¸Ąā¸¨ā¸ąā¸ā¸ĸā¸ ā¸˛ā¸žā¸—ā¸ĩā¸Ąā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸”āš‰ā¸§ā¸ĸ BITWARDEN +āšā¸œā¸™ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸š Teams āšā¸Ĩ⏰ Enterprise ā¸Ąā¸˛ā¸žā¸Ŗāš‰ā¸­ā¸Ąā¸ā¸ąā¸šā¸Ÿā¸ĩāš€ā¸ˆā¸­ā¸ŖāšŒā¸—ā¸˛ā¸‡ā¸˜ā¸¸ā¸Ŗā¸ā¸´ā¸ˆā¸Ŗā¸°ā¸”ā¸ąā¸šā¸Ąā¸ˇā¸­ā¸­ā¸˛ā¸Šā¸ĩā¸ž ā¸•ā¸ąā¸§ā¸­ā¸ĸāšˆā¸˛ā¸‡āš€ā¸Šāšˆā¸™ ā¸ā¸˛ā¸Ŗā¸Ŗā¸§ā¸Ą SSO, ā¸ā¸˛ā¸Ŗāš‚ā¸Žā¸Ēā¸•āšŒāš€ā¸­ā¸‡ (Self-hosting), ā¸ā¸˛ā¸Ŗā¸Ŗā¸§ā¸Ąāš„ā¸”āš€ā¸Ŗā¸ā¸—ā¸­ā¸Ŗā¸ĩāšā¸Ĩā¸°ā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸”āš€ā¸•ā¸Ŗā¸ĩā¸ĸā¸Ą SCIM, ā¸™āš‚ā¸ĸ⏚⏞ā¸ĸā¸Ŗā¸°ā¸”ā¸ąā¸šā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗ, ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸–ā¸ļ⏇ API, ā¸šā¸ąā¸™ā¸—ā¸ļā¸āš€ā¸Ģā¸•ā¸¸ā¸ā¸˛ā¸Ŗā¸“āšŒ āšā¸Ĩā¸°ā¸­ā¸ˇāšˆā¸™ āš† -Use Bitwarden to secure your workforce and share sensitive information with colleagues. +āšƒā¸Šāš‰ Bitwarden āš€ā¸žā¸ˇāšˆā¸­ā¸›ā¸ā¸›āš‰ā¸­ā¸‡ā¸šā¸¸ā¸„ā¸Ĩā¸˛ā¸ā¸Ŗā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āšā¸Ĩā¸°āšā¸Šā¸ŖāšŒā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸Ēā¸ŗā¸„ā¸ąā¸ā¸ā¸ąā¸šāš€ā¸žā¸ˇāšˆā¸­ā¸™ā¸Ŗāšˆā¸§ā¸Ąā¸‡ā¸˛ā¸™ +āš€ā¸Ģā¸•ā¸¸ā¸œā¸Ĩāš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ąā¸—ā¸ĩāšˆā¸„ā¸§ā¸Ŗāš€ā¸Ĩ⏎⏭⏁ Bitwarden: -More reasons to choose Bitwarden: +ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸Ģā¸ąā¸Ēā¸Ŗā¸°ā¸”ā¸ąā¸šāš‚ā¸Ĩ⏁ +⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™āš„ā¸”āš‰ā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗā¸›ā¸ā¸›āš‰ā¸­ā¸‡ā¸”āš‰ā¸§ā¸ĸā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸Ģā¸ąā¸Ēāšā¸šā¸šā¸•āš‰ā¸™ā¸—ā¸˛ā¸‡ā¸–ā¸ļ⏇⏛ā¸Ĩ⏞ā¸ĸ⏗⏞⏇ ā¸‚ā¸ąāš‰ā¸™ā¸Ēā¸šā¸‡ (AES-256 bit, salted hashing āšā¸Ĩ⏰ PBKDF2 SHA-256) ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸ˆā¸ļ⏇⏛ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸāšā¸Ĩā¸°āš€ā¸›āš‡ā¸™ā¸Ēāšˆā¸§ā¸™ā¸•ā¸ąā¸§ -World-Class Encryption -Passwords are protected with advanced end-to-end encryption (AES-256 bit, salted hashing, and PBKDF2 SHA-256) so your data stays secure and private. +ā¸ā¸˛ā¸Ŗā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šāš‚ā¸”ā¸ĸā¸šā¸¸ā¸„ā¸„ā¸Ĩ⏗ā¸ĩāšˆā¸Ēā¸˛ā¸Ą +Bitwarden ā¸”ā¸ŗāš€ā¸™ā¸´ā¸™ā¸ā¸˛ā¸Ŗā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šā¸„ā¸§ā¸˛ā¸Ąā¸›ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸāš‚ā¸”ā¸ĸā¸šā¸¸ā¸„ā¸„ā¸Ĩ⏗ā¸ĩāšˆā¸Ēā¸˛ā¸Ąā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸„ā¸Ŗā¸­ā¸šā¸„ā¸Ĩā¸¸ā¸Ąā¸ā¸ąā¸šā¸šā¸Ŗā¸´ā¸Šā¸ąā¸—ā¸„ā¸§ā¸˛ā¸Ąā¸›ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸ⏗ā¸ĩāšˆā¸Ąā¸ĩā¸Šā¸ˇāšˆā¸­āš€ā¸Ēā¸ĩā¸ĸ⏇⏭ā¸ĸāšˆā¸˛ā¸‡ā¸Ēā¸Ąāšˆā¸ŗāš€ā¸Ēā¸Ąā¸­ ā¸ā¸˛ā¸Ŗā¸•ā¸Ŗā¸§ā¸ˆā¸Ēā¸­ā¸šā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸›ā¸ĩāš€ā¸Ģā¸Ĩāšˆā¸˛ā¸™ā¸ĩāš‰ā¸Ŗā¸§ā¸Ąā¸–ā¸ļā¸‡ā¸ā¸˛ā¸Ŗā¸›ā¸Ŗā¸°āš€ā¸Ąā¸´ā¸™ā¸‹ā¸­ā¸ŖāšŒā¸Ēāš‚ā¸„āš‰ā¸”āšā¸Ĩ⏰⏁⏞⏪⏗⏔ā¸Ēā¸­ā¸šāš€ā¸ˆā¸˛ā¸°ā¸Ŗā¸°ā¸šā¸šā¸„ā¸Ŗā¸­ā¸šā¸„ā¸Ĩā¸¸ā¸Ąā¸—ā¸ąāš‰ā¸‡ IP, āš€ā¸‹ā¸´ā¸ŖāšŒā¸Ÿāš€ā¸§ā¸­ā¸ŖāšŒ āšā¸Ĩā¸°āš€ā¸§āš‡ā¸šāšā¸­ā¸›ā¸žā¸Ĩā¸´āš€ā¸„ā¸Šā¸ąā¸™ā¸‚ā¸­ā¸‡ Bitwarden -3rd-party Audits -Bitwarden regularly conducts comprehensive third-party security audits with notable security firms. These annual audits include source code assessments and penetration testing across Bitwarden IPs, servers, and web applications. - -Advanced 2FA -Secure your login with a third-party authenticator, emailed codes, or FIDO2 WebAuthn credentials such as a hardware security key or passkey. +2FA ā¸‚ā¸ąāš‰ā¸™ā¸Ēā¸šā¸‡ +ā¸›ā¸ā¸›āš‰ā¸­ā¸‡ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ēā¸šāšˆā¸Ŗā¸°ā¸šā¸šā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸”āš‰ā¸§ā¸ĸāšā¸­ā¸›ā¸ĸ⏎⏙ā¸ĸā¸ąā¸™ā¸•ā¸ąā¸§ā¸•ā¸™ā¸‚ā¸­ā¸‡ā¸šā¸¸ā¸„ā¸„ā¸Ĩ⏗ā¸ĩāšˆā¸Ēā¸˛ā¸Ą, ⏪ā¸Ģā¸ąā¸Ē⏗⏞⏇⏭ā¸ĩāš€ā¸Ąā¸Ĩ ā¸Ģā¸Ŗā¸ˇā¸­ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ FIDO2 WebAuthn āš€ā¸Šāšˆā¸™ ⏄ā¸ĩā¸ĸāšŒā¸„ā¸§ā¸˛ā¸Ąā¸›ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸā¸Žā¸˛ā¸ŖāšŒā¸”āšā¸§ā¸ŖāšŒā¸Ģā¸Ŗā¸ˇā¸­ā¸žā¸˛ā¸Ē⏄ā¸ĩā¸ĸāšŒ Bitwarden Send -Transmit data directly to others while maintaining end-to-end encrypted security and limiting exposure. +ā¸Ēāšˆā¸‡ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāš‚ā¸”ā¸ĸ⏕⏪⏇⏖ā¸ļā¸‡ā¸œā¸šāš‰ā¸­ā¸ˇāšˆā¸™āšƒā¸™ā¸‚ā¸“ā¸°ā¸—ā¸ĩāšˆā¸ĸā¸ąā¸‡ā¸„ā¸‡ā¸Ŗā¸ąā¸ā¸Šā¸˛ā¸„ā¸§ā¸˛ā¸Ąā¸›ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸā¸”āš‰ā¸§ā¸ĸā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸Ģā¸ąā¸Ēāšā¸šā¸šā¸•āš‰ā¸™ā¸—ā¸˛ā¸‡ā¸–ā¸ļ⏇⏛ā¸Ĩ⏞ā¸ĸ⏗⏞⏇ āšā¸Ĩā¸°ā¸ˆā¸ŗā¸ā¸ąā¸”ā¸ā¸˛ā¸Ŗāš€ā¸›ā¸´ā¸”āš€ā¸œā¸ĸā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ -Built-in Generator -Create long, complex, and distinct passwords and unique usernames for every site you visit. Integrate with email alias providers for additional privacy. +ā¸•ā¸ąā¸§ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡āšƒā¸™ā¸•ā¸ąā¸§ +ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸—ā¸ĩāšˆā¸ĸ⏞⏧ ā¸‹ā¸ąā¸šā¸‹āš‰ā¸­ā¸™ āšā¸Ĩā¸°āšā¸•ā¸ā¸•āšˆā¸˛ā¸‡ā¸ā¸ąā¸™ ā¸Ŗā¸§ā¸Ąā¸–ā¸ļā¸‡ā¸Šā¸ˇāšˆā¸­ā¸œā¸šāš‰āšƒā¸Šāš‰ā¸—ā¸ĩāšˆāš„ā¸Ąāšˆā¸‹āš‰ā¸ŗā¸ā¸ąā¸™ā¸Ē⏺ā¸Ģā¸Ŗā¸ąā¸šā¸—ā¸¸ā¸āš€ā¸§āš‡ā¸šāš„ā¸‹ā¸•āšŒā¸—ā¸ĩāšˆā¸„ā¸¸ā¸“āš€ā¸ĸā¸ĩāšˆā¸ĸā¸Ąā¸Šā¸Ą ⏜ā¸Ēā¸˛ā¸™ā¸Ŗā¸§ā¸Ąā¸ā¸ąā¸šā¸œā¸šāš‰āšƒā¸Ģāš‰ā¸šā¸Ŗā¸´ā¸ā¸˛ā¸Ŗā¸™ā¸˛ā¸Ąāšā¸ā¸‡ā¸­ā¸ĩāš€ā¸Ąā¸Ĩāš€ā¸žā¸ˇāšˆā¸­ā¸„ā¸§ā¸˛ā¸Ąāš€ā¸›āš‡ā¸™ā¸Ēāšˆā¸§ā¸™ā¸•ā¸ąā¸§āš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ą -Global Translations -Bitwarden translations exist for more than 60 languages, translated by the global community though Crowdin. +ā¸ā¸˛ā¸Ŗāšā¸›ā¸Ĩā¸ ā¸˛ā¸Šā¸˛ā¸—ā¸ąāšˆā¸§āš‚ā¸Ĩ⏁ +Bitwarden ā¸Ąā¸ĩā¸ā¸˛ā¸Ŗāšā¸›ā¸Ĩā¸ ā¸˛ā¸Šā¸˛ā¸Ąā¸˛ā¸ā¸ā¸§āšˆā¸˛ 60 ā¸ ā¸˛ā¸Šā¸˛ āš‚ā¸”ā¸ĸā¸Šā¸¸ā¸Ąā¸Šā¸™ā¸—ā¸ąāšˆā¸§āš‚ā¸Ĩā¸ā¸œāšˆā¸˛ā¸™ Crowdin -Cross-Platform Applications -Secure and share sensitive data within your Bitwarden Vault from any browser, mobile device, or desktop OS, and more. +āšā¸­ā¸›ā¸žā¸Ĩā¸´āš€ā¸„ā¸Šā¸ąā¸™ā¸‚āš‰ā¸˛ā¸Ąāšā¸žā¸Ĩā¸•ā¸Ÿā¸­ā¸ŖāšŒā¸Ą +ā¸›ā¸ā¸›āš‰ā¸­ā¸‡āšā¸Ĩā¸°āšā¸Šā¸ŖāšŒā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸Ēā¸ŗā¸„ā¸ąā¸ā¸ ā¸˛ā¸ĸāšƒā¸™ā¸•ā¸šāš‰ā¸™ā¸´ā¸Ŗā¸ ā¸ąā¸ĸ Bitwarden ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸ˆā¸˛ā¸āš€ā¸šā¸Ŗā¸˛ā¸§āšŒāš€ā¸‹ā¸­ā¸ŖāšŒ ā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒā¸Ąā¸ˇā¸­ā¸–ā¸ˇā¸­ ā¸Ģā¸Ŗā¸ˇā¸­ā¸Ŗā¸°ā¸šā¸šā¸›ā¸ā¸´ā¸šā¸ąā¸•ā¸´ā¸ā¸˛ā¸Ŗāš€ā¸”ā¸Ēā¸āšŒā¸—āš‡ā¸­ā¸›āšƒā¸”ā¸āš‡āš„ā¸”āš‰ āšā¸Ĩā¸°ā¸­ā¸ˇāšˆā¸™ āš† -Bitwarden secures more than just passwords -End-to-end encrypted credential management solutions from Bitwarden empower organizations to secure everything, including developer secrets and passkey experiences. Visit Bitwarden.com to learn more about Bitwarden Secrets Manager and Bitwarden Passwordless.dev! +Bitwarden ā¸›ā¸ā¸›āš‰ā¸­ā¸‡ā¸Ąā¸˛ā¸ā¸ā¸§āšˆā¸˛āšā¸„āšˆā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ +āš‚ā¸‹ā¸Ĩā¸šā¸Šā¸ąā¸™ā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸›ā¸Ŗā¸°ā¸ˆā¸ŗā¸•ā¸ąā¸§ā¸—ā¸ĩāšˆāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸Ģā¸ąā¸Ēāšā¸šā¸š End-to-end ⏈⏞⏁ Bitwarden ā¸Šāšˆā¸§ā¸ĸāšƒā¸Ģāš‰ā¸­ā¸‡ā¸„āšŒā¸ā¸Ŗā¸›ā¸ā¸›āš‰ā¸­ā¸‡ā¸—ā¸¸ā¸ā¸Ēā¸´āšˆā¸‡ ā¸Ŗā¸§ā¸Ąā¸–ā¸ļā¸‡ā¸„ā¸§ā¸˛ā¸Ąā¸Ĩā¸ąā¸šā¸‚ā¸­ā¸‡ā¸™ā¸ąā¸ā¸žā¸ąā¸’ā¸™ā¸˛āšā¸Ĩ⏰⏛⏪⏰ā¸Ēā¸šā¸ā¸˛ā¸Ŗā¸“āšŒā¸žā¸˛ā¸Ē⏄ā¸ĩā¸ĸāšŒ āš€ā¸ĸā¸ĩāšˆā¸ĸā¸Ąā¸Šā¸Ą Bitwarden.com āš€ā¸žā¸ˇāšˆā¸­āš€ā¸Ŗā¸ĩā¸ĸā¸™ā¸Ŗā¸šāš‰āš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ąāš€ā¸ā¸ĩāšˆā¸ĸā¸§ā¸ā¸ąā¸š Bitwarden Secrets Manager āšā¸Ĩ⏰ Bitwarden Passwordless.dev! - At home, at work, or on the go, Bitwarden easily secures all your passwords, passkeys, and sensitive information. + āš„ā¸Ąāšˆā¸§āšˆā¸˛ā¸ˆā¸°ā¸—ā¸ĩāšˆā¸šāš‰ā¸˛ā¸™ ⏗ā¸ĩāšˆā¸—ā¸ŗā¸‡ā¸˛ā¸™ ā¸Ģ⏪⏎⏭⏪⏰ā¸Ģā¸§āšˆā¸˛ā¸‡āš€ā¸”ā¸´ā¸™ā¸—ā¸˛ā¸‡ Bitwarden ā¸Šāšˆā¸§ā¸ĸā¸›ā¸ā¸›āš‰ā¸­ā¸‡ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ ā¸žā¸˛ā¸Ē⏄ā¸ĩā¸ĸāšŒ āšā¸Ĩā¸°ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸Ēā¸ŗā¸„ā¸ąā¸ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš„ā¸”āš‰ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸‡āšˆā¸˛ā¸ĸ⏔⏞ā¸ĸ ā¸‹ā¸´ā¸‡ā¸„āšŒāšā¸Ĩā¸°āš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡ā¸•ā¸šāš‰ā¸™ā¸´ā¸Ŗā¸ ā¸ąā¸ĸā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸ˆā¸˛ā¸ā¸Ģā¸Ĩ⏞ā¸ĸā¸­ā¸¸ā¸›ā¸ā¸Ŗā¸“āšŒ @@ -178,15 +177,15 @@ End-to-end encrypted credential management solutions from Bitwarden empower orga ā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™āšā¸Ĩ⏰⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”ā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸ˆā¸˛ā¸ā¸•ā¸šāš‰ā¸™ā¸´ā¸Ŗā¸ ā¸ąā¸ĸ⏗ā¸ĩāšˆā¸›ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸ - ā¸ā¸Ŗā¸­ā¸ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸Ĩāš‡ā¸­ā¸ā¸­ā¸´ā¸™āš‚ā¸”ā¸ĸā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´ā¸šā¸™ā¸—ā¸¸ā¸āš€ā¸§āš‡ā¸šāš„ā¸‹ā¸•āšŒā¸—ā¸ĩāšˆā¸„ā¸¸ā¸“āšƒā¸Šāš‰ā¸‡ā¸˛ā¸™āš„ā¸”āš‰ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸Ŗā¸§ā¸”āš€ā¸Ŗāš‡ā¸§ + ā¸›āš‰ā¸­ā¸™ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩāš€ā¸‚āš‰ā¸˛ā¸Ēā¸šāšˆā¸Ŗā¸°ā¸šā¸šā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸Ĩā¸‡āšƒā¸™āš€ā¸§āš‡ā¸šāš„ā¸‹ā¸•āšŒā¸—ā¸ĩāšˆā¸„ā¸¸ā¸“āš€ā¸ĸā¸ĩāšˆā¸ĸā¸Ąā¸Šā¸Ąā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸Ŗā¸§ā¸”āš€ā¸Ŗāš‡ā¸§āš‚ā¸”ā¸ĸā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´ - ā¸•ā¸šāš‰āš€ā¸‹ā¸Ÿā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“ā¸ĸā¸ąā¸‡ā¸Ēā¸˛ā¸Ąā¸˛ā¸Ŗā¸–āš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡āš„ā¸”āš‰āš‚ā¸”ā¸ĸā¸Ēā¸°ā¸”ā¸§ā¸ā¸œāšˆā¸˛ā¸™āš€ā¸Ąā¸™ā¸šā¸„ā¸Ĩ⏴⏁⏂⏧⏞ + āš€ā¸‚āš‰ā¸˛ā¸–ā¸ļā¸‡ā¸•ā¸šāš‰ā¸™ā¸´ā¸Ŗā¸ ā¸ąā¸ĸā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš„ā¸”āš‰ā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸Ēā¸°ā¸”ā¸§ā¸ā¸ˆā¸˛ā¸āš€ā¸Ąā¸™ā¸šā¸„ā¸Ĩ⏴⏁⏂⏧⏞ - ā¸Ēāšˆā¸Ąā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸—ā¸ĩāšˆāšā¸‚āš‡ā¸‡āšā¸ā¸Ŗāšˆā¸‡āšā¸Ĩ⏰⏛ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸāš‚ā¸”ā¸ĸā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´ + ā¸Ēā¸Ŗāš‰ā¸˛ā¸‡ā¸Ŗā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸—ā¸ĩāšˆā¸Ŗā¸ąā¸”ā¸ā¸¸ā¸Ą āšā¸šā¸šā¸Ēā¸¸āšˆā¸Ą āšā¸Ĩ⏰⏛ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸāš‚ā¸”ā¸ĸā¸­ā¸ąā¸•āš‚ā¸™ā¸Ąā¸ąā¸•ā¸´ - ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš„ā¸”āš‰ā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸›ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸāš‚ā¸”ā¸ĸāšƒā¸Šāš‰ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸Ģā¸ąā¸Ē AES 256 ā¸šā¸´ā¸• + ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩā¸‚ā¸­ā¸‡ā¸„ā¸¸ā¸“āš„ā¸”āš‰ā¸Ŗā¸ąā¸šā¸ā¸˛ā¸Ŗā¸ˆā¸ąā¸”ā¸ā¸˛ā¸Ŗā¸­ā¸ĸāšˆā¸˛ā¸‡ā¸›ā¸Ĩā¸­ā¸”ā¸ ā¸ąā¸ĸāš‚ā¸”ā¸ĸāšƒā¸Šāš‰ā¸ā¸˛ā¸Ŗāš€ā¸‚āš‰ā¸˛ā¸Ŗā¸Ģā¸ąā¸Ē AES-256 bit diff --git a/apps/browser/tailwind.config.js b/apps/browser/tailwind.config.js index 134001bbf13..faaa7fa4128 100644 --- a/apps/browser/tailwind.config.js +++ b/apps/browser/tailwind.config.js @@ -12,5 +12,6 @@ config.content = [ "../../libs/vault/src/**/*.{html,ts}", "../../libs/pricing/src/**/*.{html,ts}", ]; +config.corePlugins.preflight = true; module.exports = config; diff --git a/apps/cli/package.json b/apps/cli/package.json index adddc99b4d7..c80f79feff8 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,7 +1,7 @@ { "name": "@bitwarden/cli", "description": "A secure and free password manager for all of your devices.", - "version": "2025.12.0", + "version": "2026.1.0", "keywords": [ "bitwarden", "password", @@ -69,26 +69,26 @@ "browser-hrtime": "1.1.8", "chalk": "4.1.2", "commander": "14.0.0", - "core-js": "3.45.0", + "core-js": "3.47.0", "form-data": "4.0.4", "https-proxy-agent": "7.0.6", "inquirer": "8.2.6", "jsdom": "26.1.0", "jszip": "3.10.1", - "koa": "2.16.3", + "koa": "3.1.1", "koa-bodyparser": "4.4.1", "koa-json": "2.0.2", "lowdb": "1.0.0", "lunr": "2.3.9", "multer": "2.0.2", - "node-fetch": "2.6.12", + "node-fetch": "2.7.0", "node-forge": "1.3.2", - "open": "10.1.2", + "open": "11.0.0", "papaparse": "5.5.3", "proper-lockfile": "4.1.2", "rxjs": "7.8.1", "semver": "7.7.3", - "tldts": "7.0.18", + "tldts": "7.0.19", "zxcvbn": "4.4.2" } } diff --git a/apps/cli/src/admin-console/commands/confirm.command.ts b/apps/cli/src/admin-console/commands/confirm.command.ts index adae5091172..4458054e244 100644 --- a/apps/cli/src/admin-console/commands/confirm.command.ts +++ b/apps/cli/src/admin-console/commands/confirm.command.ts @@ -9,9 +9,7 @@ import { import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { OrganizationId } from "@bitwarden/common/types/guid"; @@ -28,7 +26,6 @@ export class ConfirmCommand { private encryptService: EncryptService, private organizationUserApiService: OrganizationUserApiService, private accountService: AccountService, - private configService: ConfigService, private i18nService: I18nService, ) {} @@ -80,11 +77,7 @@ export class ConfirmCommand { const key = await this.encryptService.encapsulateKeyUnsigned(orgKey, publicKey); const req = new OrganizationUserConfirmRequest(); req.key = key.encryptedString; - if ( - await firstValueFrom(this.configService.getFeatureFlag$(FeatureFlag.CreateDefaultLocation)) - ) { - req.defaultUserCollectionName = await this.getEncryptedDefaultUserCollectionName(orgKey); - } + req.defaultUserCollectionName = await this.getEncryptedDefaultUserCollectionName(orgKey); await this.organizationUserApiService.postOrganizationUserConfirm( options.organizationId, id, diff --git a/apps/cli/src/admin-console/models/response/collection.response.ts b/apps/cli/src/admin-console/models/response/collection.response.ts index a0d1ce1047d..4c56fdcd84a 100644 --- a/apps/cli/src/admin-console/models/response/collection.response.ts +++ b/apps/cli/src/admin-console/models/response/collection.response.ts @@ -1,4 +1,4 @@ -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { CollectionWithIdExport } from "@bitwarden/common/models/export/collection-with-id.export"; import { BaseResponse } from "../../../models/response/base.response"; diff --git a/apps/cli/src/admin-console/models/response/organization-collection.response.ts b/apps/cli/src/admin-console/models/response/organization-collection.response.ts index a0d62b4c7b6..4b5c9a08f2b 100644 --- a/apps/cli/src/admin-console/models/response/organization-collection.response.ts +++ b/apps/cli/src/admin-console/models/response/organization-collection.response.ts @@ -1,4 +1,4 @@ -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { SelectionReadOnly } from "../selection-read-only"; diff --git a/apps/cli/src/auth/commands/login.command.ts b/apps/cli/src/auth/commands/login.command.ts index d0ab062d0b3..89d774b443b 100644 --- a/apps/cli/src/auth/commands/login.command.ts +++ b/apps/cli/src/auth/commands/login.command.ts @@ -13,6 +13,7 @@ import { SsoLoginCredentials, SsoUrlService, UserApiLoginCredentials, + UserDecryptionOptionsServiceAbstraction, } from "@bitwarden/auth/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; @@ -31,6 +32,7 @@ import { TwoFactorService, TwoFactorApiService } from "@bitwarden/common/auth/tw import { ClientType } from "@bitwarden/common/enums"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; +import { EncryptedMigrator } from "@bitwarden/common/key-management/encrypted-migrator/encrypted-migrator.abstraction"; import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service"; import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; @@ -81,6 +83,8 @@ export class LoginCommand { protected ssoUrlService: SsoUrlService, protected i18nService: I18nService, protected masterPasswordService: MasterPasswordServiceAbstraction, + protected userDecryptionOptionsService: UserDecryptionOptionsServiceAbstraction, + protected encryptedMigrator: EncryptedMigrator, ) {} async run(email: string, password: string, options: OptionValues) { @@ -111,20 +115,14 @@ export class LoginCommand { } else if (options.sso != null && this.canInteract) { // If the optional Org SSO Identifier isn't provided, the option value is `true`. const orgSsoIdentifier = options.sso === true ? null : options.sso; - const passwordOptions: any = { - type: "password", - length: 64, - uppercase: true, - lowercase: true, - numbers: true, - special: false, - }; - const state = await this.passwordGenerationService.generatePassword(passwordOptions); - ssoCodeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions); - const codeVerifierHash = await this.cryptoFunctionService.hash(ssoCodeVerifier, "sha256"); - const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash); + const ssoPromptData = await this.makeSsoPromptData(); + ssoCodeVerifier = ssoPromptData.ssoCodeVerifier; try { - const ssoParams = await this.openSsoPrompt(codeChallenge, state, orgSsoIdentifier); + const ssoParams = await this.openSsoPrompt( + ssoPromptData.codeChallenge, + ssoPromptData.state, + orgSsoIdentifier, + ); ssoCode = ssoParams.ssoCode; orgIdentifier = ssoParams.orgIdentifier; } catch { @@ -229,9 +227,43 @@ export class LoginCommand { new PasswordLoginCredentials(email, password, twoFactor), ); } + + // Begin Acting on initial AuthResult + if (response.requiresEncryptionKeyMigration) { return Response.error(this.i18nService.t("legacyEncryptionUnsupported")); } + + // Opting for not checking feature flag since the server will not respond with + // SsoOrganizationIdentifier if the feature flag is not enabled. + if (response.requiresSso && this.canInteract) { + const ssoPromptData = await this.makeSsoPromptData(); + ssoCodeVerifier = ssoPromptData.ssoCodeVerifier; + try { + const ssoParams = await this.openSsoPrompt( + ssoPromptData.codeChallenge, + ssoPromptData.state, + response.ssoOrganizationIdentifier, + ); + ssoCode = ssoParams.ssoCode; + orgIdentifier = ssoParams.orgIdentifier; + if (ssoCode != null && ssoCodeVerifier != null) { + response = await this.loginStrategyService.logIn( + new SsoLoginCredentials( + ssoCode, + ssoCodeVerifier, + this.ssoRedirectUri, + orgIdentifier, + undefined, // email to look up 2FA token not required as CLI can't remember 2FA token + twoFactor, + ), + ); + } + } catch { + return Response.badRequest("Something went wrong. Try again."); + } + } + if (response.requiresTwoFactor) { const twoFactorProviders = await this.twoFactorService.getSupportedProviders(null); if (twoFactorProviders.length === 0) { @@ -277,6 +309,10 @@ export class LoginCommand { if (twoFactorToken == null && selectedProvider.type === TwoFactorProviderType.Email) { const emailReq = new TwoFactorEmailRequest(); emailReq.email = await this.loginStrategyService.getEmail(); + // if the user was logging in with SSO, we need to include the SSO session token + if (response.ssoEmail2FaSessionToken != null) { + emailReq.ssoEmail2FaSessionToken = response.ssoEmail2FaSessionToken; + } emailReq.masterPasswordHash = await this.loginStrategyService.getMasterPasswordHash(); await this.twoFactorApiService.postTwoFactorEmail(emailReq); } @@ -322,15 +358,18 @@ export class LoginCommand { response = await this.loginStrategyService.logInNewDeviceVerification(newDeviceToken); } + // We check response two factor again here since MFA could fail based on the logic on ln 226 if (response.requiresTwoFactor) { return Response.error("Login failed."); } - if (response.resetMasterPassword) { - return Response.error( - "In order to log in with SSO from the CLI, you must first log in" + - " through the web vault to set your master password.", - ); + // If we are in the SSO flow and we got a successful login response (we are past rejection scenarios + // and should always have a userId here), validate that SSO user in MP encryption org has MP set + // This must be done here b/c we have 2 places we try to login with SSO above and neither has a + // common handleSsoAuthnResult method to consoldiate this logic into (1. the normal SSO flow and + // 2. the requiresSso automatic authentication flow) + if (ssoCode != null && ssoCodeVerifier != null && response.userId) { + await this.validateSsoUserInMpEncryptionOrgHasMp(response.userId); } // Check if Key Connector domain confirmation is required @@ -367,6 +406,8 @@ export class LoginCommand { } } + await this.encryptedMigrator.runMigrations(response.userId, password); + return await this.handleSuccessResponse(response); } catch (e) { if ( @@ -688,6 +729,27 @@ export class LoginCommand { }; } + /// Generate SSO prompt data: code verifier, code challenge, and state + private async makeSsoPromptData(): Promise<{ + ssoCodeVerifier: string; + codeChallenge: string; + state: string; + }> { + const passwordOptions: any = { + type: "password", + length: 64, + uppercase: true, + lowercase: true, + numbers: true, + special: false, + }; + const state = await this.passwordGenerationService.generatePassword(passwordOptions); + const ssoCodeVerifier = await this.passwordGenerationService.generatePassword(passwordOptions); + const codeVerifierHash = await this.cryptoFunctionService.hash(ssoCodeVerifier, "sha256"); + const codeChallenge = Utils.fromBufferToUrlB64(codeVerifierHash); + return { ssoCodeVerifier, codeChallenge, state }; + } + private async openSsoPrompt( codeChallenge: string, state: string, @@ -778,4 +840,35 @@ export class LoginCommand { const checkStateSplit = checkState.split("_identifier="); return stateSplit[0] === checkStateSplit[0]; } + + /** + * Validate that a user logging in with SSO that is in an org using MP encryption + * has a MP set. If not, they cannot set a MP in the CLI and must use another client. + * @param userId + * @returns void + */ + private async validateSsoUserInMpEncryptionOrgHasMp(userId: UserId): Promise { + const userDecryptionOptions = await firstValueFrom( + this.userDecryptionOptionsService.userDecryptionOptionsById$(userId), + ); + + // device trust isn't supported in the CLI as we don't have persistent device key storage. + const notUsingTrustedDeviceEncryption = !userDecryptionOptions.trustedDeviceOption; + const notUsingKeyConnector = !userDecryptionOptions.keyConnectorOption; + + if ( + notUsingTrustedDeviceEncryption && + notUsingKeyConnector && + !userDecryptionOptions.hasMasterPassword + ) { + // If user is in an org that is using MP encryption and they JIT provisioned but + // have not yet set a MP and come to the CLI to login, they won't be able to unlock + // or set a MP in the CLI as it isn't supported. + await this.logoutCallback(); + throw Response.error( + "In order to log in with SSO from the CLI, you must first log in" + + " through the web vault, the desktop, or the extension to set your master password.", + ); + } + } } diff --git a/apps/cli/src/base-program.ts b/apps/cli/src/base-program.ts index 69a5e4e1bde..2ce0d425007 100644 --- a/apps/cli/src/base-program.ts +++ b/apps/cli/src/base-program.ts @@ -172,9 +172,7 @@ export abstract class BaseProgram { } else { const command = new UnlockCommand( this.serviceContainer.accountService, - this.serviceContainer.masterPasswordService, this.serviceContainer.keyService, - this.serviceContainer.userVerificationService, this.serviceContainer.cryptoFunctionService, this.serviceContainer.logService, this.serviceContainer.keyConnectorService, @@ -182,8 +180,8 @@ export abstract class BaseProgram { this.serviceContainer.organizationApiService, this.serviceContainer.logout, this.serviceContainer.i18nService, + this.serviceContainer.encryptedMigrator, this.serviceContainer.masterPasswordUnlockService, - this.serviceContainer.configService, ); const response = await command.run(null, null); if (!response.success) { diff --git a/apps/cli/src/commands/edit.command.ts b/apps/cli/src/commands/edit.command.ts index 14a218c7141..dbcb0489187 100644 --- a/apps/cli/src/commands/edit.command.ts +++ b/apps/cli/src/commands/edit.command.ts @@ -138,10 +138,8 @@ export class EditCommand { ); } - const encCipher = await this.cipherService.encrypt(cipherView, activeUserId); try { - const updatedCipher = await this.cipherService.updateWithServer(encCipher); - const decCipher = await this.cipherService.decrypt(updatedCipher, activeUserId); + const decCipher = await this.cipherService.updateWithServer(cipherView, activeUserId); const res = new CipherResponse(decCipher); return Response.success(res); } catch (e) { @@ -186,15 +184,15 @@ export class EditCommand { return Response.notFound(); } - let folderView = await folder.decrypt(); + const userKey = await firstValueFrom(this.keyService.userKey$(activeUserId)); + let folderView = await folder.decrypt(userKey); folderView = FolderExport.toView(req, folderView); - const userKey = await this.keyService.getUserKey(activeUserId); const encFolder = await this.folderService.encrypt(folderView, userKey); try { const folder = await this.folderApiService.save(encFolder, activeUserId); const updatedFolder = new Folder(folder); - const decFolder = await updatedFolder.decrypt(); + const decFolder = await updatedFolder.decrypt(userKey); const res = new FolderResponse(decFolder); return Response.success(res); } catch (e) { diff --git a/apps/cli/src/commands/get.command.ts b/apps/cli/src/commands/get.command.ts index 93e711d748f..db070344628 100644 --- a/apps/cli/src/commands/get.command.ts +++ b/apps/cli/src/commands/get.command.ts @@ -1,12 +1,11 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { filter, firstValueFrom, map, switchMap } from "rxjs"; -import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionService } from "@bitwarden/admin-console/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; @@ -417,10 +416,11 @@ export class GetCommand extends DownloadCommand { private async getFolder(id: string) { let decFolder: FolderView = null; const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + const userKey = await firstValueFrom(this.keyService.userKey$(activeUserId)); if (Utils.isGuid(id)) { const folder = await this.folderService.getFromState(id, activeUserId); if (folder != null) { - decFolder = await folder.decrypt(); + decFolder = await folder.decrypt(userKey); } } else if (id.trim() !== "") { let folders = await this.folderService.getAllDecryptedFromState(activeUserId); diff --git a/apps/cli/src/commands/list.command.ts b/apps/cli/src/commands/list.command.ts index ff210cf222d..2430035e34a 100644 --- a/apps/cli/src/commands/list.command.ts +++ b/apps/cli/src/commands/list.command.ts @@ -1,16 +1,15 @@ import { firstValueFrom, map } from "rxjs"; +import { OrganizationUserApiService, CollectionService } from "@bitwarden/admin-console/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { - OrganizationUserApiService, - CollectionService, CollectionData, Collection, CollectionDetailsResponse as ApiCollectionDetailsResponse, CollectionResponse as ApiCollectionResponse, -} from "@bitwarden/admin-console/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; -import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +} from "@bitwarden/common/admin-console/models/collections"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { EventType } from "@bitwarden/common/enums"; diff --git a/apps/cli/src/commands/status.command.ts b/apps/cli/src/commands/status.command.ts index f7fc8541a5f..7ae1e657630 100644 --- a/apps/cli/src/commands/status.command.ts +++ b/apps/cli/src/commands/status.command.ts @@ -1,12 +1,12 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { firstValueFrom, map } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; +import { UserId } from "@bitwarden/user-core"; import { Response } from "../models/response"; import { TemplateResponse } from "../models/response/template.response"; @@ -17,16 +17,17 @@ export class StatusCommand { private syncService: SyncService, private accountService: AccountService, private authService: AuthService, + private userAutoUnlockKeyService: UserAutoUnlockKeyService, ) {} async run(): Promise { try { const baseUrl = await this.baseUrl(); - const status = await this.status(); const lastSync = await this.syncService.getLastSync(); const [userId, email] = await firstValueFrom( this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])), ); + const status = await this.status(userId); return Response.success( new TemplateResponse({ @@ -42,12 +43,18 @@ export class StatusCommand { } } - private async baseUrl(): Promise { + private async baseUrl(): Promise { const env = await firstValueFrom(this.envService.environment$); return env.getUrls().base; } - private async status(): Promise<"unauthenticated" | "locked" | "unlocked"> { + private async status( + userId: UserId | undefined, + ): Promise<"unauthenticated" | "locked" | "unlocked"> { + if (userId != null) { + await this.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet(userId); + } + const authStatus = await this.authService.getAuthStatus(); if (authStatus === AuthenticationStatus.Unlocked) { return "unlocked"; diff --git a/apps/cli/src/key-management/commands/unlock.command.spec.ts b/apps/cli/src/key-management/commands/unlock.command.spec.ts index 928a750dca6..a722469f7bb 100644 --- a/apps/cli/src/key-management/commands/unlock.command.spec.ts +++ b/apps/cli/src/key-management/commands/unlock.command.spec.ts @@ -3,19 +3,16 @@ import { of } from "rxjs"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; -import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; -import { MasterPasswordVerificationResponse } from "@bitwarden/common/auth/types/verification"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; +import { EncryptedMigrator } from "@bitwarden/common/key-management/encrypted-migrator/encrypted-migrator.abstraction"; import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service"; import { MasterPasswordUnlockService } from "@bitwarden/common/key-management/master-password/abstractions/master-password-unlock.service"; -import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { mockAccountInfoWith } from "@bitwarden/common/spec"; import { CsprngArray } from "@bitwarden/common/types/csprng"; -import { MasterKey, UserKey } from "@bitwarden/common/types/key"; +import { UserKey } from "@bitwarden/common/types/key"; import { KeyService } from "@bitwarden/key-management"; import { ConsoleLogService } from "@bitwarden/logging"; import { UserId } from "@bitwarden/user-core"; @@ -30,9 +27,7 @@ describe("UnlockCommand", () => { let command: UnlockCommand; const accountService = mock(); - const masterPasswordService = mock(); const keyService = mock(); - const userVerificationService = mock(); const cryptoFunctionService = mock(); const logService = mock(); const keyConnectorService = mock(); @@ -40,15 +35,16 @@ describe("UnlockCommand", () => { const organizationApiService = mock(); const logout = jest.fn(); const i18nService = mock(); + const encryptedMigrator = mock(); const masterPasswordUnlockService = mock(); - const configService = mock(); const mockMasterPassword = "testExample"; const activeAccount: Account = { id: "user-id" as UserId, - email: "user@example.com", - emailVerified: true, - name: "User", + ...mockAccountInfoWith({ + email: "user@example.com", + name: "User", + }), }; const mockUserKey = new SymmetricCryptoKey(new Uint8Array(64)) as UserKey; const mockSessionKey = new Uint8Array(64) as CsprngArray; @@ -69,9 +65,6 @@ describe("UnlockCommand", () => { ); expectedSuccessMessage.raw = b64sessionKey; - // Legacy test data - const mockMasterKey = new SymmetricCryptoKey(new Uint8Array(32)) as MasterKey; - beforeEach(async () => { jest.clearAllMocks(); @@ -82,9 +75,7 @@ describe("UnlockCommand", () => { command = new UnlockCommand( accountService, - masterPasswordService, keyService, - userVerificationService, cryptoFunctionService, logService, keyConnectorService, @@ -92,8 +83,8 @@ describe("UnlockCommand", () => { organizationApiService, logout, i18nService, + encryptedMigrator, masterPasswordUnlockService, - configService, ); }); @@ -128,116 +119,46 @@ describe("UnlockCommand", () => { }, ); - describe("UnlockWithMasterPasswordUnlockData feature flag enabled", () => { - beforeEach(() => { - configService.getFeatureFlag$.mockReturnValue(of(true)); - }); + it("calls masterPasswordUnlockService successfully", async () => { + masterPasswordUnlockService.unlockWithMasterPassword.mockResolvedValue(mockUserKey); - it("calls masterPasswordUnlockService successfully", async () => { - masterPasswordUnlockService.unlockWithMasterPassword.mockResolvedValue(mockUserKey); + const response = await command.run(mockMasterPassword, {}); - const response = await command.run(mockMasterPassword, {}); - - expect(response).not.toBeNull(); - expect(response.success).toEqual(true); - expect(response.data).toEqual(expectedSuccessMessage); - expect(masterPasswordUnlockService.unlockWithMasterPassword).toHaveBeenCalledWith( - mockMasterPassword, - activeAccount.id, - ); - expect(keyService.setUserKey).toHaveBeenCalledWith(mockUserKey, activeAccount.id); - }); - - it("returns error response if unlockWithMasterPassword fails", async () => { - masterPasswordUnlockService.unlockWithMasterPassword.mockRejectedValue( - new Error("Unlock failed"), - ); - - const response = await command.run(mockMasterPassword, {}); - - expect(response).not.toBeNull(); - expect(response.success).toEqual(false); - expect(response.message).toEqual("Unlock failed"); - expect(masterPasswordUnlockService.unlockWithMasterPassword).toHaveBeenCalledWith( - mockMasterPassword, - activeAccount.id, - ); - expect(keyService.setUserKey).not.toHaveBeenCalled(); - }); + expect(response).not.toBeNull(); + expect(response.success).toEqual(true); + expect(response.data).toEqual(expectedSuccessMessage); + expect(masterPasswordUnlockService.unlockWithMasterPassword).toHaveBeenCalledWith( + mockMasterPassword, + activeAccount.id, + ); + expect(keyService.setUserKey).toHaveBeenCalledWith(mockUserKey, activeAccount.id); }); - describe("unlock with feature flag off", () => { - beforeEach(() => { - configService.getFeatureFlag$.mockReturnValue(of(false)); - }); + it("returns error response if unlockWithMasterPassword fails", async () => { + masterPasswordUnlockService.unlockWithMasterPassword.mockRejectedValue( + new Error("Unlock failed"), + ); - it("calls decryptUserKeyWithMasterKey successfully", async () => { - userVerificationService.verifyUserByMasterPassword.mockResolvedValue({ - masterKey: mockMasterKey, - } as MasterPasswordVerificationResponse); - masterPasswordService.decryptUserKeyWithMasterKey.mockResolvedValue(mockUserKey); + const response = await command.run(mockMasterPassword, {}); - const response = await command.run(mockMasterPassword, {}); - - expect(response).not.toBeNull(); - expect(response.success).toEqual(true); - expect(response.data).toEqual(expectedSuccessMessage); - expect(userVerificationService.verifyUserByMasterPassword).toHaveBeenCalledWith( - { - type: VerificationType.MasterPassword, - secret: mockMasterPassword, - }, - activeAccount.id, - activeAccount.email, - ); - expect(masterPasswordService.decryptUserKeyWithMasterKey).toHaveBeenCalledWith( - mockMasterKey, - activeAccount.id, - ); - expect(keyService.setUserKey).toHaveBeenCalledWith(mockUserKey, activeAccount.id); - }); - - it("returns error response when verifyUserByMasterPassword throws", async () => { - userVerificationService.verifyUserByMasterPassword.mockRejectedValue( - new Error("Verification failed"), - ); - - const response = await command.run(mockMasterPassword, {}); - - expect(response).not.toBeNull(); - expect(response.success).toEqual(false); - expect(response.message).toEqual("Verification failed"); - expect(userVerificationService.verifyUserByMasterPassword).toHaveBeenCalledWith( - { - type: VerificationType.MasterPassword, - secret: mockMasterPassword, - }, - activeAccount.id, - activeAccount.email, - ); - expect(masterPasswordService.decryptUserKeyWithMasterKey).not.toHaveBeenCalled(); - expect(keyService.setUserKey).not.toHaveBeenCalled(); - }); + expect(response).not.toBeNull(); + expect(response.success).toEqual(false); + expect(response.message).toEqual("Unlock failed"); + expect(masterPasswordUnlockService.unlockWithMasterPassword).toHaveBeenCalledWith( + mockMasterPassword, + activeAccount.id, + ); + expect(keyService.setUserKey).not.toHaveBeenCalled(); }); describe("calls convertToKeyConnectorCommand if required", () => { let convertToKeyConnectorSpy: jest.SpyInstance; beforeEach(() => { keyConnectorService.convertAccountRequired$ = of(true); - - // Feature flag on masterPasswordUnlockService.unlockWithMasterPassword.mockResolvedValue(mockUserKey); - - // Feature flag off - userVerificationService.verifyUserByMasterPassword.mockResolvedValue({ - masterKey: mockMasterKey, - } as MasterPasswordVerificationResponse); - masterPasswordService.decryptUserKeyWithMasterKey.mockResolvedValue(mockUserKey); }); - test.each([true, false])("returns failure when feature flag is %s", async (flagValue) => { - configService.getFeatureFlag$.mockReturnValue(of(flagValue)); - + it("returns error on failure", async () => { // Mock the ConvertToKeyConnectorCommand const mockRun = jest.fn().mockResolvedValue({ success: false, message: "convert failed" }); convertToKeyConnectorSpy = jest @@ -252,67 +173,32 @@ describe("UnlockCommand", () => { expect(keyService.setUserKey).toHaveBeenCalledWith(mockUserKey, activeAccount.id); expect(convertToKeyConnectorSpy).toHaveBeenCalled(); - if (flagValue === true) { - expect(masterPasswordUnlockService.unlockWithMasterPassword).toHaveBeenCalledWith( - mockMasterPassword, - activeAccount.id, - ); - } else { - expect(userVerificationService.verifyUserByMasterPassword).toHaveBeenCalledWith( - { - type: VerificationType.MasterPassword, - secret: mockMasterPassword, - }, - activeAccount.id, - activeAccount.email, - ); - expect(masterPasswordService.decryptUserKeyWithMasterKey).toHaveBeenCalledWith( - mockMasterKey, - activeAccount.id, - ); - } + expect(masterPasswordUnlockService.unlockWithMasterPassword).toHaveBeenCalledWith( + mockMasterPassword, + activeAccount.id, + ); }); - test.each([true, false])( - "returns expected success when feature flag is %s", - async (flagValue) => { - configService.getFeatureFlag$.mockReturnValue(of(flagValue)); + it("returns success on successful conversion", async () => { + // Mock the ConvertToKeyConnectorCommand + const mockRun = jest.fn().mockResolvedValue({ success: true }); + const convertToKeyConnectorSpy = jest + .spyOn(ConvertToKeyConnectorCommand.prototype, "run") + .mockImplementation(mockRun); - // Mock the ConvertToKeyConnectorCommand - const mockRun = jest.fn().mockResolvedValue({ success: true }); - const convertToKeyConnectorSpy = jest - .spyOn(ConvertToKeyConnectorCommand.prototype, "run") - .mockImplementation(mockRun); + const response = await command.run(mockMasterPassword, {}); - const response = await command.run(mockMasterPassword, {}); + expect(response).not.toBeNull(); + expect(response.success).toEqual(true); + expect(response.data).toEqual(expectedSuccessMessage); + expect(keyService.setUserKey).toHaveBeenCalledWith(mockUserKey, activeAccount.id); + expect(convertToKeyConnectorSpy).toHaveBeenCalled(); - expect(response).not.toBeNull(); - expect(response.success).toEqual(true); - expect(response.data).toEqual(expectedSuccessMessage); - expect(keyService.setUserKey).toHaveBeenCalledWith(mockUserKey, activeAccount.id); - expect(convertToKeyConnectorSpy).toHaveBeenCalled(); - - if (flagValue === true) { - expect(masterPasswordUnlockService.unlockWithMasterPassword).toHaveBeenCalledWith( - mockMasterPassword, - activeAccount.id, - ); - } else { - expect(userVerificationService.verifyUserByMasterPassword).toHaveBeenCalledWith( - { - type: VerificationType.MasterPassword, - secret: mockMasterPassword, - }, - activeAccount.id, - activeAccount.email, - ); - expect(masterPasswordService.decryptUserKeyWithMasterKey).toHaveBeenCalledWith( - mockMasterKey, - activeAccount.id, - ); - } - }, - ); + expect(masterPasswordUnlockService.unlockWithMasterPassword).toHaveBeenCalledWith( + mockMasterPassword, + activeAccount.id, + ); + }); }); }); }); diff --git a/apps/cli/src/key-management/commands/unlock.command.ts b/apps/cli/src/key-management/commands/unlock.command.ts index 4ae8ce823a4..5f82b721d07 100644 --- a/apps/cli/src/key-management/commands/unlock.command.ts +++ b/apps/cli/src/key-management/commands/unlock.command.ts @@ -4,19 +4,13 @@ import { firstValueFrom } from "rxjs"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; -import { VerificationType } from "@bitwarden/common/auth/enums/verification-type"; -import { MasterPasswordVerification } from "@bitwarden/common/auth/types/verification"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; +import { EncryptedMigrator } from "@bitwarden/common/key-management/encrypted-migrator/encrypted-migrator.abstraction"; import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service"; import { MasterPasswordUnlockService } from "@bitwarden/common/key-management/master-password/abstractions/master-password-unlock.service"; -import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; -import { MasterKey } from "@bitwarden/common/types/key"; import { KeyService } from "@bitwarden/key-management"; import { Response } from "../../models/response"; @@ -28,9 +22,7 @@ import { ConvertToKeyConnectorCommand } from "../convert-to-key-connector.comman export class UnlockCommand { constructor( private accountService: AccountService, - private masterPasswordService: InternalMasterPasswordServiceAbstraction, private keyService: KeyService, - private userVerificationService: UserVerificationService, private cryptoFunctionService: CryptoFunctionService, private logService: ConsoleLogService, private keyConnectorService: KeyConnectorService, @@ -38,8 +30,8 @@ export class UnlockCommand { private organizationApiService: OrganizationApiServiceAbstraction, private logout: () => Promise, private i18nService: I18nService, + private encryptedMigrator: EncryptedMigrator, private masterPasswordUnlockService: MasterPasswordUnlockService, - private configService: ConfigService, ) {} async run(password: string, cmdOptions: Record) { @@ -59,46 +51,15 @@ export class UnlockCommand { } const userId = activeAccount.id; - if ( - await firstValueFrom( - this.configService.getFeatureFlag$(FeatureFlag.UnlockWithMasterPasswordUnlockData), - ) - ) { - try { - const userKey = await this.masterPasswordUnlockService.unlockWithMasterPassword( - password, - userId, - ); - - await this.keyService.setUserKey(userKey, userId); - } catch (e) { - return Response.error(e.message); - } - } else { - const email = activeAccount.email; - const verification = { - type: VerificationType.MasterPassword, - secret: password, - } as MasterPasswordVerification; - - let masterKey: MasterKey; - try { - const response = await this.userVerificationService.verifyUserByMasterPassword( - verification, - userId, - email, - ); - masterKey = response.masterKey; - } catch (e) { - // verification failure throws - return Response.error(e.message); - } - - const userKey = await this.masterPasswordService.decryptUserKeyWithMasterKey( - masterKey, + try { + const userKey = await this.masterPasswordUnlockService.unlockWithMasterPassword( + password, userId, ); + await this.keyService.setUserKey(userKey, userId); + } catch (e) { + return Response.error(e.message); } if (await firstValueFrom(this.keyConnectorService.convertAccountRequired$)) { @@ -116,6 +77,8 @@ export class UnlockCommand { } } + await this.encryptedMigrator.runMigrations(userId, password); + return this.successResponse(); } diff --git a/apps/cli/src/key-management/session-timeout/services/cli-session-timeout-type.service.ts b/apps/cli/src/key-management/session-timeout/services/cli-session-timeout-type.service.ts new file mode 100644 index 00000000000..8143b37b8a3 --- /dev/null +++ b/apps/cli/src/key-management/session-timeout/services/cli-session-timeout-type.service.ts @@ -0,0 +1,15 @@ +import { SessionTimeoutTypeService } from "@bitwarden/common/key-management/session-timeout"; +import { + VaultTimeout, + VaultTimeoutStringType, +} from "@bitwarden/common/key-management/vault-timeout"; + +export class CliSessionTimeoutTypeService implements SessionTimeoutTypeService { + async isAvailable(timeout: VaultTimeout): Promise { + return timeout === VaultTimeoutStringType.Never; + } + + async getOrPromoteToAvailable(_: VaultTimeout): Promise { + return VaultTimeoutStringType.Never; + } +} diff --git a/apps/cli/src/oss-serve-configurator.ts b/apps/cli/src/oss-serve-configurator.ts index bd51cf4dd91..e0385534cb7 100644 --- a/apps/cli/src/oss-serve-configurator.ts +++ b/apps/cli/src/oss-serve-configurator.ts @@ -122,6 +122,7 @@ export class OssServeConfigurator { this.serviceContainer.syncService, this.serviceContainer.accountService, this.serviceContainer.authService, + this.serviceContainer.userAutoUnlockKeyService, ); this.deleteCommand = new DeleteCommand( this.serviceContainer.cipherService, @@ -146,7 +147,6 @@ export class OssServeConfigurator { this.serviceContainer.encryptService, this.serviceContainer.organizationUserApiService, this.serviceContainer.accountService, - this.serviceContainer.configService, this.serviceContainer.i18nService, ); this.restoreCommand = new RestoreCommand( @@ -166,9 +166,7 @@ export class OssServeConfigurator { ); this.unlockCommand = new UnlockCommand( this.serviceContainer.accountService, - this.serviceContainer.masterPasswordService, this.serviceContainer.keyService, - this.serviceContainer.userVerificationService, this.serviceContainer.cryptoFunctionService, this.serviceContainer.logService, this.serviceContainer.keyConnectorService, @@ -176,8 +174,8 @@ export class OssServeConfigurator { this.serviceContainer.organizationApiService, async () => await this.serviceContainer.logout(), this.serviceContainer.i18nService, + this.serviceContainer.encryptedMigrator, this.serviceContainer.masterPasswordUnlockService, - this.serviceContainer.configService, ); this.sendCreateCommand = new SendCreateCommand( diff --git a/apps/cli/src/platform/services/cli-sdk-load.service.ts b/apps/cli/src/platform/services/cli-sdk-load.service.ts index 638e64a8214..13a4c19d51d 100644 --- a/apps/cli/src/platform/services/cli-sdk-load.service.ts +++ b/apps/cli/src/platform/services/cli-sdk-load.service.ts @@ -3,6 +3,8 @@ import * as sdk from "@bitwarden/sdk-internal"; export class CliSdkLoadService extends SdkLoadService { async load(): Promise { + // CLI uses stdout for user interaction / automations so we cannot log info / debug here. + SdkLoadService.logLevel = sdk.LogLevel.Error; const module = await import("@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm"); (sdk as any).init(module); } diff --git a/apps/cli/src/program.ts b/apps/cli/src/program.ts index a5f12b34035..7856fc3588c 100644 --- a/apps/cli/src/program.ts +++ b/apps/cli/src/program.ts @@ -195,6 +195,8 @@ export class Program extends BaseProgram { this.serviceContainer.ssoUrlService, this.serviceContainer.i18nService, this.serviceContainer.masterPasswordService, + this.serviceContainer.userDecryptionOptionsService, + this.serviceContainer.encryptedMigrator, ); const response = await command.run(email, password, options); this.processResponse(response, true); @@ -277,6 +279,11 @@ export class Program extends BaseProgram { }) .option("--check", "Check lock status.", async () => { await this.exitIfNotAuthed(); + const userId = (await firstValueFrom(this.serviceContainer.accountService.activeAccount$)) + ?.id; + await this.serviceContainer.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet( + userId, + ); const authStatus = await this.serviceContainer.authService.getAuthStatus(); if (authStatus === AuthenticationStatus.Unlocked) { @@ -296,9 +303,7 @@ export class Program extends BaseProgram { await this.exitIfNotAuthed(); const command = new UnlockCommand( this.serviceContainer.accountService, - this.serviceContainer.masterPasswordService, this.serviceContainer.keyService, - this.serviceContainer.userVerificationService, this.serviceContainer.cryptoFunctionService, this.serviceContainer.logService, this.serviceContainer.keyConnectorService, @@ -306,8 +311,8 @@ export class Program extends BaseProgram { this.serviceContainer.organizationApiService, async () => await this.serviceContainer.logout(), this.serviceContainer.i18nService, + this.serviceContainer.encryptedMigrator, this.serviceContainer.masterPasswordUnlockService, - this.serviceContainer.configService, ); const response = await command.run(password, cmd); this.processResponse(response); @@ -517,6 +522,7 @@ export class Program extends BaseProgram { this.serviceContainer.syncService, this.serviceContainer.accountService, this.serviceContainer.authService, + this.serviceContainer.userAutoUnlockKeyService, ); const response = await command.run(); this.processResponse(response); diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 122dd6ea052..7bb8da27040 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -69,6 +69,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs import { DefaultBillingAccountProfileStateService } from "@bitwarden/common/billing/services/account/billing-account-profile-state.service"; import { HibpApiService } from "@bitwarden/common/dirt/services/hibp-api.service"; import { ClientType } from "@bitwarden/common/enums"; +import { DefaultAccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/default-account-cryptographic-state.service"; import { DefaultKeyGenerationService, KeyGenerationService, @@ -76,6 +77,10 @@ import { import { EncryptServiceImplementation } from "@bitwarden/common/key-management/crypto/services/encrypt.service.implementation"; import { DeviceTrustServiceAbstraction } from "@bitwarden/common/key-management/device-trust/abstractions/device-trust.service.abstraction"; import { DeviceTrustService } from "@bitwarden/common/key-management/device-trust/services/device-trust.service.implementation"; +import { DefaultEncryptedMigrator } from "@bitwarden/common/key-management/encrypted-migrator/default-encrypted-migrator"; +import { EncryptedMigrator } from "@bitwarden/common/key-management/encrypted-migrator/encrypted-migrator.abstraction"; +import { DefaultChangeKdfApiService } from "@bitwarden/common/key-management/kdf/change-kdf-api.service"; +import { DefaultChangeKdfService } from "@bitwarden/common/key-management/kdf/change-kdf.service"; import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/services/key-connector.service"; import { MasterPasswordUnlockService } from "@bitwarden/common/key-management/master-password/abstractions/master-password-unlock.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; @@ -99,6 +104,7 @@ import { EnvironmentService, RegionConfig, } from "@bitwarden/common/platform/abstractions/environment.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 { LogLevelType } from "@bitwarden/common/platform/enums"; @@ -119,6 +125,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 { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service"; import { SyncService } from "@bitwarden/common/platform/sync"; @@ -140,11 +147,13 @@ import { SendService } from "@bitwarden/common/tools/send/services/send.service" import { UserId } from "@bitwarden/common/types/guid"; import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; import { CipherEncryptionService } from "@bitwarden/common/vault/abstractions/cipher-encryption.service"; +import { CipherSdkService } from "@bitwarden/common/vault/abstractions/cipher-sdk.service"; import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherAuthorizationService, DefaultCipherAuthorizationService, } from "@bitwarden/common/vault/services/cipher-authorization.service"; +import { DefaultCipherSdkService } from "@bitwarden/common/vault/services/cipher-sdk.service"; import { CipherService } from "@bitwarden/common/vault/services/cipher.service"; import { DefaultCipherArchiveService } from "@bitwarden/common/vault/services/default-cipher-archive.service"; import { DefaultCipherEncryptionService } from "@bitwarden/common/vault/services/default-cipher-encryption.service"; @@ -207,6 +216,7 @@ import { import { CliBiometricsService } from "../key-management/cli-biometrics-service"; import { CliProcessReloadService } from "../key-management/cli-process-reload.service"; +import { CliSessionTimeoutTypeService } from "../key-management/session-timeout/services/cli-session-timeout-type.service"; import { flagEnabled } from "../platform/flags"; import { CliPlatformUtilsService } from "../platform/services/cli-platform-utils.service"; import { CliSdkLoadService } from "../platform/services/cli-sdk-load.service"; @@ -246,6 +256,7 @@ export class ServiceContainer { twoFactorApiService: TwoFactorApiService; hibpApiService: HibpApiService; environmentService: EnvironmentService; + cipherSdkService: CipherSdkService; cipherService: CipherService; folderService: InternalFolderService; organizationUserApiService: OrganizationUserApiService; @@ -317,6 +328,7 @@ export class ServiceContainer { kdfConfigService: KdfConfigService; taskSchedulerService: TaskSchedulerService; sdkService: SdkService; + registerSdkService: RegisterSdkService; sdkLoadService: SdkLoadService; cipherAuthorizationService: CipherAuthorizationService; ssoUrlService: SsoUrlService; @@ -324,10 +336,12 @@ export class ServiceContainer { cipherEncryptionService: CipherEncryptionService; restrictedItemTypesService: RestrictedItemTypesService; cliRestrictedItemTypesService: CliRestrictedItemTypesService; + encryptedMigrator: EncryptedMigrator; securityStateService: SecurityStateService; masterPasswordUnlockService: MasterPasswordUnlockService; cipherArchiveService: CipherArchiveService; lockService: LockService; + private accountCryptographicStateService: DefaultAccountCryptographicStateService; constructor() { let p = null; @@ -486,10 +500,7 @@ export class ServiceContainer { const pinStateService = new PinStateService(this.stateProvider); this.pinService = new PinService( - this.accountService, this.encryptService, - this.kdfConfigService, - this.keyGenerationService, this.logService, this.keyService, this.sdkService, @@ -518,7 +529,13 @@ export class ServiceContainer { this.ssoUrlService = new SsoUrlService(); this.organizationService = new DefaultOrganizationService(this.stateProvider); - this.policyService = new DefaultPolicyService(this.stateProvider, this.organizationService); + this.policyService = new DefaultPolicyService( + this.stateProvider, + this.organizationService, + this.accountService, + ); + + const sessionTimeoutTypeService = new CliSessionTimeoutTypeService(); this.vaultTimeoutSettingsService = new DefaultVaultTimeoutSettingsService( this.accountService, @@ -531,6 +548,7 @@ export class ServiceContainer { this.stateProvider, this.logService, VaultTimeoutStringType.Never, // default vault timeout + sessionTimeoutTypeService, ); const refreshAccessTokenErrorCallback = () => { @@ -620,26 +638,10 @@ export class ServiceContainer { this.accountService, ); - this.keyConnectorService = new KeyConnectorService( - this.accountService, - this.masterPasswordService, - this.keyService, - this.apiService, - this.tokenService, - this.logService, - this.organizationService, - this.keyGenerationService, - logoutCallback, + this.accountCryptographicStateService = new DefaultAccountCryptographicStateService( this.stateProvider, ); - this.twoFactorService = new DefaultTwoFactorService( - this.i18nService, - this.platformUtilsService, - this.globalStateProvider, - this.twoFactorApiService, - ); - const sdkClientFactory = flagEnabled("sdk") ? new DefaultSdkClientFactory() : new NoopSdkClientFactory(); @@ -658,6 +660,41 @@ export class ServiceContainer { customUserAgent, ); + this.registerSdkService = new DefaultRegisterSdkService( + sdkClientFactory, + this.environmentService, + this.platformUtilsService, + this.accountService, + this.apiService, + this.stateProvider, + this.configService, + customUserAgent, + ); + + 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.twoFactorService = new DefaultTwoFactorService( + this.i18nService, + this.platformUtilsService, + this.globalStateProvider, + this.twoFactorApiService, + ); + this.passwordStrengthService = new PasswordStrengthService(); this.passwordGenerationService = legacyPasswordGenerationServiceFactory( @@ -734,6 +771,7 @@ export class ServiceContainer { this.kdfConfigService, this.taskSchedulerService, this.configService, + this.accountCryptographicStateService, ); this.restrictedItemTypesService = new RestrictedItemTypesService( @@ -759,6 +797,8 @@ export class ServiceContainer { this.logService, ); + this.cipherSdkService = new DefaultCipherSdkService(this.sdkService, this.logService); + this.cipherService = new CipherService( this.keyService, this.domainSettingsService, @@ -774,6 +814,7 @@ export class ServiceContainer { this.logService, this.cipherEncryptionService, this.messagingService, + this.cipherSdkService, ); this.cipherArchiveService = new DefaultCipherArchiveService( @@ -869,6 +910,7 @@ export class ServiceContainer { this.stateProvider, this.securityStateService, this.kdfConfigService, + this.accountCryptographicStateService, ); this.totpService = new TotpService(this.sdkService); @@ -895,7 +937,7 @@ export class ServiceContainer { this.collectionService, this.keyService, this.encryptService, - this.pinService, + this.keyGenerationService, this.accountService, this.restrictedItemTypesService, ); @@ -903,7 +945,7 @@ export class ServiceContainer { this.individualExportService = new IndividualVaultExportService( this.folderService, this.cipherService, - this.pinService, + this.keyGenerationService, this.keyService, this.encryptService, this.cryptoFunctionService, @@ -917,7 +959,7 @@ export class ServiceContainer { this.organizationExportService = new OrganizationVaultExportService( this.cipherService, this.vaultExportApiService, - this.pinService, + this.keyGenerationService, this.keyService, this.encryptService, this.cryptoFunctionService, @@ -971,6 +1013,21 @@ export class ServiceContainer { ); this.masterPasswordApiService = new MasterPasswordApiService(this.apiService, this.logService); + const changeKdfApiService = new DefaultChangeKdfApiService(this.apiService); + const changeKdfService = new DefaultChangeKdfService( + changeKdfApiService, + this.sdkService, + this.keyService, + this.masterPasswordService, + ); + this.encryptedMigrator = new DefaultEncryptedMigrator( + this.kdfConfigService, + changeKdfService, + this.logService, + this.configService, + this.masterPasswordService, + this.syncService, + ); } async logout() { @@ -1007,7 +1064,6 @@ export class ServiceContainer { this.containerService.attachToGlobal(global); await this.i18nService.init(); this.twoFactorService.init(); - this.encryptService.init(this.configService); // If a user has a BW_SESSION key stored in their env (not process.env.BW_SESSION), // this should set the user key to unlock the vault on init. diff --git a/apps/cli/src/tools/send/commands/create.command.ts b/apps/cli/src/tools/send/commands/create.command.ts index 7803f6f94d4..91e579c26c1 100644 --- a/apps/cli/src/tools/send/commands/create.command.ts +++ b/apps/cli/src/tools/send/commands/create.command.ts @@ -9,9 +9,9 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { SendType } from "@bitwarden/common/tools/send/types/send-type"; import { NodeUtils } from "@bitwarden/node/node-utils"; import { Response } from "../../../models/response"; diff --git a/apps/cli/src/tools/send/commands/edit.command.ts b/apps/cli/src/tools/send/commands/edit.command.ts index bf53c8a5cb9..2c6d41d66ac 100644 --- a/apps/cli/src/tools/send/commands/edit.command.ts +++ b/apps/cli/src/tools/send/commands/edit.command.ts @@ -5,9 +5,9 @@ import { firstValueFrom } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { SendType } from "@bitwarden/common/tools/send/types/send-type"; import { Response } from "../../../models/response"; import { CliUtils } from "../../../utils"; diff --git a/apps/cli/src/tools/send/commands/receive.command.ts b/apps/cli/src/tools/send/commands/receive.command.ts index a412f7c1667..5cbf458c87f 100644 --- a/apps/cli/src/tools/send/commands/receive.command.ts +++ b/apps/cli/src/tools/send/commands/receive.command.ts @@ -13,11 +13,11 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EncArrayBuffer } from "@bitwarden/common/platform/models/domain/enc-array-buffer"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { SendAccess } from "@bitwarden/common/tools/send/models/domain/send-access"; import { SendAccessRequest } from "@bitwarden/common/tools/send/models/request/send-access.request"; import { SendAccessView } from "@bitwarden/common/tools/send/models/view/send-access.view"; import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; +import { SendType } from "@bitwarden/common/tools/send/types/send-type"; import { KeyService } from "@bitwarden/key-management"; import { NodeUtils } from "@bitwarden/node/node-utils"; diff --git a/apps/cli/src/tools/send/commands/template.command.ts b/apps/cli/src/tools/send/commands/template.command.ts index c1c2c97b03d..09213ac5fa8 100644 --- a/apps/cli/src/tools/send/commands/template.command.ts +++ b/apps/cli/src/tools/send/commands/template.command.ts @@ -1,4 +1,4 @@ -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; +import { SendType } from "@bitwarden/common/tools/send/types/send-type"; import { Response } from "../../../models/response"; import { TemplateResponse } from "../../../models/response/template.response"; diff --git a/apps/cli/src/tools/send/models/send-access.response.ts b/apps/cli/src/tools/send/models/send-access.response.ts index 07877bfb548..7bd54801307 100644 --- a/apps/cli/src/tools/send/models/send-access.response.ts +++ b/apps/cli/src/tools/send/models/send-access.response.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { SendAccessView } from "@bitwarden/common/tools/send/models/view/send-access.view"; +import { SendType } from "@bitwarden/common/tools/send/types/send-type"; import { BaseResponse } from "../../../models/response/base.response"; diff --git a/apps/cli/src/tools/send/models/send.response.ts b/apps/cli/src/tools/send/models/send.response.ts index a0c1d3f83c6..b7655226be0 100644 --- a/apps/cli/src/tools/send/models/send.response.ts +++ b/apps/cli/src/tools/send/models/send.response.ts @@ -1,8 +1,8 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; +import { SendType } from "@bitwarden/common/tools/send/types/send-type"; import { BaseResponse } from "../../../models/response/base.response"; diff --git a/apps/cli/src/tools/send/send.program.ts b/apps/cli/src/tools/send/send.program.ts index 33bf4518ccd..869d77a379c 100644 --- a/apps/cli/src/tools/send/send.program.ts +++ b/apps/cli/src/tools/send/send.program.ts @@ -7,7 +7,7 @@ import * as chalk from "chalk"; import { program, Command, Option, OptionValues } from "commander"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; +import { SendType } from "@bitwarden/common/tools/send/types/send-type"; import { BaseProgram } from "../../base-program"; import { Response } from "../../models/response"; diff --git a/apps/cli/src/utils.ts b/apps/cli/src/utils.ts index e321adbfd5e..72746cb9b71 100644 --- a/apps/cli/src/utils.ts +++ b/apps/cli/src/utils.ts @@ -1,12 +1,10 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import * as fs from "fs"; import * as path from "path"; import * as inquirer from "inquirer"; import * as JSZip from "jszip"; -import { CollectionView } from "@bitwarden/admin-console/common"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; diff --git a/apps/cli/src/vault.program.ts b/apps/cli/src/vault.program.ts index 21f87feab00..3e08038fe64 100644 --- a/apps/cli/src/vault.program.ts +++ b/apps/cli/src/vault.program.ts @@ -494,7 +494,6 @@ export class VaultProgram extends BaseProgram { this.serviceContainer.encryptService, this.serviceContainer.organizationUserApiService, this.serviceContainer.accountService, - this.serviceContainer.configService, this.serviceContainer.i18nService, ); const response = await command.run(object, id, cmd); diff --git a/apps/cli/src/vault/create.command.ts b/apps/cli/src/vault/create.command.ts index 5602c593942..e1a91966afd 100644 --- a/apps/cli/src/vault/create.command.ts +++ b/apps/cli/src/vault/create.command.ts @@ -103,10 +103,11 @@ export class CreateCommand { return Response.error("Creating this item type is restricted by organizational policy."); } - const cipher = await this.cipherService.encrypt(CipherExport.toView(req), activeUserId); - const newCipher = await this.cipherService.createWithServer(cipher); - const decCipher = await this.cipherService.decrypt(newCipher, activeUserId); - const res = new CipherResponse(decCipher); + const newCipher = await this.cipherService.createWithServer( + CipherExport.toView(req), + activeUserId, + ); + const res = new CipherResponse(newCipher); return Response.success(res); } catch (e) { return Response.error(e); @@ -181,12 +182,12 @@ export class CreateCommand { private async createFolder(req: FolderExport) { const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); - const userKey = await this.keyService.getUserKey(activeUserId); + const userKey = await firstValueFrom(this.keyService.userKey$(activeUserId)); const folder = await this.folderService.encrypt(FolderExport.toView(req), userKey); try { const folderData = await this.folderApiService.save(folder, activeUserId); const newFolder = new Folder(folderData); - const decFolder = await newFolder.decrypt(); + const decFolder = await newFolder.decrypt(userKey); const res = new FolderResponse(decFolder); return Response.success(res); } catch (e) { diff --git a/apps/cli/stores/chocolatey/bitwarden-cli.nuspec b/apps/cli/stores/chocolatey/bitwarden-cli.nuspec index f7f86bc843f..9552ccc282c 100644 --- a/apps/cli/stores/chocolatey/bitwarden-cli.nuspec +++ b/apps/cli/stores/chocolatey/bitwarden-cli.nuspec @@ -10,7 +10,7 @@ Bitwarden Inc. https://bitwarden.com/ https://raw.githubusercontent.com/bitwarden/brand/master/icons/256x256.png - Copyright Š 2015-2025 Bitwarden Inc. + Copyright Š 2015-2026 Bitwarden Inc. https://github.com/bitwarden/clients/ https://help.bitwarden.com/article/cli/ https://github.com/bitwarden/clients/issues diff --git a/apps/desktop/custom-appx-manifest.xml b/apps/desktop/custom-appx-manifest.xml new file mode 100644 index 00000000000..2f7796c97cf --- /dev/null +++ b/apps/desktop/custom-appx-manifest.xml @@ -0,0 +1,111 @@ + + + + + + + + ${displayName} + ${publisherDisplayName} + A secure and free password manager for all of your devices. + assets\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/desktop/desktop_native/Cargo.lock b/apps/desktop/desktop_native/Cargo.lock index f6380c747d8..3e5225d4b5a 100644 --- a/apps/desktop/desktop_native/Cargo.lock +++ b/apps/desktop/desktop_native/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" - [[package]] name = "aead" version = "0.5.2" @@ -114,9 +99,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.94" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "arboard" @@ -138,14 +123,14 @@ dependencies = [ [[package]] name = "ashpd" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" +checksum = "da0986d5b4f0802160191ad75f8d33ada000558757db3defb70299ca95d9fcbd" dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.9.1", + "rand 0.9.2", "serde", "serde_repr", "tokio", @@ -344,26 +329,12 @@ name = "autotype" version = "0.0.0" dependencies = [ "anyhow", + "itertools", "mockall", "serial_test", "tracing", - "windows 0.61.1", - "windows-core 0.61.0", -] - -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", + "windows", + "windows-core", ] [[package]] @@ -415,9 +386,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bitwarden-russh" @@ -457,7 +428,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", - "windows 0.61.1", + "windows", ] [[package]] @@ -501,6 +472,12 @@ dependencies = [ "cipher", ] +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + [[package]] name = "byteorder" version = "1.5.0" @@ -509,9 +486,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "camino" @@ -556,9 +533,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.46" +version = "1.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" +checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" dependencies = [ "find-msvc-tools", "shlex", @@ -614,7 +591,7 @@ dependencies = [ "hex", "oo7", "pbkdf2", - "rand 0.9.1", + "rand 0.9.2", "rusqlite", "security-framework", "serde", @@ -623,7 +600,7 @@ dependencies = [ "tokio", "tracing", "verifysign", - "windows 0.61.1", + "windows", ] [[package]] @@ -709,9 +686,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "convert_case" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +checksum = "baaaa0ecca5b51987b9423ccdc971514dd8b0bb7b4060b983d3664dad3f1f89f" dependencies = [ "unicode-segmentation", ] @@ -770,16 +747,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ctor" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "ctor" version = "0.5.0" @@ -867,7 +834,7 @@ dependencies = [ "memsec", "oo7", "pin-project", - "rand 0.9.1", + "rand 0.9.2", "scopeguard", "secmem-proc", "security-framework", @@ -877,13 +844,13 @@ dependencies = [ "sha2", "ssh-key", "sysinfo", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "tokio-util", "tracing", "typenum", "widestring", - "windows 0.61.1", + "windows", "windows-future", "zbus", "zbus_polkit", @@ -1060,6 +1027,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "elliptic-curve" version = "0.13.8" @@ -1207,9 +1180,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" [[package]] name = "fixedbitset" @@ -1409,17 +1382,11 @@ dependencies = [ "polyval", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "goblin" @@ -1499,14 +1466,14 @@ dependencies = [ [[package]] name = "homedir" -version = "0.3.4" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bdbbd5bc8c5749697ccaa352fa45aff8730cf21c68029c0eef1ffed7c3d6ba2" +checksum = "68df315d2857b2d8d2898be54a85e1d001bbbe0dbb5f8ef847b48dd3a23c4527" dependencies = [ "cfg-if", - "nix 0.29.0", + "nix", "widestring", - "windows 0.57.0", + "windows", ] [[package]] @@ -1657,12 +1624,31 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1674,9 +1660,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libloading" @@ -1745,19 +1731,18 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.25" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "macos_provider" @@ -1841,15 +1826,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" -dependencies = [ - "adler2", -] - [[package]] name = "mio" version = "1.0.3" @@ -1863,9 +1839,9 @@ dependencies = [ [[package]] name = "mockall" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" +checksum = "f58d964098a5f9c6b63d0798e5372fd04708193510a7af313c22e9f29b7b620b" dependencies = [ "cfg-if", "downcast", @@ -1877,9 +1853,9 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" +checksum = "ca41ce716dda6a9be188b385aa78ee5260fc25cd3802cb2a8afdc6afbe6b6dbf" dependencies = [ "cfg-if", "proc-macro2", @@ -1889,32 +1865,33 @@ dependencies = [ [[package]] name = "napi" -version = "2.16.17" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55740c4ae1d8696773c78fdafd5d0e5fe9bc9f1b071c7ba493ba5c413a9184f3" +checksum = "f1b74e3dce5230795bb4d2821b941706dee733c7308752507254b0497f39cad7" dependencies = [ "bitflags", - "ctor 0.2.9", - "napi-derive", + "ctor", + "napi-build", "napi-sys", - "once_cell", + "nohash-hasher", + "rustc-hash", "tokio", ] [[package]] name = "napi-build" -version = "2.2.0" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03acbfa4f156a32188bfa09b86dc11a431b5725253fc1fc6f6df5bed273382c4" +checksum = "dcae8ad5609d14afb3a3b91dee88c757016261b151e9dcecabf1b2a31a6cab14" [[package]] name = "napi-derive" -version = "2.16.13" +version = "3.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cbe2585d8ac223f7d34f13701434b9d5f4eb9c332cccce8dee57ea18ab8ab0c" +checksum = "7552d5a579b834614bbd496db5109f1b9f1c758f08224b0dee1e408333adf0d0" dependencies = [ - "cfg-if", "convert_case", + "ctor", "napi-derive-backend", "proc-macro2", "quote", @@ -1923,40 +1900,26 @@ dependencies = [ [[package]] name = "napi-derive-backend" -version = "1.0.75" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1639aaa9eeb76e91c6ae66da8ce3e89e921cd3885e99ec85f4abacae72fc91bf" +checksum = "5f6a81ac7486b70f2532a289603340862c06eea5a1e650c1ffeda2ce1238516a" dependencies = [ "convert_case", - "once_cell", "proc-macro2", "quote", - "regex", "semver", "syn", ] [[package]] name = "napi-sys" -version = "2.4.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427802e8ec3a734331fec1035594a210ce1ff4dc5bc1950530920ab717964ea3" +checksum = "3e4e7135a8f97aa0f1509cce21a8a1f9dcec1b50d8dee006b48a5adb69a9d64d" dependencies = [ "libloading", ] -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "nix" version = "0.30.1" @@ -1970,6 +1933,12 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + [[package]] name = "nom" version = "7.1.3" @@ -2024,11 +1993,10 @@ dependencies = [ [[package]] name = "num-bigint-dig" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" dependencies = [ - "byteorder", "lazy_static", "libm", "num-integer", @@ -2173,15 +2141,6 @@ dependencies = [ "objc2-core-foundation", ] -[[package]] -name = "object" -version = "0.36.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -2190,9 +2149,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "oo7" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb23d3ec3527d65a83be1c1795cb883c52cfa57147d42acc797127df56fc489" +checksum = "e3299dd401feaf1d45afd8fd1c0586f10fcfb22f244bb9afa942cec73503b89d" dependencies = [ "aes", "ashpd", @@ -2208,7 +2167,7 @@ dependencies = [ "num", "num-bigint-dig", "pbkdf2", - "rand 0.9.1", + "rand 0.9.2", "serde", "sha2", "subtle", @@ -2297,9 +2256,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -2307,15 +2266,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] @@ -2548,7 +2507,7 @@ dependencies = [ name = "process_isolation" version = "0.0.0" dependencies = [ - "ctor 0.5.0", + "ctor", "desktop_core", "libc", "tracing", @@ -2591,9 +2550,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -2660,19 +2619,7 @@ checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.12", -] - -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", + "thiserror 2.0.17", ] [[package]] @@ -2704,9 +2651,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.6" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ "const-oid", "digest", @@ -2748,10 +2695,10 @@ dependencies = [ ] [[package]] -name = "rustc-demangle" -version = "0.1.24" +name = "rustc-hash" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc_version" @@ -2798,6 +2745,12 @@ dependencies = [ "rustix 1.0.7", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + [[package]] name = "ryu" version = "1.0.20" @@ -2870,15 +2823,15 @@ dependencies = [ "libc", "rustix 1.0.7", "rustix-linux-procfs", - "thiserror 2.0.12", - "windows 0.61.1", + "thiserror 2.0.17", + "windows", ] [[package]] name = "security-framework" -version = "3.5.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc198e42d9b7510827939c9a15f5062a0c913f3371d765977e586d2fe6c16f4a" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ "bitflags", "core-foundation", @@ -2960,11 +2913,12 @@ dependencies = [ [[package]] name = "serial_test" -version = "3.2.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9" +checksum = "0d0b343e184fc3b7bb44dff0705fffcf4b3756ba6aff420dddd8b24ca145e555" dependencies = [ - "futures", + "futures-executor", + "futures-util", "log", "once_cell", "parking_lot", @@ -2974,9 +2928,9 @@ dependencies = [ [[package]] name = "serial_test_derive" -version = "3.2.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" +checksum = "6f50427f258fb77356e4cd4aa0e87e2bd2c66dbcee41dc405282cae2bfc26c83" dependencies = [ "proc-macro2", "quote", @@ -2996,9 +2950,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -3056,9 +3010,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smawk" @@ -3068,12 +3022,12 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "socket2" -version = "0.5.9" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] @@ -3188,16 +3142,16 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.35.0" +version = "0.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b897c8ea620e181c7955369a31be5f48d9a9121cb59fd33ecef9ff2a34323422" +checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f" dependencies = [ "libc", "memchr", "ntapi", "objc2-core-foundation", "objc2-io-kit", - "windows 0.61.1", + "windows", ] [[package]] @@ -3239,11 +3193,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.17", ] [[package]] @@ -3259,9 +3213,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -3289,11 +3243,10 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.0" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", "libc", "mio", @@ -3303,14 +3256,14 @@ dependencies = [ "socket2", "tokio-macros", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -3319,9 +3272,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.13" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -3680,6 +3633,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.1" @@ -3745,6 +3709,51 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] + [[package]] name = "wayland-backend" version = "0.3.10" @@ -3852,16 +3861,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" -dependencies = [ - "windows-core 0.57.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.61.1" @@ -3869,7 +3868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ "windows-collections", - "windows-core 0.61.0", + "windows-core", "windows-future", "windows-link 0.1.3", "windows-numerics", @@ -3881,19 +3880,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.0", -] - -[[package]] -name = "windows-core" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" -dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets 0.52.6", + "windows-core", ] [[package]] @@ -3902,8 +3889,8 @@ version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", + "windows-implement", + "windows-interface", "windows-link 0.1.3", "windows-result 0.3.4", "windows-strings 0.4.2", @@ -3915,21 +3902,10 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link 0.1.3", ] -[[package]] -name = "windows-implement" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-implement" version = "0.60.0" @@ -3941,17 +3917,6 @@ dependencies = [ "syn", ] -[[package]] -name = "windows-interface" -version = "0.57.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-interface" version = "0.59.1" @@ -3981,7 +3946,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link 0.1.3", ] @@ -3996,15 +3961,6 @@ dependencies = [ "windows-strings 0.5.1", ] -[[package]] -name = "windows-result" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-result" version = "0.3.4" @@ -4262,8 +4218,8 @@ name = "windows_plugin_authenticator" version = "0.0.0" dependencies = [ "hex", - "windows 0.61.1", - "windows-core 0.61.0", + "windows", + "windows-core", ] [[package]] @@ -4434,9 +4390,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.11.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d07e46d035fb8e375b2ce63ba4e4ff90a7f73cf2ffb0138b29e1158d2eaadf7" +checksum = "b622b18155f7a93d1cd2dc8c01d2d6a44e08fb9ebb7b3f9e6ed101488bad6c91" dependencies = [ "async-broadcast", "async-executor", @@ -4452,14 +4408,15 @@ dependencies = [ "futures-core", "futures-lite", "hex", - "nix 0.30.1", + "nix", "ordered-stream", "serde", "serde_repr", "tokio", "tracing", "uds_windows", - "windows-sys 0.60.2", + "uuid", + "windows-sys 0.61.2", "winnow", "zbus_macros", "zbus_names", @@ -4468,9 +4425,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.11.0" +version = "5.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e797a9c847ed3ccc5b6254e8bcce056494b375b511b3d6edcec0aeb4defaca" +checksum = "1cdb94821ca8a87ca9c298b5d1cbd80e2a8b67115d99f6e4551ac49e42b6a314" dependencies = [ "proc-macro-crate", "proc-macro2", diff --git a/apps/desktop/desktop_native/Cargo.toml b/apps/desktop/desktop_native/Cargo.toml index 0b09daa9bdd..facd9554af1 100644 --- a/apps/desktop/desktop_native/Cargo.toml +++ b/apps/desktop/desktop_native/Cargo.toml @@ -9,7 +9,7 @@ members = [ "napi", "process_isolation", "proxy", - "windows_plugin_authenticator" + "windows_plugin_authenticator", ] [workspace.package] @@ -21,13 +21,13 @@ publish = false [workspace.dependencies] aes = "=0.8.4" aes-gcm = "=0.10.3" -anyhow = "=1.0.94" +anyhow = "=1.0.100" arboard = { version = "=3.6.1", default-features = false } -ashpd = "=0.11.0" +ashpd = "=0.12.0" base64 = "=0.22.1" bitwarden-russh = { git = "https://github.com/bitwarden/bitwarden-russh.git", rev = "a641316227227f8777fdf56ac9fa2d6b5f7fe662" } byteorder = "=1.5.0" -bytes = "=1.10.1" +bytes = "=1.11.0" cbc = "=0.1.2" chacha20poly1305 = "=0.10.1" core-foundation = "=0.10.1" @@ -37,33 +37,34 @@ ed25519 = "=2.2.3" embed_plist = "=1.2.2" futures = "=0.3.31" hex = "=0.4.3" -homedir = "=0.3.4" +homedir = "=0.3.6" interprocess = "=2.2.1" -libc = "=0.2.177" +itertools = "=0.14.0" +libc = "=0.2.178" linux-keyutils = "=0.2.4" memsec = "=0.7.0" -napi = "=2.16.17" -napi-build = "=2.2.0" -napi-derive = "=2.16.13" -oo7 = "=0.4.3" +napi = "=3.3.0" +napi-build = "=2.2.3" +napi-derive = "=3.2.5" +oo7 = "=0.5.0" pin-project = "=1.1.10" pkcs8 = "=0.10.2" -rand = "=0.9.1" -rsa = "=0.9.6" +rand = "=0.9.2" +rsa = "=0.9.10" russh-cryptovec = "=0.7.3" scopeguard = "=1.2.0" secmem-proc = "=0.3.7" -security-framework = "=3.5.0" +security-framework = "=3.5.1" security-framework-sys = "=2.15.0" serde = "=1.0.209" serde_json = "=1.0.127" -sha2 = "=0.10.8" +sha2 = "=0.10.9" ssh-encoding = "=0.2.0" ssh-key = { version = "=0.6.7", default-features = false } -sysinfo = "=0.35.0" -thiserror = "=2.0.12" -tokio = "=1.45.0" -tokio-util = "=0.7.13" +sysinfo = "=0.37.2" +thiserror = "=2.0.17" +tokio = "=1.48.0" +tokio-util = "=0.7.17" tracing = "=0.1.41" tracing-subscriber = { version = "=0.3.20", features = [ "fmt", @@ -77,7 +78,7 @@ windows = "=0.61.1" windows-core = "=0.61.0" windows-future = "=0.2.0" windows-registry = "=0.6.1" -zbus = "=5.11.0" +zbus = "=5.12.0" zbus_polkit = "=5.0.0" zeroizing-alloc = "=0.1.0" diff --git a/apps/desktop/desktop_native/autotype/Cargo.toml b/apps/desktop/desktop_native/autotype/Cargo.toml index 267074d0bc8..59dd36c6c91 100644 --- a/apps/desktop/desktop_native/autotype/Cargo.toml +++ b/apps/desktop/desktop_native/autotype/Cargo.toml @@ -5,12 +5,10 @@ license.workspace = true edition.workspace = true publish.workspace = true -[dependencies] -anyhow = { workspace = true } - [target.'cfg(windows)'.dependencies] -mockall = "=0.13.1" -serial_test = "=3.2.0" +itertools.workspace = true +mockall = "=0.14.0" +serial_test = "=3.3.1" tracing.workspace = true windows = { workspace = true, features = [ "Win32_UI_Input_KeyboardAndMouse", @@ -18,5 +16,17 @@ windows = { workspace = true, features = [ ] } windows-core = { workspace = true } +[dependencies] +anyhow = { workspace = true } + +[target.'cfg(windows)'.dev-dependencies] +windows = { workspace = true, features = [ + "Win32_UI_Input_KeyboardAndMouse", + "Win32_UI_WindowsAndMessaging", + "Win32_Foundation", + "Win32_System_LibraryLoader", + "Win32_Graphics_Gdi", +] } + [lints] workspace = true diff --git a/apps/desktop/desktop_native/autotype/src/lib.rs b/apps/desktop/desktop_native/autotype/src/lib.rs index c87fea23b60..917f0f797b6 100644 --- a/apps/desktop/desktop_native/autotype/src/lib.rs +++ b/apps/desktop/desktop_native/autotype/src/lib.rs @@ -1,5 +1,11 @@ use anyhow::Result; +#[cfg(target_os = "windows")] +mod modifier_keys; + +#[cfg(target_os = "windows")] +pub(crate) use modifier_keys::*; + #[cfg_attr(target_os = "linux", path = "linux.rs")] #[cfg_attr(target_os = "macos", path = "macos.rs")] #[cfg_attr(target_os = "windows", path = "windows/mod.rs")] @@ -28,6 +34,6 @@ pub fn get_foreground_window_title() -> Result { /// This function returns an `anyhow::Error` if there is any /// issue in typing the input. Detailed reasons will /// vary based on platform implementation. -pub fn type_input(input: Vec, keyboard_shortcut: Vec) -> Result<()> { +pub fn type_input(input: &[u16], keyboard_shortcut: &[String]) -> Result<()> { windowing::type_input(input, keyboard_shortcut) } diff --git a/apps/desktop/desktop_native/autotype/src/linux.rs b/apps/desktop/desktop_native/autotype/src/linux.rs index 9fda0ed9e33..e7b0ee8117e 100644 --- a/apps/desktop/desktop_native/autotype/src/linux.rs +++ b/apps/desktop/desktop_native/autotype/src/linux.rs @@ -2,6 +2,6 @@ pub fn get_foreground_window_title() -> anyhow::Result { todo!("Bitwarden does not yet support Linux autotype"); } -pub fn type_input(_input: Vec, _keyboard_shortcut: Vec) -> anyhow::Result<()> { +pub fn type_input(_input: &[u16], _keyboard_shortcut: &[String]) -> anyhow::Result<()> { todo!("Bitwarden does not yet support Linux autotype"); } diff --git a/apps/desktop/desktop_native/autotype/src/macos.rs b/apps/desktop/desktop_native/autotype/src/macos.rs index c6681a3291e..56995a7f810 100644 --- a/apps/desktop/desktop_native/autotype/src/macos.rs +++ b/apps/desktop/desktop_native/autotype/src/macos.rs @@ -2,6 +2,6 @@ pub fn get_foreground_window_title() -> anyhow::Result { todo!("Bitwarden does not yet support macOS autotype"); } -pub fn type_input(_input: Vec, _keyboard_shortcut: Vec) -> anyhow::Result<()> { +pub fn type_input(_input: &[u16], _keyboard_shortcut: &[String]) -> anyhow::Result<()> { todo!("Bitwarden does not yet support macOS autotype"); } diff --git a/apps/desktop/desktop_native/autotype/src/modifier_keys.rs b/apps/desktop/desktop_native/autotype/src/modifier_keys.rs new file mode 100644 index 00000000000..c451a3b25e4 --- /dev/null +++ b/apps/desktop/desktop_native/autotype/src/modifier_keys.rs @@ -0,0 +1,45 @@ +// Electron modifier keys +// +pub(crate) const CONTROL_KEY_STR: &str = "Control"; +pub(crate) const ALT_KEY_STR: &str = "Alt"; +pub(crate) const SUPER_KEY_STR: &str = "Super"; + +// numeric values for modifier keys +pub(crate) const CONTROL_KEY: u16 = 0x11; +pub(crate) const ALT_KEY: u16 = 0x12; +pub(crate) const SUPER_KEY: u16 = 0x5B; + +/// A mapping of to +static MODIFIER_KEYS: [(&str, u16); 3] = [ + (CONTROL_KEY_STR, CONTROL_KEY), + (ALT_KEY_STR, ALT_KEY), + (SUPER_KEY_STR, SUPER_KEY), +]; + +/// Provides a mapping of the valid modifier keys' electron +/// string representation to the numeric representation. +pub(crate) fn get_numeric_modifier_key(modifier: &str) -> Option { + for (modifier_str, modifier_num) in MODIFIER_KEYS { + if modifier_str == modifier { + return Some(modifier_num); + } + } + None +} + +#[cfg(test)] +mod test { + use super::get_numeric_modifier_key; + + #[test] + fn valid_modifier_keys() { + assert_eq!(get_numeric_modifier_key("Control").unwrap(), 0x11); + assert_eq!(get_numeric_modifier_key("Alt").unwrap(), 0x12); + assert_eq!(get_numeric_modifier_key("Super").unwrap(), 0x5B); + } + + #[test] + fn does_not_contain_invalid_modifier_keys() { + assert!(get_numeric_modifier_key("Shift").is_none()); + } +} diff --git a/apps/desktop/desktop_native/autotype/src/windows/mod.rs b/apps/desktop/desktop_native/autotype/src/windows/mod.rs index 3ea63b2b8f4..ed985749303 100644 --- a/apps/desktop/desktop_native/autotype/src/windows/mod.rs +++ b/apps/desktop/desktop_native/autotype/src/windows/mod.rs @@ -1,6 +1,10 @@ use anyhow::Result; +use itertools::Itertools; use tracing::debug; -use windows::Win32::Foundation::{GetLastError, SetLastError, WIN32_ERROR}; +use windows::Win32::{ + Foundation::{GetLastError, SetLastError, WIN32_ERROR}, + UI::Input::KeyboardAndMouse::INPUT, +}; mod type_input; mod window_title; @@ -12,7 +16,7 @@ const WIN32_SUCCESS: WIN32_ERROR = WIN32_ERROR(0); /// win32 errors. #[cfg_attr(test, mockall::automock)] trait ErrorOperations { - /// https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-setlasterror + /// fn set_last_error(err: u32) { debug!(err, "Calling SetLastError"); unsafe { @@ -20,7 +24,7 @@ trait ErrorOperations { } } - /// https://learn.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror + /// fn get_last_error() -> WIN32_ERROR { let last_err = unsafe { GetLastError() }; debug!("GetLastError(): {}", last_err.to_hresult().message()); @@ -36,6 +40,22 @@ pub fn get_foreground_window_title() -> Result { window_title::get_foreground_window_title() } -pub fn type_input(input: Vec, keyboard_shortcut: Vec) -> Result<()> { - type_input::type_input(input, keyboard_shortcut) +/// `KeyboardShortcutInput` is an `INPUT` of one of the valid shortcut keys: +/// - Control +/// - Alt +/// - Super +/// - \[a-z\]\[A-Z\] +struct KeyboardShortcutInput(INPUT); + +pub fn type_input(input: &[u16], keyboard_shortcut: &[String]) -> Result<()> { + debug!(?keyboard_shortcut, "type_input() called."); + + // convert the raw string input to Windows input and error + // if any key is not a valid keyboard shortcut input + let keyboard_shortcut: Vec = keyboard_shortcut + .iter() + .map(|s| KeyboardShortcutInput::try_from(s.as_str())) + .try_collect()?; + + type_input::type_input(input, &keyboard_shortcut) } diff --git a/apps/desktop/desktop_native/autotype/src/windows/type_input.rs b/apps/desktop/desktop_native/autotype/src/windows/type_input.rs index 10f30f5ee4f..f7879e676bf 100644 --- a/apps/desktop/desktop_native/autotype/src/windows/type_input.rs +++ b/apps/desktop/desktop_native/autotype/src/windows/type_input.rs @@ -5,7 +5,11 @@ use windows::Win32::UI::Input::KeyboardAndMouse::{ VIRTUAL_KEY, }; -use super::{ErrorOperations, Win32ErrorOperations}; +use super::{ErrorOperations, KeyboardShortcutInput, Win32ErrorOperations}; +use crate::get_numeric_modifier_key; + +const IS_VIRTUAL_KEY: bool = true; +const IS_REAL_KEY: bool = false; /// `InputOperations` provides an interface to Window32 API for /// working with inputs. @@ -13,7 +17,7 @@ use super::{ErrorOperations, Win32ErrorOperations}; trait InputOperations { /// Attempts to type the provided input wherever the user's cursor is. /// - /// https://learn.microsoft.com/en-in/windows/win32/api/winuser/nf-winuser-sendinput + /// fn send_input(inputs: &[INPUT]) -> u32; } @@ -21,8 +25,11 @@ struct Win32InputOperations; impl InputOperations for Win32InputOperations { fn send_input(inputs: &[INPUT]) -> u32 { - const INPUT_STRUCT_SIZE: i32 = std::mem::size_of::() as i32; - let insert_count = unsafe { SendInput(inputs, INPUT_STRUCT_SIZE) }; + const INPUT_STRUCT_SIZE: usize = std::mem::size_of::(); + + let size = i32::try_from(INPUT_STRUCT_SIZE).expect("INPUT size to fit in i32"); + + let insert_count = unsafe { SendInput(inputs, size) }; debug!(insert_count, "SendInput() called."); @@ -33,40 +40,37 @@ impl InputOperations for Win32InputOperations { /// Attempts to type the input text wherever the user's cursor is. /// /// `input` must be a vector of utf-16 encoded characters to insert. -/// `keyboard_shortcut` must be a vector of Strings, where valid shortcut keys: Control, Alt, Super, -/// Shift, letters a - Z +/// `keyboard_shortcut` is a vector of valid shortcut keys. /// -/// https://learn.microsoft.com/en-in/windows/win32/api/winuser/nf-winuser-sendinput -pub(super) fn type_input(input: Vec, keyboard_shortcut: Vec) -> Result<()> { +/// +pub(super) fn type_input(input: &[u16], keyboard_shortcut: &[KeyboardShortcutInput]) -> Result<()> { // the length of this vec is always shortcut keys to release + (2x length of input chars) let mut keyboard_inputs: Vec = Vec::with_capacity(keyboard_shortcut.len() + (input.len() * 2)); - debug!(?keyboard_shortcut, "Converting keyboard shortcut to input."); - - // Add key "up" inputs for the shortcut - for key in keyboard_shortcut { - keyboard_inputs.push(convert_shortcut_key_to_up_input(key)?); + // insert the keyboard shortcut + for shortcut in keyboard_shortcut { + keyboard_inputs.push(shortcut.0); } - add_input(&input, &mut keyboard_inputs); + add_input(input, &mut keyboard_inputs); - send_input::(keyboard_inputs) + send_input::(&keyboard_inputs) } // Add key "down" and "up" inputs for the input // (currently in this form: {username}/t{password}) fn add_input(input: &[u16], keyboard_inputs: &mut Vec) { - const TAB_KEY: u8 = 9; + const TAB_KEY: u16 = 9; for i in input { - let next_down_input = if *i == TAB_KEY.into() { - build_virtual_key_input(InputKeyPress::Down, *i as u8) + let next_down_input = if *i == TAB_KEY { + build_virtual_key_input(InputKeyPress::Down, *i) } else { build_unicode_input(InputKeyPress::Down, *i) }; - let next_up_input = if *i == TAB_KEY.into() { - build_virtual_key_input(InputKeyPress::Up, *i as u8) + let next_up_input = if *i == TAB_KEY { + build_virtual_key_input(InputKeyPress::Up, *i) } else { build_unicode_input(InputKeyPress::Up, *i) }; @@ -76,26 +80,24 @@ fn add_input(input: &[u16], keyboard_inputs: &mut Vec) { } } -/// Converts a valid shortcut key to an "up" keyboard input. -/// -/// `input` must be a valid shortcut key: Control, Alt, Super, Shift, letters [a-z][A-Z] -fn convert_shortcut_key_to_up_input(key: String) -> Result { - const SHIFT_KEY: u8 = 0x10; - const SHIFT_KEY_STR: &str = "Shift"; - const CONTROL_KEY: u8 = 0x11; - const CONTROL_KEY_STR: &str = "Control"; - const ALT_KEY: u8 = 0x12; - const ALT_KEY_STR: &str = "Alt"; - const LEFT_WINDOWS_KEY: u8 = 0x5B; - const LEFT_WINDOWS_KEY_STR: &str = "Super"; +impl TryFrom<&str> for KeyboardShortcutInput { + type Error = anyhow::Error; - Ok(match key.as_str() { - SHIFT_KEY_STR => build_virtual_key_input(InputKeyPress::Up, SHIFT_KEY), - CONTROL_KEY_STR => build_virtual_key_input(InputKeyPress::Up, CONTROL_KEY), - ALT_KEY_STR => build_virtual_key_input(InputKeyPress::Up, ALT_KEY), - LEFT_WINDOWS_KEY_STR => build_virtual_key_input(InputKeyPress::Up, LEFT_WINDOWS_KEY), - _ => build_unicode_input(InputKeyPress::Up, get_alphabetic_hotkey(key)?), - }) + fn try_from(key: &str) -> std::result::Result { + // not modifier key + if key.len() == 1 { + let input = build_unicode_input(InputKeyPress::Up, get_alphabetic_hotkey(key)?); + return Ok(KeyboardShortcutInput(input)); + } + // the modifier keys are using the Up keypress variant because the user has already + // pressed those keys in order to trigger the feature. + if let Some(numeric_modifier_key) = get_numeric_modifier_key(key) { + let input = build_virtual_key_input(InputKeyPress::Up, numeric_modifier_key); + Ok(KeyboardShortcutInput(input)) + } else { + Err(anyhow!("Unsupported modifier key: {key}")) + } + } } /// Given a letter that is a String, get the utf16 encoded @@ -105,7 +107,7 @@ fn convert_shortcut_key_to_up_input(key: String) -> Result { /// Because we only accept [a-z][A-Z], the decimal u16 /// cast of the letter is safe because the unicode code point /// of these characters fits in a u16. -fn get_alphabetic_hotkey(letter: String) -> Result { +fn get_alphabetic_hotkey(letter: &str) -> Result { if letter.len() != 1 { error!( len = letter.len(), @@ -135,23 +137,28 @@ fn get_alphabetic_hotkey(letter: String) -> Result { } /// An input key can be either pressed (down), or released (up). +#[derive(Copy, Clone)] enum InputKeyPress { Down, Up, } -/// A function for easily building keyboard unicode INPUT structs used in SendInput(). -/// -/// Before modifying this function, make sure you read the SendInput() documentation: -/// https://learn.microsoft.com/en-in/windows/win32/api/winuser/nf-winuser-sendinput -fn build_unicode_input(key_press: InputKeyPress, character: u16) -> INPUT { +/// Before modifying this function, make sure you read the `SendInput()` documentation: +/// +/// +fn build_input(key_press: InputKeyPress, character: u16, is_virtual: bool) -> INPUT { + let (w_vk, w_scan) = if is_virtual { + (VIRTUAL_KEY(character), 0) + } else { + (VIRTUAL_KEY::default(), character) + }; match key_press { InputKeyPress::Down => INPUT { r#type: INPUT_KEYBOARD, Anonymous: INPUT_0 { ki: KEYBDINPUT { - wVk: Default::default(), - wScan: character, + wVk: w_vk, + wScan: w_scan, dwFlags: KEYEVENTF_UNICODE, time: 0, dwExtraInfo: 0, @@ -162,8 +169,8 @@ fn build_unicode_input(key_press: InputKeyPress, character: u16) -> INPUT { r#type: INPUT_KEYBOARD, Anonymous: INPUT_0 { ki: KEYBDINPUT { - wVk: Default::default(), - wScan: character, + wVk: w_vk, + wScan: w_scan, dwFlags: KEYEVENTF_KEYUP | KEYEVENTF_UNICODE, time: 0, dwExtraInfo: 0, @@ -173,53 +180,29 @@ fn build_unicode_input(key_press: InputKeyPress, character: u16) -> INPUT { } } -/// A function for easily building keyboard virtual-key INPUT structs used in SendInput(). -/// -/// Before modifying this function, make sure you read the SendInput() documentation: -/// https://learn.microsoft.com/en-in/windows/win32/api/winuser/nf-winuser-sendinput -/// https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes -fn build_virtual_key_input(key_press: InputKeyPress, virtual_key: u8) -> INPUT { - match key_press { - InputKeyPress::Down => INPUT { - r#type: INPUT_KEYBOARD, - Anonymous: INPUT_0 { - ki: KEYBDINPUT { - wVk: VIRTUAL_KEY(virtual_key as u16), - wScan: Default::default(), - dwFlags: Default::default(), - time: 0, - dwExtraInfo: 0, - }, - }, - }, - InputKeyPress::Up => INPUT { - r#type: INPUT_KEYBOARD, - Anonymous: INPUT_0 { - ki: KEYBDINPUT { - wVk: VIRTUAL_KEY(virtual_key as u16), - wScan: Default::default(), - dwFlags: KEYEVENTF_KEYUP, - time: 0, - dwExtraInfo: 0, - }, - }, - }, - } +/// A function for easily building keyboard unicode `INPUT` structs used in `SendInput()`. +fn build_unicode_input(key_press: InputKeyPress, character: u16) -> INPUT { + build_input(key_press, character, IS_REAL_KEY) } -fn send_input(inputs: Vec) -> Result<()> +/// A function for easily building keyboard virtual-key `INPUT` structs used in `SendInput()`. +fn build_virtual_key_input(key_press: InputKeyPress, character: u16) -> INPUT { + build_input(key_press, character, IS_VIRTUAL_KEY) +} + +fn send_input(inputs: &[INPUT]) -> Result<()> where I: InputOperations, E: ErrorOperations, { - let insert_count = I::send_input(&inputs); + let insert_count = I::send_input(inputs); if insert_count == 0 { let last_err = E::get_last_error().to_hresult().message(); error!(GetLastError = %last_err, "SendInput sent 0 inputs. Input was blocked by another thread."); return Err(anyhow!("SendInput sent 0 inputs. Input was blocked by another thread. GetLastError: {last_err}")); - } else if insert_count != inputs.len() as u32 { + } else if insert_count != u32::try_from(inputs.len()).expect("to convert inputs len to u32") { let last_err = E::get_last_error().to_hresult().message(); error!(sent = %insert_count, expected = inputs.len(), GetLastError = %last_err, "SendInput sent does not match expected." @@ -237,8 +220,9 @@ where mod tests { //! For the mocking of the traits that are static methods, we need to use the `serial_test` //! crate in order to mock those, since the mock expectations set have to be global in - //! absence of a `self`. More info: https://docs.rs/mockall/latest/mockall/#static-methods + //! absence of a `self`. More info: + use itertools::Itertools; use serial_test::serial; use windows::Win32::Foundation::WIN32_ERROR; @@ -249,7 +233,7 @@ mod tests { fn get_alphabetic_hot_key_succeeds() { for c in ('a'..='z').chain('A'..='Z') { let letter = c.to_string(); - let converted = get_alphabetic_hotkey(letter).unwrap(); + let converted = get_alphabetic_hotkey(&letter).unwrap(); assert_eq!(converted, c as u16); } } @@ -258,27 +242,65 @@ mod tests { #[should_panic = "Final keyboard shortcut key should be a single character: foo"] fn get_alphabetic_hot_key_fail_not_single_char() { let letter = String::from("foo"); - get_alphabetic_hotkey(letter).unwrap(); + get_alphabetic_hotkey(&letter).unwrap(); } #[test] #[should_panic = "Letter is not ASCII Alphabetic ([a-z][A-Z]): '}'"] fn get_alphabetic_hot_key_fail_not_alphabetic() { let letter = String::from("}"); - get_alphabetic_hotkey(letter).unwrap(); + get_alphabetic_hotkey(&letter).unwrap(); } #[test] #[serial] fn send_input_succeeds() { let ctxi = MockInputOperations::send_input_context(); + ctxi.checkpoint(); ctxi.expect().returning(|_| 1); - send_input::(vec![build_unicode_input( + send_input::(&[build_unicode_input( InputKeyPress::Up, 0, )]) .unwrap(); + + drop(ctxi); + } + + #[test] + #[serial] + fn keyboard_shortcut_conversion_succeeds() { + let keyboard_shortcut = ["Control", "Alt", "B"]; + let _: Vec = keyboard_shortcut + .iter() + .map(|s| KeyboardShortcutInput::try_from(*s)) + .try_collect() + .unwrap(); + } + + #[test] + #[serial] + #[should_panic = "Letter is not ASCII Alphabetic ([a-z][A-Z]): '1'"] + fn keyboard_shortcut_conversion_fails_invalid_key() { + let keyboard_shortcut = ["Control", "Alt", "1"]; + let _: Vec = keyboard_shortcut + .iter() + .map(|s| KeyboardShortcutInput::try_from(*s)) + .try_collect() + .unwrap(); + } + + #[test] + #[serial] + #[should_panic(expected = "Unsupported modifier key: Shift")] + fn keyboard_shortcut_conversion_fails_with_shift() { + let keyboard_shortcut = ["Control", "Shift", "B"]; + let _: Vec = keyboard_shortcut + .iter() + .map(|s| KeyboardShortcutInput::try_from(*s)) + .try_collect() + .unwrap(); } #[test] @@ -288,16 +310,21 @@ mod tests { )] fn send_input_fails_sent_zero() { let ctxi = MockInputOperations::send_input_context(); + ctxi.checkpoint(); ctxi.expect().returning(|_| 0); let ctxge = MockErrorOperations::get_last_error_context(); + ctxge.checkpoint(); ctxge.expect().returning(|| WIN32_ERROR(1)); - send_input::(vec![build_unicode_input( + send_input::(&[build_unicode_input( InputKeyPress::Up, 0, )]) .unwrap(); + + drop(ctxge); + drop(ctxi); } #[test] @@ -305,15 +332,20 @@ mod tests { #[should_panic(expected = "SendInput does not match expected. sent: 2, expected: 1")] fn send_input_fails_sent_mismatch() { let ctxi = MockInputOperations::send_input_context(); + ctxi.checkpoint(); ctxi.expect().returning(|_| 2); let ctxge = MockErrorOperations::get_last_error_context(); + ctxge.checkpoint(); ctxge.expect().returning(|| WIN32_ERROR(1)); - send_input::(vec![build_unicode_input( + send_input::(&[build_unicode_input( InputKeyPress::Up, 0, )]) .unwrap(); + + drop(ctxge); + drop(ctxi); } } diff --git a/apps/desktop/desktop_native/autotype/src/windows/window_title.rs b/apps/desktop/desktop_native/autotype/src/windows/window_title.rs index d56a811ab5c..12e6501a7c5 100644 --- a/apps/desktop/desktop_native/autotype/src/windows/window_title.rs +++ b/apps/desktop/desktop_native/autotype/src/windows/window_title.rs @@ -11,10 +11,10 @@ use super::{ErrorOperations, Win32ErrorOperations, WIN32_SUCCESS}; #[cfg_attr(test, mockall::automock)] trait WindowHandleOperations { - // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowtextlengthw + // fn get_window_text_length_w(&self) -> Result; - // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowtextw + // fn get_window_text_w(&self, buffer: &mut Vec) -> Result; } @@ -70,7 +70,7 @@ pub(super) fn get_foreground_window_title() -> Result { /// Retrieves the foreground window handle and validates it. fn get_foreground_window_handle() -> Result { - // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getforegroundwindow + // let handle = unsafe { GetForegroundWindow() }; debug!("GetForegroundWindow() called."); @@ -87,7 +87,7 @@ fn get_foreground_window_handle() -> Result { /// /// # Errors /// -/// - If the length zero and GetLastError() != 0, return the GetLastError() message. +/// - If the length zero and `GetLastError()` != 0, return the `GetLastError()` message. fn get_window_title_length(window_handle: &H) -> Result where H: WindowHandleOperations, @@ -128,7 +128,7 @@ where /// # Errors /// /// - If the actual window title length (what the win32 API declares was written into the buffer), -/// is length zero and GetLastError() != 0 , return the GetLastError() message. +/// is length zero and `GetLastError()` != 0 , return the `GetLastError()` message. fn get_window_title(window_handle: &H, expected_title_length: usize) -> Result where H: WindowHandleOperations, @@ -140,7 +140,7 @@ where // The upstream will make a contains comparison on what we return, so an empty string // will not result on a match. warn!("Window title length is zero."); - return Ok(String::from("")); + return Ok(String::new()); } let mut buffer: Vec = vec![0; expected_title_length + 1]; // add extra space for the null character @@ -171,7 +171,7 @@ where mod tests { //! For the mocking of the traits that are static methods, we need to use the `serial_test` //! crate in order to mock those, since the mock expectations set have to be global in - //! absence of a `self`. More info: https://docs.rs/mockall/latest/mockall/#static-methods + //! absence of a `self`. More info: use mockall::predicate; use serial_test::serial; @@ -186,6 +186,7 @@ mod tests { let mut mock_handle = MockWindowHandleOperations::new(); let ctxse = MockErrorOperations::set_last_error_context(); + ctxse.checkpoint(); ctxse .expect() .once() @@ -198,6 +199,7 @@ mod tests { .returning(|| Ok(0)); let ctxge = MockErrorOperations::get_last_error_context(); + ctxge.checkpoint(); ctxge.expect().returning(|| WIN32_ERROR(0)); let len = get_window_title_length::( @@ -206,6 +208,9 @@ mod tests { .unwrap(); assert_eq!(len, 0); + + drop(ctxge); + drop(ctxse); } #[test] @@ -215,6 +220,7 @@ mod tests { let mut mock_handle = MockWindowHandleOperations::new(); let ctxse = MockErrorOperations::set_last_error_context(); + ctxse.checkpoint(); ctxse.expect().with(predicate::eq(0)).returning(|_| {}); mock_handle @@ -223,13 +229,18 @@ mod tests { .returning(|| Ok(0)); let ctxge = MockErrorOperations::get_last_error_context(); + ctxge.checkpoint(); ctxge.expect().returning(|| WIN32_ERROR(1)); get_window_title_length::(&mock_handle) .unwrap(); + + drop(ctxge); + drop(ctxse); } #[test] + #[serial] fn get_window_title_succeeds() { let mut mock_handle = MockWindowHandleOperations::new(); @@ -246,11 +257,11 @@ mod tests { .unwrap(); assert_eq!(title.len(), 43); // That extra slot in the buffer for null char - assert_eq!(title, "*******************************************"); } #[test] + #[serial] fn get_window_title_returns_empty_string() { let mock_handle = MockWindowHandleOperations::new(); @@ -273,10 +284,13 @@ mod tests { .returning(|_| Ok(0)); let ctxge = MockErrorOperations::get_last_error_context(); + ctxge.checkpoint(); ctxge.expect().returning(|| WIN32_ERROR(1)); get_window_title::(&mock_handle, 42) .unwrap(); + + drop(ctxge); } #[test] @@ -290,9 +304,12 @@ mod tests { .returning(|_| Ok(0)); let ctxge = MockErrorOperations::get_last_error_context(); + ctxge.checkpoint(); ctxge.expect().returning(|| WIN32_ERROR(0)); get_window_title::(&mock_handle, 42) .unwrap(); + + drop(ctxge); } } diff --git a/apps/desktop/desktop_native/autotype/tests/integration_tests.rs b/apps/desktop/desktop_native/autotype/tests/integration_tests.rs new file mode 100644 index 00000000000..b87219f77fe --- /dev/null +++ b/apps/desktop/desktop_native/autotype/tests/integration_tests.rs @@ -0,0 +1,324 @@ +#![cfg(target_os = "windows")] + +use std::{ + sync::{Arc, Mutex}, + thread, + time::Duration, +}; + +use autotype::{get_foreground_window_title, type_input}; +use serial_test::serial; +use tracing::debug; +use windows::Win32::{ + Foundation::{COLORREF, HINSTANCE, HMODULE, HWND, LPARAM, LRESULT, WPARAM}, + Graphics::Gdi::{CreateSolidBrush, UpdateWindow, ValidateRect, COLOR_WINDOW}, + System::LibraryLoader::{GetModuleHandleA, GetModuleHandleW}, + UI::WindowsAndMessaging::*, +}; +use windows_core::{s, w, Result, PCSTR, PCWSTR}; + +struct TestWindow { + handle: HWND, + capture: Option, +} + +impl Drop for TestWindow { + fn drop(&mut self) { + // Clean up the InputCapture pointer + unsafe { + let capture_ptr = GetWindowLongPtrW(self.handle, GWLP_USERDATA) as *mut InputCapture; + if !capture_ptr.is_null() { + let _ = Box::from_raw(capture_ptr); + } + CloseWindow(self.handle).expect("window handle should be closeable"); + DestroyWindow(self.handle).expect("window handle should be destroyable"); + } + } +} + +// state to capture keyboard input +#[derive(Clone)] +struct InputCapture { + chars: Arc>>, +} + +impl InputCapture { + fn new() -> Self { + Self { + chars: Arc::new(Mutex::new(Vec::new())), + } + } + + fn get_chars(&self) -> Vec { + self.chars + .lock() + .expect("mutex should not be poisoned") + .clone() + } +} + +// Custom window procedure that captures input +unsafe extern "system" fn capture_input_proc( + handle: HWND, + msg: u32, + wparam: WPARAM, + lparam: LPARAM, +) -> LRESULT { + match msg { + WM_CREATE => { + // Store the InputCapture pointer in window data + let create_struct = lparam.0 as *const CREATESTRUCTW; + let capture_ptr = (*create_struct).lpCreateParams as *mut InputCapture; + SetWindowLongPtrW(handle, GWLP_USERDATA, capture_ptr as isize); + LRESULT(0) + } + WM_CHAR => { + // Get the InputCapture from window data + let capture_ptr = GetWindowLongPtrW(handle, GWLP_USERDATA) as *mut InputCapture; + if !capture_ptr.is_null() { + let capture = &*capture_ptr; + if let Some(ch) = char::from_u32(wparam.0 as u32) { + capture + .chars + .lock() + .expect("mutex should not be poisoned") + .push(ch); + } + } + LRESULT(0) + } + WM_DESTROY => { + PostQuitMessage(0); + LRESULT(0) + } + _ => DefWindowProcW(handle, msg, wparam, lparam), + } +} + +// A pointer to the window procedure +type ProcType = unsafe extern "system" fn(HWND, u32, WPARAM, LPARAM) -> LRESULT; + +// +extern "system" fn show_window_proc( + handle: HWND, // the window handle + message: u32, // the system message + wparam: WPARAM, /* additional message information. The contents of the wParam parameter + * depend on the value of the message parameter. */ + lparam: LPARAM, /* additional message information. The contents of the lParam parameter + * depend on the value of the message parameter. */ +) -> LRESULT { + unsafe { + match message { + WM_PAINT => { + debug!("WM_PAINT"); + let res = ValidateRect(Some(handle), None); + debug_assert!(res.ok().is_ok()); + LRESULT(0) + } + WM_DESTROY => { + debug!("WM_DESTROY"); + PostQuitMessage(0); + LRESULT(0) + } + _ => DefWindowProcA(handle, message, wparam, lparam), + } + } +} + +impl TestWindow { + fn set_foreground(&self) -> Result<()> { + unsafe { + let _ = ShowWindow(self.handle, SW_SHOW); + let _ = SetForegroundWindow(self.handle); + let _ = UpdateWindow(self.handle); + let _ = SetForegroundWindow(self.handle); + } + std::thread::sleep(std::time::Duration::from_millis(100)); + Ok(()) + } + + fn wait_for_input(&self, timeout_ms: u64) { + let start = std::time::Instant::now(); + while start.elapsed().as_millis() < timeout_ms as u128 { + process_messages(); + thread::sleep(Duration::from_millis(10)); + } + } +} + +fn process_messages() { + unsafe { + let mut msg = MSG::default(); + while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() { + let _ = TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } +} + +fn create_input_window(title: PCWSTR, proc_type: ProcType) -> Result { + unsafe { + let instance = GetModuleHandleW(None).unwrap_or(HMODULE(std::ptr::null_mut())); + let instance: HINSTANCE = instance.into(); + debug_assert!(!instance.is_invalid()); + + let window_class = w!("show_window"); + + // Register window class with our custom proc + let wc = WNDCLASSW { + lpfnWndProc: Some(proc_type), + hInstance: instance, + lpszClassName: window_class, + hbrBackground: CreateSolidBrush(COLORREF( + (COLOR_WINDOW.0 + 1).try_into().expect("i32 to fit in u32"), + )), + ..Default::default() + }; + + let _atom = RegisterClassW(&wc); + + let capture = InputCapture::new(); + + // Pass InputCapture as lpParam + let capture_ptr = Box::into_raw(Box::new(capture.clone())); + + // Create window + // + let handle = CreateWindowExW( + WINDOW_EX_STYLE(0), + window_class, + title, + WS_OVERLAPPEDWINDOW | WS_VISIBLE, + CW_USEDEFAULT, + CW_USEDEFAULT, + 400, + 300, + None, + None, + Some(instance), + Some(capture_ptr as *const _), + ) + .expect("window should be created"); + + // Process pending messages + process_messages(); + thread::sleep(Duration::from_millis(100)); + + Ok(TestWindow { + handle, + capture: Some(capture), + }) + } +} + +fn create_title_window(title: PCSTR, proc_type: ProcType) -> Result { + unsafe { + let instance = GetModuleHandleA(None)?; + let instance: HINSTANCE = instance.into(); + debug_assert!(!instance.is_invalid()); + + let window_class = s!("input_window"); + + // Register window class with our custom proc + // + let wc = WNDCLASSA { + hCursor: LoadCursorW(None, IDC_ARROW)?, + hInstance: instance, + lpszClassName: window_class, + style: CS_HREDRAW | CS_VREDRAW, + lpfnWndProc: Some(proc_type), + ..Default::default() + }; + + let _atom = RegisterClassA(&wc); + + // Create window + // + let handle = CreateWindowExA( + WINDOW_EX_STYLE::default(), + window_class, + title, + WS_OVERLAPPEDWINDOW | WS_VISIBLE, + CW_USEDEFAULT, + CW_USEDEFAULT, + 800, + 600, + None, + None, + Some(instance), + None, + ) + .expect("window should be created"); + + Ok(TestWindow { + handle, + capture: None, + }) + } +} + +#[serial] +#[test] +fn test_get_active_window_title_success() { + let title; + { + let window = create_title_window(s!("TITLE_FOOBAR"), show_window_proc).unwrap(); + window.set_foreground().unwrap(); + title = get_foreground_window_title().unwrap(); + } + + assert_eq!(title, "TITLE_FOOBAR\0".to_owned()); + + thread::sleep(Duration::from_millis(100)); +} + +#[serial] +#[test] +fn test_get_active_window_title_doesnt_fail_if_empty_title() { + let title; + { + let window = create_title_window(s!(""), show_window_proc).unwrap(); + window.set_foreground().unwrap(); + title = get_foreground_window_title(); + } + + assert_eq!(title.unwrap(), "".to_owned()); + + thread::sleep(Duration::from_millis(100)); +} + +#[serial] +#[test] +fn test_type_input_success() { + const TAB: u16 = 0x09; + let chars; + { + let window = create_input_window(w!("foo"), capture_input_proc).unwrap(); + window.set_foreground().unwrap(); + + type_input( + &[ + 0x66, 0x6F, 0x6C, 0x6C, 0x6F, 0x77, 0x5F, 0x74, 0x68, 0x65, TAB, 0x77, 0x68, 0x69, + 0x74, 0x65, 0x5F, 0x72, 0x61, 0x62, 0x62, 0x69, 0x74, + ], + &["Control".to_owned(), "Alt".to_owned(), "B".to_owned()], + ) + .unwrap(); + + // Wait for and process input messages + window.wait_for_input(250); + + // Verify captured input + let capture = window.capture.as_ref().unwrap(); + chars = capture.get_chars(); + } + + assert!(!chars.is_empty(), "No input captured"); + + let input_str = String::from_iter(chars.iter()); + let input_str = input_str.replace("\t", "_"); + + assert_eq!(input_str, "follow_the_white_rabbit"); + + thread::sleep(Duration::from_millis(100)); +} diff --git a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/Cargo.toml b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/Cargo.toml index ff641731661..5cc457809f2 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/Cargo.toml +++ b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/Cargo.toml @@ -9,19 +9,19 @@ publish.workspace = true [target.'cfg(target_os = "windows")'.dependencies] aes-gcm = { workspace = true } +anyhow = { workspace = true } +base64 = { workspace = true } chacha20poly1305 = { workspace = true } chromium_importer = { path = "../chromium_importer" } clap = { version = "=4.5.53", features = ["derive"] } scopeguard = { workspace = true } sysinfo = { workspace = true } -windows = { workspace = true, features = [ - "Win32_System_Pipes", -] } -anyhow = { workspace = true } -base64 = { workspace = true } tokio = { workspace = true, features = ["full"] } tracing = { workspace = true } tracing-subscriber = { workspace = true } +windows = { workspace = true, features = [ + "Win32_System_Pipes", +] } [build-dependencies] embed-resource = "=3.0.6" diff --git a/apps/desktop/desktop_native/build.js b/apps/desktop/desktop_native/build.js index a7ed89a9c17..b20aa7e5af8 100644 --- a/apps/desktop/desktop_native/build.js +++ b/apps/desktop/desktop_native/build.js @@ -11,8 +11,8 @@ const rustTargetsMap = { "aarch64-pc-windows-msvc": { nodeArch: 'arm64', platform: 'win32' }, "x86_64-apple-darwin": { nodeArch: 'x64', platform: 'darwin' }, "aarch64-apple-darwin": { nodeArch: 'arm64', platform: 'darwin' }, - 'x86_64-unknown-linux-musl': { nodeArch: 'x64', platform: 'linux' }, - 'aarch64-unknown-linux-musl': { nodeArch: 'arm64', platform: 'linux' }, + 'x86_64-unknown-linux-gnu': { nodeArch: 'x64', platform: 'linux' }, + 'aarch64-unknown-linux-gnu': { nodeArch: 'arm64', platform: 'linux' }, } // Ensure the dist directory exists @@ -20,47 +20,79 @@ fs.mkdirSync(path.join(__dirname, "dist"), { recursive: true }); const args = process.argv.slice(2); // Get arguments passed to the script const mode = args.includes("--release") ? "release" : "debug"; +const isRelease = mode === "release"; const targetArg = args.find(arg => arg.startsWith("--target=")); const target = targetArg ? targetArg.split("=")[1] : null; let crossPlatform = process.argv.length > 2 && process.argv[2] === "cross-platform"; +/** + * Execute a command. + * @param {string} bin Executable to run. + * @param {string[]} args Arguments for executable. + * @param {string} [workingDirectory] Path to working directory, relative to the script directory. Defaults to the script directory. + * @param {string} [useShell] Whether to use a shell to execute the command. Defaults to false. + */ +function runCommand(bin, args, workingDirectory = "", useShell = false) { + const options = { stdio: 'inherit', cwd: path.resolve(__dirname, workingDirectory), shell: useShell } + console.debug("Running command:", bin, args, options) + child_process.execFileSync(bin, args, options) +} + function buildNapiModule(target, release = true) { - const targetArg = target ? `--target ${target}` : ""; + const targetArg = target ? `--target=${target}` : ""; const releaseArg = release ? "--release" : ""; - child_process.execSync(`npm run build -- ${releaseArg} ${targetArg}`, { stdio: 'inherit', cwd: path.join(__dirname, "napi") }); + const crossCompileArg = effectivePlatform(target) !== process.platform ? "--cross-compile" : ""; + runCommand("npm", ["run", "build", "--", crossCompileArg, releaseArg, targetArg].filter(s => s != ''), "./napi", true); +} + +/** + * Build a Rust binary with Cargo. + * + * If {@link target} is specified, cross-compilation helpers are used to build if necessary, and the resulting + * binary is copied to the `dist` folder. + * @param {string} bin Name of cargo binary package in `desktop_native` workspace. + * @param {string?} target Rust compiler target, e.g. `aarch64-pc-windows-msvc`. + * @param {boolean} release Whether to build in release mode. + */ +function cargoBuild(bin, target, release) { + const targetArg = target ? `--target=${target}` : ""; + const releaseArg = release ? "--release" : ""; + const args = ["build", "--bin", bin, releaseArg, targetArg] + // Use cross-compilation helper if necessary + if (effectivePlatform(target) === "win32" && process.platform !== "win32") { + args.unshift("xwin") + } + runCommand("cargo", args.filter(s => s != '')) + + // Infer the architecture and platform if not passed explicitly + let nodeArch, platform; + if (target) { + nodeArch = rustTargetsMap[target].nodeArch; + platform = rustTargetsMap[target].platform; + } + else { + nodeArch = process.arch; + platform = process.platform; + } + + // Copy the resulting binary to the dist folder + const profileFolder = isRelease ? "release" : "debug"; + const ext = platform === "win32" ? ".exe" : ""; + const src = path.join(__dirname, "target", target ? target : "", profileFolder, `${bin}${ext}`) + const dst = path.join(__dirname, "dist", `${bin}.${platform}-${nodeArch}${ext}`) + console.log(`Copying ${src} to ${dst}`); + fs.copyFileSync(src, dst); } function buildProxyBin(target, release = true) { - const targetArg = target ? `--target ${target}` : ""; - const releaseArg = release ? "--release" : ""; - child_process.execSync(`cargo build --bin desktop_proxy ${releaseArg} ${targetArg}`, {stdio: 'inherit', cwd: path.join(__dirname, "proxy")}); - - if (target) { - // Copy the resulting binary to the dist folder - const targetFolder = release ? "release" : "debug"; - const ext = process.platform === "win32" ? ".exe" : ""; - const nodeArch = rustTargetsMap[target].nodeArch; - fs.copyFileSync(path.join(__dirname, "target", target, targetFolder, `desktop_proxy${ext}`), path.join(__dirname, "dist", `desktop_proxy.${process.platform}-${nodeArch}${ext}`)); - } + cargoBuild("desktop_proxy", target, release) } function buildImporterBinaries(target, release = true) { // These binaries are only built for Windows, so we can skip them on other platforms - if (process.platform !== "win32") { - return; - } - - const bin = "bitwarden_chromium_import_helper"; - const targetArg = target ? `--target ${target}` : ""; - const releaseArg = release ? "--release" : ""; - child_process.execSync(`cargo build --bin ${bin} ${releaseArg} ${targetArg}`); - - if (target) { - // Copy the resulting binary to the dist folder - const targetFolder = release ? "release" : "debug"; - const nodeArch = rustTargetsMap[target].nodeArch; - fs.copyFileSync(path.join(__dirname, "target", target, targetFolder, `${bin}.exe`), path.join(__dirname, "dist", `${bin}.${process.platform}-${nodeArch}.exe`)); + if (effectivePlatform(target) == "win32") { + cargoBuild("bitwarden_chromium_import_helper", target, release) } } @@ -69,17 +101,29 @@ function buildProcessIsolation() { return; } - child_process.execSync(`cargo build --release`, { - stdio: 'inherit', - cwd: path.join(__dirname, "process_isolation") - }); + runCommand("cargo", ["build", "--package", "process_isolation", "--release"]); console.log("Copying process isolation library to dist folder"); fs.copyFileSync(path.join(__dirname, "target", "release", "libprocess_isolation.so"), path.join(__dirname, "dist", `libprocess_isolation.so`)); } function installTarget(target) { - child_process.execSync(`rustup target add ${target}`, { stdio: 'inherit', cwd: __dirname }); + runCommand("rustup", ["target", "add", target]); + // Install cargo-xwin for cross-platform builds targeting Windows + if (target.includes('windows') && process.platform !== 'win32') { + runCommand("cargo", ["install", "--version", "0.20.2", "--locked", "cargo-xwin"]); + // install tools needed for packaging Appx, only supported on macOS for now. + if (process.platform === "darwin") { + runCommand("brew", ["install", "iinuwa/msix-packaging-tap/msix-packaging", "osslsigncode"]); + } + } +} + +function effectivePlatform(target) { + if (target) { + return rustTargetsMap[target].platform + } + return process.platform } if (!crossPlatform && !target) { @@ -94,9 +138,9 @@ if (!crossPlatform && !target) { if (target) { console.log(`Building for target: ${target} in ${mode} mode`); installTarget(target); - buildNapiModule(target, mode === "release"); - buildProxyBin(target, mode === "release"); - buildImporterBinaries(false, mode === "release"); + buildNapiModule(target, isRelease); + buildProxyBin(target, isRelease); + buildImporterBinaries(target, isRelease); buildProcessIsolation(); return; } @@ -113,8 +157,8 @@ if (process.platform === "linux") { platformTargets.forEach(([target, _]) => { installTarget(target); - buildNapiModule(target); - buildProxyBin(target); - buildImporterBinaries(target); + buildNapiModule(target, isRelease); + buildProxyBin(target, isRelease); + buildImporterBinaries(target, isRelease); buildProcessIsolation(); }); diff --git a/apps/desktop/desktop_native/chromium_importer/Cargo.toml b/apps/desktop/desktop_native/chromium_importer/Cargo.toml index 9e9a9e0fee8..9bb1c0b87f2 100644 --- a/apps/desktop/desktop_native/chromium_importer/Cargo.toml +++ b/apps/desktop/desktop_native/chromium_importer/Cargo.toml @@ -16,6 +16,12 @@ rusqlite = { version = "=0.37.0", features = ["bundled"] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } +[target.'cfg(target_os = "linux")'.dependencies] +cbc = { workspace = true, features = ["alloc"] } +oo7 = { workspace = true } +pbkdf2 = "=0.12.2" +sha1 = "=0.10.6" + [target.'cfg(target_os = "macos")'.dependencies] cbc = { workspace = true, features = ["alloc"] } pbkdf2 = "=0.12.2" @@ -25,20 +31,14 @@ sha1 = "=0.10.6" [target.'cfg(target_os = "windows")'.dependencies] aes-gcm = { workspace = true } base64 = { workspace = true } +tokio = { workspace = true, features = ["full"] } +tracing = { workspace = true } +verifysign = "=0.2.4" windows = { workspace = true, features = [ "Win32_Security_Cryptography", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging", ] } -verifysign = "=0.2.4" -tokio = { workspace = true, features = ["full"] } -tracing = { workspace = true } - -[target.'cfg(target_os = "linux")'.dependencies] -cbc = { workspace = true, features = ["alloc"] } -oo7 = { workspace = true } -pbkdf2 = "=0.12.2" -sha1 = "=0.10.6" [lints] workspace = true diff --git a/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs index e57b40b5778..7011a2cce63 100644 --- a/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs @@ -61,8 +61,8 @@ impl InstalledBrowserRetriever for DefaultInstalledBrowserRetriever { let mut browsers = Vec::with_capacity(SUPPORTED_BROWSER_MAP.len()); for (browser, config) in SUPPORTED_BROWSER_MAP.iter() { - let data_dir = get_browser_data_dir(config)?; - if data_dir.exists() { + let data_dir = get_and_validate_data_dir(config); + if data_dir.is_ok() { browsers.push((*browser).to_string()); } } @@ -114,7 +114,7 @@ pub async fn import_logins( #[derive(Debug, Clone, Copy)] pub(crate) struct BrowserConfig { pub name: &'static str, - pub data_dir: &'static str, + pub data_dir: &'static [&'static str], } pub(crate) static SUPPORTED_BROWSER_MAP: LazyLock< @@ -126,11 +126,19 @@ pub(crate) static SUPPORTED_BROWSER_MAP: LazyLock< .collect::>() }); -fn get_browser_data_dir(config: &BrowserConfig) -> Result { - let dir = dirs::home_dir() - .ok_or_else(|| anyhow!("Home directory not found"))? - .join(config.data_dir); - Ok(dir) +fn get_and_validate_data_dir(config: &BrowserConfig) -> Result { + for data_dir in config.data_dir.iter() { + let dir = dirs::home_dir() + .ok_or_else(|| anyhow!("Home directory not found"))? + .join(data_dir); + if dir.exists() { + return Ok(dir); + } + } + Err(anyhow!( + "Browser user data directory '{:?}' not found", + config.data_dir + )) } // @@ -174,13 +182,7 @@ fn load_local_state_for_browser(browser_name: &String) -> Result<(PathBuf, Local .get(browser_name.as_str()) .ok_or_else(|| anyhow!("Unsupported browser: {}", browser_name))?; - let data_dir = get_browser_data_dir(config)?; - if !data_dir.exists() { - return Err(anyhow!( - "Browser user data directory '{}' not found", - data_dir.display() - )); - } + let data_dir = get_and_validate_data_dir(config)?; let local_state = load_local_state(&data_dir)?; diff --git a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/linux.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/linux.rs index 14e38797640..6fb6e6134c7 100644 --- a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/linux.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/linux.rs @@ -18,19 +18,22 @@ use crate::{ pub(crate) const SUPPORTED_BROWSERS: &[BrowserConfig] = &[ BrowserConfig { name: "Chrome", - data_dir: ".config/google-chrome", + data_dir: &[".config/google-chrome", "snap/chromium/common/chromium"], }, BrowserConfig { name: "Chromium", - data_dir: "snap/chromium/common/chromium", + data_dir: &["snap/chromium/common/chromium"], }, BrowserConfig { name: "Brave", - data_dir: "snap/brave/current/.config/BraveSoftware/Brave-Browser", + data_dir: &[ + "snap/brave/current/.config/BraveSoftware/Brave-Browser", + ".config/BraveSoftware/Brave-Browser", + ], }, BrowserConfig { name: "Opera", - data_dir: "snap/opera/current/.config/opera", + data_dir: &["snap/opera/current/.config/opera", ".config/opera"], }, ]; diff --git a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/macos.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/macos.rs index 5d0b4f0c75c..6cd746d60b6 100644 --- a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/macos.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/macos.rs @@ -14,31 +14,31 @@ use crate::{ pub(crate) const SUPPORTED_BROWSERS: &[BrowserConfig] = &[ BrowserConfig { name: "Chrome", - data_dir: "Library/Application Support/Google/Chrome", + data_dir: &["Library/Application Support/Google/Chrome"], }, BrowserConfig { name: "Chromium", - data_dir: "Library/Application Support/Chromium", + data_dir: &["Library/Application Support/Chromium"], }, BrowserConfig { name: "Microsoft Edge", - data_dir: "Library/Application Support/Microsoft Edge", + data_dir: &["Library/Application Support/Microsoft Edge"], }, BrowserConfig { name: "Brave", - data_dir: "Library/Application Support/BraveSoftware/Brave-Browser", + data_dir: &["Library/Application Support/BraveSoftware/Brave-Browser"], }, BrowserConfig { name: "Arc", - data_dir: "Library/Application Support/Arc/User Data", + data_dir: &["Library/Application Support/Arc/User Data"], }, BrowserConfig { name: "Opera", - data_dir: "Library/Application Support/com.operasoftware.Opera", + data_dir: &["Library/Application Support/com.operasoftware.Opera"], }, BrowserConfig { name: "Vivaldi", - data_dir: "Library/Application Support/Vivaldi", + data_dir: &["Library/Application Support/Vivaldi"], }, ]; diff --git a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/mod.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/mod.rs index 9cc89ed2161..524b5994873 100644 --- a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/mod.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/mod.rs @@ -25,27 +25,27 @@ pub use signature::*; pub(crate) const SUPPORTED_BROWSERS: &[BrowserConfig] = &[ BrowserConfig { name: "Brave", - data_dir: "AppData/Local/BraveSoftware/Brave-Browser/User Data", + data_dir: &["AppData/Local/BraveSoftware/Brave-Browser/User Data"], }, BrowserConfig { name: "Chrome", - data_dir: "AppData/Local/Google/Chrome/User Data", + data_dir: &["AppData/Local/Google/Chrome/User Data"], }, BrowserConfig { name: "Chromium", - data_dir: "AppData/Local/Chromium/User Data", + data_dir: &["AppData/Local/Chromium/User Data"], }, BrowserConfig { name: "Microsoft Edge", - data_dir: "AppData/Local/Microsoft/Edge/User Data", + data_dir: &["AppData/Local/Microsoft/Edge/User Data"], }, BrowserConfig { name: "Opera", - data_dir: "AppData/Roaming/Opera Software/Opera Stable", + data_dir: &["AppData/Roaming/Opera Software/Opera Stable"], }, BrowserConfig { name: "Vivaldi", - data_dir: "AppData/Local/Vivaldi/User Data", + data_dir: &["AppData/Local/Vivaldi/User Data"], }, ]; diff --git a/apps/desktop/desktop_native/chromium_importer/src/metadata.rs b/apps/desktop/desktop_native/chromium_importer/src/metadata.rs index 114c9f8df84..ea723291fe3 100644 --- a/apps/desktop/desktop_native/chromium_importer/src/metadata.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/metadata.rs @@ -7,9 +7,9 @@ pub struct NativeImporterMetadata { /// Identifies the importer pub id: String, /// Describes the strategies used to obtain imported data - pub loaders: Vec<&'static str>, + pub loaders: Vec, /// Identifies the instructions for the importer - pub instructions: &'static str, + pub instructions: String, } /// Returns a map of supported importers based on the current platform. @@ -24,21 +24,22 @@ pub fn get_supported_importers( let installed_browsers = T::get_installed_browsers().unwrap_or_default(); const IMPORTERS: &[(&str, &str)] = &[ + ("arccsv", "Arc"), + ("bravecsv", "Brave"), ("chromecsv", "Chrome"), ("chromiumcsv", "Chromium"), - ("bravecsv", "Brave"), + ("edgecsv", "Microsoft Edge"), ("operacsv", "Opera"), ("vivaldicsv", "Vivaldi"), - ("edgecsv", "Microsoft Edge"), ]; let supported: HashSet<&'static str> = PLATFORM_SUPPORTED_BROWSERS.iter().map(|b| b.name).collect(); for (id, browser_name) in IMPORTERS { - let mut loaders: Vec<&'static str> = vec!["file"]; + let mut loaders: Vec = vec!["file".to_string()]; if supported.contains(browser_name) { - loaders.push("chromium"); + loaders.push("chromium".to_string()); } if installed_browsers.contains(&browser_name.to_string()) { @@ -47,7 +48,7 @@ pub fn get_supported_importers( NativeImporterMetadata { id: id.to_string(), loaders, - instructions: "chromium", + instructions: "chromium".to_string(), }, ); } @@ -79,12 +80,9 @@ mod tests { map.keys().cloned().collect() } - fn get_loaders( - map: &HashMap, - id: &str, - ) -> HashSet<&'static str> { + fn get_loaders(map: &HashMap, id: &str) -> HashSet { map.get(id) - .map(|m| m.loaders.iter().copied().collect::>()) + .map(|m| m.loaders.iter().cloned().collect::>()) .unwrap_or_default() } @@ -94,6 +92,7 @@ mod tests { let map = get_supported_importers::(); let expected: HashSet = HashSet::from([ + "arccsv".to_string(), "chromecsv".to_string(), "chromiumcsv".to_string(), "bravecsv".to_string(), @@ -107,7 +106,7 @@ mod tests { for (key, meta) in map.iter() { assert_eq!(&meta.id, key); assert_eq!(meta.instructions, "chromium"); - assert!(meta.loaders.iter().any(|l| *l == "file")); + assert!(meta.loaders.contains(&"file".to_owned())); } } @@ -116,6 +115,7 @@ mod tests { fn macos_specific_loaders_match_const_array() { let map = get_supported_importers::(); let ids = [ + "arccsv", "chromecsv", "chromiumcsv", "bravecsv", @@ -147,7 +147,7 @@ mod tests { for (key, meta) in map.iter() { assert_eq!(&meta.id, key); assert_eq!(meta.instructions, "chromium"); - assert!(meta.loaders.iter().any(|l| *l == "file")); + assert!(meta.loaders.contains(&"file".to_owned())); } } @@ -183,7 +183,7 @@ mod tests { for (key, meta) in map.iter() { assert_eq!(&meta.id, key); assert_eq!(meta.instructions, "chromium"); - assert!(meta.loaders.iter().any(|l| *l == "file")); + assert!(meta.loaders.contains(&"file".to_owned())); } } diff --git a/apps/desktop/desktop_native/core/Cargo.toml b/apps/desktop/desktop_native/core/Cargo.toml index dc9246f55c6..aa5d564c9e5 100644 --- a/apps/desktop/desktop_native/core/Cargo.toml +++ b/apps/desktop/desktop_native/core/Cargo.toml @@ -13,7 +13,7 @@ default = [ "dep:security-framework", "dep:security-framework-sys", "dep:zbus", - "dep:zbus_polkit" + "dep:zbus_polkit", ] manual_test = [] @@ -46,6 +46,23 @@ tracing = { workspace = true } typenum = { workspace = true } zeroizing-alloc = { workspace = true } +[target.'cfg(target_os = "linux")'.dependencies] +ashpd = { workspace = true } +homedir = { workspace = true } +libc = { workspace = true } +linux-keyutils = { workspace = true } +oo7 = { workspace = true } +zbus = { workspace = true, optional = true } +zbus_polkit = { workspace = true, optional = true } + +[target.'cfg(target_os = "macos")'.dependencies] +core-foundation = { workspace = true, optional = true } +desktop_objc = { path = "../objc" } +homedir = { workspace = true } +secmem-proc = { workspace = true } +security-framework = { workspace = true, optional = true } +security-framework-sys = { workspace = true, optional = true } + [target.'cfg(windows)'.dependencies] pin-project = { workspace = true } scopeguard = { workspace = true } @@ -68,22 +85,5 @@ windows = { workspace = true, features = [ ], optional = true } windows-future = { workspace = true } -[target.'cfg(target_os = "macos")'.dependencies] -core-foundation = { workspace = true, optional = true } -homedir = { workspace = true } -secmem-proc = { workspace = true } -security-framework = { workspace = true, optional = true } -security-framework-sys = { workspace = true, optional = true } -desktop_objc = { path = "../objc" } - -[target.'cfg(target_os = "linux")'.dependencies] -ashpd = { workspace = true } -homedir = { workspace = true } -libc = { workspace = true } -linux-keyutils = { workspace = true } -oo7 = { workspace = true } -zbus = { workspace = true, optional = true } -zbus_polkit = { workspace = true, optional = true } - [lints] workspace = true diff --git a/apps/desktop/desktop_native/core/src/biometric_v2/windows.rs b/apps/desktop/desktop_native/core/src/biometric_v2/windows.rs index 32d2eb7e6e6..669dd757c40 100644 --- a/apps/desktop/desktop_native/core/src/biometric_v2/windows.rs +++ b/apps/desktop/desktop_native/core/src/biometric_v2/windows.rs @@ -285,8 +285,8 @@ async fn windows_hello_authenticate_with_crypto( return Err(anyhow!("Failed to sign data")); } - let signature_buffer = signature.Result()?; - let signature_value = unsafe { as_mut_bytes(&signature_buffer)? }; + let mut signature_buffer = signature.Result()?; + let signature_value = unsafe { as_mut_bytes(&mut signature_buffer)? }; // The signature is deterministic based on the challenge and keychain key. Thus, it can be // hashed to a key. It is unclear what entropy this key provides. @@ -368,7 +368,7 @@ fn decrypt_data( Ok(plaintext) } -unsafe fn as_mut_bytes(buffer: &IBuffer) -> Result<&mut [u8]> { +unsafe fn as_mut_bytes(buffer: &mut IBuffer) -> Result<&mut [u8]> { let interop = buffer.cast::()?; unsafe { diff --git a/apps/desktop/desktop_native/core/src/ssh_agent/peerinfo/gather.rs b/apps/desktop/desktop_native/core/src/ssh_agent/peerinfo/gather.rs index 699203d613d..bf8e24dd79c 100644 --- a/apps/desktop/desktop_native/core/src/ssh_agent/peerinfo/gather.rs +++ b/apps/desktop/desktop_native/core/src/ssh_agent/peerinfo/gather.rs @@ -3,8 +3,12 @@ use sysinfo::{Pid, System}; use super::models::PeerInfo; pub fn get_peer_info(peer_pid: u32) -> Result { - let s = System::new_all(); - if let Some(process) = s.process(Pid::from_u32(peer_pid)) { + let mut system = System::new(); + system.refresh_processes( + sysinfo::ProcessesToUpdate::Some(&[Pid::from_u32(peer_pid)]), + true, + ); + if let Some(process) = system.process(Pid::from_u32(peer_pid)) { let peer_process_name = match process.name().to_str() { Some(name) => name.to_string(), None => { diff --git a/apps/desktop/desktop_native/deny.toml b/apps/desktop/desktop_native/deny.toml index 7d7a126f694..66b80e9984c 100644 --- a/apps/desktop/desktop_native/deny.toml +++ b/apps/desktop/desktop_native/deny.toml @@ -1,9 +1,10 @@ # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html [advisories] +# Allow unmaintained crates in transient deps but not direct +unmaintained = "workspace" ignore = [ # Vulnerability in `rsa` crate: https://rustsec.org/advisories/RUSTSEC-2023-0071.html { id = "RUSTSEC-2023-0071", reason = "There is no fix available yet." }, - { id = "RUSTSEC-2024-0436", reason = "paste crate is unmaintained."} ] # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html diff --git a/apps/desktop/desktop_native/macos_provider/Cargo.toml b/apps/desktop/desktop_native/macos_provider/Cargo.toml index 50f1834851d..d73bd2fa049 100644 --- a/apps/desktop/desktop_native/macos_provider/Cargo.toml +++ b/apps/desktop/desktop_native/macos_provider/Cargo.toml @@ -5,14 +5,14 @@ license = { workspace = true } version = { workspace = true } publish = { workspace = true } -[[bin]] -name = "uniffi-bindgen" -path = "uniffi-bindgen.rs" - [lib] crate-type = ["staticlib", "cdylib"] bench = false +[[bin]] +name = "uniffi-bindgen" +path = "uniffi-bindgen.rs" + [dependencies] uniffi = { workspace = true, features = ["cli"] } @@ -23,8 +23,8 @@ serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } tokio = { workspace = true, features = ["sync"] } tracing = { workspace = true } +tracing-oslog = "=0.3.0" tracing-subscriber = { workspace = true } -tracing-oslog = "0.3.0" [build-dependencies] uniffi = { workspace = true, features = ["build"] } diff --git a/apps/desktop/desktop_native/macos_provider/README.md b/apps/desktop/desktop_native/macos_provider/README.md new file mode 100644 index 00000000000..1d4c1902465 --- /dev/null +++ b/apps/desktop/desktop_native/macos_provider/README.md @@ -0,0 +1,35 @@ +# Explainer: Mac OS Native Passkey Provider + +This document describes the changes introduced in https://github.com/bitwarden/clients/pull/13963, where we introduce the MacOS Native Passkey Provider. It gives the high level explanation of the architecture and some of the quirks and additional good to know context. + +## The high level +MacOS has native APIs (similar to iOS) to allow Credential Managers to provide credentials to the MacOS autofill system (in the PR referenced above, we only provide passkeys). + +We’ve written a Swift-based native autofill-extension. It’s bundled in the app-bundle in PlugIns, similar to the safari-extension. + +This swift extension currently communicates with our Electron app through IPC based on a unix socket. The IPC implementation is done in Rust and utilized through UniFFI + NAPI bindings. + +Footnotes: + +* We're not using the IPC framework as the implementation pre-dates the IPC framework. +* Alternatives like XPC or CFMessagePort may have better support for when the app is sandboxed. + +Electron receives the messages and passes it to Angular (through the electron-renderer event system). + +Our existing fido2 services in the renderer respond to events, displaying UI as necessary, and returns the signature back through the same mechanism, allowing people to authenticate with passkeys through the native system + UI. See [Mac OS Native Passkey Workflows](https://bitwarden.atlassian.net/wiki/spaces/EN/pages/1828356098/Mac+OS+Native+Passkey+Workflows) for demo videos. + +## Typescript + UI implementations + +We utilize the same FIDO2 implementation and interface that is already present for our browser authentication. It was designed by @coroiu with multiple ‘ui environments' in mind. + +Therefore, a lot of the plumbing is implemented in /autofill/services/desktop-fido2-user-interface.service.ts, which implements the interface that our fido2 authenticator/client expects to drive UI related behaviors. + +We’ve also implemented a couple FIDO2 UI components to handle registration/sign in flows, but also improved the “modal mode” of the desktop app. + +## Modal mode + +When modal mode is activated, the desktop app turns into a smaller modal that is always on top and cannot be resized. This is done to improve the UX of performing a passkey operation (or SSH operation). Once the operation is completed, the app returns to normal mode and its previous position. + +We are not using electron modal windows, for a couple reason. It would require us to send data in yet another layer of IPC, but also because we'd need to bootstrap entire renderer/app instead of reusing the existing window. + +Some modal modes may hide the 'traffic buttons' (window controls) due to design requirements. diff --git a/apps/desktop/desktop_native/macos_provider/build.sh b/apps/desktop/desktop_native/macos_provider/build.sh index 21e2e045af4..2f7a2d03541 100755 --- a/apps/desktop/desktop_native/macos_provider/build.sh +++ b/apps/desktop/desktop_native/macos_provider/build.sh @@ -8,6 +8,9 @@ rm -r tmp mkdir -p ./tmp/target/universal-darwin/release/ +rustup target add aarch64-apple-darwin +rustup target add x86_64-apple-darwin + cargo build --package macos_provider --target aarch64-apple-darwin --release cargo build --package macos_provider --target x86_64-apple-darwin --release diff --git a/apps/desktop/desktop_native/macos_provider/src/lib.rs b/apps/desktop/desktop_native/macos_provider/src/lib.rs index a5a134b0bfe..8619a77a0f2 100644 --- a/apps/desktop/desktop_native/macos_provider/src/lib.rs +++ b/apps/desktop/desktop_native/macos_provider/src/lib.rs @@ -57,6 +57,14 @@ trait Callback: Send + Sync { fn error(&self, error: BitwardenError); } +#[derive(uniffi::Enum, Debug)] +/// Store the connection status between the macOS credential provider extension +/// and the desktop application's IPC server. +pub enum ConnectionStatus { + Connected, + Disconnected, +} + #[derive(uniffi::Object)] pub struct MacOSProviderClient { to_server_send: tokio::sync::mpsc::Sender, @@ -65,8 +73,24 @@ pub struct MacOSProviderClient { response_callbacks_counter: AtomicU32, #[allow(clippy::type_complexity)] response_callbacks_queue: Arc, Instant)>>>, + + // Flag to track connection status - atomic for thread safety without locks + connection_status: Arc, } +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +/// Store native desktop status information to use for IPC communication +/// between the application and the macOS credential provider. +pub struct NativeStatus { + key: String, + value: String, +} + +// In our callback management, 0 is a reserved sequence number indicating that a message does not +// have a callback. +const NO_CALLBACK_INDICATOR: u32 = 0; + #[uniffi::export] impl MacOSProviderClient { // FIXME: Remove unwraps! They panic and terminate the whole application. @@ -93,13 +117,16 @@ impl MacOSProviderClient { let client = MacOSProviderClient { to_server_send, - response_callbacks_counter: AtomicU32::new(0), + response_callbacks_counter: AtomicU32::new(1), /* Start at 1 since 0 is reserved for + * "no callback" scenarios */ response_callbacks_queue: Arc::new(Mutex::new(HashMap::new())), + connection_status: Arc::new(std::sync::atomic::AtomicBool::new(false)), }; let path = desktop_core::ipc::path("af"); let queue = client.response_callbacks_queue.clone(); + let connection_status = client.connection_status.clone(); std::thread::spawn(move || { let rt = tokio::runtime::Builder::new_current_thread() @@ -117,9 +144,11 @@ impl MacOSProviderClient { match serde_json::from_str::(&message) { Ok(SerializedMessage::Command(CommandMessage::Connected)) => { info!("Connected to server"); + connection_status.store(true, std::sync::atomic::Ordering::Relaxed); } Ok(SerializedMessage::Command(CommandMessage::Disconnected)) => { info!("Disconnected from server"); + connection_status.store(false, std::sync::atomic::Ordering::Relaxed); } Ok(SerializedMessage::Message { sequence_number, @@ -157,12 +186,17 @@ impl MacOSProviderClient { client } + pub fn send_native_status(&self, key: String, value: String) { + let status = NativeStatus { key, value }; + self.send_message(status, None); + } + pub fn prepare_passkey_registration( &self, request: PasskeyRegistrationRequest, callback: Arc, ) { - self.send_message(request, Box::new(callback)); + self.send_message(request, Some(Box::new(callback))); } pub fn prepare_passkey_assertion( @@ -170,7 +204,7 @@ impl MacOSProviderClient { request: PasskeyAssertionRequest, callback: Arc, ) { - self.send_message(request, Box::new(callback)); + self.send_message(request, Some(Box::new(callback))); } pub fn prepare_passkey_assertion_without_user_interface( @@ -178,7 +212,18 @@ impl MacOSProviderClient { request: PasskeyAssertionWithoutUserInterfaceRequest, callback: Arc, ) { - self.send_message(request, Box::new(callback)); + self.send_message(request, Some(Box::new(callback))); + } + + pub fn get_connection_status(&self) -> ConnectionStatus { + let is_connected = self + .connection_status + .load(std::sync::atomic::Ordering::Relaxed); + if is_connected { + ConnectionStatus::Connected + } else { + ConnectionStatus::Disconnected + } } } @@ -200,7 +245,6 @@ enum SerializedMessage { } impl MacOSProviderClient { - // FIXME: Remove unwraps! They panic and terminate the whole application. #[allow(clippy::unwrap_used)] fn add_callback(&self, callback: Box) -> u32 { let sequence_number = self @@ -209,20 +253,23 @@ impl MacOSProviderClient { self.response_callbacks_queue .lock() - .unwrap() + .expect("response callbacks queue mutex should not be poisoned") .insert(sequence_number, (callback, Instant::now())); sequence_number } - // FIXME: Remove unwraps! They panic and terminate the whole application. #[allow(clippy::unwrap_used)] fn send_message( &self, message: impl Serialize + DeserializeOwned, - callback: Box, + callback: Option>, ) { - let sequence_number = self.add_callback(callback); + let sequence_number = if let Some(callback) = callback { + self.add_callback(callback) + } else { + NO_CALLBACK_INDICATOR + }; let message = serde_json::to_string(&SerializedMessage::Message { sequence_number, @@ -232,15 +279,17 @@ impl MacOSProviderClient { if let Err(e) = self.to_server_send.blocking_send(message) { // Make sure we remove the callback from the queue if we can't send the message - if let Some((cb, _)) = self - .response_callbacks_queue - .lock() - .unwrap() - .remove(&sequence_number) - { - cb.error(BitwardenError::Internal(format!( - "Error sending message: {e}" - ))); + if sequence_number != NO_CALLBACK_INDICATOR { + if let Some((callback, _)) = self + .response_callbacks_queue + .lock() + .expect("response callbacks queue mutex should not be poisoned") + .remove(&sequence_number) + { + callback.error(BitwardenError::Internal(format!( + "Error sending message: {e}" + ))); + } } } } diff --git a/apps/desktop/desktop_native/macos_provider/src/registration.rs b/apps/desktop/desktop_native/macos_provider/src/registration.rs index 9e697b75c16..c961566a86c 100644 --- a/apps/desktop/desktop_native/macos_provider/src/registration.rs +++ b/apps/desktop/desktop_native/macos_provider/src/registration.rs @@ -14,6 +14,7 @@ pub struct PasskeyRegistrationRequest { user_verification: UserVerification, supported_algorithms: Vec, window_xy: Position, + excluded_credentials: Vec>, } #[derive(uniffi::Record, Serialize, Deserialize)] diff --git a/apps/desktop/desktop_native/napi/index.d.ts b/apps/desktop/desktop_native/napi/index.d.ts index 01bfa65d571..375c65edb8d 100644 --- a/apps/desktop/desktop_native/napi/index.d.ts +++ b/apps/desktop/desktop_native/napi/index.d.ts @@ -1,125 +1,7 @@ -/* tslint:disable */ -/* eslint-disable */ - /* auto-generated by NAPI-RS */ - -export declare namespace passwords { - /** The error message returned when a password is not found during retrieval or deletion. */ - export const PASSWORD_NOT_FOUND: string - /** - * Fetch the stored password from the keychain. - * Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist. - */ - export function getPassword(service: string, account: string): Promise - /** - * Save the password to the keychain. Adds an entry if none exists otherwise updates the - * existing entry. - */ - export function setPassword(service: string, account: string, password: string): Promise - /** - * Delete the stored password from the keychain. - * Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist. - */ - export function deletePassword(service: string, account: string): Promise - /** Checks if the os secure storage is available */ - export function isAvailable(): Promise -} -export declare namespace biometrics { - export function prompt(hwnd: Buffer, message: string): Promise - export function available(): Promise - export function setBiometricSecret(service: string, account: string, secret: string, keyMaterial: KeyMaterial | undefined | null, ivB64: string): Promise - /** - * Retrieves the biometric secret for the given service and account. - * Throws Error with message [`passwords::PASSWORD_NOT_FOUND`] if the secret does not exist. - */ - export function getBiometricSecret(service: string, account: string, keyMaterial?: KeyMaterial | undefined | null): Promise - /** - * Derives key material from biometric data. Returns a string encoded with a - * base64 encoded key and the base64 encoded challenge used to create it - * separated by a `|` character. - * - * If the iv is provided, it will be used as the challenge. Otherwise a random challenge will - * be generated. - * - * `format!("|")` - */ - export function deriveKeyMaterial(iv?: string | undefined | null): Promise - export interface KeyMaterial { - osKeyPartB64: string - clientKeyPartB64?: string - } - export interface OsDerivedKey { - keyB64: string - ivB64: string - } -} -export declare namespace biometrics_v2 { - export function initBiometricSystem(): BiometricLockSystem - export function authenticate(biometricLockSystem: BiometricLockSystem, hwnd: Buffer, message: string): Promise - export function authenticateAvailable(biometricLockSystem: BiometricLockSystem): Promise - export function enrollPersistent(biometricLockSystem: BiometricLockSystem, userId: string, key: Buffer): Promise - export function provideKey(biometricLockSystem: BiometricLockSystem, userId: string, key: Buffer): Promise - export function unlock(biometricLockSystem: BiometricLockSystem, userId: string, hwnd: Buffer): Promise - export function unlockAvailable(biometricLockSystem: BiometricLockSystem, userId: string): Promise - export function hasPersistent(biometricLockSystem: BiometricLockSystem, userId: string): Promise - export function unenroll(biometricLockSystem: BiometricLockSystem, userId: string): Promise - export class BiometricLockSystem { } -} -export declare namespace clipboards { - export function read(): Promise - export function write(text: string, password: boolean): Promise -} -export declare namespace sshagent { - export interface PrivateKey { - privateKey: string - name: string - cipherId: string - } - export interface SshKey { - privateKey: string - publicKey: string - keyFingerprint: string - } - export interface SshUiRequest { - cipherId?: string - isList: boolean - processName: string - isForwarding: boolean - namespace?: string - } - export function serve(callback: (err: Error | null, arg: SshUiRequest) => any): Promise - export function stop(agentState: SshAgentState): void - export function isRunning(agentState: SshAgentState): boolean - export function setKeys(agentState: SshAgentState, newKeys: Array): void - export function lock(agentState: SshAgentState): void - export function clearKeys(agentState: SshAgentState): void - export class SshAgentState { } -} -export declare namespace processisolations { - export function disableCoredumps(): Promise - export function isCoreDumpingDisabled(): Promise - export function isolateProcess(): Promise -} -export declare namespace powermonitors { - export function onLock(callback: (err: Error | null, ) => any): Promise - export function isLockMonitorAvailable(): Promise -} -export declare namespace windows_registry { - export function createKey(key: string, subkey: string, value: string): Promise - export function deleteKey(key: string, subkey: string): Promise -} -export declare namespace ipc { - export interface IpcMessage { - clientId: number - kind: IpcMessageType - message?: string - } - export const enum IpcMessageType { - Connected = 0, - Disconnected = 1, - Message = 2 - } - export class IpcServer { +/* eslint-disable */ +export declare namespace autofill { + export class AutofillIpcServer { /** * Create and start the IPC server without blocking. * @@ -127,49 +9,18 @@ export declare namespace ipc { * connection and must be the same for both the server and client. @param callback * This function will be called whenever a message is received from a client. */ - static listen(name: string, callback: (error: null | Error, message: IpcMessage) => void): Promise + static listen(name: string, registrationCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyRegistrationRequest) => void, assertionCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionRequest) => void, assertionWithoutUserInterfaceCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionWithoutUserInterfaceRequest) => void, nativeStatusCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: NativeStatus) => void): Promise /** Return the path to the IPC server. */ getPath(): string /** Stop the IPC server. */ stop(): void - /** - * Send a message over the IPC server to all the connected clients - * - * @return The number of clients that the message was sent to. Note that the number of - * messages actually received may be less, as some clients could disconnect before - * receiving the message. - */ - send(message: string): number + completeRegistration(clientId: number, sequenceNumber: number, response: PasskeyRegistrationResponse): number + completeAssertion(clientId: number, sequenceNumber: number, response: PasskeyAssertionResponse): number + completeError(clientId: number, sequenceNumber: number, error: string): number } -} -export declare namespace autostart { - export function setAutostart(autostart: boolean, params: Array): Promise -} -export declare namespace autofill { - export function runCommand(value: string): Promise - export const enum UserVerification { - Preferred = 'preferred', - Required = 'required', - Discouraged = 'discouraged' - } - export interface Position { - x: number - y: number - } - export interface PasskeyRegistrationRequest { - rpId: string - userName: string - userHandle: Array - clientDataHash: Array - userVerification: UserVerification - supportedAlgorithms: Array - windowXy: Position - } - export interface PasskeyRegistrationResponse { - rpId: string - clientDataHash: Array - credentialId: Array - attestationObject: Array + export interface NativeStatus { + key: string + value: string } export interface PasskeyAssertionRequest { rpId: string @@ -178,6 +29,14 @@ export declare namespace autofill { allowedCredentials: Array> windowXy: Position } + export interface PasskeyAssertionResponse { + rpId: string + userHandle: Array + signature: Array + clientDataHash: Array + authenticatorData: Array + credentialId: Array + } export interface PasskeyAssertionWithoutUserInterfaceRequest { rpId: string credentialId: Array @@ -188,50 +47,93 @@ export declare namespace autofill { userVerification: UserVerification windowXy: Position } - export interface PasskeyAssertionResponse { + export interface PasskeyRegistrationRequest { rpId: string + userName: string userHandle: Array - signature: Array clientDataHash: Array - authenticatorData: Array + userVerification: UserVerification + supportedAlgorithms: Array + windowXy: Position + excludedCredentials: Array> + } + export interface PasskeyRegistrationResponse { + rpId: string + clientDataHash: Array credentialId: Array + attestationObject: Array } - export class IpcServer { - /** - * Create and start the IPC server without blocking. - * - * @param name The endpoint name to listen on. This name uniquely identifies the IPC - * connection and must be the same for both the server and client. @param callback - * This function will be called whenever a message is received from a client. - */ - static listen(name: string, registrationCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyRegistrationRequest) => void, assertionCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionRequest) => void, assertionWithoutUserInterfaceCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionWithoutUserInterfaceRequest) => void): Promise - /** Return the path to the IPC server. */ - getPath(): string - /** Stop the IPC server. */ - stop(): void - completeRegistration(clientId: number, sequenceNumber: number, response: PasskeyRegistrationResponse): number - completeAssertion(clientId: number, sequenceNumber: number, response: PasskeyAssertionResponse): number - completeError(clientId: number, sequenceNumber: number, error: string): number + export interface Position { + x: number + y: number + } + export function runCommand(value: string): Promise + export const enum UserVerification { + Preferred = 'preferred', + Required = 'required', + Discouraged = 'discouraged' } } -export declare namespace passkey_authenticator { - export function register(): void + +export declare namespace autostart { + export function setAutostart(autostart: boolean, params: Array): Promise } -export declare namespace logging { - export const enum LogLevel { - Trace = 0, - Debug = 1, - Info = 2, - Warn = 3, - Error = 4 + +export declare namespace autotype { + export function getForegroundWindowTitle(): string + export function typeInput(input: Array, keyboardShortcut: Array): void +} + +export declare namespace biometrics { + export function available(): Promise + /** + * Derives key material from biometric data. Returns a string encoded with a + * base64 encoded key and the base64 encoded challenge used to create it + * separated by a `|` character. + * + * If the iv is provided, it will be used as the challenge. Otherwise a random challenge will + * be generated. + * + * `format!("|")` + */ + export function deriveKeyMaterial(iv?: string | undefined | null): Promise + /** + * Retrieves the biometric secret for the given service and account. + * Throws Error with message [`passwords::PASSWORD_NOT_FOUND`] if the secret does not exist. + */ + export function getBiometricSecret(service: string, account: string, keyMaterial?: KeyMaterial | undefined | null): Promise + export interface KeyMaterial { + osKeyPartB64: string + clientKeyPartB64?: string } - export function initNapiLog(jsLogFn: (err: Error | null, arg0: LogLevel, arg1: string) => any): void + export interface OsDerivedKey { + keyB64: string + ivB64: string + } + export function prompt(hwnd: Buffer, message: string): Promise + export function setBiometricSecret(service: string, account: string, secret: string, keyMaterial: KeyMaterial | undefined | null, ivB64: string): Promise } + +export declare namespace biometrics_v2 { + export class BiometricLockSystem { + + } + export function authenticate(biometricLockSystem: BiometricLockSystem, hwnd: Buffer, message: string): Promise + export function authenticateAvailable(biometricLockSystem: BiometricLockSystem): Promise + export function enrollPersistent(biometricLockSystem: BiometricLockSystem, userId: string, key: Buffer): Promise + export function hasPersistent(biometricLockSystem: BiometricLockSystem, userId: string): Promise + export function initBiometricSystem(): BiometricLockSystem + export function provideKey(biometricLockSystem: BiometricLockSystem, userId: string, key: Buffer): Promise + export function unenroll(biometricLockSystem: BiometricLockSystem, userId: string): Promise + export function unlock(biometricLockSystem: BiometricLockSystem, userId: string, hwnd: Buffer): Promise + export function unlockAvailable(biometricLockSystem: BiometricLockSystem, userId: string): Promise +} + export declare namespace chromium_importer { - export interface ProfileInfo { - id: string - name: string - } + export function getAvailableProfiles(browser: string): Array + /** Returns OS aware metadata describing supported Chromium based importers as a JSON string. */ + export function getMetadata(): Record + export function importLogins(browser: string, profileId: string): Promise> export interface Login { url: string username: string @@ -252,12 +154,130 @@ export declare namespace chromium_importer { loaders: Array instructions: string } - /** Returns OS aware metadata describing supported Chromium based importers as a JSON string. */ - export function getMetadata(): Record - export function getAvailableProfiles(browser: string): Array - export function importLogins(browser: string, profileId: string): Promise> + export interface ProfileInfo { + id: string + name: string + } } -export declare namespace autotype { - export function getForegroundWindowTitle(): string - export function typeInput(input: Array, keyboardShortcut: Array): void + +export declare namespace clipboards { + export function read(): Promise + export function write(text: string, password: boolean): Promise +} + +export declare namespace ipc { + export class NativeIpcServer { + /** + * Create and start the IPC server without blocking. + * + * @param name The endpoint name to listen on. This name uniquely identifies the IPC + * connection and must be the same for both the server and client. @param callback + * This function will be called whenever a message is received from a client. + */ + static listen(name: string, callback: (error: null | Error, message: IpcMessage) => void): Promise + /** Return the path to the IPC server. */ + getPath(): string + /** Stop the IPC server. */ + stop(): void + /** + * Send a message over the IPC server to all the connected clients + * + * @return The number of clients that the message was sent to. Note that the number of + * messages actually received may be less, as some clients could disconnect before + * receiving the message. + */ + send(message: string): number + } + export interface IpcMessage { + clientId: number + kind: IpcMessageType + message?: string + } + export const enum IpcMessageType { + Connected = 0, + Disconnected = 1, + Message = 2 + } +} + +export declare namespace logging { + export function initNapiLog(jsLogFn: ((err: Error | null, arg0: LogLevel, arg1: string) => any)): void + export const enum LogLevel { + Trace = 0, + Debug = 1, + Info = 2, + Warn = 3, + Error = 4 + } +} + +export declare namespace passkey_authenticator { + export function register(): void +} + +export declare namespace passwords { + /** + * Delete the stored password from the keychain. + * Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist. + */ + export function deletePassword(service: string, account: string): Promise + /** + * Fetch the stored password from the keychain. + * Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist. + */ + export function getPassword(service: string, account: string): Promise + /** Checks if the os secure storage is available */ + export function isAvailable(): Promise + /** The error message returned when a password is not found during retrieval or deletion. */ + export const PASSWORD_NOT_FOUND: string + /** + * Save the password to the keychain. Adds an entry if none exists otherwise updates the + * existing entry. + */ + export function setPassword(service: string, account: string, password: string): Promise +} + +export declare namespace powermonitors { + export function isLockMonitorAvailable(): Promise + export function onLock(callback: ((err: Error | null, ) => any)): Promise +} + +export declare namespace processisolations { + export function disableCoredumps(): Promise + export function isCoreDumpingDisabled(): Promise + export function isolateProcess(): Promise +} + +export declare namespace sshagent { + export class SshAgentState { + + } + export function clearKeys(agentState: SshAgentState): void + export function isRunning(agentState: SshAgentState): boolean + export function lock(agentState: SshAgentState): void + export interface PrivateKey { + privateKey: string + name: string + cipherId: string + } + export function serve(callback: ((err: Error | null, arg: SshUiRequest) => Promise)): Promise + export function setKeys(agentState: SshAgentState, newKeys: Array): void + export interface SshKey { + privateKey: string + publicKey: string + keyFingerprint: string + } + export interface SshUiRequest { + cipherId?: string + isList: boolean + processName: string + isForwarding: boolean + namespace?: string + } + export function stop(agentState: SshAgentState): void +} + +export declare namespace windows_registry { + export function createKey(key: string, subkey: string, value: string): Promise + export function deleteKey(key: string, subkey: string): Promise } diff --git a/apps/desktop/desktop_native/napi/index.js b/apps/desktop/desktop_native/napi/index.js index 64819be4405..0362d9ee2bb 100644 --- a/apps/desktop/desktop_native/napi/index.js +++ b/apps/desktop/desktop_native/napi/index.js @@ -82,20 +82,20 @@ switch (platform) { switch (arch) { case "x64": nativeBinding = loadFirstAvailable( - ["desktop_napi.linux-x64-musl.node", "desktop_napi.linux-x64-gnu.node"], - "@bitwarden/desktop-napi-linux-x64-musl", + ["desktop_napi.linux-x64-gnu.node"], + "@bitwarden/desktop-napi-linux-x64-gnu", ); break; case "arm64": nativeBinding = loadFirstAvailable( - ["desktop_napi.linux-arm64-musl.node", "desktop_napi.linux-arm64-gnu.node"], - "@bitwarden/desktop-napi-linux-arm64-musl", + ["desktop_napi.linux-arm64-gnu.node"], + "@bitwarden/desktop-napi-linux-arm64-gnu", ); break; case "arm": nativeBinding = loadFirstAvailable( - ["desktop_napi.linux-arm-musl.node", "desktop_napi.linux-arm-gnu.node"], - "@bitwarden/desktop-napi-linux-arm-musl", + ["desktop_napi.linux-arm-gnu.node"], + "@bitwarden/desktop-napi-linux-arm-gnu", ); localFileExisted = existsSync(join(__dirname, "desktop_napi.linux-arm-gnueabihf.node")); try { diff --git a/apps/desktop/desktop_native/napi/package.json b/apps/desktop/desktop_native/napi/package.json index ca17377c9f2..0717bfd53ea 100644 --- a/apps/desktop/desktop_native/napi/package.json +++ b/apps/desktop/desktop_native/napi/package.json @@ -9,21 +9,17 @@ "author": "", "license": "GPL-3.0", "devDependencies": { - "@napi-rs/cli": "2.18.4" + "@napi-rs/cli": "3.2.0" }, "napi": { - "name": "desktop_napi", - "triples": { - "defaults": true, - "additional": [ - "x86_64-unknown-linux-musl", - "aarch64-unknown-linux-gnu", - "i686-pc-windows-msvc", - "armv7-unknown-linux-gnueabihf", - "aarch64-apple-darwin", - "aarch64-unknown-linux-musl", - "aarch64-pc-windows-msvc" - ] - } + "binaryName": "desktop_napi", + "targets": [ + "aarch64-apple-darwin", + "aarch64-pc-windows-msvc", + "aarch64-unknown-linux-gnu", + "armv7-unknown-linux-gnueabihf", + "i686-pc-windows-msvc", + "x86_64-unknown-linux-gnu" + ] } } diff --git a/apps/desktop/desktop_native/napi/scripts/build.js b/apps/desktop/desktop_native/napi/scripts/build.js index a6680f5d311..ad24b99d2fb 100644 --- a/apps/desktop/desktop_native/napi/scripts/build.js +++ b/apps/desktop/desktop_native/napi/scripts/build.js @@ -2,13 +2,21 @@ const { execSync } = require('child_process'); const args = process.argv.slice(2); + const isRelease = args.includes('--release'); +const argsString = args.join(' '); + if (isRelease) { console.log('Building release mode.'); + + execSync(`napi build --platform --no-js ${argsString}`, { stdio: 'inherit'}); + } else { console.log('Building debug mode.'); - process.env.RUST_LOG = 'debug'; -} -execSync(`napi build --platform --js false`, { stdio: 'inherit', env: process.env }); + execSync(`napi build --platform --no-js ${argsString}`, { + stdio: 'inherit', + env: { ...process.env, RUST_LOG: 'debug' } + }); +} diff --git a/apps/desktop/desktop_native/napi/src/lib.rs b/apps/desktop/desktop_native/napi/src/lib.rs index c34e7574f68..588f757631c 100644 --- a/apps/desktop/desktop_native/napi/src/lib.rs +++ b/apps/desktop/desktop_native/napi/src/lib.rs @@ -290,7 +290,7 @@ pub mod sshagent { use napi::{ bindgen_prelude::Promise, - threadsafe_function::{ErrorStrategy::CalleeHandled, ThreadsafeFunction}, + threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}, }; use tokio::{self, sync::Mutex}; use tracing::error; @@ -326,13 +326,15 @@ pub mod sshagent { #[allow(clippy::unused_async)] // FIXME: Remove unused async! #[napi] pub async fn serve( - callback: ThreadsafeFunction, + callback: ThreadsafeFunction>, ) -> napi::Result { let (auth_request_tx, mut auth_request_rx) = tokio::sync::mpsc::channel::(32); let (auth_response_tx, auth_response_rx) = tokio::sync::broadcast::channel::<(u32, bool)>(32); let auth_response_tx_arc = Arc::new(Mutex::new(auth_response_tx)); + // Wrap callback in Arc so it can be shared across spawned tasks + let callback = Arc::new(callback); tokio::spawn(async move { let _ = auth_response_rx; @@ -342,42 +344,50 @@ pub mod sshagent { tokio::spawn(async move { let auth_response_tx_arc = cloned_response_tx_arc; let callback = cloned_callback; - let promise_result: Result, napi::Error> = callback - .call_async(Ok(SshUIRequest { + // In NAPI v3, obtain the JS callback return as a Promise and await it + // in Rust + let (tx, rx) = std::sync::mpsc::channel::>(); + let status = callback.call_with_return_value( + Ok(SshUIRequest { cipher_id: request.cipher_id, is_list: request.is_list, process_name: request.process_name, is_forwarding: request.is_forwarding, namespace: request.namespace, - })) - .await; - match promise_result { - Ok(promise_result) => match promise_result.await { - Ok(result) => { - let _ = auth_response_tx_arc - .lock() - .await - .send((request.request_id, result)) - .expect("should be able to send auth response to agent"); - } - Err(e) => { - error!(error = %e, "Calling UI callback promise was rejected"); - let _ = auth_response_tx_arc - .lock() - .await - .send((request.request_id, false)) - .expect("should be able to send auth response to agent"); + }), + ThreadsafeFunctionCallMode::Blocking, + move |ret: Result, napi::Error>, _env| { + if let Ok(p) = ret { + let _ = tx.send(p); } + Ok(()) }, - Err(e) => { - error!(error = %e, "Calling UI callback could not create promise"); - let _ = auth_response_tx_arc - .lock() - .await - .send((request.request_id, false)) - .expect("should be able to send auth response to agent"); + ); + + let result = if status == napi::Status::Ok { + match rx.recv() { + Ok(promise) => match promise.await { + Ok(v) => v, + Err(e) => { + error!(error = %e, "UI callback promise rejected"); + false + } + }, + Err(e) => { + error!(error = %e, "Failed to receive UI callback promise"); + false + } } - } + } else { + error!(error = ?status, "Calling UI callback failed"); + false + }; + + let _ = auth_response_tx_arc + .lock() + .await + .send((request.request_id, result)) + .expect("should be able to send auth response to agent"); }); } }); @@ -465,14 +475,12 @@ pub mod processisolations { #[napi] pub mod powermonitors { use napi::{ - threadsafe_function::{ - ErrorStrategy::CalleeHandled, ThreadsafeFunction, ThreadsafeFunctionCallMode, - }, + threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}, tokio, }; #[napi] - pub async fn on_lock(callback: ThreadsafeFunction<(), CalleeHandled>) -> napi::Result<()> { + pub async fn on_lock(callback: ThreadsafeFunction<()>) -> napi::Result<()> { let (tx, mut rx) = tokio::sync::mpsc::channel::<()>(32); desktop_core::powermonitor::on_lock(tx) .await @@ -511,9 +519,7 @@ pub mod windows_registry { #[napi] pub mod ipc { use desktop_core::ipc::server::{Message, MessageType}; - use napi::threadsafe_function::{ - ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode, - }; + use napi::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}; #[napi(object)] pub struct IpcMessage { @@ -550,12 +556,12 @@ pub mod ipc { } #[napi] - pub struct IpcServer { + pub struct NativeIpcServer { server: desktop_core::ipc::server::Server, } #[napi] - impl IpcServer { + impl NativeIpcServer { /// Create and start the IPC server without blocking. /// /// @param name The endpoint name to listen on. This name uniquely identifies the IPC @@ -566,7 +572,7 @@ pub mod ipc { pub async fn listen( name: String, #[napi(ts_arg_type = "(error: null | Error, message: IpcMessage) => void")] - callback: ThreadsafeFunction, + callback: ThreadsafeFunction, ) -> napi::Result { let (send, mut recv) = tokio::sync::mpsc::channel::(32); tokio::spawn(async move { @@ -583,7 +589,7 @@ pub mod ipc { )) })?; - Ok(IpcServer { server }) + Ok(NativeIpcServer { server }) } /// Return the path to the IPC server. @@ -630,8 +636,9 @@ pub mod autostart { #[napi] pub mod autofill { use desktop_core::ipc::server::{Message, MessageType}; - use napi::threadsafe_function::{ - ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode, + use napi::{ + bindgen_prelude::FnArgs, + threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}, }; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use tracing::error; @@ -686,6 +693,7 @@ pub mod autofill { pub user_verification: UserVerification, pub supported_algorithms: Vec, pub window_xy: Position, + pub excluded_credentials: Vec>, } #[napi(object)] @@ -724,6 +732,14 @@ pub mod autofill { pub window_xy: Position, } + #[napi(object)] + #[derive(Debug, Serialize, Deserialize)] + #[serde(rename_all = "camelCase")] + pub struct NativeStatus { + pub key: String, + pub value: String, + } + #[napi(object)] #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -737,14 +753,14 @@ pub mod autofill { } #[napi] - pub struct IpcServer { + pub struct AutofillIpcServer { server: desktop_core::ipc::server::Server, } // FIXME: Remove unwraps! They panic and terminate the whole application. #[allow(clippy::unwrap_used)] #[napi] - impl IpcServer { + impl AutofillIpcServer { /// Create and start the IPC server without blocking. /// /// @param name The endpoint name to listen on. This name uniquely identifies the IPC @@ -760,23 +776,24 @@ pub mod autofill { ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyRegistrationRequest) => void" )] registration_callback: ThreadsafeFunction< - (u32, u32, PasskeyRegistrationRequest), - ErrorStrategy::CalleeHandled, + FnArgs<(u32, u32, PasskeyRegistrationRequest)>, >, #[napi( ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionRequest) => void" )] assertion_callback: ThreadsafeFunction< - (u32, u32, PasskeyAssertionRequest), - ErrorStrategy::CalleeHandled, + FnArgs<(u32, u32, PasskeyAssertionRequest)>, >, #[napi( ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionWithoutUserInterfaceRequest) => void" )] assertion_without_user_interface_callback: ThreadsafeFunction< - (u32, u32, PasskeyAssertionWithoutUserInterfaceRequest), - ErrorStrategy::CalleeHandled, + FnArgs<(u32, u32, PasskeyAssertionWithoutUserInterfaceRequest)>, >, + #[napi( + ts_arg_type = "(error: null | Error, clientId: number, sequenceNumber: number, message: NativeStatus) => void" + )] + native_status_callback: ThreadsafeFunction<(u32, u32, NativeStatus)>, ) -> napi::Result { let (send, mut recv) = tokio::sync::mpsc::channel::(32); tokio::spawn(async move { @@ -801,7 +818,7 @@ pub mod autofill { Ok(msg) => { let value = msg .value - .map(|value| (client_id, msg.sequence_number, value)) + .map(|value| (client_id, msg.sequence_number, value).into()) .map_err(|e| napi::Error::from_reason(format!("{e:?}"))); assertion_callback @@ -820,7 +837,7 @@ pub mod autofill { Ok(msg) => { let value = msg .value - .map(|value| (client_id, msg.sequence_number, value)) + .map(|value| (client_id, msg.sequence_number, value).into()) .map_err(|e| napi::Error::from_reason(format!("{e:?}"))); assertion_without_user_interface_callback @@ -838,7 +855,7 @@ pub mod autofill { Ok(msg) => { let value = msg .value - .map(|value| (client_id, msg.sequence_number, value)) + .map(|value| (client_id, msg.sequence_number, value).into()) .map_err(|e| napi::Error::from_reason(format!("{e:?}"))); registration_callback .call(value, ThreadsafeFunctionCallMode::NonBlocking); @@ -849,6 +866,21 @@ pub mod autofill { } } + match serde_json::from_str::>(&message) { + Ok(msg) => { + let value = msg + .value + .map(|value| (client_id, msg.sequence_number, value)) + .map_err(|e| napi::Error::from_reason(format!("{e:?}"))); + native_status_callback + .call(value, ThreadsafeFunctionCallMode::NonBlocking); + continue; + } + Err(error) => { + error!(%error, "Unable to deserialze native status."); + } + } + error!(message, "Received an unknown message2"); } } @@ -863,7 +895,7 @@ pub mod autofill { )) })?; - Ok(IpcServer { server }) + Ok(AutofillIpcServer { server }) } /// Return the path to the IPC server. @@ -956,8 +988,9 @@ pub mod logging { use std::{fmt::Write, sync::OnceLock}; - use napi::threadsafe_function::{ - ErrorStrategy::CalleeHandled, ThreadsafeFunction, ThreadsafeFunctionCallMode, + use napi::{ + bindgen_prelude::FnArgs, + threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}, }; use tracing::Level; use tracing_subscriber::{ @@ -968,7 +1001,7 @@ pub mod logging { Layer, }; - struct JsLogger(OnceLock>); + struct JsLogger(OnceLock>>); static JS_LOGGER: JsLogger = JsLogger(OnceLock::new()); #[napi] @@ -1040,13 +1073,13 @@ pub mod logging { let msg = (event.metadata().level().into(), buffer); if let Some(logger) = JS_LOGGER.0.get() { - let _ = logger.call(Ok(msg), ThreadsafeFunctionCallMode::NonBlocking); + let _ = logger.call(Ok(msg.into()), ThreadsafeFunctionCallMode::NonBlocking); }; } } #[napi] - pub fn init_napi_log(js_log_fn: ThreadsafeFunction<(LogLevel, String), CalleeHandled>) { + pub fn init_napi_log(js_log_fn: ThreadsafeFunction>) { let _ = JS_LOGGER.0.set(js_log_fn); // the log level hierarchy is determined by: @@ -1117,8 +1150,8 @@ pub mod chromium_importer { #[napi(object)] pub struct NativeImporterMetadata { pub id: String, - pub loaders: Vec<&'static str>, - pub instructions: &'static str, + pub loaders: Vec, + pub instructions: String, } impl From<_LoginImportResult> for LoginImportResult { @@ -1195,7 +1228,7 @@ pub mod chromium_importer { #[napi] pub mod autotype { #[napi] - pub fn get_foreground_window_title() -> napi::Result { + pub fn get_foreground_window_title() -> napi::Result { autotype::get_foreground_window_title().map_err(|_| { napi::Error::from_reason( "Autotype Error: failed to get foreground window title".to_string(), @@ -1208,8 +1241,7 @@ pub mod autotype { input: Vec, keyboard_shortcut: Vec, ) -> napi::Result<(), napi::Status> { - autotype::type_input(input, keyboard_shortcut).map_err(|_| { - napi::Error::from_reason("Autotype Error: failed to type input".to_string()) - }) + autotype::type_input(&input, &keyboard_shortcut) + .map_err(|e| napi::Error::from_reason(format!("Autotype Error: {e}"))) } } diff --git a/apps/desktop/desktop_native/objc/Cargo.toml b/apps/desktop/desktop_native/objc/Cargo.toml index 5ef791fb586..1edf30ac996 100644 --- a/apps/desktop/desktop_native/objc/Cargo.toml +++ b/apps/desktop/desktop_native/objc/Cargo.toml @@ -14,8 +14,8 @@ tokio = { workspace = true } tracing = { workspace = true } [target.'cfg(target_os = "macos")'.build-dependencies] -cc = "=1.2.46" -glob = "=0.3.2" +cc = "=1.2.51" +glob = "=0.3.3" [lints] workspace = true diff --git a/apps/desktop/desktop_native/objc/src/native/autofill/commands/sync.m b/apps/desktop/desktop_native/objc/src/native/autofill/commands/sync.m index fc13c04591a..037a97c7590 100644 --- a/apps/desktop/desktop_native/objc/src/native/autofill/commands/sync.m +++ b/apps/desktop/desktop_native/objc/src/native/autofill/commands/sync.m @@ -14,40 +14,64 @@ void runSync(void* context, NSDictionary *params) { // Map credentials to ASPasswordCredential objects NSMutableArray *mappedCredentials = [NSMutableArray arrayWithCapacity:credentials.count]; + for (NSDictionary *credential in credentials) { - NSString *type = credential[@"type"]; - - if ([type isEqualToString:@"password"]) { - NSString *cipherId = credential[@"cipherId"]; - NSString *uri = credential[@"uri"]; - NSString *username = credential[@"username"]; - - ASCredentialServiceIdentifier *serviceId = [[ASCredentialServiceIdentifier alloc] - initWithIdentifier:uri type:ASCredentialServiceIdentifierTypeURL]; - ASPasswordCredentialIdentity *credential = [[ASPasswordCredentialIdentity alloc] - initWithServiceIdentifier:serviceId user:username recordIdentifier:cipherId]; - - [mappedCredentials addObject:credential]; - } - - if (@available(macos 14, *)) { - if ([type isEqualToString:@"fido2"]) { + @try { + NSString *type = credential[@"type"]; + + if ([type isEqualToString:@"password"]) { NSString *cipherId = credential[@"cipherId"]; - NSString *rpId = credential[@"rpId"]; - NSString *userName = credential[@"userName"]; - NSData *credentialId = decodeBase64URL(credential[@"credentialId"]); - NSData *userHandle = decodeBase64URL(credential[@"userHandle"]); + NSString *uri = credential[@"uri"]; + NSString *username = credential[@"username"]; + + // Skip credentials with null username since MacOS crashes if we send credentials with empty usernames + if ([username isKindOfClass:[NSNull class]] || username.length == 0) { + NSLog(@"Skipping credential, username is empty: %@", credential); + continue; + } - Class passkeyCredentialIdentityClass = NSClassFromString(@"ASPasskeyCredentialIdentity"); - id credential = [[passkeyCredentialIdentityClass alloc] - initWithRelyingPartyIdentifier:rpId - userName:userName - credentialID:credentialId - userHandle:userHandle - recordIdentifier:cipherId]; + ASCredentialServiceIdentifier *serviceId = [[ASCredentialServiceIdentifier alloc] + initWithIdentifier:uri type:ASCredentialServiceIdentifierTypeURL]; + ASPasswordCredentialIdentity *passwordIdentity = [[ASPasswordCredentialIdentity alloc] + initWithServiceIdentifier:serviceId user:username recordIdentifier:cipherId]; - [mappedCredentials addObject:credential]; + [mappedCredentials addObject:passwordIdentity]; + } + else if (@available(macos 14, *)) { + // Fido2CredentialView uses `userName` (camelCase) while Login uses `username`. + // This is intentional. Fido2 fields are flattened from the FIDO2 spec's nested structure + // (user.name -> userName, rp.id -> rpId) to maintain a clear distinction between these fields. + if ([type isEqualToString:@"fido2"]) { + NSString *cipherId = credential[@"cipherId"]; + NSString *rpId = credential[@"rpId"]; + NSString *userName = credential[@"userName"]; + + // Skip credentials with null username since MacOS crashes if we send credentials with empty usernames + if ([userName isKindOfClass:[NSNull class]] || userName.length == 0) { + NSLog(@"Skipping credential, username is empty: %@", credential); + continue; + } + + NSData *credentialId = decodeBase64URL(credential[@"credentialId"]); + NSData *userHandle = decodeBase64URL(credential[@"userHandle"]); + + Class passkeyCredentialIdentityClass = NSClassFromString(@"ASPasskeyCredentialIdentity"); + id passkeyIdentity = [[passkeyCredentialIdentityClass alloc] + initWithRelyingPartyIdentifier:rpId + userName:userName + credentialID:credentialId + userHandle:userHandle + recordIdentifier:cipherId]; + + [mappedCredentials addObject:passkeyIdentity]; + } } + } @catch (NSException *exception) { + // Silently skip any credential that causes an exception + // to make sure we don't fail the entire sync + // There is likely some invalid data in the credential, and not something the user should/could be asked to correct. + NSLog(@"ERROR: Exception processing credential: %@ - %@", exception.name, exception.reason); + continue; } } diff --git a/apps/desktop/desktop_native/objc/src/native/utils.m b/apps/desktop/desktop_native/objc/src/native/utils.m index 040c723a8ac..8f9493a7afb 100644 --- a/apps/desktop/desktop_native/objc/src/native/utils.m +++ b/apps/desktop/desktop_native/objc/src/native/utils.m @@ -18,9 +18,26 @@ NSString *serializeJson(NSDictionary *dictionary, NSError *error) { } NSData *decodeBase64URL(NSString *base64URLString) { + if (base64URLString.length == 0) { + return nil; + } + + // Replace URL-safe characters with standard base64 characters NSString *base64String = [base64URLString stringByReplacingOccurrencesOfString:@"-" withString:@"+"]; base64String = [base64String stringByReplacingOccurrencesOfString:@"_" withString:@"/"]; - + + // Add padding if needed + // Base 64 strings should be a multiple of 4 in length + NSUInteger paddingLength = 4 - (base64String.length % 4); + if (paddingLength < 4) { + NSMutableString *paddedString = [NSMutableString stringWithString:base64String]; + for (NSUInteger i = 0; i < paddingLength; i++) { + [paddedString appendString:@"="]; + } + base64String = paddedString; + } + + // Decode the string NSData *nsdataFromBase64String = [[NSData alloc] initWithBase64EncodedString:base64String options:0]; diff --git a/apps/desktop/desktop_native/rust-toolchain.toml b/apps/desktop/desktop_native/rust-toolchain.toml index 898a61f3f4b..0992ce9d294 100644 --- a/apps/desktop/desktop_native/rust-toolchain.toml +++ b/apps/desktop/desktop_native/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.85.0" +channel = "1.91.1" components = [ "rustfmt", "clippy" ] profile = "minimal" diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/Cargo.toml b/apps/desktop/desktop_native/windows_plugin_authenticator/Cargo.toml index 17c834325a4..9fd873d868e 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/Cargo.toml +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/Cargo.toml @@ -6,6 +6,7 @@ license = { workspace = true } publish = { workspace = true } [target.'cfg(windows)'.dependencies] +hex = { workspace = true } windows = { workspace = true, features = [ "Win32_Foundation", "Win32_Security", @@ -13,7 +14,6 @@ windows = { workspace = true, features = [ "Win32_System_LibraryLoader", ] } windows-core = { workspace = true } -hex = { workspace = true } [lints] workspace = true diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/lib.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/lib.rs index 893fdf765fc..b38a1c725f2 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/lib.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/lib.rs @@ -153,7 +153,7 @@ fn add_authenticator() -> std::result::Result<(), String> { } } -type EXPERIMENTAL_WebAuthNPluginAddAuthenticatorFnDeclaration = unsafe extern "cdecl" fn( +type EXPERIMENTAL_WebAuthNPluginAddAuthenticatorFnDeclaration = unsafe extern "C" fn( pPluginAddAuthenticatorOptions: *const webauthn::ExperimentalWebAuthnPluginAddAuthenticatorOptions, ppPluginAddAuthenticatorResponse: *mut *mut webauthn::ExperimentalWebAuthnPluginAddAuthenticatorResponse, ) -> HRESULT; diff --git a/apps/desktop/electron-builder.beta.json b/apps/desktop/electron-builder.beta.json index 630a956560d..3e1ca673c3c 100644 --- a/apps/desktop/electron-builder.beta.json +++ b/apps/desktop/electron-builder.beta.json @@ -1,11 +1,13 @@ { + "$schema": "https://raw.githubusercontent.com/electron-userland/electron-builder/master/packages/app-builder-lib/scheme.json", + "extraMetadata": { "name": "bitwarden-beta" }, "productName": "Bitwarden Beta", "appId": "com.bitwarden.desktop.beta", "buildDependenciesFromSource": true, - "copyright": "Copyright Š 2015-2025 Bitwarden Inc.", + "copyright": "Copyright Š 2015-2026 Bitwarden Inc.", "directories": { "buildResources": "resources", "output": "dist", @@ -13,14 +15,15 @@ }, "afterSign": "scripts/after-sign.js", "afterPack": "scripts/after-pack.js", - "asarUnpack": ["**/*.node"], + "beforePack": "scripts/before-pack.js", "files": [ - "**/*", - "!**/node_modules/@bitwarden/desktop-napi/**/*", - "**/node_modules/@bitwarden/desktop-napi/index.js", - "**/node_modules/@bitwarden/desktop-napi/desktop_napi.${platform}-${arch}*.node" + "!node_modules/@bitwarden/desktop-napi/scripts", + "!node_modules/@bitwarden/desktop-napi/src", + "!node_modules/@bitwarden/desktop-napi/Cargo.toml", + "!node_modules/@bitwarden/desktop-napi/build.rs", + "!node_modules/@bitwarden/desktop-napi/package.json" ], - "electronVersion": "36.8.1", + "electronVersion": "37.7.0", "generateUpdatesFilesForAllChannels": true, "publish": { "provider": "generic", @@ -34,11 +37,11 @@ }, "extraFiles": [ { - "from": "desktop_native/dist/desktop_proxy.${platform}-${arch}.exe", + "from": "desktop_native/dist/desktop_proxy.win32-${arch}.exe", "to": "desktop_proxy.exe" }, { - "from": "desktop_native/dist/bitwarden_chromium_import_helper.${platform}-${arch}.exe", + "from": "desktop_native/dist/bitwarden_chromium_import_helper.win32-${arch}.exe", "to": "bitwarden_chromium_import_helper.exe" } ] @@ -58,9 +61,10 @@ "appx": { "artifactName": "Bitwarden-Beta-${version}-${arch}.${ext}", "backgroundColor": "#175DDC", + "customManifestPath": "./custom-appx-manifest.xml", "applicationId": "BitwardenBeta", "identityName": "8bitSolutionsLLC.BitwardenBeta", - "publisher": "CN=14D52771-DE3C-4886-B8BF-825BA7690418", + "publisher": "CN=Bitwarden Inc., O=Bitwarden Inc., L=Santa Barbara, S=California, C=US, SERIALNUMBER=7654941, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.2=Delaware, OID.1.3.6.1.4.1.311.60.2.1.3=US", "publisherDisplayName": "Bitwarden Inc", "languages": [ "en-US", diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json index 6e89799e9c4..481d12f02b4 100644 --- a/apps/desktop/electron-builder.json +++ b/apps/desktop/electron-builder.json @@ -1,11 +1,13 @@ { + "$schema": "https://raw.githubusercontent.com/electron-userland/electron-builder/master/packages/app-builder-lib/scheme.json", + "extraMetadata": { "name": "bitwarden" }, "productName": "Bitwarden", "appId": "com.bitwarden.desktop", "buildDependenciesFromSource": true, - "copyright": "Copyright Š 2015-2025 Bitwarden Inc.", + "copyright": "Copyright Š 2015-2026 Bitwarden Inc.", "directories": { "buildResources": "resources", "output": "dist", @@ -13,14 +15,15 @@ }, "afterSign": "scripts/after-sign.js", "afterPack": "scripts/after-pack.js", - "asarUnpack": ["**/*.node"], + "beforePack": "scripts/before-pack.js", "files": [ - "**/*", - "!**/node_modules/@bitwarden/desktop-napi/**/*", - "**/node_modules/@bitwarden/desktop-napi/index.js", - "**/node_modules/@bitwarden/desktop-napi/desktop_napi.${platform}-${arch}*.node" + "!node_modules/@bitwarden/desktop-napi/scripts", + "!node_modules/@bitwarden/desktop-napi/src", + "!node_modules/@bitwarden/desktop-napi/Cargo.toml", + "!node_modules/@bitwarden/desktop-napi/build.rs", + "!node_modules/@bitwarden/desktop-napi/package.json" ], - "electronVersion": "37.7.0", + "electronVersion": "39.2.6", "generateUpdatesFilesForAllChannels": true, "publish": { "provider": "generic", @@ -82,7 +85,8 @@ "signIgnore": [ "MacOS/desktop_proxy", "MacOS/desktop_proxy.inherit", - "Contents/Plugins/autofill-extension.appex" + "Contents/Plugins/autofill-extension.appex", + "Frameworks/Electron Framework.framework/(Electron Framework|Libraries|Resources|Versions/Current)/.*" ], "target": ["dmg", "zip"] }, @@ -94,11 +98,11 @@ }, "extraFiles": [ { - "from": "desktop_native/dist/desktop_proxy.${platform}-${arch}.exe", + "from": "desktop_native/dist/desktop_proxy.win32-${arch}.exe", "to": "desktop_proxy.exe" }, { - "from": "desktop_native/dist/bitwarden_chromium_import_helper.${platform}-${arch}.exe", + "from": "desktop_native/dist/bitwarden_chromium_import_helper.win32-${arch}.exe", "to": "bitwarden_chromium_import_helper.exe" } ] @@ -172,9 +176,10 @@ "appx": { "artifactName": "${productName}-${version}-${arch}.${ext}", "backgroundColor": "#175DDC", + "customManifestPath": "./custom-appx-manifest.xml", "applicationId": "bitwardendesktop", "identityName": "8bitSolutionsLLC.bitwardendesktop", - "publisher": "CN=14D52771-DE3C-4886-B8BF-825BA7690418", + "publisher": "CN=Bitwarden Inc., O=Bitwarden Inc., L=Santa Barbara, S=California, C=US, SERIALNUMBER=7654941, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.2=Delaware, OID.1.3.6.1.4.1.311.60.2.1.3=US", "publisherDisplayName": "Bitwarden Inc", "languages": [ "en-US", diff --git a/apps/desktop/macos/autofill-extension/Base.lproj/CredentialProviderViewController.xib b/apps/desktop/macos/autofill-extension/Base.lproj/CredentialProviderViewController.xib index 1e47cc54de2..132882c6477 100644 --- a/apps/desktop/macos/autofill-extension/Base.lproj/CredentialProviderViewController.xib +++ b/apps/desktop/macos/autofill-extension/Base.lproj/CredentialProviderViewController.xib @@ -8,63 +8,56 @@ + + + + + diff --git a/apps/desktop/macos/autofill-extension/CredentialProviderViewController.swift b/apps/desktop/macos/autofill-extension/CredentialProviderViewController.swift index 5568b2e75db..3de9468c8ab 100644 --- a/apps/desktop/macos/autofill-extension/CredentialProviderViewController.swift +++ b/apps/desktop/macos/autofill-extension/CredentialProviderViewController.swift @@ -11,63 +11,138 @@ import os class CredentialProviderViewController: ASCredentialProviderViewController { let logger: Logger - // There is something a bit strange about the initialization/deinitialization in this class. - // Sometimes deinit won't be called after a request has successfully finished, - // which would leave this class hanging in memory and the IPC connection open. - // - // If instead I make this a static, the deinit gets called correctly after each request. - // I think we still might want a static regardless, to be able to reuse the connection if possible. - let client: MacOsProviderClient = { - let logger = Logger(subsystem: "com.bitwarden.desktop.autofill-extension", category: "credential-provider") + @IBOutlet weak var statusLabel: NSTextField! + @IBOutlet weak var logoImageView: NSImageView! + + // The IPC client to communicate with the Bitwarden desktop app + private var client: MacOsProviderClient? + + // Timer for checking connection status + private var connectionMonitorTimer: Timer? + private var lastConnectionStatus: ConnectionStatus = .disconnected + + // We changed the getClient method to be async, here's why: + // This is so that we can check if the app is running, and launch it, without blocking the main thread + // Blocking the main thread caused MacOS layouting to 'fail' or at least be very delayed, which caused our getWindowPositioning code to sent 0,0. + // We also properly retry the IPC connection which sometimes would take some time to be up and running, depending on CPU load, phase of jupiters moon, etc. + private func getClient() async -> MacOsProviderClient { + if let client = self.client { + return client + } + let logger = Logger(subsystem: "com.bitwarden.desktop.autofill-extension", category: "credential-provider") + // Check if the Electron app is running let workspace = NSWorkspace.shared let isRunning = workspace.runningApplications.contains { app in app.bundleIdentifier == "com.bitwarden.desktop" } - + if !isRunning { - logger.log("[autofill-extension] Bitwarden Desktop not running, attempting to launch") - - // Try to launch the app + logger.log("[autofill-extension] Bitwarden Desktop not running, attempting to launch") + + // Launch the app and wait for it to be ready if let appURL = workspace.urlForApplication(withBundleIdentifier: "com.bitwarden.desktop") { - let semaphore = DispatchSemaphore(value: 0) - - workspace.openApplication(at: appURL, - configuration: NSWorkspace.OpenConfiguration()) { app, error in - if let error = error { - logger.error("[autofill-extension] Failed to launch Bitwarden Desktop: \(error.localizedDescription)") - } else if let app = app { - logger.log("[autofill-extension] Successfully launched Bitwarden Desktop") - } else { - logger.error("[autofill-extension] Failed to launch Bitwarden Desktop: unknown error") + await withCheckedContinuation { continuation in + workspace.openApplication(at: appURL, configuration: NSWorkspace.OpenConfiguration()) { app, error in + if let error = error { + logger.error("[autofill-extension] Failed to launch Bitwarden Desktop: \(error.localizedDescription)") + } else { + logger.log("[autofill-extension] Successfully launched Bitwarden Desktop") + } + continuation.resume() } - semaphore.signal() } - - // Wait for launch completion with timeout - _ = semaphore.wait(timeout: .now() + 5.0) - - // Add a small delay to allow for initialization - Thread.sleep(forTimeInterval: 1.0) - } else { - logger.error("[autofill-extension] Could not find Bitwarden Desktop app") } - } else { - logger.log("[autofill-extension] Bitwarden Desktop is running") + } + + logger.log("[autofill-extension] Connecting to Bitwarden over IPC") + + // Retry connecting to the Bitwarden IPC with an increasing delay + let maxRetries = 20 + let delayMs = 500 + var newClient: MacOsProviderClient? + + for attempt in 1...maxRetries { + logger.log("[autofill-extension] Connection attempt \(attempt)") + + // Create a new client instance for each retry + newClient = MacOsProviderClient.connect() + try? await Task.sleep(nanoseconds: UInt64(100 * attempt + (delayMs * 1_000_000))) // Convert ms to nanoseconds + let connectionStatus = newClient!.getConnectionStatus() + + logger.log("[autofill-extension] Connection attempt \(attempt), status: \(connectionStatus == .connected ? "connected" : "disconnected")") + + if connectionStatus == .connected { + logger.log("[autofill-extension] Successfully connected to Bitwarden (attempt \(attempt))") + break + } else { + if attempt < maxRetries { + logger.log("[autofill-extension] Retrying connection") + } else { + logger.error("[autofill-extension] Failed to connect after \(maxRetries) attempts, final status: \(connectionStatus == .connected ? "connected" : "disconnected")") + } + } } - logger.log("[autofill-extension] Connecting to Bitwarden over IPC") - - return MacOsProviderClient.connect() - }() + self.client = newClient + return newClient! + } + + // Setup the connection monitoring timer + private func setupConnectionMonitoring() { + // Check connection status every 1 second + connectionMonitorTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in + self?.checkConnectionStatus() + } + + // Make sure timer runs even when UI is busy + RunLoop.current.add(connectionMonitorTimer!, forMode: .common) + + // Initial check + checkConnectionStatus() + } + + // Check the connection status by calling into Rust + // If the connection is has changed and is now disconnected, cancel the request + private func checkConnectionStatus() { + // Only check connection status if the client has been initialized. + // Initialization is done asynchronously, so we might be called before it's ready + // In that case we just skip this check and wait for the next timer tick and re-check + guard let client = self.client else { + return + } + + // Get the current connection status from Rust + let currentStatus = client.getConnectionStatus() + + // Only post notification if state changed + if currentStatus != lastConnectionStatus { + if(currentStatus == .connected) { + logger.log("[autofill-extension] Connection status changed: Connected") + } else { + logger.log("[autofill-extension] Connection status changed: Disconnected") + } + + // Save the new status + lastConnectionStatus = currentStatus + + // If we just disconnected, try to cancel the request + if currentStatus == .disconnected { + self.extensionContext.cancelRequest(withError: BitwardenError.Internal("Bitwarden desktop app disconnected")) + } + } + } init() { logger = Logger(subsystem: "com.bitwarden.desktop.autofill-extension", category: "credential-provider") logger.log("[autofill-extension] initializing extension") - super.init(nibName: nil, bundle: nil) + super.init(nibName: "CredentialProviderViewController", bundle: nil) + + // Setup connection monitoring now that self is available + setupConnectionMonitoring() } required init?(coder: NSCoder) { @@ -76,45 +151,109 @@ class CredentialProviderViewController: ASCredentialProviderViewController { deinit { logger.log("[autofill-extension] deinitializing extension") - } - - - @IBAction func cancel(_ sender: AnyObject?) { - self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code: ASExtensionError.userCanceled.rawValue)) - } - - @IBAction func passwordSelected(_ sender: AnyObject?) { - let passwordCredential = ASPasswordCredential(user: "j_appleseed", password: "apple1234") - self.extensionContext.completeRequest(withSelectedCredential: passwordCredential, completionHandler: nil) - } - - private func getWindowPosition() -> Position { - let frame = self.view.window?.frame ?? .zero - let screenHeight = NSScreen.main?.frame.height ?? 0 - // frame.width and frame.height is always 0. Estimating works OK for now. - let estimatedWidth:CGFloat = 400; - let estimatedHeight:CGFloat = 200; - let centerX = Int32(round(frame.origin.x + estimatedWidth/2)) - let centerY = Int32(round(screenHeight - (frame.origin.y + estimatedHeight/2))) - - return Position(x: centerX, y:centerY) + // Stop the connection monitor timer + connectionMonitorTimer?.invalidate() + connectionMonitorTimer = nil } - override func loadView() { - let view = NSView() - // Hide the native window since we only need the IPC connection - view.isHidden = true - self.view = view + private func getWindowPosition() async -> Position { + let screenHeight = NSScreen.main?.frame.height ?? 1440 + + logger.log("[autofill-extension] position: Getting window position") + + // To whomever is reading this. Sorry. But MacOS couldn't give us an accurate window positioning, possibly due to animations + // So I added some retry logic, as well as a fall back to the mouse position which is likely at the sort of the right place. + // In my testing we often succed after 4-7 attempts. + // Wait for window frame to stabilize (animation to complete) + var lastFrame: CGRect = .zero + var stableCount = 0 + let requiredStableChecks = 3 + let maxAttempts = 20 + var attempts = 0 + + while stableCount < requiredStableChecks && attempts < maxAttempts { + let currentFrame: CGRect = self.view.window?.frame ?? .zero + + if currentFrame.equalTo(lastFrame) && !currentFrame.equalTo(.zero) { + stableCount += 1 + } else { + stableCount = 0 + lastFrame = currentFrame + } + + try? await Task.sleep(nanoseconds: 16_666_666) // ~60fps (16.67ms) + attempts += 1 + } + + let finalWindowFrame = self.view.window?.frame ?? .zero + logger.log("[autofill-extension] position: Final window frame: \(NSStringFromRect(finalWindowFrame))") + + // Use stabilized window frame if available, otherwise fallback to mouse position + if finalWindowFrame.origin.x != 0 || finalWindowFrame.origin.y != 0 { + let centerX = Int32(round(finalWindowFrame.origin.x)) + let centerY = Int32(round(screenHeight - finalWindowFrame.origin.y)) + logger.log("[autofill-extension] position: Using window position: x=\(centerX), y=\(centerY)") + return Position(x: centerX, y: centerY) + } else { + // Fallback to mouse position + let mouseLocation = NSEvent.mouseLocation + let mouseX = Int32(round(mouseLocation.x)) + let mouseY = Int32(round(screenHeight - mouseLocation.y)) + logger.log("[autofill-extension] position: Using mouse position fallback: x=\(mouseX), y=\(mouseY)") + return Position(x: mouseX, y: mouseY) + } } - + + override func viewDidLoad() { + super.viewDidLoad() + + // Initially hide the view + self.view.isHidden = true + } + + override func prepareInterfaceForExtensionConfiguration() { + // Show the configuration UI + self.view.isHidden = false + + // Set the localized message + statusLabel.stringValue = NSLocalizedString("autofillConfigurationMessage", comment: "Message shown when Bitwarden is enabled in system settings") + + // Send the native status request asynchronously + Task { + let client = await getClient() + client.sendNativeStatus(key: "request-sync", value: "") + } + + // Complete the configuration after 2 seconds + DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { [weak self] in + self?.extensionContext.completeExtensionConfigurationRequest() + } + } + + /* + In order to implement this method, we need to query the state of the vault to be unlocked and have one and only one matching credential so that it doesn't need to show ui. + If we do show UI, it's going to fail and disconnect after the platform timeout which is 3s. + For now we just claim to always need UI displayed. + */ override func provideCredentialWithoutUserInteraction(for credentialRequest: any ASCredentialRequest) { + let error = ASExtensionError(.userInteractionRequired) + self.extensionContext.cancelRequest(withError: error) + return + } + + /* + Implement this method if provideCredentialWithoutUserInteraction(for:) can fail with + ASExtensionError.userInteractionRequired. In this case, the system may present your extension's + UI and call this method. Show appropriate UI for authenticating the user then provide the password + by completing the extension request with the associated ASPasswordCredential. + */ + override func prepareInterfaceToProvideCredential(for credentialRequest: ASCredentialRequest) { let timeoutTimer = createTimer() - if let request = credentialRequest as? ASPasskeyCredentialRequest { if let passkeyIdentity = request.credentialIdentity as? ASPasskeyCredentialIdentity { - - logger.log("[autofill-extension] provideCredentialWithoutUserInteraction2(passkey) called \(request)") + + logger.log("[autofill-extension] prepareInterfaceToProvideCredential (passkey) called \(request)") class CallbackImpl: PreparePasskeyAssertionCallback { let ctx: ASCredentialProviderExtensionContext @@ -154,18 +293,25 @@ class CredentialProviderViewController: ASCredentialProviderViewController { UserVerification.discouraged } - let req = PasskeyAssertionWithoutUserInterfaceRequest( - rpId: passkeyIdentity.relyingPartyIdentifier, - credentialId: passkeyIdentity.credentialID, - userName: passkeyIdentity.userName, - userHandle: passkeyIdentity.userHandle, - recordIdentifier: passkeyIdentity.recordIdentifier, - clientDataHash: request.clientDataHash, - userVerification: userVerification, - windowXy: self.getWindowPosition() - ) - - self.client.preparePasskeyAssertionWithoutUserInterface(request: req, callback: CallbackImpl(self.extensionContext, self.logger, timeoutTimer)) + /* + We're still using the old request type here, because we're sending the same data, we're expecting a single credential to be used + */ + Task { + let windowPosition = await self.getWindowPosition() + let req = PasskeyAssertionWithoutUserInterfaceRequest( + rpId: passkeyIdentity.relyingPartyIdentifier, + credentialId: passkeyIdentity.credentialID, + userName: passkeyIdentity.userName, + userHandle: passkeyIdentity.userHandle, + recordIdentifier: passkeyIdentity.recordIdentifier, + clientDataHash: request.clientDataHash, + userVerification: userVerification, + windowXy: windowPosition + ) + + let client = await getClient() + client.preparePasskeyAssertionWithoutUserInterface(request: req, callback: CallbackImpl(self.extensionContext, self.logger, timeoutTimer)) + } return } } @@ -176,16 +322,6 @@ class CredentialProviderViewController: ASCredentialProviderViewController { self.extensionContext.cancelRequest(withError: BitwardenError.Internal("Invalid authentication request")) } - /* - Implement this method if provideCredentialWithoutUserInteraction(for:) can fail with - ASExtensionError.userInteractionRequired. In this case, the system may present your extension's - UI and call this method. Show appropriate UI for authenticating the user then provide the password - by completing the extension request with the associated ASPasswordCredential. - - override func prepareInterfaceToProvideCredential(for credentialIdentity: ASPasswordCredentialIdentity) { - } - */ - private func createTimer() -> DispatchWorkItem { // Create a timer for 600 second timeout let timeoutTimer = DispatchWorkItem { [weak self] in @@ -246,18 +382,32 @@ class CredentialProviderViewController: ASCredentialProviderViewController { UserVerification.discouraged } - let req = PasskeyRegistrationRequest( - rpId: passkeyIdentity.relyingPartyIdentifier, - userName: passkeyIdentity.userName, - userHandle: passkeyIdentity.userHandle, - clientDataHash: request.clientDataHash, - userVerification: userVerification, - supportedAlgorithms: request.supportedAlgorithms.map{ Int32($0.rawValue) }, - windowXy: self.getWindowPosition() - ) + // Convert excluded credentials to an array of credential IDs + var excludedCredentialIds: [Data] = [] + if #available(macOSApplicationExtension 15.0, *) { + if let excludedCreds = request.excludedCredentials { + excludedCredentialIds = excludedCreds.map { $0.credentialID } + } + } + logger.log("[autofill-extension] prepareInterface(passkey) calling preparePasskeyRegistration") - self.client.preparePasskeyRegistration(request: req, callback: CallbackImpl(self.extensionContext, self.logger, timeoutTimer)) + Task { + let windowPosition = await self.getWindowPosition() + let req = PasskeyRegistrationRequest( + rpId: passkeyIdentity.relyingPartyIdentifier, + userName: passkeyIdentity.userName, + userHandle: passkeyIdentity.userHandle, + clientDataHash: request.clientDataHash, + userVerification: userVerification, + supportedAlgorithms: request.supportedAlgorithms.map{ Int32($0.rawValue) }, + windowXy: windowPosition, + excludedCredentials: excludedCredentialIds + ) + + let client = await getClient() + client.preparePasskeyRegistration(request: req, callback: CallbackImpl(self.extensionContext, self.logger, timeoutTimer)) + } return } } @@ -310,18 +460,22 @@ class CredentialProviderViewController: ASCredentialProviderViewController { UserVerification.discouraged } - let req = PasskeyAssertionRequest( - rpId: requestParameters.relyingPartyIdentifier, - clientDataHash: requestParameters.clientDataHash, - userVerification: userVerification, - allowedCredentials: requestParameters.allowedCredentials, - windowXy: self.getWindowPosition() - //extensionInput: requestParameters.extensionInput, - ) - let timeoutTimer = createTimer() - self.client.preparePasskeyAssertion(request: req, callback: CallbackImpl(self.extensionContext, self.logger, timeoutTimer)) + Task { + let windowPosition = await self.getWindowPosition() + let req = PasskeyAssertionRequest( + rpId: requestParameters.relyingPartyIdentifier, + clientDataHash: requestParameters.clientDataHash, + userVerification: userVerification, + allowedCredentials: requestParameters.allowedCredentials, + windowXy: windowPosition + //extensionInput: requestParameters.extensionInput, // We don't support extensions yet + ) + + let client = await getClient() + client.preparePasskeyAssertion(request: req, callback: CallbackImpl(self.extensionContext, self.logger, timeoutTimer)) + } return } } diff --git a/apps/desktop/macos/autofill-extension/Info.plist b/apps/desktop/macos/autofill-extension/Info.plist index 539cfa35b9d..7de0d4d152b 100644 --- a/apps/desktop/macos/autofill-extension/Info.plist +++ b/apps/desktop/macos/autofill-extension/Info.plist @@ -10,9 +10,9 @@ ProvidesPasskeys + ShowsConfigurationUI + - ASCredentialProviderExtensionShowsConfigurationUI - NSExtensionPointIdentifier com.apple.authentication-services-credential-provider-ui diff --git a/apps/desktop/macos/autofill-extension/autofill_extension.entitlements b/apps/desktop/macos/autofill-extension/autofill_extension.entitlements index 86c7195768e..d5c7b8a2cc8 100644 --- a/apps/desktop/macos/autofill-extension/autofill_extension.entitlements +++ b/apps/desktop/macos/autofill-extension/autofill_extension.entitlements @@ -2,11 +2,9 @@ - com.apple.developer.authentication-services.autofill-credential-provider - - com.apple.security.app-sandbox - - com.apple.security.application-groups + com.apple.security.app-sandbox + + com.apple.security.application-groups LTZ2PFU5D6.com.bitwarden.desktop diff --git a/apps/desktop/macos/autofill-extension/autofill_extension_enabled.entitlements b/apps/desktop/macos/autofill-extension/autofill_extension_enabled.entitlements new file mode 100644 index 00000000000..49fda8f8af8 --- /dev/null +++ b/apps/desktop/macos/autofill-extension/autofill_extension_enabled.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.application-groups + + LTZ2PFU5D6.com.bitwarden.desktop + + com.apple.developer.authentication-services.autofill-credential-provider + + + diff --git a/apps/desktop/macos/autofill-extension/bitwarden-icon.png b/apps/desktop/macos/autofill-extension/bitwarden-icon.png new file mode 100644 index 00000000000..9a05bc7bbdd Binary files /dev/null and b/apps/desktop/macos/autofill-extension/bitwarden-icon.png differ diff --git a/apps/desktop/macos/autofill-extension/en.lproj/Localizable.strings b/apps/desktop/macos/autofill-extension/en.lproj/Localizable.strings new file mode 100644 index 00000000000..95730dff286 --- /dev/null +++ b/apps/desktop/macos/autofill-extension/en.lproj/Localizable.strings @@ -0,0 +1,2 @@ +/* Message shown during passkey configuration */ +"autofillConfigurationMessage" = "Enabling Bitwarden..."; diff --git a/apps/desktop/macos/desktop.xcodeproj/project.pbxproj b/apps/desktop/macos/desktop.xcodeproj/project.pbxproj index ff257097f26..c3cb34b6bea 100644 --- a/apps/desktop/macos/desktop.xcodeproj/project.pbxproj +++ b/apps/desktop/macos/desktop.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 3368DB392C654B8100896B75 /* BitwardenMacosProviderFFI.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3368DB382C654B8100896B75 /* BitwardenMacosProviderFFI.xcframework */; }; 3368DB3B2C654F3800896B75 /* BitwardenMacosProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3368DB3A2C654F3800896B75 /* BitwardenMacosProvider.swift */; }; + 9AE299092DF9D82E00AAE454 /* bitwarden-icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 9AE299082DF9D82E00AAE454 /* bitwarden-icon.png */; }; + 9AE299122DFB57A200AAE454 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9AE2990D2DFB57A200AAE454 /* Localizable.strings */; }; E1DF713F2B342F6900F29026 /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E1DF713E2B342F6900F29026 /* AuthenticationServices.framework */; }; E1DF71422B342F6900F29026 /* CredentialProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1DF71412B342F6900F29026 /* CredentialProviderViewController.swift */; }; E1DF71452B342F6900F29026 /* CredentialProviderViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = E1DF71432B342F6900F29026 /* CredentialProviderViewController.xib */; }; @@ -18,6 +20,8 @@ 3368DB382C654B8100896B75 /* BitwardenMacosProviderFFI.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = BitwardenMacosProviderFFI.xcframework; path = ../desktop_native/macos_provider/BitwardenMacosProviderFFI.xcframework; sourceTree = ""; }; 3368DB3A2C654F3800896B75 /* BitwardenMacosProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitwardenMacosProvider.swift; sourceTree = ""; }; 968ED08A2C52A47200FFFEE6 /* ReleaseAppStore.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ReleaseAppStore.xcconfig; sourceTree = ""; }; + 9AE299082DF9D82E00AAE454 /* bitwarden-icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bitwarden-icon.png"; sourceTree = ""; }; + 9AE2990C2DFB57A200AAE454 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Localizable.strings; sourceTree = ""; }; D83832AB2D67B9AE003FB9F8 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; D83832AD2D67B9D0003FB9F8 /* ReleaseDeveloper.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ReleaseDeveloper.xcconfig; sourceTree = ""; }; E1DF713C2B342F6900F29026 /* autofill-extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "autofill-extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -41,6 +45,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 9AE2990E2DFB57A200AAE454 /* en.lproj */ = { + isa = PBXGroup; + children = ( + 9AE2990D2DFB57A200AAE454 /* Localizable.strings */, + ); + path = en.lproj; + sourceTree = ""; + }; E1DF711D2B342E2800F29026 = { isa = PBXGroup; children = ( @@ -73,6 +85,8 @@ E1DF71402B342F6900F29026 /* autofill-extension */ = { isa = PBXGroup; children = ( + 9AE2990E2DFB57A200AAE454 /* en.lproj */, + 9AE299082DF9D82E00AAE454 /* bitwarden-icon.png */, 3368DB3A2C654F3800896B75 /* BitwardenMacosProvider.swift */, E1DF71412B342F6900F29026 /* CredentialProviderViewController.swift */, E1DF71432B342F6900F29026 /* CredentialProviderViewController.xib */, @@ -124,6 +138,7 @@ knownRegions = ( en, Base, + sv, ); mainGroup = E1DF711D2B342E2800F29026; productRefGroup = E1DF71272B342E2800F29026 /* Products */; @@ -141,6 +156,8 @@ buildActionMask = 2147483647; files = ( E1DF71452B342F6900F29026 /* CredentialProviderViewController.xib in Resources */, + 9AE299122DFB57A200AAE454 /* Localizable.strings in Resources */, + 9AE299092DF9D82E00AAE454 /* bitwarden-icon.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -159,6 +176,14 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ + 9AE2990D2DFB57A200AAE454 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 9AE2990C2DFB57A200AAE454 /* en */, + ); + name = Localizable.strings; + sourceTree = ""; + }; E1DF71432B342F6900F29026 /* CredentialProviderViewController.xib */ = { isa = PBXVariantGroup; children = ( @@ -231,7 +256,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D83832AD2D67B9D0003FB9F8 /* ReleaseDeveloper.xcconfig */; buildSettings = { - CODE_SIGN_ENTITLEMENTS = "autofill-extension/autofill_extension.entitlements"; + CODE_SIGN_ENTITLEMENTS = "autofill-extension/autofill_extension_enabled.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; CODE_SIGN_STYLE = Manual; @@ -384,7 +409,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = D83832AB2D67B9AE003FB9F8 /* Debug.xcconfig */; buildSettings = { - CODE_SIGN_ENTITLEMENTS = "autofill-extension/autofill_extension.entitlements"; + CODE_SIGN_ENTITLEMENTS = "autofill-extension/autofill_extension_enabled.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; CODE_SIGN_STYLE = Manual; diff --git a/apps/desktop/macos/desktop.xcodeproj/xcshareddata/xcschemes/autofill-extension.xcscheme b/apps/desktop/macos/desktop.xcodeproj/xcshareddata/xcschemes/autofill-extension.xcscheme new file mode 100644 index 00000000000..18357be4570 --- /dev/null +++ b/apps/desktop/macos/desktop.xcodeproj/xcshareddata/xcschemes/autofill-extension.xcscheme @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/desktop/native-messaging-test-runner/package-lock.json b/apps/desktop/native-messaging-test-runner/package-lock.json index 9ad1ffb3ec0..f3650b3aaf6 100644 --- a/apps/desktop/native-messaging-test-runner/package-lock.json +++ b/apps/desktop/native-messaging-test-runner/package-lock.json @@ -15,11 +15,11 @@ "@bitwarden/storage-core": "file:../../../libs/storage-core", "module-alias": "2.2.3", "ts-node": "10.9.2", - "uuid": "13.0.0", - "yargs": "18.0.0" + "uuid": "9.0.1", + "yargs": "17.7.2" }, "devDependencies": { - "@types/node": "22.19.1", + "@types/node": "22.19.3", "typescript": "5.4.2" } }, @@ -117,11 +117,10 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.19.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", - "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", + "version": "22.19.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", + "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -150,30 +149,6 @@ "node": ">=0.4.0" } }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -181,19 +156,83 @@ "license": "MIT" }, "node_modules/cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", - "license": "ISC", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dependencies": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=20" + "node": ">=12" } }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -209,12 +248,6 @@ "node": ">=0.3.1" } }, - "node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "license": "MIT" - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -233,16 +266,12 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", - "license": "MIT", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/make-error": { @@ -257,36 +286,49 @@ "integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==", "license": "MIT" }, - "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dependencies": { - "ansi-regex": "^6.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=12" + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "engines": { + "node": ">=8" } }, "node_modules/ts-node": { @@ -337,7 +379,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -353,16 +394,15 @@ "license": "MIT" }, "node_modules/uuid": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", - "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], - "license": "MIT", "bin": { - "uuid": "dist-node/bin/uuid" + "uuid": "dist/bin/uuid" } }, "node_modules/v8-compile-cache-lib": { @@ -371,23 +411,6 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "license": "MIT" }, - "node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -398,29 +421,28 @@ } }, "node_modules/yargs": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", - "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", - "license": "MIT", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dependencies": { - "cliui": "^9.0.1", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", - "string-width": "^7.2.0", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^22.0.0" + "yargs-parser": "^21.1.1" }, "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", - "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", - "license": "ISC", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" + "node": ">=12" } }, "node_modules/yn": { diff --git a/apps/desktop/native-messaging-test-runner/package.json b/apps/desktop/native-messaging-test-runner/package.json index 21a6ba3626a..cdc9158bdca 100644 --- a/apps/desktop/native-messaging-test-runner/package.json +++ b/apps/desktop/native-messaging-test-runner/package.json @@ -20,17 +20,23 @@ "@bitwarden/logging": "dist/libs/logging/src", "module-alias": "2.2.3", "ts-node": "10.9.2", - "uuid": "13.0.0", - "yargs": "18.0.0" + "uuid": "9.0.1", + "yargs": "17.7.2" }, "devDependencies": { - "@types/node": "22.19.1", + "@types/node": "22.19.3", "typescript": "5.4.2" }, "_moduleAliases": { "@bitwarden/common": "dist/libs/common/src", "@bitwarden/node/services/node-crypto-function.service": "dist/libs/node/src/services/node-crypto-function.service", "@bitwarden/storage-core": "dist/libs/storage-core/src", - "@bitwarden/logging": "dist/libs/logging/src" + "@bitwarden/logging": "dist/libs/logging/src", + "@bitwarden/client-type": "dist/libs/client-type/src", + "@bitwarden/state": "dist/libs/state/src", + "@bitwarden/state-internal": "dist/libs/state-internal/src", + "@bitwarden/messaging": "dist/libs/messaging/src", + "@bitwarden/guid": "dist/libs/guid/src", + "@bitwarden/serialization": "dist/libs/serialization/src" } } diff --git a/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-create.ts b/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-create.ts index 8d2d734677a..46021eb72ca 100644 --- a/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-create.ts +++ b/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-create.ts @@ -11,6 +11,7 @@ import { NativeMessagingVersion } from "@bitwarden/common/enums"; import { CredentialCreatePayload } from "../../../src/models/native-messaging/encrypted-message-payloads/credential-create-payload"; import { LogUtils } from "../log-utils"; import NativeMessageService from "../native-message.service"; +import { TestRunnerSdkLoadService } from "../sdk-load.service"; import * as config from "../variables"; const argv: any = yargs(hideBin(process.argv)).option("name", { @@ -25,6 +26,10 @@ const { name } = argv; // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises (async () => { + // Initialize SDK before using crypto functions + const sdkLoadService = new TestRunnerSdkLoadService(); + await sdkLoadService.loadAndInit(); + const nativeMessageService = new NativeMessageService(NativeMessagingVersion.One); // Handshake LogUtils.logInfo("Sending Handshake"); @@ -42,7 +47,10 @@ const { name } = argv; // Get active account userId const status = await nativeMessageService.checkStatus(handshakeResponse.sharedKey); - const activeUser = status.payload.filter((a) => a.active === true && a.status === "unlocked")[0]; + const activeUser = status.payload.filter( + (a: { active: boolean; status: string; id: string }) => + a.active === true && a.status === "unlocked", + )[0]; if (activeUser === undefined) { LogUtils.logError("No active or unlocked user"); } diff --git a/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-retrieval.ts b/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-retrieval.ts index 2e55afbb36f..70b0bad9d66 100644 --- a/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-retrieval.ts +++ b/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-retrieval.ts @@ -7,6 +7,7 @@ import { NativeMessagingVersion } from "@bitwarden/common/enums"; import { LogUtils } from "../log-utils"; import NativeMessageService from "../native-message.service"; +import { TestRunnerSdkLoadService } from "../sdk-load.service"; import * as config from "../variables"; const argv: any = yargs(hideBin(process.argv)).option("uri", { @@ -21,6 +22,10 @@ const { uri } = argv; // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises (async () => { + // Initialize SDK before using crypto functions + const sdkLoadService = new TestRunnerSdkLoadService(); + await sdkLoadService.loadAndInit(); + const nativeMessageService = new NativeMessageService(NativeMessagingVersion.One); // Handshake LogUtils.logInfo("Sending Handshake"); diff --git a/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-update.ts b/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-update.ts index 93598bf9eef..7ba5eef143a 100644 --- a/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-update.ts +++ b/apps/desktop/native-messaging-test-runner/src/commands/bw-credential-update.ts @@ -11,6 +11,7 @@ import { NativeMessagingVersion } from "@bitwarden/common/enums"; import { CredentialUpdatePayload } from "../../../src/models/native-messaging/encrypted-message-payloads/credential-update-payload"; import { LogUtils } from "../log-utils"; import NativeMessageService from "../native-message.service"; +import { TestRunnerSdkLoadService } from "../sdk-load.service"; import * as config from "../variables"; // Command line arguments @@ -49,6 +50,10 @@ const { name, username, password, uri, credentialId } = argv; // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises (async () => { + // Initialize SDK before using crypto functions + const sdkLoadService = new TestRunnerSdkLoadService(); + await sdkLoadService.loadAndInit(); + const nativeMessageService = new NativeMessageService(NativeMessagingVersion.One); // Handshake LogUtils.logInfo("Sending Handshake"); @@ -67,7 +72,10 @@ const { name, username, password, uri, credentialId } = argv; // Get active account userId const status = await nativeMessageService.checkStatus(handshakeResponse.sharedKey); - const activeUser = status.payload.filter((a) => a.active === true && a.status === "unlocked")[0]; + const activeUser = status.payload.filter( + (a: { active: boolean; status: string; id: string }) => + a.active === true && a.status === "unlocked", + )[0]; if (activeUser === undefined) { LogUtils.logError("No active or unlocked user"); } diff --git a/apps/desktop/native-messaging-test-runner/src/commands/bw-generate-password.ts b/apps/desktop/native-messaging-test-runner/src/commands/bw-generate-password.ts index da914c67b4a..a0b449b02c7 100644 --- a/apps/desktop/native-messaging-test-runner/src/commands/bw-generate-password.ts +++ b/apps/desktop/native-messaging-test-runner/src/commands/bw-generate-password.ts @@ -7,6 +7,7 @@ import { NativeMessagingVersion } from "@bitwarden/common/enums"; import { LogUtils } from "../log-utils"; import NativeMessageService from "../native-message.service"; +import { TestRunnerSdkLoadService } from "../sdk-load.service"; import * as config from "../variables"; const argv: any = yargs(hideBin(process.argv)).option("userId", { @@ -21,6 +22,10 @@ const { userId } = argv; // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises (async () => { + // Initialize SDK before using crypto functions + const sdkLoadService = new TestRunnerSdkLoadService(); + await sdkLoadService.loadAndInit(); + const nativeMessageService = new NativeMessageService(NativeMessagingVersion.One); // Handshake LogUtils.logInfo("Sending Handshake"); diff --git a/apps/desktop/native-messaging-test-runner/src/commands/bw-handshake.ts b/apps/desktop/native-messaging-test-runner/src/commands/bw-handshake.ts index 2ba5d469aaa..77a6ac652ad 100644 --- a/apps/desktop/native-messaging-test-runner/src/commands/bw-handshake.ts +++ b/apps/desktop/native-messaging-test-runner/src/commands/bw-handshake.ts @@ -4,11 +4,16 @@ import { NativeMessagingVersion } from "@bitwarden/common/enums"; import { LogUtils } from "../log-utils"; import NativeMessageService from "../native-message.service"; +import { TestRunnerSdkLoadService } from "../sdk-load.service"; import * as config from "../variables"; // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises (async () => { + // Initialize SDK before using crypto functions + const sdkLoadService = new TestRunnerSdkLoadService(); + await sdkLoadService.loadAndInit(); + const nativeMessageService = new NativeMessageService(NativeMessagingVersion.One); const response = await nativeMessageService.sendHandshake( diff --git a/apps/desktop/native-messaging-test-runner/src/commands/bw-status.ts b/apps/desktop/native-messaging-test-runner/src/commands/bw-status.ts index 466e3fca52b..7014c4713c2 100644 --- a/apps/desktop/native-messaging-test-runner/src/commands/bw-status.ts +++ b/apps/desktop/native-messaging-test-runner/src/commands/bw-status.ts @@ -4,11 +4,16 @@ import { NativeMessagingVersion } from "@bitwarden/common/enums"; import { LogUtils } from "../log-utils"; import NativeMessageService from "../native-message.service"; +import { TestRunnerSdkLoadService } from "../sdk-load.service"; import * as config from "../variables"; // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. // eslint-disable-next-line @typescript-eslint/no-floating-promises (async () => { + // Initialize SDK before using crypto functions + const sdkLoadService = new TestRunnerSdkLoadService(); + await sdkLoadService.loadAndInit(); + const nativeMessageService = new NativeMessageService(NativeMessagingVersion.One); LogUtils.logInfo("Sending Handshake"); diff --git a/apps/desktop/native-messaging-test-runner/src/deferred.ts b/apps/desktop/native-messaging-test-runner/src/deferred.ts index da34d80ebb2..d3350ade5a4 100644 --- a/apps/desktop/native-messaging-test-runner/src/deferred.ts +++ b/apps/desktop/native-messaging-test-runner/src/deferred.ts @@ -4,8 +4,8 @@ // while allowing an unrelated event to fulfill it elsewhere. export default class Deferred { private promise: Promise; - private resolver: (T?) => void; - private rejecter: (Error?) => void; + private resolver!: (value?: T) => void; + private rejecter!: (reason?: Error) => void; constructor() { this.promise = new Promise((resolve, reject) => { diff --git a/apps/desktop/native-messaging-test-runner/src/ipc.service.ts b/apps/desktop/native-messaging-test-runner/src/ipc.service.ts index d8616e9757a..adb1e693d24 100644 --- a/apps/desktop/native-messaging-test-runner/src/ipc.service.ts +++ b/apps/desktop/native-messaging-test-runner/src/ipc.service.ts @@ -13,7 +13,7 @@ import { race } from "./race"; const DEFAULT_MESSAGE_TIMEOUT = 10 * 1000; // 10 seconds -export type MessageHandler = (MessageCommon) => void; +export type MessageHandler = (message: MessageCommon) => void; // FIXME: update to use a const object instead of a typescript enum // eslint-disable-next-line @bitwarden/platform/no-enums diff --git a/apps/desktop/native-messaging-test-runner/src/race.ts b/apps/desktop/native-messaging-test-runner/src/race.ts index 5ed778aa35b..a1c6cb04c5f 100644 --- a/apps/desktop/native-messaging-test-runner/src/race.ts +++ b/apps/desktop/native-messaging-test-runner/src/race.ts @@ -8,8 +8,8 @@ export const race = ({ promise: Promise; timeout: number; error?: Error; -}) => { - let timer = null; +}): Promise => { + let timer: NodeJS.Timeout | null = null; // Similar to Promise.all, but instead of waiting for all, it resolves once one promise finishes. // Using this so we can reject if the timeout threshold is hit @@ -20,7 +20,9 @@ export const race = ({ }), promise.then((value) => { - clearTimeout(timer); + if (timer != null) { + clearTimeout(timer); + } return value; }), ]); diff --git a/apps/desktop/native-messaging-test-runner/src/sdk-load.service.ts b/apps/desktop/native-messaging-test-runner/src/sdk-load.service.ts new file mode 100644 index 00000000000..d3f8289dffb --- /dev/null +++ b/apps/desktop/native-messaging-test-runner/src/sdk-load.service.ts @@ -0,0 +1,22 @@ +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; + +import { LogUtils } from "./log-utils"; + +/** + * SDK Load Service for the native messaging test runner. + * For Node.js environments, the SDK's Node.js build automatically loads WASM from the filesystem. + * No additional initialization is needed. + */ +export class TestRunnerSdkLoadService extends SdkLoadService { + async load(): Promise { + // In Node.js, @bitwarden/sdk-internal automatically loads the WASM file + // from node/bitwarden_wasm_internal_bg.wasm using fs.readFileSync. + // No explicit loading is required. + } + + override async loadAndInit(): Promise { + LogUtils.logInfo("Initializing SDK"); + await super.loadAndInit(); + LogUtils.logSuccess("SDK initialized"); + } +} diff --git a/apps/desktop/native-messaging-test-runner/tsconfig.json b/apps/desktop/native-messaging-test-runner/tsconfig.json index dcdf992f986..708559efc07 100644 --- a/apps/desktop/native-messaging-test-runner/tsconfig.json +++ b/apps/desktop/native-messaging-test-runner/tsconfig.json @@ -1,4 +1,5 @@ { + "extends": "../../../tsconfig.base.json", "compilerOptions": { "baseUrl": "./", "outDir": "dist", @@ -18,7 +19,13 @@ "@bitwarden/auth/*": ["../../../libs/auth/src/*"], "@bitwarden/common/*": ["../../../libs/common/src/*"], "@bitwarden/key-management": ["../../../libs/key-management/src/"], - "@bitwarden/node/*": ["../../../libs/node/src/*"] + "@bitwarden/node/*": ["../../../libs/node/src/*"], + "@bitwarden/state": ["../../../libs/state/src/index.ts"], + "@bitwarden/state-internal": ["../../../libs/state-internal/src/index.ts"], + "@bitwarden/client-type": ["../../../libs/client-type/src/index.ts"], + "@bitwarden/messaging": ["../../../libs/messaging/src/index.ts"], + "@bitwarden/guid": ["../../../libs/guid/src/index.ts"], + "@bitwarden/serialization": ["../../../libs/serialization/src/index.ts"] }, "plugins": [ { @@ -26,5 +33,6 @@ } ] }, - "exclude": ["node_modules"] + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] } diff --git a/apps/desktop/package.json b/apps/desktop/package.json index bb8118cb7eb..aabf26e76bd 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@bitwarden/desktop", "description": "A secure and free password manager for all of your devices.", - "version": "2025.12.0", + "version": "2026.1.0", "keywords": [ "bitwarden", "password", @@ -18,6 +18,7 @@ "scripts": { "postinstall": "electron-rebuild", "start": "cross-env ELECTRON_IS_DEV=0 ELECTRON_NO_UPDATER=1 electron ./build", + "build-native-macos": "cd desktop_native && ./macos_provider/build.sh && node build.js cross-platform", "build-native": "cd desktop_native && node build.js", "build": "concurrently -n Main,Rend,Prel -c yellow,cyan \"npm run build:main\" \"npm run build:renderer\" \"npm run build:preload\"", "build:dev": "concurrently -n Main,Rend,Prel -c yellow,cyan \"npm run build:main:dev\" \"npm run build:renderer:dev\" \"npm run build:preload:dev\"", @@ -28,7 +29,7 @@ "build:macos-extension:mas": "./desktop_native/macos_provider/build.sh && node scripts/build-macos-extension.js mas", "build:macos-extension:masdev": "./desktop_native/macos_provider/build.sh && node scripts/build-macos-extension.js mas-dev", "build:main": "cross-env NODE_ENV=production webpack --config webpack.config.js --config-name main", - "build:main:dev": "npm run build-native && cross-env NODE_ENV=development webpack --config webpack.config.js --config-name main", + "build:main:dev": "cross-env NODE_ENV=development webpack --config webpack.config.js --config-name main", "build:main:watch": "npm run build-native && cross-env NODE_ENV=development webpack --config webpack.config.js --config-name main --watch", "build:renderer": "cross-env NODE_ENV=production webpack --config webpack.config.js --config-name renderer", "build:renderer:dev": "cross-env NODE_ENV=development webpack --config webpack.config.js --config-name renderer", @@ -39,33 +40,29 @@ "clean:dist": "rimraf ./dist", "pack:dir": "npm run clean:dist && electron-builder --dir -p never", "pack:lin:flatpak": "flatpak-builder --repo=../../.flatpak-repo ../../.flatpak ./resources/com.bitwarden.desktop.devel.yaml --install-deps-from=flathub --force-clean && flatpak build-bundle ../../.flatpak-repo/ ./dist/com.bitwarden.desktop.flatpak com.bitwarden.desktop", - "pack:lin": "npm run clean:dist && electron-builder --linux --x64 -p never && export SNAP_FILE=$(realpath ./dist/bitwarden_*.snap) && unsquashfs -d ./dist/tmp-snap/ $SNAP_FILE && mkdir -p ./dist/tmp-snap/meta/polkit/ && cp ./resources/com.bitwarden.desktop.policy ./dist/tmp-snap/meta/polkit/polkit.com.bitwarden.desktop.policy && rm $SNAP_FILE && snap pack --compression=lzo ./dist/tmp-snap/ && mv ./*.snap ./dist/ && rm -rf ./dist/tmp-snap/ && tar -czvf ./dist/bitwarden_desktop_x64.tar.gz -C ./dist/linux-unpacked/ .", - "pack:lin:arm64": "npm run clean:dist && electron-builder --linux --arm64 -p never && export SNAP_FILE=$(realpath ./dist/bitwarden_*.snap) && unsquashfs -d ./dist/tmp-snap/ $SNAP_FILE && mkdir -p ./dist/tmp-snap/meta/polkit/ && cp ./resources/com.bitwarden.desktop.policy ./dist/tmp-snap/meta/polkit/polkit.com.bitwarden.desktop.policy && rm $SNAP_FILE && snap pack --compression=lzo ./dist/tmp-snap/ && mv ./*.snap ./dist/ && rm -rf ./dist/tmp-snap/ && tar -czvf ./dist/bitwarden_desktop_arm64.tar.gz -C ./dist/linux-arm64-unpacked/ .", + "pack:lin": "npm run clean:dist && electron-builder --linux --x64 -p never && export SNAP_FILE=$(realpath ./dist/bitwarden_*.snap) && unsquashfs -d ./dist/tmp-snap/ $SNAP_FILE && mkdir -p ./dist/tmp-snap/meta/polkit/ && cp ./resources/com.bitwarden.desktop.policy ./dist/tmp-snap/meta/polkit/polkit.com.bitwarden.desktop.policy && rm $SNAP_FILE && snap pack --compression=lzo ./dist/tmp-snap/ && mv ./*.snap ./dist/ && rm -rf ./dist/tmp-snap/ && cp ./resources/com.bitwarden.desktop.desktop ./dist/linux-unpacked/resources && cp -r ./resources/icons ./dist/linux-unpacked/resources && tar -czvf ./dist/bitwarden_desktop_x64.tar.gz -C ./dist/linux-unpacked/ .", + "pack:lin:arm64": "npm run clean:dist && electron-builder --linux --arm64 -p never && export SNAP_FILE=$(realpath ./dist/bitwarden_*.snap) && unsquashfs -d ./dist/tmp-snap/ $SNAP_FILE && mkdir -p ./dist/tmp-snap/meta/polkit/ && cp ./resources/com.bitwarden.desktop.policy ./dist/tmp-snap/meta/polkit/polkit.com.bitwarden.desktop.policy && rm $SNAP_FILE && snap pack --compression=lzo ./dist/tmp-snap/ && mv ./*.snap ./dist/ && rm -rf ./dist/tmp-snap/ && cp ./resources/com.bitwarden.desktop.desktop ./dist/linux-arm64-unpacked/resources && cp -r ./resources/icons ./dist/linux-arm64-unpacked/resources && tar -czvf ./dist/bitwarden_desktop_arm64.tar.gz -C ./dist/linux-arm64-unpacked/ .", "pack:mac": "npm run clean:dist && electron-builder --mac --universal -p never", "pack:mac:with-extension": "npm run clean:dist && npm run build:macos-extension:mac && electron-builder --mac --universal -p never", "pack:mac:arm64": "npm run clean:dist && electron-builder --mac --arm64 -p never", - "pack:mac:mas": "npm run clean:dist && electron-builder --mac mas --universal -p never", - "pack:mac:mas:with-extension": "npm run clean:dist && npm run build:macos-extension:mas && electron-builder --mac mas --universal -p never", - "pack:mac:masdev": "npm run clean:dist && electron-builder --mac mas-dev --universal -p never", - "pack:mac:masdev:with-extension": "npm run clean:dist && npm run build:macos-extension:masdev && electron-builder --mac mas-dev --universal -p never", - "pack:win": "npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p never -c.win.signtoolOptions.certificateSubjectName=\"8bit Solutions LLC\"", - "pack:win:beta": "npm run clean:dist && electron-builder --config electron-builder.beta.json --win --x64 --arm64 --ia32 -p never -c.win.signtoolOptions.certificateSubjectName=\"8bit Solutions LLC\"", + "pack:mac:mas": "npm run clean:dist && npm run build:macos-extension:mas && electron-builder --mac mas --universal -p never", + "pack:mac:masdev": "npm run clean:dist && electron-builder --mac mas-dev --universal -p never -c.mac.identity=null -c.mas.identity=$CSC_NAME -c.mas.provisioningProfile=bitwarden_desktop_developer_id.provisionprofile -c.mas.entitlements=resources/entitlements.mas.autofill-enabled.plist", + "pack:local:mac": "npm run clean:dist && npm run build:macos-extension:masdev && electron-builder --mac mas-dev --universal -p never -c.mac.provisioningProfile=\"\" -c.mas.provisioningProfile=\"\"", + "pack:win": "npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p never", + "pack:win:beta": "npm run clean:dist && electron-builder --config electron-builder.beta.json --win --x64 --arm64 --ia32 -p never", "pack:win:ci": "npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p never", "dist:dir": "npm run build && npm run pack:dir", "dist:lin": "npm run build && npm run pack:lin", "dist:lin:arm64": "npm run build && npm run pack:lin:arm64", "dist:mac": "npm run build && npm run pack:mac", - "dist:mac:with-extension": "npm run build && npm run pack:mac:with-extension", "dist:mac:mas": "npm run build && npm run pack:mac:mas", - "dist:mac:mas:with-extension": "npm run build && npm run pack:mac:mas:with-extension", - "dist:mac:masdev": "npm run build:dev && npm run pack:mac:masdev", - "dist:mac:masdev:with-extension": "npm run build:dev && npm run pack:mac:masdev:with-extension", + "dist:mac:masdev": "npm run build && npm run pack:mac:masdev", "dist:win": "npm run build && npm run pack:win", "dist:win:ci": "npm run build && npm run pack:win:ci", "publish:lin": "npm run build && npm run clean:dist && electron-builder --linux --x64 -p always", "publish:mac": "npm run build && npm run clean:dist && electron-builder --mac -p always", "publish:mac:mas": "npm run dist:mac:mas && npm run upload:mas", - "publish:win": "npm run build && npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p always -c.win.signtoolOptions.certificateSubjectName=\"8bit Solutions LLC\"", + "publish:win": "npm run build && npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p always", "publish:win:dev": "npm run build:dev && npm run clean:dist && electron-builder --win --x64 --arm64 --ia32 -p always", "upload:mas": "xcrun altool --upload-app --type osx --file \"$(find ./dist/mas-universal/Bitwarden*.pkg)\" --apiKey $APP_STORE_CONNECT_AUTH_KEY --apiIssuer $APP_STORE_CONNECT_TEAM_ISSUER", "test": "jest", diff --git a/apps/desktop/resources/com.bitwarden.desktop.devel.yaml b/apps/desktop/resources/com.bitwarden.desktop.devel.yaml index e72df98e22b..0f6e3ea370d 100644 --- a/apps/desktop/resources/com.bitwarden.desktop.devel.yaml +++ b/apps/desktop/resources/com.bitwarden.desktop.devel.yaml @@ -33,7 +33,10 @@ modules: - mkdir -p /app/bin - mkdir -p /app/bin/Bitwarden/ - cp -r ./* /app/bin/ + - mkdir -p ${FLATPAK_DEST}/share/applications/ + - cp com.bitwarden.desktop.desktop ${FLATPAK_DEST}/share/applications/ - install bitwarden.sh /app/bin/bitwarden.sh + - desktop-file-edit --set-key=Exec --set-value="bitwarden.sh %U" ${FLATPAK_DEST}/share/applications/com.bitwarden.desktop.desktop sources: - type: dir only-arches: [x86_64] @@ -41,6 +44,8 @@ modules: - type: dir only-arches: [aarch64] path: ../dist/linux-arm64-unpacked + - type: file + path: ./com.bitwarden.desktop.desktop - type: script dest-filename: bitwarden.sh commands: diff --git a/apps/desktop/resources/entitlements.mac.plist b/apps/desktop/resources/entitlements.mac.plist index fe49256d71c..7763b84624d 100644 --- a/apps/desktop/resources/entitlements.mac.plist +++ b/apps/desktop/resources/entitlements.mac.plist @@ -6,8 +6,6 @@ LTZ2PFU5D6.com.bitwarden.desktop com.apple.developer.team-identifier LTZ2PFU5D6 - com.apple.developer.authentication-services.autofill-credential-provider - com.apple.security.cs.allow-jit diff --git a/apps/desktop/resources/entitlements.mas.autofill-enabled.plist b/apps/desktop/resources/entitlements.mas.autofill-enabled.plist new file mode 100644 index 00000000000..f25780e5c12 --- /dev/null +++ b/apps/desktop/resources/entitlements.mas.autofill-enabled.plist @@ -0,0 +1,42 @@ + + + + + com.apple.application-identifier + LTZ2PFU5D6.com.bitwarden.desktop + com.apple.developer.team-identifier + LTZ2PFU5D6 + com.apple.security.app-sandbox + + com.apple.security.application-groups + + LTZ2PFU5D6.com.bitwarden.desktop + + com.apple.security.cs.allow-jit + + com.apple.security.device.usb + + com.apple.security.files.user-selected.read-write + + com.apple.security.network.client + + com.apple.security.temporary-exception.files.home-relative-path.read-write + + /Library/Application Support/Mozilla/NativeMessagingHosts/ + /Library/Application Support/Google/Chrome/NativeMessagingHosts/ + /Library/Application Support/Google/Chrome Beta/NativeMessagingHosts/ + /Library/Application Support/Google/Chrome Dev/NativeMessagingHosts/ + /Library/Application Support/Google/Chrome Canary/NativeMessagingHosts/ + /Library/Application Support/Chromium/NativeMessagingHosts/ + /Library/Application Support/Microsoft Edge/NativeMessagingHosts/ + /Library/Application Support/Microsoft Edge Beta/NativeMessagingHosts/ + /Library/Application Support/Microsoft Edge Dev/NativeMessagingHosts/ + /Library/Application Support/Microsoft Edge Canary/NativeMessagingHosts/ + /Library/Application Support/Vivaldi/NativeMessagingHosts/ + /Library/Application Support/Zen/NativeMessagingHosts/ + /Library/Application Support/net.imput.helium + + com.apple.developer.authentication-services.autofill-credential-provider + + + diff --git a/apps/desktop/resources/entitlements.mas.inherit.plist b/apps/desktop/resources/entitlements.mas.inherit.plist index fca5f02d52d..7194d9409fc 100644 --- a/apps/desktop/resources/entitlements.mas.inherit.plist +++ b/apps/desktop/resources/entitlements.mas.inherit.plist @@ -4,9 +4,9 @@ com.apple.security.app-sandbox - com.apple.security.inherit - com.apple.security.cs.allow-jit + com.apple.security.inherit + diff --git a/apps/desktop/resources/entitlements.mas.plist b/apps/desktop/resources/entitlements.mas.plist index 3ebd56f0fd7..9760af69e8b 100644 --- a/apps/desktop/resources/entitlements.mas.plist +++ b/apps/desktop/resources/entitlements.mas.plist @@ -6,19 +6,19 @@ LTZ2PFU5D6.com.bitwarden.desktop com.apple.developer.team-identifier LTZ2PFU5D6 - com.apple.developer.authentication-services.autofill-credential-provider - com.apple.security.app-sandbox com.apple.security.application-groups LTZ2PFU5D6.com.bitwarden.desktop - com.apple.security.network.client + com.apple.security.cs.allow-jit + + com.apple.security.device.usb com.apple.security.files.user-selected.read-write - com.apple.security.device.usb + com.apple.security.network.client com.apple.security.temporary-exception.files.home-relative-path.read-write @@ -32,10 +32,9 @@ /Library/Application Support/Microsoft Edge Beta/NativeMessagingHosts/ /Library/Application Support/Microsoft Edge Dev/NativeMessagingHosts/ /Library/Application Support/Microsoft Edge Canary/NativeMessagingHosts/ - /Library/Application Support/Vivaldi/NativeMessagingHosts/ + /Library/Application Support/Vivaldi/NativeMessagingHosts/ /Library/Application Support/Zen/NativeMessagingHosts/ + /Library/Application Support/net.imput.helium/NativeMessagingHosts/ - com.apple.security.cs.allow-jit - diff --git a/apps/desktop/resources/linux-wrapper.sh b/apps/desktop/resources/linux-wrapper.sh index 3c5d16c3a3d..e1cb69274d7 100644 --- a/apps/desktop/resources/linux-wrapper.sh +++ b/apps/desktop/resources/linux-wrapper.sh @@ -12,9 +12,13 @@ if [ -e "/usr/lib/x86_64-linux-gnu/libdbus-1.so.3" ]; then export LD_PRELOAD="/usr/lib/x86_64-linux-gnu/libdbus-1.so.3" fi +# A bug in Electron 39 (which now enables Wayland by default) causes a crash on +# systems using Wayland with hardware acceleration. Platform decided to +# configure Electron to use X11 (with an opt-out) until the upstream bug is +# fixed. The follow-up task is https://bitwarden.atlassian.net/browse/PM-31080. PARAMS="--enable-features=UseOzonePlatform,WaylandWindowDecorations --ozone-platform-hint=auto" -if [ "$USE_X11" = "true" ]; then - PARAMS="" +if [ "$USE_X11" != "false" ]; then + PARAMS="--ozone-platform=x11" fi $APP_PATH/bitwarden-app $PARAMS "$@" diff --git a/apps/desktop/scripts/after-pack.js b/apps/desktop/scripts/after-pack.js index 5fc42f31ac3..34378ee092b 100644 --- a/apps/desktop/scripts/after-pack.js +++ b/apps/desktop/scripts/after-pack.js @@ -6,9 +6,12 @@ const path = require("path"); const { flipFuses, FuseVersion, FuseV1Options } = require("@electron/fuses"); const builder = require("electron-builder"); const fse = require("fs-extra"); - exports.default = run; +/** + * + * @param {builder.AfterPackContext} context + */ async function run(context) { console.log("## After pack"); // console.log(context); diff --git a/apps/desktop/scripts/after-sign.js b/apps/desktop/scripts/after-sign.js index 7c9ad381dc2..0e0e22fc24a 100644 --- a/apps/desktop/scripts/after-sign.js +++ b/apps/desktop/scripts/after-sign.js @@ -16,7 +16,9 @@ async function run(context) { const appPath = `${context.appOutDir}/${appName}.app`; const macBuild = context.electronPlatformName === "darwin"; const copySafariExtension = ["darwin", "mas"].includes(context.electronPlatformName); - const copyAutofillExtension = ["darwin", "mas"].includes(context.electronPlatformName); + const isMasDevBuild = + context.electronPlatformName === "mas" && context.targets.at(0)?.name === "mas-dev"; + const copyAutofillExtension = ["darwin"].includes(context.electronPlatformName) || isMasDevBuild; let shouldResign = false; @@ -31,7 +33,6 @@ async function run(context) { fse.mkdirSync(path.join(appPath, "Contents/PlugIns")); } fse.copySync(extensionPath, path.join(appPath, "Contents/PlugIns/autofill-extension.appex")); - shouldResign = true; } } diff --git a/apps/desktop/scripts/appx-cross-build.ps1 b/apps/desktop/scripts/appx-cross-build.ps1 new file mode 100755 index 00000000000..62619d5ea37 --- /dev/null +++ b/apps/desktop/scripts/appx-cross-build.ps1 @@ -0,0 +1,226 @@ +#!/usr/bin/env pwsh + +<# +.SYNOPSIS +Script to build, package and sign the Bitwarden desktop client as a Windows Appx +package. + +.DESCRIPTION +This script provides cross-platform support for packaging and signing the +Bitwarden desktop client as a Windows Appx package. + +Currently, only macOS -> Windows Appx is supported, but Linux -> Windows Appx +could be added in the future by providing Linux binaries for the msix-packaging +project. + +.NOTES +The reason this script exists is because electron-builder does not currently +support cross-platform Appx packaging without proprietary tools (Parallels +Windows VM). This script uses third-party tools (makemsix from msix-packaging +and osslsigncode) to package and sign the Appx. + +The signing certificate must have the same subject as the publisher name. This +can be generated on the Windows target using PowerShell 5.1 and copied to the +host, or directly on the host with OpenSSL. + +Using Windows PowerShell 5.1: +```powershell +$publisher = "CN=Bitwarden Inc., O=Bitwarden Inc., L=Santa Barbara, S=California, C=US, SERIALNUMBER=7654941, OID.2.5.4.15=Private Organization, OID.1.3.6.1.4.1.311.60.2.1.2=Delaware, OID.1.3.6.1.4.1.311.60.2.1.3=US" +$certificate = New-SelfSignedCertificate -Type Custom -KeyUsage DigitalSignature -CertStoreLocation "Cert:\CurrentUser\My" -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", "2.5.29.19={text}") -Subject $publisher -FriendlyName "Bitwarden Developer Signing Certificate" +$password = Read-Host -AsSecureString +Export-PfxCertificate -cert "Cert:\CurrentUser\My\${$certificate.Thumbprint}" -FilePath "C:\path/to/pfx" -Password $password +``` + +Using OpenSSL: +```sh +subject="jurisdictionCountryName=US/jurisdictionStateOrProvinceName=Delaware/businessCategory=Private Organization/serialNumber=7654941, C=US, ST=California, L=Santa Barbara, O=Bitwarden Inc., CN=Bitwarden Inc." +keyfile="/tmp/mysigning.rsa.pem" +certfile="/tmp/mysigning.cert.pem" +p12file="/tmp/mysigning.p12" +openssl req -x509 -keyout "$keyfile" -out "$certfile" -subj "$subject" \ + -newkey rsa:2048 -days 3650 -nodes \ + -addext 'keyUsage=critical,digitalSignature' \ + -addext 'extendedKeyUsage=critical,codeSigning' \ + -addext 'basicConstraints=critical,CA:FALSE' +openssl pkcs12 -inkey "$keyfile" -in "$certfile" -export -out "$p12file" +rm $keyfile +``` + +.EXAMPLE +./scripts/cross-build.ps1 -Architecture arm64 -CertificatePath ~/Development/code-signing.pfx -CertificatePassword (Read-Host -AsSecureString) -Release -Beta + +Reads the signing certificate password from user input, then builds, packages +and signs the Appx. + +Alternatively, you can specify the CERTIFICATE_PASSWORD environment variable. +#> +param( + [Parameter(Mandatory=$true)] + [ValidateSet("X64", "ARM64")]$Architecture, + [string] + # Path to PKCS12 certificate file. If not specified, the Appx will not be signed. + $CertificatePath, + [SecureString] + # Password for PKCS12 certificate. Alternatively, may be specified in + # CERTIFICATE_PASSWORD environment variable. If not specified, the Appx will + # not be signed. + $CertificatePassword, + [Switch] + # Whether to build the Beta version of the app. + $Beta=$false, + [Switch] + # Whether to build in release mode. + $Release=$false +) +$ErrorActionPreference = "Stop" +$PSNativeCommandUseErrorActionPreference = $true +$startTime = Get-Date +$originalLocation = Get-Location +if (!(Get-Command makemsix -ErrorAction SilentlyContinue)) { + Write-Error "The `makemsix` tool from the msix-packaging project is required to construct Appx package." + Write-Error "On macOS, you can install with Homebrew:" + Write-Error " brew install iinuwa/msix-packaging-tap/msix-packaging" + Exit 1 +} + +if (!(Get-Command osslsigncode -ErrorAction SilentlyContinue)) { + Write-Error "The `osslsigncode` tool is required to sign the Appx package." + Write-Error "On macOS, you can install with Homebrew:" + Write-Error " brew install osslsigncode" + Exit 1 +} + +if (!(Get-Command cargo-xwin -ErrorAction SilentlyContinue)) { + Write-Error "The `cargo-xwin` tool is required to cross-compile Windows native code." + Write-Error "You can install with cargo:" + Write-Error " cargo install --version 0.20.2 --locked cargo-xwin" + Exit 1 +} + +try { + +# Resolve certificate file before we change directories. +$CertificateFile = Get-Item $CertificatePath -ErrorAction SilentlyContinue + +cd $PSScriptRoot/.. + +if ($Beta) { + $electronConfigFile = Get-Item "./electron-builder.beta.json" +} +else { + $electronConfigFile = Get-Item "./electron-builder.json" +} + +$builderConfig = Get-Content $electronConfigFile | ConvertFrom-Json +$packageConfig = Get-Content package.json | ConvertFrom-Json +$manifestTemplate = Get-Content $builderConfig.appx.customManifestPath + +$srcDir = Get-Location +$assetsDir = Get-Item $builderConfig.directories.buildResources +$buildDir = Get-Item $builderConfig.directories.app +$outDir = Join-Path (Get-Location) ($builderConfig.directories.output ?? "dist") + +if ($Release) { + $buildConfiguration = "--release" +} +$arch = "$Architecture".ToLower() +$ext = "appx" +$version = Get-Date -Format "yyyy.M.d.1HHmm" +$productName = $builderConfig.productName +$artifactName = "${productName}-$($packageConfig.version)-${arch}.$ext" + +Write-Host "Building native code" +$rustTarget = switch ($arch) { + x64 { "x86_64-pc-windows-msvc" } + arm64 { "aarch64-pc-windows-msvc" } + default { + Write-Error "Unsupported architecture: $Architecture. Supported architectures are x64 and arm64" + Exit(1) + } +} +npm run build-native -- cross-platform $buildConfiguration "--target=$rustTarget" + +Write-Host "Building Javascript code" +if ($Release) { + npm run build +} +else { + npm run build:dev +} + +Write-Host "Cleaning output folder" +Remove-Item -Recurse -Force $outDir -ErrorAction Ignore + +Write-Host "Packaging Electron executable" +& npx electron-builder --config $electronConfigFile --publish never --dir --win --$arch + +cd $outDir +New-Item -Type Directory (Join-Path $outDir "appx") + +Write-Host "Building Appx directory structure" +$appxDir = (Join-Path $outDir appx/app) +if ($arch -eq "x64") { + Move-Item (Join-Path $outDir "win-unpacked") $appxDir +} +else { + Move-Item (Join-Path $outDir "win-${arch}-unpacked") $appxDir +} + +Write-Host "Copying Assets" +New-Item -Type Directory (Join-Path $outDir appx/assets) +Copy-Item $srcDir/resources/appx/* $outDir/appx/assets/ + +Write-Host "Building Appx manifest" +$translationMap = @{ + 'arch' = $arch + 'applicationId' = $builderConfig.appx.applicationId + 'displayName' = $productName + 'executable' = "app\${productName}.exe" + 'publisher' = $builderConfig.appx.publisher + 'publisherDisplayName' = $builderConfig.appx.publisherDisplayName + 'version' = $version +} + +$manifest = $manifestTemplate +$translationMap.Keys | ForEach-Object { + $manifest = $manifest.Replace("`${$_}", $translationMap[$_]) +} +$manifest | Out-File appx/AppxManifest.xml +$unsignedArtifactpath = [System.IO.Path]::GetFileNameWithoutExtension($artifactName) + "-unsigned.$ext" +Write-Host "Creating unsigned Appx" +makemsix pack -d appx -p $unsignedArtifactpath + +$outfile = Join-Path $outDir $unsignedArtifactPath +if ($null -eq $CertificatePath) { + Write-Warning "No Certificate specified. Not signing Appx." +} +elseif ($null -eq $CertificatePassword -and $null -eq $env:CERTIFICATE_PASSWORD) { + Write-Warning "No certificate password specified in CertificatePassword argument nor CERTIFICATE_PASSWORD environment variable. Not signing Appx." +} +else { + $cert = $CertificateFile + $pw = $null + if ($null -ne $CertificatePassword) { + $pw = ConvertFrom-SecureString -SecureString $CertificatePassword -AsPlainText + } else { + $pw = $env:CERTIFICATE_PASSWORD + } + $unsigned = $outfile + $outfile = (Join-Path $outDir $artifactName) + Write-Host "Signing $artifactName with $cert" + osslsigncode sign ` + -pkcs12 "$cert" ` + -pass "$pw" ` + -in $unsigned ` + -out $outfile + Remove-Item $unsigned +} + +$endTime = Get-Date +$elapsed = $endTime - $startTime +Write-Host "Successfully packaged $(Get-Item $outfile)" +Write-Host ("Finished at $($endTime.ToString('HH:mm:ss')) in $($elapsed.ToString('mm')) minutes and $($elapsed.ToString('ss')).$($elapsed.ToString('fff')) seconds") +} +finally { + Set-Location -Path $originalLocation +} diff --git a/apps/desktop/scripts/before-pack.js b/apps/desktop/scripts/before-pack.js new file mode 100644 index 00000000000..ca9bf924b2d --- /dev/null +++ b/apps/desktop/scripts/before-pack.js @@ -0,0 +1,31 @@ +/* eslint-disable no-console */ +/** @import { BeforePackContext } from 'app-builder-lib' */ +exports.default = run; + +/** + * @param {BeforePackContext} context + */ +async function run(context) { + console.log("## before pack"); + console.log("Stripping .node files that don't belong to this platform..."); + removeExtraNodeFiles(context); +} + +/** + * Removes Node files for platforms besides the current platform being packaged. + * + * @param {BeforePackContext} context + */ +function removeExtraNodeFiles(context) { + // When doing cross-platform builds, due to electron-builder limitiations, + // .node files for other platforms may be generated and unpacked, so we + // remove them manually here before signing and distributing. + const packagerPlatform = context.packager.platform.nodeName; + const platforms = ["darwin", "linux", "win32"]; + const fileFilter = context.packager.info._configuration.files[0].filter; + for (const platform of platforms) { + if (platform != packagerPlatform) { + fileFilter.push(`!node_modules/@bitwarden/desktop-napi/desktop_napi.${platform}-*.node`); + } + } +} diff --git a/apps/desktop/scripts/nx-serve.js b/apps/desktop/scripts/nx-serve.js index b92a045f8e8..235691f9ce8 100644 --- a/apps/desktop/scripts/nx-serve.js +++ b/apps/desktop/scripts/nx-serve.js @@ -37,6 +37,6 @@ concurrently( { prefix: "name", outputStream: process.stdout, - killOthers: ["success", "failure"], + killOthersOn: ["success", "failure"], }, ); diff --git a/apps/desktop/scripts/start.js b/apps/desktop/scripts/start.js index 0e11ebd9083..4ffbe2eebeb 100644 --- a/apps/desktop/scripts/start.js +++ b/apps/desktop/scripts/start.js @@ -34,6 +34,6 @@ concurrently( { prefix: "name", outputStream: process.stdout, - killOthers: ["success", "failure"], + killOthersOn: ["success", "failure"], }, ); diff --git a/apps/desktop/sign.js b/apps/desktop/sign.js index 6a42666c46f..f115e9b8097 100644 --- a/apps/desktop/sign.js +++ b/apps/desktop/sign.js @@ -1,22 +1,60 @@ /* eslint-disable @typescript-eslint/no-require-imports, no-console */ +const child_process = require("child_process"); exports.default = async function (configuration) { - if (parseInt(process.env.ELECTRON_BUILDER_SIGN) === 1 && configuration.path.slice(-4) == ".exe") { + const ext = configuration.path.split(".").at(-1); + if (parseInt(process.env.ELECTRON_BUILDER_SIGN) === 1 && ["exe", "appx"].includes(ext)) { console.log(`[*] Signing file: ${configuration.path}`); - require("child_process").execSync( - `azuresigntool sign -v ` + - `-kvu ${process.env.SIGNING_VAULT_URL} ` + - `-kvi ${process.env.SIGNING_CLIENT_ID} ` + - `-kvt ${process.env.SIGNING_TENANT_ID} ` + - `-kvs ${process.env.SIGNING_CLIENT_SECRET} ` + - `-kvc ${process.env.SIGNING_CERT_NAME} ` + - `-fd ${configuration.hash} ` + - `-du ${configuration.site} ` + - `-tr http://timestamp.digicert.com ` + - `"${configuration.path}"`, + child_process.execFileSync( + "azuresigntool", + // prettier-ignore + [ + "sign", + "-v", + "-kvu", process.env.SIGNING_VAULT_URL, + "-kvi", process.env.SIGNING_CLIENT_ID, + "-kvt", process.env.SIGNING_TENANT_ID, + "-kvs", process.env.SIGNING_CLIENT_SECRET, + "-kvc", process.env.SIGNING_CERT_NAME, + "-fd", configuration.hash, + "-du", configuration.site, + "-tr", "http://timestamp.digicert.com", + configuration.path, + ], { stdio: "inherit", }, ); + } else if (process.env.ELECTRON_BUILDER_SIGN_CERT && ["exe", "appx"].includes(ext)) { + console.log(`[*] Signing file: ${configuration.path}`); + if (process.platform !== "win32") { + console.warn( + "Signing Windows executables on non-Windows platforms is not supported. Not signing.", + ); + return; + } + const certFile = process.env.ELECTRON_BUILDER_SIGN_CERT; + const certPw = process.env.ELECTRON_BUILDER_SIGN_CERT_PW; + if (!certPw) { + throw new Error( + "The certificate file password must be set in ELECTRON_BUILDER_SIGN_CERT_PW in order to sign files.", + ); + } + try { + child_process.execFileSync( + "signtool.exe", + ["sign", "/fd", "SHA256", "/a", "/f", certFile, "/p", certPw, configuration.path], + { + stdio: "inherit", + }, + ); + console.info(`Signed ${configuration.path} successfully.`); + } catch (error) { + throw new Error( + `Failed to sign ${configuration.path}: ${error.message}\n` + + `Check that ELECTRON_BUILDER_SIGN_CERT points to a valid PKCS12 file ` + + `and ELECTRON_BUILDER_SIGN_CERT_PW is correct.`, + ); + } } }; diff --git a/apps/desktop/src/app/accounts/settings.component.html b/apps/desktop/src/app/accounts/settings.component.html index 8abd84ee39c..d5042918d2f 100644 --- a/apps/desktop/src/app/accounts/settings.component.html +++ b/apps/desktop/src/app/accounts/settings.component.html @@ -44,12 +44,12 @@

    {{ "vaultTimeoutHeader" | i18n }}

    - - + {{ diff --git a/apps/desktop/src/app/accounts/settings.component.spec.ts b/apps/desktop/src/app/accounts/settings.component.spec.ts index a424f230778..bffa06d2654 100644 --- a/apps/desktop/src/app/accounts/settings.component.spec.ts +++ b/apps/desktop/src/app/accounts/settings.component.spec.ts @@ -187,9 +187,8 @@ describe("SettingsComponent", () => { i18nService.userSetLocale$ = of("en"); pinServiceAbstraction.isPinSet.mockResolvedValue(false); policyService.policiesByType$.mockReturnValue(of([null])); - desktopAutotypeService.resolvedAutotypeEnabled$ = of(false); desktopAutotypeService.autotypeEnabledUserSetting$ = of(false); - desktopAutotypeService.autotypeKeyboardShortcut$ = of(["Control", "Shift", "B"]); + desktopAutotypeService.autotypeKeyboardShortcut$ = of(["Control", "Alt", "B"]); billingAccountProfileStateService.hasPremiumFromAnySource$.mockReturnValue(of(false)); configService.getFeatureFlag$.mockReturnValue(of(false)); }); diff --git a/apps/desktop/src/app/accounts/settings.component.ts b/apps/desktop/src/app/accounts/settings.component.ts index 68863312ffe..3952335af48 100644 --- a/apps/desktop/src/app/accounts/settings.component.ts +++ b/apps/desktop/src/app/accounts/settings.component.ts @@ -55,7 +55,7 @@ import { } from "@bitwarden/components"; import { KeyService, BiometricStateService, BiometricsStatus } from "@bitwarden/key-management"; import { - SessionTimeoutInputComponent, + SessionTimeoutInputLegacyComponent, SessionTimeoutSettingsComponent, } from "@bitwarden/key-management-ui"; import { PermitCipherDetailsPopoverComponent } from "@bitwarden/vault"; @@ -97,7 +97,7 @@ import { NativeMessagingManifestService } from "../services/native-messaging-man SectionHeaderComponent, SelectModule, TypographyModule, - SessionTimeoutInputComponent, + SessionTimeoutInputLegacyComponent, SessionTimeoutSettingsComponent, PermitCipherDetailsPopoverComponent, PremiumBadgeComponent, @@ -970,10 +970,20 @@ export class SettingsComponent implements OnInit, OnDestroy { } async saveAutotypeShortcut() { + // disable the shortcut so that the user can't re-enter the existing + // shortcut and trigger the feature during the settings menu. + // it is not necessary to check if it's already enabled, because + // the edit shortcut is only avaialble if the feature is enabled + // in the settings. + await this.desktopAutotypeService.setAutotypeEnabledState(false); + const dialogRef = AutotypeShortcutComponent.open(this.dialogService); const newShortcutArray = await firstValueFrom(dialogRef.closed); + // re-enable + await this.desktopAutotypeService.setAutotypeEnabledState(true); + if (!newShortcutArray) { return; } diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index b6e86ba19ff..e9b6dfdc9e5 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -14,6 +14,7 @@ import { } from "@bitwarden/angular/auth/guards"; import { ChangePasswordComponent } from "@bitwarden/angular/auth/password-management/change-password"; import { SetInitialPasswordComponent } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.component"; +import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; import { DevicesIcon, RegistrationUserAddIcon, @@ -39,15 +40,25 @@ import { TwoFactorAuthGuard, NewDeviceVerificationComponent, } from "@bitwarden/auth/angular"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components"; -import { LockComponent, ConfirmKeyConnectorDomainComponent } from "@bitwarden/key-management-ui"; +import { + LockComponent, + ConfirmKeyConnectorDomainComponent, + RemovePasswordComponent, +} from "@bitwarden/key-management-ui"; import { maxAccountsGuardFn } from "../auth/guards/max-accounts.guard"; -import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component"; +import { reactiveUnlockVaultGuard } from "../autofill/guards/reactive-vault-guard"; +import { Fido2CreateComponent } from "../autofill/modal/credentials/fido2-create.component"; +import { Fido2ExcludedCiphersComponent } from "../autofill/modal/credentials/fido2-excluded-ciphers.component"; +import { Fido2VaultComponent } from "../autofill/modal/credentials/fido2-vault.component"; import { VaultV2Component } from "../vault/app/vault/vault-v2.component"; +import { VaultComponent } from "../vault/app/vault-v3/vault.component"; -import { Fido2PlaceholderComponent } from "./components/fido2placeholder.component"; +import { DesktopLayoutComponent } from "./layout/desktop-layout.component"; import { SendComponent } from "./tools/send/send.component"; +import { SendV2Component } from "./tools/send-v2/send-v2.component"; /** * Data properties acceptable for use in route objects in the desktop @@ -99,7 +110,12 @@ const routes: Routes = [ { path: "vault", component: VaultV2Component, - canActivate: [authGuard], + canActivate: [ + authGuard, + canAccessFeature(FeatureFlag.DesktopUiMigrationMilestone1, false, "new-vault", false), + ], + // Needed to ensure feature flag changes are picked up on account switching + runGuardsAndResolvers: "always", }, { path: "send", @@ -107,17 +123,16 @@ const routes: Routes = [ canActivate: [authGuard], }, { - path: "remove-password", - component: RemovePasswordComponent, - canActivate: [authGuard], + path: "fido2-assertion", + component: Fido2VaultComponent, }, { - path: "passkeys", - component: Fido2PlaceholderComponent, + path: "fido2-creation", + component: Fido2CreateComponent, }, { - path: "passkeys", - component: Fido2PlaceholderComponent, + path: "fido2-excluded", + component: Fido2ExcludedCiphersComponent, }, { path: "", @@ -263,7 +278,7 @@ const routes: Routes = [ }, { path: "lock", - canActivate: [lockGuard()], + canActivate: [lockGuard(), reactiveUnlockVaultGuard], data: { pageIcon: LockIcon, pageTitle: { @@ -312,19 +327,46 @@ const routes: Routes = [ pageIcon: LockIcon, } satisfies AnonLayoutWrapperData, }, + { + path: "remove-password", + component: RemovePasswordComponent, + canActivate: [authGuard], + data: { + pageTitle: { + key: "verifyYourOrganization", + }, + pageIcon: LockIcon, + } satisfies RouteDataProperties & AnonLayoutWrapperData, + }, { path: "confirm-key-connector-domain", component: ConfirmKeyConnectorDomainComponent, canActivate: [], data: { pageTitle: { - key: "confirmKeyConnectorDomain", + key: "verifyYourOrganization", }, pageIcon: DomainIcon, } satisfies RouteDataProperties & AnonLayoutWrapperData, }, ], }, + { + path: "", + component: DesktopLayoutComponent, + canActivate: [authGuard], + children: [ + { + path: "new-vault", + component: VaultComponent, + }, + { + path: "new-sends", + component: SendV2Component, + data: { pageTitle: { key: "send" } } satisfies RouteDataProperties, + }, + ], + }, ]; @NgModule({ diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index 6243ba1e538..01eb8c728e5 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -31,6 +31,7 @@ import { DocumentLangSetter } from "@bitwarden/angular/platform/i18n"; import { ModalService } from "@bitwarden/angular/services/modal.service"; import { FingerprintDialogComponent } from "@bitwarden/auth/angular"; import { + AuthRequestServiceAbstraction, DESKTOP_SSO_CALLBACK, LockService, LogoutReason, @@ -40,11 +41,13 @@ import { EventUploadService } from "@bitwarden/common/abstractions/event/event-u import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthRequestAnsweringService } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { PendingAuthRequestsStateService } from "@bitwarden/common/auth/services/auth-request-answering/pending-auth-requests.state"; import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service"; import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service"; import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; @@ -70,6 +73,7 @@ import { SyncService } from "@bitwarden/common/platform/sync"; import { UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { InternalFolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; @@ -104,7 +108,7 @@ const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours - +
    @@ -141,6 +145,7 @@ export class AppComponent implements OnInit, OnDestroy { @ViewChild("loginApproval", { read: ViewContainerRef, static: true }) loginApprovalModalRef: ViewContainerRef; + showHeader$ = this.accountService.showHeader$; loading = false; private lastActivity: Date = null; @@ -149,6 +154,8 @@ export class AppComponent implements OnInit, OnDestroy { private isIdle = false; private activeUserId: UserId = null; private activeSimpleDialog: DialogRef = null; + private processingPendingAuthRequests = false; + private shouldRerunAuthRequestProcessing = false; private destroy$ = new Subject(); @@ -197,6 +204,10 @@ export class AppComponent implements OnInit, OnDestroy { private readonly tokenService: TokenService, private desktopAutotypeDefaultSettingPolicy: DesktopAutotypeDefaultSettingPolicy, private readonly lockService: LockService, + private premiumUpgradePromptService: PremiumUpgradePromptService, + private pendingAuthRequestsState: PendingAuthRequestsStateService, + private authRequestService: AuthRequestServiceAbstraction, + private authRequestAnsweringService: AuthRequestAnsweringService, ) { this.deviceTrustToastService.setupListeners$.pipe(takeUntilDestroyed()).subscribe(); @@ -209,6 +220,8 @@ export class AppComponent implements OnInit, OnDestroy { this.activeUserId = account?.id; }); + this.authRequestAnsweringService.setupUnlockListenersForProcessingAuthRequests(this.destroy$); + this.ngZone.runOutsideAngular(() => { setTimeout(async () => { await this.updateAppMenu(); @@ -304,7 +317,7 @@ export class AppComponent implements OnInit, OnDestroy { await this.openModal(SettingsComponent, this.settingsRef); break; case "openPremium": - this.dialogService.open(PremiumComponent); + await this.premiumUpgradePromptService.promptForPremium(); break; case "showFingerprintPhrase": { const activeUserId = await firstValueFrom( @@ -479,9 +492,8 @@ export class AppComponent implements OnInit, OnDestroy { this.loading = true; await this.syncService.fullSync(false); this.loading = false; - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.router.navigate(["vault"]); + // Force reload to ensure route guards are activated + await this.router.navigate(["vault"], { onSameUrlNavigation: "reload" }); } this.messagingService.send("finishSwitchAccount"); break; @@ -496,13 +508,31 @@ export class AppComponent implements OnInit, OnDestroy { await this.checkForSystemTimeout(VaultTimeoutStringType.OnIdle); break; case "openLoginApproval": - if (message.notificationId != null) { - this.dialogService.closeAll(); - const dialogRef = LoginApprovalDialogComponent.open(this.dialogService, { - notificationId: message.notificationId, - }); - await firstValueFrom(dialogRef.closed); + if (this.processingPendingAuthRequests) { + // If an "openLoginApproval" message is received while we are currently processing other + // auth requests, then set a flag so we remember to process that new auth request + this.shouldRerunAuthRequestProcessing = true; + return; } + + /** + * This do/while loop allows us to: + * - a) call processPendingAuthRequests() once on "openLoginApproval" + * - b) remember to re-call processPendingAuthRequests() if another "openLoginApproval" was + * received while we were processing the original auth requests + */ + do { + this.shouldRerunAuthRequestProcessing = false; + + try { + await this.processPendingAuthRequests(); + } catch (error) { + this.logService.error(`Error processing pending auth requests: ${error}`); + this.shouldRerunAuthRequestProcessing = false; // Reset flag to prevent infinite loop on persistent errors + } + // If an "openLoginApproval" message was received while processPendingAuthRequests() was running, then + // shouldRerunAuthRequestProcessing will have been set to true + } while (this.shouldRerunAuthRequestProcessing); break; case "redrawMenu": await this.updateAppMenu(); @@ -884,4 +914,39 @@ export class AppComponent implements OnInit, OnDestroy { DeleteAccountComponent.open(this.dialogService); } + + private async processPendingAuthRequests() { + this.processingPendingAuthRequests = true; + + try { + // Always query server for all pending requests and open a dialog for each + const pendingList = await firstValueFrom(this.authRequestService.getPendingAuthRequests$()); + + if (Array.isArray(pendingList) && pendingList.length > 0) { + const respondedIds = new Set(); + + for (const req of pendingList) { + if (req?.id == null) { + continue; + } + + const dialogRef = LoginApprovalDialogComponent.open(this.dialogService, { + notificationId: req.id, + }); + + const result = await firstValueFrom(dialogRef.closed); + + if (result !== undefined && typeof result === "boolean") { + respondedIds.add(req.id); + + if (respondedIds.size === pendingList.length && this.activeUserId != null) { + await this.pendingAuthRequestsState.clear(this.activeUserId); + } + } + } + } + } finally { + this.processingPendingAuthRequests = false; + } + } } diff --git a/apps/desktop/src/app/app.module.ts b/apps/desktop/src/app/app.module.ts index 4f53e587994..52a52ffd225 100644 --- a/apps/desktop/src/app/app.module.ts +++ b/apps/desktop/src/app/app.module.ts @@ -8,6 +8,7 @@ import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { ColorPasswordCountPipe } from "@bitwarden/angular/pipes/color-password-count.pipe"; import { ColorPasswordPipe } from "@bitwarden/angular/pipes/color-password.pipe"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { CalloutModule, DialogModule } from "@bitwarden/components"; import { AssignCollectionsComponent } from "@bitwarden/vault"; @@ -15,7 +16,7 @@ import { DeleteAccountComponent } from "../auth/delete-account.component"; import { LoginModule } from "../auth/login/login.module"; import { SshAgentService } from "../autofill/services/ssh-agent.service"; import { PremiumComponent } from "../billing/app/accounts/premium.component"; -import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component"; +import { DesktopPremiumUpgradePromptService } from "../services/desktop-premium-upgrade-prompt.service"; import { VaultFilterModule } from "../vault/app/vault/vault-filter/vault-filter.module"; import { VaultV2Component } from "../vault/app/vault/vault-v2.component"; @@ -50,10 +51,15 @@ import { SharedModule } from "./shared/shared.module"; ColorPasswordCountPipe, HeaderComponent, PremiumComponent, - RemovePasswordComponent, SearchComponent, ], - providers: [SshAgentService], + providers: [ + SshAgentService, + { + provide: PremiumUpgradePromptService, + useClass: DesktopPremiumUpgradePromptService, + }, + ], bootstrap: [AppComponent], }) export class AppModule {} diff --git a/apps/desktop/src/app/components/fido2placeholder.component.ts b/apps/desktop/src/app/components/fido2placeholder.component.ts deleted file mode 100644 index f1f52dae439..00000000000 --- a/apps/desktop/src/app/components/fido2placeholder.component.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { CommonModule } from "@angular/common"; -import { Component, OnDestroy, OnInit } from "@angular/core"; -import { Router } from "@angular/router"; -import { BehaviorSubject, Observable } from "rxjs"; - -import { - DesktopFido2UserInterfaceService, - DesktopFido2UserInterfaceSession, -} from "../../autofill/services/desktop-fido2-user-interface.service"; -import { DesktopSettingsService } from "../../platform/services/desktop-settings.service"; - -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -@Component({ - standalone: true, - imports: [CommonModule], - template: ` -
    -

    Select your passkey

    - -
    - -
    - -
    - - -
    - `, -}) -export class Fido2PlaceholderComponent implements OnInit, OnDestroy { - session?: DesktopFido2UserInterfaceSession = null; - private cipherIdsSubject = new BehaviorSubject([]); - cipherIds$: Observable; - - constructor( - private readonly desktopSettingsService: DesktopSettingsService, - private readonly fido2UserInterfaceService: DesktopFido2UserInterfaceService, - private readonly router: Router, - ) {} - - ngOnInit() { - this.session = this.fido2UserInterfaceService.getCurrentSession(); - this.cipherIds$ = this.session?.availableCipherIds$; - } - - async chooseCipher(cipherId: string) { - // For now: Set UV to true - this.session?.confirmChosenCipher(cipherId, true); - - await this.router.navigate(["/"]); - await this.desktopSettingsService.setModalMode(false); - } - - ngOnDestroy() { - this.cipherIdsSubject.complete(); // Clean up the BehaviorSubject - } - - async confirmPasskey() { - try { - // Retrieve the current UI session to control the flow - if (!this.session) { - // todo: handle error - throw new Error("No session found"); - } - - // If we want to we could submit information to the session in order to create the credential - // const cipher = await session.createCredential({ - // userHandle: "userHandle2", - // userName: "username2", - // credentialName: "zxsd2", - // rpId: "webauthn.io", - // userVerification: true, - // }); - - this.session.notifyConfirmNewCredential(true); - - // Not sure this clean up should happen here or in session. - // The session currently toggles modal on and send us here - // But if this route is somehow opened outside of session we want to make sure we clean up? - await this.router.navigate(["/"]); - await this.desktopSettingsService.setModalMode(false); - } catch { - // TODO: Handle error appropriately - } - } - - async closeModal() { - await this.router.navigate(["/"]); - await this.desktopSettingsService.setModalMode(false); - - this.session.notifyConfirmNewCredential(false); - // little bit hacky: - this.session.confirmChosenCipher(null); - } -} diff --git a/apps/desktop/src/app/layout/desktop-layout.component.html b/apps/desktop/src/app/layout/desktop-layout.component.html new file mode 100644 index 00000000000..cb969f573fc --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-layout.component.html @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/apps/desktop/src/app/layout/desktop-layout.component.spec.ts b/apps/desktop/src/app/layout/desktop-layout.component.spec.ts new file mode 100644 index 00000000000..c838f47a06c --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-layout.component.spec.ts @@ -0,0 +1,111 @@ +import { ChangeDetectionStrategy, Component } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { RouterModule } from "@angular/router"; +import { mock } from "jest-mock-extended"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { FakeGlobalStateProvider } from "@bitwarden/common/spec"; +import { DialogService, NavigationModule } from "@bitwarden/components"; +import { GlobalStateProvider } from "@bitwarden/state"; + +import { VaultFilterComponent } from "../../vault/app/vault-v3/vault-filter/vault-filter.component"; +import { SendFiltersNavComponent } from "../tools/send-v2/send-filters-nav.component"; + +import { DesktopLayoutComponent } from "./desktop-layout.component"; + +// Mock the child component to isolate DesktopLayoutComponent testing +@Component({ + selector: "app-send-filters-nav", + template: "", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class MockSendFiltersNavComponent {} + +@Component({ + selector: "app-vault-filter", + template: "", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class MockVaultFiltersNavComponent {} + +Object.defineProperty(window, "matchMedia", { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: true, + media: query, + onchange: null, + addListener: jest.fn(), + removeListener: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), +}); + +describe("DesktopLayoutComponent", () => { + let component: DesktopLayoutComponent; + let fixture: ComponentFixture; + + const fakeGlobalStateProvider = new FakeGlobalStateProvider(); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DesktopLayoutComponent, RouterModule.forRoot([]), NavigationModule], + providers: [ + { + provide: I18nService, + useValue: mock(), + }, + { + provide: GlobalStateProvider, + useValue: fakeGlobalStateProvider, + }, + { + provide: DialogService, + useValue: mock(), + }, + ], + }) + .overrideComponent(DesktopLayoutComponent, { + remove: { imports: [SendFiltersNavComponent, VaultFilterComponent] }, + add: { imports: [MockSendFiltersNavComponent, MockVaultFiltersNavComponent] }, + }) + .compileComponents(); + + fixture = TestBed.createComponent(DesktopLayoutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("creates component", () => { + expect(component).toBeTruthy(); + }); + + it("renders bit-layout component", () => { + const compiled = fixture.nativeElement; + const layoutElement = compiled.querySelector("bit-layout"); + + expect(layoutElement).toBeTruthy(); + }); + + it("supports content projection for side-nav", () => { + const compiled = fixture.nativeElement; + const ngContent = compiled.querySelectorAll("ng-content"); + + expect(ngContent).toBeTruthy(); + }); + + it("renders send filters navigation component", () => { + const compiled = fixture.nativeElement; + const sendFiltersNav = compiled.querySelector("app-send-filters-nav"); + + expect(sendFiltersNav).toBeTruthy(); + }); + + it("renders vault filters navigation component", () => { + const compiled = fixture.nativeElement; + const vaultFiltersNav = compiled.querySelector("app-vault-filter"); + + expect(vaultFiltersNav).toBeTruthy(); + }); +}); diff --git a/apps/desktop/src/app/layout/desktop-layout.component.ts b/apps/desktop/src/app/layout/desktop-layout.component.ts new file mode 100644 index 00000000000..8d6ced2eb7d --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-layout.component.ts @@ -0,0 +1,47 @@ +import { Component, inject } from "@angular/core"; +import { RouterModule } from "@angular/router"; + +import { PasswordManagerLogo } from "@bitwarden/assets/svg"; +import { DialogService, LayoutComponent, NavigationModule } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; + +import { VaultFilterComponent } from "../../vault/app/vault-v3/vault-filter/vault-filter.component"; +import { ExportDesktopComponent } from "../tools/export/export-desktop.component"; +import { CredentialGeneratorComponent } from "../tools/generator/credential-generator.component"; +import { ImportDesktopComponent } from "../tools/import/import-desktop.component"; +import { SendFiltersNavComponent } from "../tools/send-v2/send-filters-nav.component"; + +import { DesktopSideNavComponent } from "./desktop-side-nav.component"; + +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection +@Component({ + selector: "app-layout", + imports: [ + RouterModule, + I18nPipe, + LayoutComponent, + NavigationModule, + DesktopSideNavComponent, + VaultFilterComponent, + SendFiltersNavComponent, + ], + templateUrl: "./desktop-layout.component.html", +}) +export class DesktopLayoutComponent { + private dialogService = inject(DialogService); + + protected readonly logo = PasswordManagerLogo; + + protected openGenerator() { + this.dialogService.open(CredentialGeneratorComponent); + } + + protected openImport() { + this.dialogService.open(ImportDesktopComponent); + } + + protected openExport() { + this.dialogService.open(ExportDesktopComponent); + } +} diff --git a/apps/desktop/src/app/layout/desktop-side-nav.component.html b/apps/desktop/src/app/layout/desktop-side-nav.component.html new file mode 100644 index 00000000000..ede3f9131b7 --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-side-nav.component.html @@ -0,0 +1,3 @@ + + + diff --git a/apps/desktop/src/app/layout/desktop-side-nav.component.spec.ts b/apps/desktop/src/app/layout/desktop-side-nav.component.spec.ts new file mode 100644 index 00000000000..9b99dbf09c2 --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-side-nav.component.spec.ts @@ -0,0 +1,82 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { mock } from "jest-mock-extended"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { FakeGlobalStateProvider } from "@bitwarden/common/spec"; +import { NavigationModule } from "@bitwarden/components"; +import { GlobalStateProvider } from "@bitwarden/state"; + +import { DesktopSideNavComponent } from "./desktop-side-nav.component"; + +Object.defineProperty(window, "matchMedia", { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: true, + media: query, + onchange: null, + addListener: jest.fn(), + removeListener: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), +}); + +describe("DesktopSideNavComponent", () => { + let component: DesktopSideNavComponent; + let fixture: ComponentFixture; + + const fakeGlobalStateProvider = new FakeGlobalStateProvider(); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DesktopSideNavComponent, NavigationModule], + providers: [ + { + provide: I18nService, + useValue: mock(), + }, + { + provide: GlobalStateProvider, + useValue: fakeGlobalStateProvider, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(DesktopSideNavComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("creates component", () => { + expect(component).toBeTruthy(); + }); + + it("renders bit-side-nav component", () => { + const compiled = fixture.nativeElement; + const sideNavElement = compiled.querySelector("bit-side-nav"); + + expect(sideNavElement).toBeTruthy(); + }); + + it("uses primary variant by default", () => { + expect(component.variant()).toBe("primary"); + }); + + it("accepts variant input", () => { + fixture.componentRef.setInput("variant", "secondary"); + fixture.detectChanges(); + + expect(component.variant()).toBe("secondary"); + }); + + it.skip("passes variant to bit-side-nav", () => { + fixture.componentRef.setInput("variant", "secondary"); + fixture.detectChanges(); + + const compiled = fixture.nativeElement; + const sideNavElement = compiled.querySelector("bit-side-nav"); + + expect(sideNavElement.getAttribute("ng-reflect-variant")).toBe("secondary"); + }); +}); diff --git a/apps/desktop/src/app/layout/desktop-side-nav.component.ts b/apps/desktop/src/app/layout/desktop-side-nav.component.ts new file mode 100644 index 00000000000..b0d9fd16fcc --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-side-nav.component.ts @@ -0,0 +1,14 @@ +import { CommonModule } from "@angular/common"; +import { ChangeDetectionStrategy, Component, input } from "@angular/core"; + +import { NavigationModule, SideNavVariant } from "@bitwarden/components"; + +@Component({ + selector: "app-side-nav", + templateUrl: "desktop-side-nav.component.html", + imports: [CommonModule, NavigationModule], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DesktopSideNavComponent { + readonly variant = input("primary"); +} diff --git a/apps/desktop/src/app/layout/header/desktop-header.component.html b/apps/desktop/src/app/layout/header/desktop-header.component.html new file mode 100644 index 00000000000..ae578312535 --- /dev/null +++ b/apps/desktop/src/app/layout/header/desktop-header.component.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/desktop/src/app/layout/header/desktop-header.component.spec.ts b/apps/desktop/src/app/layout/header/desktop-header.component.spec.ts new file mode 100644 index 00000000000..8d3db198887 --- /dev/null +++ b/apps/desktop/src/app/layout/header/desktop-header.component.spec.ts @@ -0,0 +1,122 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { ActivatedRoute } from "@angular/router"; +import { mock } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { HeaderComponent } from "@bitwarden/components"; + +import { DesktopHeaderComponent } from "./desktop-header.component"; + +describe("DesktopHeaderComponent", () => { + let component: DesktopHeaderComponent; + let fixture: ComponentFixture; + let mockI18nService: ReturnType>; + let mockActivatedRoute: { data: any }; + + beforeEach(async () => { + mockI18nService = mock(); + mockI18nService.t.mockImplementation((key: string) => `translated_${key}`); + + mockActivatedRoute = { + data: of({}), + }; + + await TestBed.configureTestingModule({ + imports: [DesktopHeaderComponent, HeaderComponent], + providers: [ + { + provide: I18nService, + useValue: mockI18nService, + }, + { + provide: ActivatedRoute, + useValue: mockActivatedRoute, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(DesktopHeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("creates component", () => { + expect(component).toBeTruthy(); + }); + + it("renders bit-header component", () => { + const compiled = fixture.nativeElement; + const headerElement = compiled.querySelector("bit-header"); + + expect(headerElement).toBeTruthy(); + }); + + describe("title resolution", () => { + it("uses title input when provided", () => { + fixture.componentRef.setInput("title", "Direct Title"); + fixture.detectChanges(); + + expect(component["resolvedTitle"]()).toBe("Direct Title"); + }); + + it("uses route data titleId when no direct title provided", () => { + mockActivatedRoute.data = of({ + pageTitle: { key: "sends" }, + }); + + fixture = TestBed.createComponent(DesktopHeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + + expect(mockI18nService.t).toHaveBeenCalledWith("sends"); + expect(component["resolvedTitle"]()).toBe("translated_sends"); + }); + + it("returns empty string when no title or route data provided", () => { + mockActivatedRoute.data = of({}); + + fixture = TestBed.createComponent(DesktopHeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + + expect(component["resolvedTitle"]()).toBe(""); + }); + + it("prioritizes direct title over route data", () => { + mockActivatedRoute.data = of({ + pageTitle: { key: "sends" }, + }); + + fixture = TestBed.createComponent(DesktopHeaderComponent); + component = fixture.componentInstance; + fixture.componentRef.setInput("title", "Override Title"); + fixture.detectChanges(); + + expect(component["resolvedTitle"]()).toBe("Override Title"); + }); + }); + + describe("icon input", () => { + it("accepts icon input", () => { + fixture.componentRef.setInput("icon", "bwi-send"); + fixture.detectChanges(); + + expect(component.icon()).toBe("bwi-send"); + }); + + it("defaults to undefined when no icon provided", () => { + expect(component.icon()).toBeUndefined(); + }); + }); + + describe("content projection", () => { + it("wraps bit-header component for slot pass-through", () => { + const compiled = fixture.nativeElement; + const bitHeader = compiled.querySelector("bit-header"); + + // Verify bit-header exists and can receive projected content + expect(bitHeader).toBeTruthy(); + }); + }); +}); diff --git a/apps/desktop/src/app/layout/header/desktop-header.component.ts b/apps/desktop/src/app/layout/header/desktop-header.component.ts new file mode 100644 index 00000000000..5a837f1ff5a --- /dev/null +++ b/apps/desktop/src/app/layout/header/desktop-header.component.ts @@ -0,0 +1,47 @@ +import { ChangeDetectionStrategy, Component, computed, inject, input } from "@angular/core"; +import { toSignal } from "@angular/core/rxjs-interop"; +import { ActivatedRoute } from "@angular/router"; +import { map } from "rxjs"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { HeaderComponent, BannerModule } from "@bitwarden/components"; + +@Component({ + selector: "app-header", + templateUrl: "./desktop-header.component.html", + imports: [BannerModule, HeaderComponent], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DesktopHeaderComponent { + private route = inject(ActivatedRoute); + private i18nService = inject(I18nService); + + /** + * Title to display in header (takes precedence over route data) + */ + readonly title = input(); + + /** + * Icon to show before the title + */ + readonly icon = input(); + + private readonly routeData = toSignal( + this.route.data.pipe( + map((params) => ({ + titleId: params["pageTitle"]?.["key"] as string | undefined, + })), + ), + { initialValue: { titleId: undefined } }, + ); + + protected readonly resolvedTitle = computed(() => { + const directTitle = this.title(); + if (directTitle) { + return directTitle; + } + + const titleId = this.routeData().titleId; + return titleId ? this.i18nService.t(titleId) : ""; + }); +} diff --git a/apps/desktop/src/app/layout/header/index.ts b/apps/desktop/src/app/layout/header/index.ts new file mode 100644 index 00000000000..793d90f81e5 --- /dev/null +++ b/apps/desktop/src/app/layout/header/index.ts @@ -0,0 +1 @@ +export { DesktopHeaderComponent } from "./desktop-header.component"; diff --git a/apps/desktop/src/app/services/init.service.ts b/apps/desktop/src/app/services/init.service.ts index 73c4d38d3b2..a6fd40cb998 100644 --- a/apps/desktop/src/app/services/init.service.ts +++ b/apps/desktop/src/app/services/init.service.ts @@ -1,5 +1,4 @@ -import { DOCUMENT } from "@angular/common"; -import { Inject, Injectable } from "@angular/core"; +import { Inject, Injectable, DOCUMENT } from "@angular/core"; import { firstValueFrom } from "rxjs"; import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction"; @@ -9,7 +8,6 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { DefaultVaultTimeoutService } from "@bitwarden/common/key-management/vault-timeout"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; @@ -55,7 +53,6 @@ export class InitService { private autotypeService: DesktopAutotypeService, private sdkLoadService: SdkLoadService, private biometricMessageHandlerService: BiometricMessageHandlerService, - private configService: ConfigService, @Inject(DOCUMENT) private document: Document, private readonly migrationRunner: MigrationRunner, ) {} @@ -66,7 +63,6 @@ export class InitService { await this.sshAgentService.init(); this.nativeMessagingService.init(); await this.migrationRunner.waitForCompletion(); // Desktop will run migrations in the main process - this.encryptService.init(this.configService); const accounts = await firstValueFrom(this.accountService.accounts$); const setUserKeyInMemoryPromises = []; diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 03d6eb5c908..66613efd115 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -1,11 +1,10 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { APP_INITIALIZER, NgModule } from "@angular/core"; -import { Router } from "@angular/router"; +import { ActivatedRoute, Router } from "@angular/router"; import { Subject, merge } from "rxjs"; -import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; -import { LoginApprovalDialogComponentServiceAbstraction } from "@bitwarden/angular/auth/login-approval"; +import { CollectionService, OrganizationUserApiService } from "@bitwarden/admin-console/common"; import { SetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.service.abstraction"; import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; import { @@ -37,6 +36,7 @@ import { } from "@bitwarden/auth/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyService as PolicyServiceAbstraction, InternalPolicyService, @@ -45,23 +45,31 @@ import { AccountService, AccountService as AccountServiceAbstraction, } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthRequestAnsweringService } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction"; import { AuthService, AuthService as AuthServiceAbstraction, } from "@bitwarden/common/auth/abstractions/auth.service"; import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; import { SsoLoginServiceAbstraction } from "@bitwarden/common/auth/abstractions/sso-login.service.abstraction"; +import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; +import { PendingAuthRequestsStateService } from "@bitwarden/common/auth/services/auth-request-answering/pending-auth-requests.state"; import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; 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 { KeyGenerationService } from "@bitwarden/common/key-management/crypto"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { WebCryptoFunctionService } from "@bitwarden/common/key-management/crypto/services/web-crypto-function.service"; -import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { + InternalMasterPasswordServiceAbstraction, + MasterPasswordServiceAbstraction, +} from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; import { DefaultProcessReloadService } from "@bitwarden/common/key-management/services/default-process-reload.service"; +import { SessionTimeoutTypeService } from "@bitwarden/common/key-management/session-timeout"; import { VaultTimeoutSettingsService, VaultTimeoutStringType, @@ -81,6 +89,7 @@ import { PlatformUtilsService, PlatformUtilsService as PlatformUtilsServiceAbstraction, } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { RegisterSdkService } from "@bitwarden/common/platform/abstractions/sdk/register-sdk.service"; import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; @@ -100,7 +109,9 @@ import { SystemService } from "@bitwarden/common/platform/services/system.servic import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state"; import { SyncService } from "@bitwarden/common/platform/sync"; import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { DialogService, ToastService } from "@bitwarden/components"; +import { GeneratorServicesModule } from "@bitwarden/generator-components"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { KdfConfigService, @@ -114,10 +125,18 @@ import { SessionTimeoutSettingsComponentService, } from "@bitwarden/key-management-ui"; import { SerializedMemoryStorageService } from "@bitwarden/storage-core"; -import { DefaultSshImportPromptService, SshImportPromptService } from "@bitwarden/vault"; +import { + DefaultSshImportPromptService, + SshImportPromptService, + VaultFilterServiceAbstraction, + VaultFilterService, + RoutedVaultFilterService, + RoutedVaultFilterBridgeService, + VAULT_FILTER_BASE_ROUTE, +} from "@bitwarden/vault"; -import { DesktopLoginApprovalDialogComponentService } from "../../auth/login/desktop-login-approval-dialog-component.service"; import { DesktopLoginComponentService } from "../../auth/login/desktop-login-component.service"; +import { DesktopAuthRequestAnsweringService } from "../../auth/services/auth-request-answering/desktop-auth-request-answering.service"; import { DesktopTwoFactorAuthDuoComponentService } from "../../auth/services/desktop-two-factor-auth-duo-component.service"; import { DesktopAutofillSettingsService } from "../../autofill/services/desktop-autofill-settings.service"; import { DesktopAutofillService } from "../../autofill/services/desktop-autofill.service"; @@ -128,7 +147,7 @@ import { DesktopBiometricsService } from "../../key-management/biometrics/deskto import { RendererBiometricsService } from "../../key-management/biometrics/renderer-biometrics.service"; import { ElectronKeyService } from "../../key-management/electron-key.service"; import { DesktopLockComponentService } from "../../key-management/lock/services/desktop-lock-component.service"; -import { DesktopSessionTimeoutSettingsComponentService } from "../../key-management/session-timeout/services/desktop-session-timeout-settings-component.service"; +import { DesktopSessionTimeoutTypeService } from "../../key-management/session-timeout/services/desktop-session-timeout-type.service"; import { flagEnabled } from "../../platform/flags"; import { DesktopSettingsService } from "../../platform/services/desktop-settings.service"; import { ElectronLogRendererService } from "../../platform/services/electron-log.renderer.service"; @@ -165,12 +184,12 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: BiometricsService, useClass: RendererBiometricsService, - deps: [], + deps: [TokenService], }), safeProvider({ provide: DesktopBiometricsService, useClass: RendererBiometricsService, - deps: [], + deps: [TokenService], }), safeProvider(NativeMessagingService), safeProvider(BiometricMessageHandlerService), @@ -200,8 +219,16 @@ const safeProviders: SafeProvider[] = [ // We manually override the value of SUPPORTS_SECURE_STORAGE here to avoid // the TokenService having to inject the PlatformUtilsService which introduces a // circular dependency on Desktop only. + // + // For Windows portable builds, we disable secure storage to ensure tokens are + // stored on disk (in bitwarden-appdata) rather than in Windows Credential + // Manager, making them portable across machines. This allows users to move the USB drive + // between computers while maintaining authentication. + // + // Note: Portable mode does not use secure storage for read/write/clear operations, + // preventing any collision with tokens from a regular desktop installation. provide: SUPPORTS_SECURE_STORAGE, - useValue: ELECTRON_SUPPORTS_SECURE_STORAGE, + useValue: ELECTRON_SUPPORTS_SECURE_STORAGE && !ipc.platform.isWindowsPortable, }), safeProvider({ provide: DEFAULT_VAULT_TIMEOUT, @@ -344,6 +371,7 @@ const safeProviders: SafeProvider[] = [ ConfigService, Fido2AuthenticatorServiceAbstraction, AccountService, + AuthService, PlatformUtilsService, ], }), @@ -404,6 +432,8 @@ const safeProviders: SafeProvider[] = [ OrganizationUserApiService, InternalUserDecryptionOptionsServiceAbstraction, MessagingServiceAbstraction, + AccountCryptographicStateService, + RegisterSdkService, ], }), safeProvider({ @@ -455,11 +485,6 @@ const safeProviders: SafeProvider[] = [ useClass: DefaultSsoComponentService, deps: [], }), - safeProvider({ - provide: LoginApprovalDialogComponentServiceAbstraction, - useClass: DesktopLoginApprovalDialogComponentService, - deps: [I18nServiceAbstraction], - }), safeProvider({ provide: SshImportPromptService, useClass: DefaultSshImportPromptService, @@ -477,6 +502,7 @@ const safeProviders: SafeProvider[] = [ PlatformUtilsServiceAbstraction, BillingAccountProfileStateService, DesktopAutotypeDefaultSettingPolicy, + LogService, ], }), safeProvider({ @@ -484,15 +510,61 @@ const safeProviders: SafeProvider[] = [ useClass: DesktopAutotypeDefaultSettingPolicy, deps: [AccountServiceAbstraction, AuthServiceAbstraction, InternalPolicyService, ConfigService], }), + safeProvider({ + provide: SessionTimeoutTypeService, + useClass: DesktopSessionTimeoutTypeService, + deps: [], + }), safeProvider({ provide: SessionTimeoutSettingsComponentService, - useClass: DesktopSessionTimeoutSettingsComponentService, - deps: [I18nServiceAbstraction], + useClass: SessionTimeoutSettingsComponentService, + deps: [I18nServiceAbstraction, SessionTimeoutTypeService, PolicyServiceAbstraction], + }), + safeProvider({ + provide: VaultFilterServiceAbstraction, + useClass: VaultFilterService, + deps: [ + OrganizationService, + FolderService, + CipherServiceAbstraction, + PolicyServiceAbstraction, + I18nServiceAbstraction, + StateProvider, + CollectionService, + AccountServiceAbstraction, + ], + }), + safeProvider({ + provide: VAULT_FILTER_BASE_ROUTE, + useValue: "/new-vault", + }), + safeProvider({ + provide: RoutedVaultFilterService, + useClass: RoutedVaultFilterService, + deps: [ActivatedRoute], + }), + safeProvider({ + provide: RoutedVaultFilterBridgeService, + useClass: RoutedVaultFilterBridgeService, + deps: [Router, RoutedVaultFilterService, VaultFilterServiceAbstraction], + }), + safeProvider({ + provide: AuthRequestAnsweringService, + useClass: DesktopAuthRequestAnsweringService, + deps: [ + AccountServiceAbstraction, + AuthService, + MasterPasswordServiceAbstraction, + MessagingServiceAbstraction, + PendingAuthRequestsStateService, + I18nServiceAbstraction, + LogService, + ], }), ]; @NgModule({ - imports: [JslibServicesModule], + imports: [JslibServicesModule, GeneratorServicesModule], declarations: [], // Do not register your dependency here! Add it to the typesafeProviders array using the helper function providers: safeProviders, diff --git a/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts b/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts index 717af25a1dc..9bb7d5077cf 100644 --- a/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts +++ b/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.spec.ts @@ -1,8 +1,10 @@ -import { MockProxy, mock } from "jest-mock-extended"; +import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject, of } from "rxjs"; import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; +import { DefaultSetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/default-set-initial-password.service.implementation"; import { + InitializeJitPasswordCredentials, SetInitialPasswordCredentials, SetInitialPasswordService, SetInitialPasswordUserType, @@ -15,15 +17,18 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; import { SetPasswordRequest } from "@bitwarden/common/auth/models/request/set-password.request"; +import { AccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/account-cryptographic-state.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { MasterPasswordSalt } from "@bitwarden/common/key-management/master-password/types/master-password.types"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { RegisterSdkService } from "@bitwarden/common/platform/abstractions/sdk/register-sdk.service"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { CsprngArray } from "@bitwarden/common/types/csprng"; -import { UserId } from "@bitwarden/common/types/guid"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { MasterKey, UserKey } from "@bitwarden/common/types/key"; import { DEFAULT_KDF_CONFIG, KdfConfigService, KeyService } from "@bitwarden/key-management"; @@ -43,6 +48,8 @@ describe("DesktopSetInitialPasswordService", () => { let organizationUserApiService: MockProxy; let userDecryptionOptionsService: MockProxy; let messagingService: MockProxy; + let accountCryptographicStateService: MockProxy; + let registerSdkService: MockProxy; beforeEach(() => { apiService = mock(); @@ -56,6 +63,8 @@ describe("DesktopSetInitialPasswordService", () => { organizationUserApiService = mock(); userDecryptionOptionsService = mock(); messagingService = mock(); + accountCryptographicStateService = mock(); + registerSdkService = mock(); sut = new DesktopSetInitialPasswordService( apiService, @@ -69,6 +78,8 @@ describe("DesktopSetInitialPasswordService", () => { organizationUserApiService, userDecryptionOptionsService, messagingService, + accountCryptographicStateService, + registerSdkService, ); }); @@ -175,4 +186,36 @@ describe("DesktopSetInitialPasswordService", () => { }); }); }); + + describe("initializePasswordJitPasswordUserV2Encryption(...)", () => { + it("should send a 'redrawMenu' message", async () => { + // Arrange + const credentials: InitializeJitPasswordCredentials = { + newPasswordHint: "newPasswordHint", + orgSsoIdentifier: "orgSsoIdentifier", + orgId: "orgId" as OrganizationId, + resetPasswordAutoEnroll: false, + newPassword: "newPassword123!", + salt: "user@example.com" as MasterPasswordSalt, + }; + const userId = "userId" as UserId; + + const superSpy = jest + .spyOn( + DefaultSetInitialPasswordService.prototype, + "initializePasswordJitPasswordUserV2Encryption", + ) + .mockResolvedValue(undefined); + + // Act + await sut.initializePasswordJitPasswordUserV2Encryption(credentials, userId); + + // Assert + expect(superSpy).toHaveBeenCalledWith(credentials, userId); + expect(messagingService.send).toHaveBeenCalledTimes(1); + expect(messagingService.send).toHaveBeenCalledWith("redrawMenu"); + + superSpy.mockRestore(); + }); + }); }); diff --git a/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.ts b/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.ts index 8de7e73fafe..f9fb8361056 100644 --- a/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.ts +++ b/apps/desktop/src/app/services/set-initial-password/desktop-set-initial-password.service.ts @@ -1,6 +1,7 @@ import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; import { DefaultSetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/default-set-initial-password.service.implementation"; import { + InitializeJitPasswordCredentials, SetInitialPasswordCredentials, SetInitialPasswordService, SetInitialPasswordUserType, @@ -9,10 +10,12 @@ import { InternalUserDecryptionOptionsServiceAbstraction } from "@bitwarden/auth import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; +import { AccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/account-cryptographic-state.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { RegisterSdkService } from "@bitwarden/common/platform/abstractions/sdk/register-sdk.service"; import { UserId } from "@bitwarden/common/types/guid"; import { KdfConfigService, KeyService } from "@bitwarden/key-management"; @@ -32,6 +35,8 @@ export class DesktopSetInitialPasswordService protected organizationUserApiService: OrganizationUserApiService, protected userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction, private messagingService: MessagingService, + protected accountCryptographicStateService: AccountCryptographicStateService, + protected registerSdkService: RegisterSdkService, ) { super( apiService, @@ -44,6 +49,8 @@ export class DesktopSetInitialPasswordService organizationApiService, organizationUserApiService, userDecryptionOptionsService, + accountCryptographicStateService, + registerSdkService, ); } @@ -56,4 +63,13 @@ export class DesktopSetInitialPasswordService this.messagingService.send("redrawMenu"); } + + override async initializePasswordJitPasswordUserV2Encryption( + credentials: InitializeJitPasswordCredentials, + userId: UserId, + ): Promise { + await super.initializePasswordJitPasswordUserV2Encryption(credentials, userId); + + this.messagingService.send("redrawMenu"); + } } diff --git a/apps/desktop/src/app/tools/export/export-desktop.component.html b/apps/desktop/src/app/tools/export/export-desktop.component.html index 9aa59c5a636..5f4f0058577 100644 --- a/apps/desktop/src/app/tools/export/export-desktop.component.html +++ b/apps/desktop/src/app/tools/export/export-desktop.component.html @@ -1,5 +1,5 @@ - {{ "exportVault" | i18n }} + {{ "exportNoun" | i18n }} - {{ "exportVault" | i18n }} + {{ "exportVerb" | i18n }} + } + +
    + + + + +
    +
    + + + @if (action() == "add" || action() == "edit") { + + } + + + @if (!action()) { + + } +
    +} diff --git a/apps/desktop/src/app/tools/send-v2/send-v2.component.spec.ts b/apps/desktop/src/app/tools/send-v2/send-v2.component.spec.ts new file mode 100644 index 00000000000..3670713f8f3 --- /dev/null +++ b/apps/desktop/src/app/tools/send-v2/send-v2.component.spec.ts @@ -0,0 +1,258 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore +import { ChangeDetectorRef } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { provideNoopAnimations } from "@angular/platform-browser/animations"; +import { ActivatedRoute } from "@angular/router"; +import { mock, MockProxy } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; +import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; +import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { SendType } from "@bitwarden/common/tools/send/types/send-type"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; +import { DialogService, ToastService } from "@bitwarden/components"; +import { + SendItemsService, + SendListFiltersService, + DefaultSendFormConfigService, + SendAddEditDialogComponent, + SendFormConfig, +} from "@bitwarden/send-ui"; + +import { SendV2Component } from "./send-v2.component"; + +describe("SendV2Component", () => { + let component: SendV2Component; + let fixture: ComponentFixture; + let sendService: MockProxy; + let accountService: MockProxy; + let policyService: MockProxy; + let sendItemsService: MockProxy; + let sendListFiltersService: MockProxy; + let changeDetectorRef: MockProxy; + let sendFormConfigService: MockProxy; + let dialogService: MockProxy; + let environmentService: MockProxy; + let platformUtilsService: MockProxy; + let sendApiService: MockProxy; + let toastService: MockProxy; + let i18nService: MockProxy; + let configService: MockProxy; + + beforeEach(async () => { + sendService = mock(); + accountService = mock(); + policyService = mock(); + changeDetectorRef = mock(); + sendFormConfigService = mock(); + dialogService = mock(); + environmentService = mock(); + platformUtilsService = mock(); + sendApiService = mock(); + toastService = mock(); + i18nService = mock(); + configService = mock(); + + // Setup configService mock - feature flag returns true to test the new drawer mode + configService.getFeatureFlag$.mockReturnValue(of(true)); + + // Setup environmentService mock + environmentService.getEnvironment.mockResolvedValue({ + getSendUrl: () => "https://send.bitwarden.com/#/", + } as any); + + // Setup i18nService mock + i18nService.t.mockImplementation((key: string) => key); + + // Mock SendItemsService with all required observables + sendItemsService = mock(); + sendItemsService.filteredAndSortedSends$ = of([]); + sendItemsService.loading$ = of(false); + sendItemsService.emptyList$ = of(false); + sendItemsService.noFilteredResults$ = of(false); + sendItemsService.latestSearchText$ = of(""); + + // Mock SendListFiltersService + sendListFiltersService = mock(); + + // Mock sendViews$ observable + sendService.sendViews$ = of([]); + + // Mock activeAccount$ observable + accountService.activeAccount$ = of({ id: "test-user-id" } as any); + policyService.policyAppliesToUser$ = jest.fn().mockReturnValue(of(false)); + + // Mock SearchService methods needed by base component + const mockSearchService = mock(); + mockSearchService.isSearchable.mockResolvedValue(false); + + await TestBed.configureTestingModule({ + imports: [SendV2Component], + providers: [ + provideNoopAnimations(), + { provide: SendService, useValue: sendService }, + { provide: I18nService, useValue: i18nService }, + { provide: PlatformUtilsService, useValue: platformUtilsService }, + { provide: EnvironmentService, useValue: environmentService }, + { provide: SearchService, useValue: mockSearchService }, + { provide: PolicyService, useValue: policyService }, + { provide: LogService, useValue: mock() }, + { provide: SendApiService, useValue: sendApiService }, + { provide: DialogService, useValue: dialogService }, + { provide: DefaultSendFormConfigService, useValue: sendFormConfigService }, + { provide: ToastService, useValue: toastService }, + { provide: AccountService, useValue: accountService }, + { provide: SendItemsService, useValue: sendItemsService }, + { provide: SendListFiltersService, useValue: sendListFiltersService }, + { provide: ChangeDetectorRef, useValue: changeDetectorRef }, + { + provide: BillingAccountProfileStateService, + useValue: mock(), + }, + { provide: MessagingService, useValue: mock() }, + { provide: ConfigService, useValue: configService }, + { + provide: ActivatedRoute, + useValue: { + data: of({}), + }, + }, + ], + }) + .overrideComponent(SendV2Component, { + set: { + providers: [ + { provide: DefaultSendFormConfigService, useValue: sendFormConfigService }, + { provide: PremiumUpgradePromptService, useValue: mock() }, + ], + }, + }) + .compileComponents(); + + fixture = TestBed.createComponent(SendV2Component); + component = fixture.componentInstance; + }); + + it("creates component", () => { + expect(component).toBeTruthy(); + }); + + describe("addSend", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it("opens dialog with correct config for Text send", async () => { + const mockConfig = { mode: "add", sendType: SendType.Text } as SendFormConfig; + const mockDialogRef = { closed: of(true) }; + + sendFormConfigService.buildConfig.mockResolvedValue(mockConfig); + const openDrawerSpy = jest + .spyOn(SendAddEditDialogComponent, "openDrawer") + .mockReturnValue(mockDialogRef as any); + + await component["addSend"](SendType.Text); + + expect(sendFormConfigService.buildConfig).toHaveBeenCalledWith( + "add", + undefined, + SendType.Text, + ); + expect(openDrawerSpy).toHaveBeenCalled(); + expect(openDrawerSpy.mock.calls[0][1]).toEqual({ + formConfig: mockConfig, + }); + }); + + it("opens dialog with correct config for File send", async () => { + const mockConfig = { mode: "add", sendType: SendType.File } as SendFormConfig; + const mockDialogRef = { closed: of(true) }; + + sendFormConfigService.buildConfig.mockResolvedValue(mockConfig); + const openDrawerSpy = jest + .spyOn(SendAddEditDialogComponent, "openDrawer") + .mockReturnValue(mockDialogRef as any); + + await component["addSend"](SendType.File); + + expect(sendFormConfigService.buildConfig).toHaveBeenCalledWith( + "add", + undefined, + SendType.File, + ); + expect(openDrawerSpy).toHaveBeenCalled(); + expect(openDrawerSpy.mock.calls[0][1]).toEqual({ + formConfig: mockConfig, + }); + }); + }); + + describe("selectSend", () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it("opens dialog with correct config for editing send", async () => { + const mockConfig = { mode: "edit", sendId: "test-send-id" } as SendFormConfig; + const mockDialogRef = { closed: of(true) }; + + sendFormConfigService.buildConfig.mockResolvedValue(mockConfig); + const openDrawerSpy = jest + .spyOn(SendAddEditDialogComponent, "openDrawer") + .mockReturnValue(mockDialogRef as any); + + await component["selectSend"]("test-send-id"); + + expect(sendFormConfigService.buildConfig).toHaveBeenCalledWith("edit", "test-send-id"); + expect(openDrawerSpy).toHaveBeenCalled(); + expect(openDrawerSpy.mock.calls[0][1]).toEqual({ + formConfig: mockConfig, + }); + }); + }); + + describe("onEditSend", () => { + it("selects the send for editing", async () => { + jest.spyOn(component as any, "selectSend").mockResolvedValue(undefined); + const mockSend = new SendView(); + mockSend.id = "edit-send-id"; + + await component["onEditSend"](mockSend); + + expect(component["selectSend"]).toHaveBeenCalledWith("edit-send-id"); + }); + }); + + describe("onCopySend", () => { + it("copies send link to clipboard and shows success toast", async () => { + const mockSend = { + accessId: "test-access-id", + urlB64Key: "test-key", + } as SendView; + + await component["onCopySend"](mockSend); + + expect(environmentService.getEnvironment).toHaveBeenCalled(); + expect(platformUtilsService.copyToClipboard).toHaveBeenCalledWith( + "https://send.bitwarden.com/#/test-access-id/test-key", + ); + expect(toastService.showToast).toHaveBeenCalledWith({ + variant: "success", + title: null, + message: expect.any(String), + }); + }); + }); +}); diff --git a/apps/desktop/src/app/tools/send-v2/send-v2.component.ts b/apps/desktop/src/app/tools/send-v2/send-v2.component.ts new file mode 100644 index 00000000000..0df71a78412 --- /dev/null +++ b/apps/desktop/src/app/tools/send-v2/send-v2.component.ts @@ -0,0 +1,287 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore +import { + ChangeDetectorRef, + Component, + computed, + effect, + inject, + signal, + viewChild, +} from "@angular/core"; +import { toSignal } from "@angular/core/rxjs-interop"; +import { combineLatest, map, switchMap, lastValueFrom } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; +import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; +import { SendType } from "@bitwarden/common/tools/send/types/send-type"; +import { SendId } from "@bitwarden/common/types/guid"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; +import { ButtonModule, DialogService, ToastService } from "@bitwarden/components"; +import { + NewSendDropdownV2Component, + SendItemsService, + SendListComponent, + SendListState, + SendAddEditDialogComponent, + DefaultSendFormConfigService, +} from "@bitwarden/send-ui"; + +import { DesktopPremiumUpgradePromptService } from "../../../services/desktop-premium-upgrade-prompt.service"; +import { DesktopHeaderComponent } from "../../layout/header"; +import { AddEditComponent } from "../send/add-edit.component"; + +const Action = Object.freeze({ + /** No action is currently active. */ + None: "", + /** The user is adding a new Send. */ + Add: "add", + /** The user is editing an existing Send. */ + Edit: "edit", +} as const); + +type Action = (typeof Action)[keyof typeof Action]; + +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection +@Component({ + selector: "app-send-v2", + imports: [ + JslibModule, + ButtonModule, + AddEditComponent, + SendListComponent, + NewSendDropdownV2Component, + DesktopHeaderComponent, + ], + providers: [ + DefaultSendFormConfigService, + { + provide: PremiumUpgradePromptService, + useClass: DesktopPremiumUpgradePromptService, + }, + ], + templateUrl: "./send-v2.component.html", +}) +export class SendV2Component { + protected readonly addEditComponent = viewChild(AddEditComponent); + + protected readonly sendId = signal(null); + protected readonly action = signal(Action.None); + + private sendFormConfigService = inject(DefaultSendFormConfigService); + private sendItemsService = inject(SendItemsService); + private policyService = inject(PolicyService); + private accountService = inject(AccountService); + private configService = inject(ConfigService); + private i18nService = inject(I18nService); + private platformUtilsService = inject(PlatformUtilsService); + private environmentService = inject(EnvironmentService); + private sendApiService = inject(SendApiService); + private dialogService = inject(DialogService); + private toastService = inject(ToastService); + private logService = inject(LogService); + private cdr = inject(ChangeDetectorRef); + + protected readonly useDrawerEditMode = toSignal( + this.configService.getFeatureFlag$(FeatureFlag.DesktopUiMigrationMilestone2), + { initialValue: false }, + ); + + protected readonly filteredSends = toSignal(this.sendItemsService.filteredAndSortedSends$, { + initialValue: [], + }); + + protected readonly loading = toSignal(this.sendItemsService.loading$, { initialValue: true }); + + protected readonly currentSearchText = toSignal(this.sendItemsService.latestSearchText$, { + initialValue: "", + }); + + protected readonly disableSend = toSignal( + this.accountService.activeAccount$.pipe( + getUserId, + switchMap((userId) => + this.policyService.policyAppliesToUser$(PolicyType.DisableSend, userId), + ), + ), + { initialValue: false }, + ); + + protected readonly listState = toSignal( + combineLatest([ + this.sendItemsService.emptyList$, + this.sendItemsService.noFilteredResults$, + ]).pipe( + map(([emptyList, noFilteredResults]): SendListState | null => { + if (emptyList) { + return SendListState.Empty; + } + if (noFilteredResults) { + return SendListState.NoResults; + } + return null; + }), + ), + { initialValue: null }, + ); + + constructor() { + // WORKAROUND: Force change detection when data updates + // This is needed because SendSearchComponent (shared lib) hasn't migrated to OnPush yet + // and doesn't trigger CD properly when search/add operations complete + // TODO: Remove this once SendSearchComponent migrates to OnPush (tracked in CL-764) + effect(() => { + this.filteredSends(); + this.cdr.markForCheck(); + }); + } + + protected readonly selectedSendType = computed(() => { + const action = this.action(); + + if (action === Action.Add) { + return undefined; + } + + const sendId = this.sendId(); + return this.filteredSends().find((s) => s.id === sendId)?.type; + }); + + protected async addSend(type: SendType): Promise { + if (this.useDrawerEditMode()) { + const formConfig = await this.sendFormConfigService.buildConfig("add", undefined, type); + + const dialogRef = SendAddEditDialogComponent.openDrawer(this.dialogService, { + formConfig, + }); + + await lastValueFrom(dialogRef.closed); + } else { + this.action.set(Action.Add); + this.sendId.set(null); + + this.cdr.detectChanges(); + void this.addEditComponent()?.resetAndLoad(); + } + } + + /** Used by old UI to add a send without specifying type (defaults to File) */ + protected async addSendWithoutType(): Promise { + await this.addSend(SendType.File); + } + + protected closeEditPanel(): void { + this.action.set(Action.None); + this.sendId.set(null); + } + + protected async savedSend(send: SendView): Promise { + await this.selectSend(send.id); + } + + protected async selectSend(sendId: string): Promise { + if (this.useDrawerEditMode()) { + const formConfig = await this.sendFormConfigService.buildConfig("edit", sendId as SendId); + + const dialogRef = SendAddEditDialogComponent.openDrawer(this.dialogService, { + formConfig, + }); + + await lastValueFrom(dialogRef.closed); + } else { + if (sendId === this.sendId() && this.action() === Action.Edit) { + return; + } + this.action.set(Action.Edit); + this.sendId.set(sendId); + const component = this.addEditComponent(); + if (component) { + component.sendId = sendId; + await component.refresh(); + } + } + } + + protected async onEditSend(send: SendView): Promise { + await this.selectSend(send.id); + } + + protected async onCopySend(send: SendView): Promise { + const env = await this.environmentService.getEnvironment(); + const link = env.getSendUrl() + send.accessId + "/" + send.urlB64Key; + this.platformUtilsService.copyToClipboard(link); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("valueCopied", this.i18nService.t("sendLink")), + }); + } + + protected async onRemovePassword(send: SendView): Promise { + if (this.disableSend()) { + return; + } + + const confirmed = await this.dialogService.openSimpleDialog({ + title: { key: "removePassword" }, + content: { key: "removePasswordConfirmation" }, + type: "warning", + }); + + if (!confirmed) { + return; + } + + try { + await this.sendApiService.removePassword(send.id); + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("removedPassword"), + }); + + if (!this.useDrawerEditMode() && this.sendId() === send.id) { + this.sendId.set(null); + await this.selectSend(send.id); + } + } catch (e) { + this.logService.error(e); + } + } + + protected async onDeleteSend(send: SendView): Promise { + const confirmed = await this.dialogService.openSimpleDialog({ + title: { key: "deleteSend" }, + content: { key: "deleteSendConfirmation" }, + type: "warning", + }); + + if (!confirmed) { + return; + } + + await this.sendApiService.delete(send.id); + + this.toastService.showToast({ + variant: "success", + title: null, + message: this.i18nService.t("deletedSend"), + }); + + if (!this.useDrawerEditMode()) { + this.closeEditPanel(); + } + } +} diff --git a/apps/desktop/src/app/tools/send/add-edit.component.html b/apps/desktop/src/app/tools/send/add-edit.component.html index 0bf2e1778e0..639c80c9060 100644 --- a/apps/desktop/src/app/tools/send/add-edit.component.html +++ b/apps/desktop/src/app/tools/send/add-edit.component.html @@ -46,7 +46,9 @@
    -
    {{ send.file.fileName }} ({{ send.file.sizeName }})
    +
    + {{ send.file.fileName }} ({{ send.file.sizeName }}) +
    diff --git a/apps/desktop/src/auth/login/desktop-login-approval-dialog-component.service.spec.ts b/apps/desktop/src/auth/login/desktop-login-approval-dialog-component.service.spec.ts deleted file mode 100644 index 2ae584d7e7f..00000000000 --- a/apps/desktop/src/auth/login/desktop-login-approval-dialog-component.service.spec.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { TestBed } from "@angular/core/testing"; -import { mock, MockProxy } from "jest-mock-extended"; -import { Subject } from "rxjs"; - -import { LoginApprovalDialogComponent } from "@bitwarden/angular/auth/login-approval"; -import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; - -import { DesktopLoginApprovalDialogComponentService } from "./desktop-login-approval-dialog-component.service"; - -describe("DesktopLoginApprovalDialogComponentService", () => { - let service: DesktopLoginApprovalDialogComponentService; - let i18nService: MockProxy; - let originalIpc: any; - - beforeEach(() => { - originalIpc = (global as any).ipc; - (global as any).ipc = { - auth: { - loginRequest: jest.fn(), - }, - platform: { - isWindowVisible: jest.fn(), - }, - }; - - i18nService = mock({ - t: jest.fn(), - userSetLocale$: new Subject(), - locale$: new Subject(), - }); - - TestBed.configureTestingModule({ - providers: [ - DesktopLoginApprovalDialogComponentService, - { provide: I18nServiceAbstraction, useValue: i18nService }, - ], - }); - - service = TestBed.inject(DesktopLoginApprovalDialogComponentService); - }); - - afterEach(() => { - jest.clearAllMocks(); - (global as any).ipc = originalIpc; - }); - - it("is created successfully", () => { - expect(service).toBeTruthy(); - }); - - it("calls ipc.auth.loginRequest with correct parameters when window is not visible", async () => { - const title = "Log in requested"; - const email = "test@bitwarden.com"; - const message = `Confirm access attempt for ${email}`; - const closeText = "Close"; - - const loginApprovalDialogComponent = { email } as LoginApprovalDialogComponent; - i18nService.t.mockImplementation((key: string) => { - switch (key) { - case "accountAccessRequested": - return title; - case "confirmAccessAttempt": - return message; - case "close": - return closeText; - default: - return ""; - } - }); - - jest.spyOn(ipc.platform, "isWindowVisible").mockResolvedValue(false); - jest.spyOn(ipc.auth, "loginRequest").mockResolvedValue(); - - await service.showLoginRequestedAlertIfWindowNotVisible(loginApprovalDialogComponent.email); - - expect(ipc.auth.loginRequest).toHaveBeenCalledWith(title, message, closeText); - }); - - it("does not call ipc.auth.loginRequest when window is visible", async () => { - const loginApprovalDialogComponent = { - email: "test@bitwarden.com", - } as LoginApprovalDialogComponent; - - jest.spyOn(ipc.platform, "isWindowVisible").mockResolvedValue(true); - jest.spyOn(ipc.auth, "loginRequest"); - - await service.showLoginRequestedAlertIfWindowNotVisible(loginApprovalDialogComponent.email); - - expect(ipc.auth.loginRequest).not.toHaveBeenCalled(); - }); -}); diff --git a/apps/desktop/src/auth/login/desktop-login-approval-dialog-component.service.ts b/apps/desktop/src/auth/login/desktop-login-approval-dialog-component.service.ts deleted file mode 100644 index 9c48f71990a..00000000000 --- a/apps/desktop/src/auth/login/desktop-login-approval-dialog-component.service.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Injectable } from "@angular/core"; - -import { - DefaultLoginApprovalDialogComponentService, - LoginApprovalDialogComponentServiceAbstraction, -} from "@bitwarden/angular/auth/login-approval"; -import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; - -@Injectable() -export class DesktopLoginApprovalDialogComponentService - extends DefaultLoginApprovalDialogComponentService - implements LoginApprovalDialogComponentServiceAbstraction -{ - constructor(private i18nService: I18nServiceAbstraction) { - super(); - } - - async showLoginRequestedAlertIfWindowNotVisible(email?: string): Promise { - const isVisible = await ipc.platform.isWindowVisible(); - if (!isVisible) { - await ipc.auth.loginRequest( - this.i18nService.t("accountAccessRequested"), - this.i18nService.t("confirmAccessAttempt", email), - this.i18nService.t("close"), - ); - } - } -} diff --git a/apps/desktop/src/auth/login/desktop-login-component.service.spec.ts b/apps/desktop/src/auth/login/desktop-login-component.service.spec.ts index c88627250c9..414bbaca56f 100644 --- a/apps/desktop/src/auth/login/desktop-login-component.service.spec.ts +++ b/apps/desktop/src/auth/login/desktop-login-component.service.spec.ts @@ -136,6 +136,7 @@ describe("DesktopLoginComponentService", () => { codeChallenge, state, email, + undefined, ); } else { expect(ssoLoginService.setSsoState).toHaveBeenCalledWith(state); @@ -145,4 +146,55 @@ describe("DesktopLoginComponentService", () => { }); }); }); + + describe("redirectToSsoLoginWithOrganizationSsoIdentifier", () => { + // Array of all permutations of isAppImage and isDev + const permutations = [ + [true, false], // Case 1: isAppImage true + [false, true], // Case 2: isDev true + [true, true], // Case 3: all true + [false, false], // Case 4: all false + ]; + + permutations.forEach(([isAppImage, isDev]) => { + it("calls redirectToSso with orgSsoIdentifier", async () => { + (global as any).ipc.platform.isAppImage = isAppImage; + (global as any).ipc.platform.isDev = isDev; + + const email = "test@bitwarden.com"; + const state = "testState"; + const codeVerifier = "testCodeVerifier"; + const codeChallenge = "testCodeChallenge"; + const orgSsoIdentifier = "orgSsoId"; + + passwordGenerationService.generatePassword.mockResolvedValueOnce(state); + passwordGenerationService.generatePassword.mockResolvedValueOnce(codeVerifier); + jest.spyOn(Utils, "fromBufferToUrlB64").mockReturnValue(codeChallenge); + + await service.redirectToSsoLoginWithOrganizationSsoIdentifier(email, orgSsoIdentifier); + + if (isAppImage || isDev) { + expect(ipc.platform.localhostCallbackService.openSsoPrompt).toHaveBeenCalledWith( + codeChallenge, + state, + email, + orgSsoIdentifier, + ); + } else { + expect(ssoUrlService.buildSsoUrl).toHaveBeenCalledWith( + expect.any(String), + expect.any(String), + expect.any(String), + expect.any(String), + expect.any(String), + email, + orgSsoIdentifier, + ); + expect(ssoLoginService.setSsoState).toHaveBeenCalledWith(state); + expect(ssoLoginService.setCodeVerifier).toHaveBeenCalledWith(codeVerifier); + expect(platformUtilsService.launchUri).toHaveBeenCalled(); + } + }); + }); + }); }); diff --git a/apps/desktop/src/auth/login/desktop-login-component.service.ts b/apps/desktop/src/auth/login/desktop-login-component.service.ts index d7e7ba0178b..6ef39eaa018 100644 --- a/apps/desktop/src/auth/login/desktop-login-component.service.ts +++ b/apps/desktop/src/auth/login/desktop-login-component.service.ts @@ -48,11 +48,12 @@ export class DesktopLoginComponentService email: string, state: string, codeChallenge: string, + orgSsoIdentifier?: string, ): Promise { // For platforms that cannot support a protocol-based (e.g. bitwarden://) callback, we use a localhost callback // Otherwise, we launch the SSO component in a browser window and wait for the callback if (ipc.platform.isAppImage || ipc.platform.isDev) { - await this.initiateSsoThroughLocalhostCallback(email, state, codeChallenge); + await this.initiateSsoThroughLocalhostCallback(email, state, codeChallenge, orgSsoIdentifier); } else { const env = await firstValueFrom(this.environmentService.environment$); const webVaultUrl = env.getWebVaultUrl(); @@ -66,6 +67,7 @@ export class DesktopLoginComponentService state, codeChallenge, email, + orgSsoIdentifier, ); this.platformUtilsService.launchUri(ssoWebAppUrl); @@ -76,9 +78,15 @@ export class DesktopLoginComponentService email: string, state: string, challenge: string, + orgSsoIdentifier?: string, ): Promise { try { - await ipc.platform.localhostCallbackService.openSsoPrompt(challenge, state, email); + await ipc.platform.localhostCallbackService.openSsoPrompt( + challenge, + state, + email, + orgSsoIdentifier, + ); // FIXME: Remove when updating file. Eslint update // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (err) { diff --git a/apps/desktop/src/auth/services/auth-request-answering/desktop-auth-request-answering.service.spec.ts b/apps/desktop/src/auth/services/auth-request-answering/desktop-auth-request-answering.service.spec.ts new file mode 100644 index 00000000000..2caaf713473 --- /dev/null +++ b/apps/desktop/src/auth/services/auth-request-answering/desktop-auth-request-answering.service.spec.ts @@ -0,0 +1,277 @@ +import { mock, MockProxy } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthRequestAnsweringService } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction"; +import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason"; +import { PendingAuthRequestsStateService } from "@bitwarden/common/auth/services/auth-request-answering/pending-auth-requests.state"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { mockAccountInfoWith } from "@bitwarden/common/spec"; +import { LogService } from "@bitwarden/logging"; +import { UserId } from "@bitwarden/user-core"; + +import { DesktopAuthRequestAnsweringService } from "./desktop-auth-request-answering.service"; + +describe("DesktopAuthRequestAnsweringService", () => { + let accountService: MockProxy; + let authService: MockProxy; + let masterPasswordService: any; // MasterPasswordServiceAbstraction has many members; we only use forceSetPasswordReason$ + let messagingService: MockProxy; + let pendingAuthRequestsState: MockProxy; + let i18nService: MockProxy; + let logService: MockProxy; + + let sut: AuthRequestAnsweringService; + + const userId = "9f4c3452-6a45-48af-a7d0-74d3e8b65e4c" as UserId; + const userAccountInfo = mockAccountInfoWith({ + name: "User", + email: "user@example.com", + }); + const userAccount: Account = { + id: userId, + ...userAccountInfo, + }; + + const authRequestId = "auth-request-id-123"; + + beforeEach(() => { + (global as any).ipc = { + platform: { + isWindowVisible: jest.fn(), + }, + auth: { + loginRequest: jest.fn(), + }, + }; + + accountService = mock(); + authService = mock(); + masterPasswordService = { + forceSetPasswordReason$: jest.fn().mockReturnValue(of(ForceSetPasswordReason.None)), + }; + messagingService = mock(); + pendingAuthRequestsState = mock(); + i18nService = mock(); + logService = mock(); + + // Common defaults + authService.activeAccountStatus$ = of(AuthenticationStatus.Locked); + accountService.activeAccount$ = of(userAccount); + accountService.accounts$ = of({ + [userId]: userAccountInfo, + }); + (global as any).ipc.platform.isWindowVisible.mockResolvedValue(false); + i18nService.t.mockImplementation( + (key: string, p1?: any) => `${key}${p1 != null ? ":" + p1 : ""}`, + ); + + sut = new DesktopAuthRequestAnsweringService( + accountService, + authService, + masterPasswordService, + messagingService, + pendingAuthRequestsState, + i18nService, + logService, + ); + }); + + describe("receivedPendingAuthRequest()", () => { + it("should throw if authRequestUserId not given", async () => { + // Act + const promise = sut.receivedPendingAuthRequest(undefined, undefined); + + // Assert + await expect(promise).rejects.toThrow("authRequestUserId required"); + }); + + it("should add a pending marker for the user to state", async () => { + // Act + await sut.receivedPendingAuthRequest(userId, authRequestId); + + // Assert + expect(pendingAuthRequestsState.add).toHaveBeenCalledTimes(1); + expect(pendingAuthRequestsState.add).toHaveBeenCalledWith(userId); + }); + + describe("given the active user is the intended recipient of the auth request, unlocked, and not required to set/change their master password", () => { + describe("given the Desktop window is visible", () => { + it("should send an 'openLoginApproval' message", async () => { + // Arrange + (global as any).ipc.platform.isWindowVisible.mockResolvedValue(true); + authService.activeAccountStatus$ = of(AuthenticationStatus.Unlocked); + + // Act + await sut.receivedPendingAuthRequest(userId, authRequestId); + + // Assert + expect(messagingService.send).toHaveBeenCalledTimes(1); + expect(messagingService.send).toHaveBeenCalledWith("openLoginApproval"); + }); + + it("should NOT create a system notification", async () => { + // Arrange + (global as any).ipc.platform.isWindowVisible.mockResolvedValue(true); + authService.activeAccountStatus$ = of(AuthenticationStatus.Unlocked); + + // Act + await sut.receivedPendingAuthRequest(userId, authRequestId); + + // Assert + expect((global as any).ipc.auth.loginRequest).not.toHaveBeenCalled(); + }); + }); + + describe("given the Desktop window is NOT visible", () => { + it("should STILL send an 'openLoginApproval' message", async () => { + // Arrange + (global as any).ipc.platform.isWindowVisible.mockResolvedValue(false); + authService.activeAccountStatus$ = of(AuthenticationStatus.Unlocked); + + // Act + await sut.receivedPendingAuthRequest(userId, authRequestId); + + // Assert + expect(messagingService.send).toHaveBeenCalledTimes(1); + expect(messagingService.send).toHaveBeenCalledWith("openLoginApproval"); + }); + + it("should create a system notification", async () => { + // Arrange + (global as any).ipc.platform.isWindowVisible.mockResolvedValue(false); + authService.activeAccountStatus$ = of(AuthenticationStatus.Unlocked); + + // Act + await sut.receivedPendingAuthRequest(userId, authRequestId); + + // Assert + expect(i18nService.t).toHaveBeenCalledWith("accountAccessRequested"); + expect(i18nService.t).toHaveBeenCalledWith("confirmAccessAttempt", "user@example.com"); + expect(i18nService.t).toHaveBeenCalledWith("close"); + + expect((global as any).ipc.auth.loginRequest).toHaveBeenCalledWith( + "accountAccessRequested", + "confirmAccessAttempt:user@example.com", + "close", + ); + }); + }); + }); + + describe("given the active user is Locked", () => { + it("should NOT send an 'openLoginApproval' message", async () => { + // Arrange + (global as any).ipc.platform.isWindowVisible.mockResolvedValue(true); + authService.activeAccountStatus$ = of(AuthenticationStatus.Locked); + + // Act + await sut.receivedPendingAuthRequest(userId, authRequestId); + + // Assert + expect(messagingService.send).not.toHaveBeenCalled(); + }); + + it("should create a system notification", async () => { + // Arrange + (global as any).ipc.platform.isWindowVisible.mockResolvedValue(true); + authService.activeAccountStatus$ = of(AuthenticationStatus.Locked); + + // Act + await sut.receivedPendingAuthRequest(userId, authRequestId); + + // Assert + expect((global as any).ipc.auth.loginRequest).toHaveBeenCalledWith( + "accountAccessRequested", + "confirmAccessAttempt:user@example.com", + "close", + ); + }); + }); + + describe("given the active user is not the intended recipient of the auth request", () => { + beforeEach(() => { + // Different active user for these tests + const differentUserId = "different-user-id" as UserId; + accountService.activeAccount$ = of({ + id: differentUserId, + ...mockAccountInfoWith({ + name: "Different User", + email: "different@example.com", + }), + }); + }); + + it("should NOT send an 'openLoginApproval' message", async () => { + // Arrange + (global as any).ipc.platform.isWindowVisible.mockResolvedValue(true); + authService.activeAccountStatus$ = of(AuthenticationStatus.Unlocked); + + // Act + // Pass in userId, not differentUserId (the active user), to mimic an auth + // request coming in for a user who is not the active user + await sut.receivedPendingAuthRequest(userId, authRequestId); // pass in userId, not differentUserId + + // Assert + expect(messagingService.send).not.toHaveBeenCalled(); + }); + + it("should create a system notification", async () => { + // Arrange + (global as any).ipc.platform.isWindowVisible.mockResolvedValue(true); + authService.activeAccountStatus$ = of(AuthenticationStatus.Unlocked); + + // Act + // Pass in userId, not differentUserId (the active user), to mimic an auth + // request coming in for a user who is not the active user + await sut.receivedPendingAuthRequest(userId, authRequestId); + + // Assert + expect((global as any).ipc.auth.loginRequest).toHaveBeenCalledWith( + "accountAccessRequested", + "confirmAccessAttempt:user@example.com", + "close", + ); + }); + }); + + describe("given the active user is required to set/change their master password", () => { + it("should NOT send an 'openLoginApproval' message", async () => { + // Arrange + (global as any).ipc.platform.isWindowVisible.mockResolvedValue(true); + authService.activeAccountStatus$ = of(AuthenticationStatus.Unlocked); + masterPasswordService.forceSetPasswordReason$ = jest + .fn() + .mockReturnValue(of(ForceSetPasswordReason.WeakMasterPassword)); + + // Act + await sut.receivedPendingAuthRequest(userId, authRequestId); + + // Assert + expect(messagingService.send).not.toHaveBeenCalled(); + }); + + it("should create a system notification", async () => { + // Arrange + (global as any).ipc.platform.isWindowVisible.mockResolvedValue(true); + authService.activeAccountStatus$ = of(AuthenticationStatus.Unlocked); + masterPasswordService.forceSetPasswordReason$ = jest + .fn() + .mockReturnValue(of(ForceSetPasswordReason.WeakMasterPassword)); + + // Act + await sut.receivedPendingAuthRequest(userId, authRequestId); + + // Assert + expect((global as any).ipc.auth.loginRequest).toHaveBeenCalledWith( + "accountAccessRequested", + "confirmAccessAttempt:user@example.com", + "close", + ); + }); + }); + }); +}); diff --git a/apps/desktop/src/auth/services/auth-request-answering/desktop-auth-request-answering.service.ts b/apps/desktop/src/auth/services/auth-request-answering/desktop-auth-request-answering.service.ts new file mode 100644 index 00000000000..3b2602660fe --- /dev/null +++ b/apps/desktop/src/auth/services/auth-request-answering/desktop-auth-request-answering.service.ts @@ -0,0 +1,85 @@ +import { firstValueFrom } from "rxjs"; + +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthRequestAnsweringService } from "@bitwarden/common/auth/abstractions/auth-request-answering/auth-request-answering.service.abstraction"; +import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { DefaultAuthRequestAnsweringService } from "@bitwarden/common/auth/services/auth-request-answering/default-auth-request-answering.service"; +import { PendingAuthRequestsStateService } from "@bitwarden/common/auth/services/auth-request-answering/pending-auth-requests.state"; +import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { LogService } from "@bitwarden/logging"; +import { UserId } from "@bitwarden/user-core"; + +export class DesktopAuthRequestAnsweringService + extends DefaultAuthRequestAnsweringService + implements AuthRequestAnsweringService +{ + constructor( + protected readonly accountService: AccountService, + protected readonly authService: AuthService, + protected readonly masterPasswordService: MasterPasswordServiceAbstraction, + protected readonly messagingService: MessagingService, + protected readonly pendingAuthRequestsState: PendingAuthRequestsStateService, + private readonly i18nService: I18nService, + private readonly logService: LogService, + ) { + super( + accountService, + authService, + masterPasswordService, + messagingService, + pendingAuthRequestsState, + ); + } + + /** + * @param authRequestUserId The UserId that the auth request is for. + * @param authRequestId The authRequestId param is not used on Desktop because clicks on a + * Desktop notification do not run any auth-request-specific actions. + * All clicks simply open the Desktop window. See electron-main-messaging.service.ts. + */ + async receivedPendingAuthRequest( + authRequestUserId: UserId, + authRequestId: string, + ): Promise { + if (!authRequestUserId) { + throw new Error("authRequestUserId required"); + } + + // Always persist the pending marker for this user to global state. + await this.pendingAuthRequestsState.add(authRequestUserId); + + const activeUserMeetsConditionsToShowApprovalDialog = + await this.activeUserMeetsConditionsToShowApprovalDialog(authRequestUserId); + + if (activeUserMeetsConditionsToShowApprovalDialog) { + // Send message to open dialog immediately for this request + this.messagingService.send("openLoginApproval"); + } + + const isWindowVisible = await ipc.platform.isWindowVisible(); + + // Create a system notification if either of the following are true: + // - User does NOT meet conditions to show dialog + // - User does meet conditions, but the Desktop window is not visible + // - In this second case, we both send the "openLoginApproval" message (above) AND + // also create the system notification to notify the user that the dialog is there. + if (!activeUserMeetsConditionsToShowApprovalDialog || !isWindowVisible) { + const accounts = await firstValueFrom(this.accountService.accounts$); + const accountInfo = accounts[authRequestUserId]; + + if (!accountInfo) { + this.logService.error("Account not found for authRequestUserId"); + return; + } + + const emailForUser = accountInfo.email; + await ipc.auth.loginRequest( + this.i18nService.t("accountAccessRequested"), + this.i18nService.t("confirmAccessAttempt", emailForUser), + this.i18nService.t("close"), + ); + } + } +} diff --git a/apps/desktop/src/platform/components/approve-ssh-request.html b/apps/desktop/src/autofill/components/approve-ssh-request.html similarity index 82% rename from apps/desktop/src/platform/components/approve-ssh-request.html rename to apps/desktop/src/autofill/components/approve-ssh-request.html index 55092788079..c691891487e 100644 --- a/apps/desktop/src/platform/components/approve-ssh-request.html +++ b/apps/desktop/src/autofill/components/approve-ssh-request.html @@ -2,13 +2,11 @@
    {{ "sshkeyApprovalTitle" | i18n }}
    - + @if (params.isAgentForwarding) { + {{ 'agentForwardingWarningText' | i18n }} - + + } {{params.applicationName}} {{ "sshkeyApprovalMessageInfix" | i18n }} {{params.cipherName}} diff --git a/apps/desktop/src/platform/components/approve-ssh-request.ts b/apps/desktop/src/autofill/components/approve-ssh-request.ts similarity index 98% rename from apps/desktop/src/platform/components/approve-ssh-request.ts rename to apps/desktop/src/autofill/components/approve-ssh-request.ts index 1741124774d..a2cae3d59e7 100644 --- a/apps/desktop/src/platform/components/approve-ssh-request.ts +++ b/apps/desktop/src/autofill/components/approve-ssh-request.ts @@ -12,6 +12,7 @@ import { FormFieldModule, IconButtonModule, DialogService, + CalloutModule, } from "@bitwarden/components"; export interface ApproveSshRequestParams { @@ -35,6 +36,7 @@ export interface ApproveSshRequestParams { ReactiveFormsModule, AsyncActionsModule, FormFieldModule, + CalloutModule, ], }) export class ApproveSshRequestComponent { diff --git a/apps/desktop/src/autofill/components/autotype-shortcut.component.html b/apps/desktop/src/autofill/components/autotype-shortcut.component.html index 6f73d4006ac..feb1f507c97 100644 --- a/apps/desktop/src/autofill/components/autotype-shortcut.component.html +++ b/apps/desktop/src/autofill/components/autotype-shortcut.component.html @@ -5,7 +5,7 @@

    - {{ "editAutotypeShortcutDescription" | i18n }} + {{ "editAutotypeKeyboardModifiersDescription" | i18n }}

    {{ "typeShortcut" | i18n }} diff --git a/apps/desktop/src/autofill/components/autotype-shortcut.component.spec.ts b/apps/desktop/src/autofill/components/autotype-shortcut.component.spec.ts index 90aa493c596..ea394274600 100644 --- a/apps/desktop/src/autofill/components/autotype-shortcut.component.spec.ts +++ b/apps/desktop/src/autofill/components/autotype-shortcut.component.spec.ts @@ -30,11 +30,9 @@ describe("AutotypeShortcutComponent", () => { const validShortcuts = [ "Control+A", "Alt+B", - "Shift+C", "Win+D", "control+e", // case insensitive "ALT+F", - "SHIFT+G", "WIN+H", ]; @@ -46,14 +44,7 @@ describe("AutotypeShortcutComponent", () => { }); it("should accept two modifiers with letter", () => { - const validShortcuts = [ - "Control+Alt+A", - "Control+Shift+B", - "Control+Win+C", - "Alt+Shift+D", - "Alt+Win+E", - "Shift+Win+F", - ]; + const validShortcuts = ["Control+Alt+A", "Control+Win+C", "Alt+Win+D", "Alt+Win+E"]; validShortcuts.forEach((shortcut) => { const control = createControl(shortcut); @@ -63,7 +54,7 @@ describe("AutotypeShortcutComponent", () => { }); it("should accept modifiers in different orders", () => { - const validShortcuts = ["Alt+Control+A", "Shift+Control+B", "Win+Alt+C"]; + const validShortcuts = ["Alt+Control+A", "Win+Control+B", "Win+Alt+C"]; validShortcuts.forEach((shortcut) => { const control = createControl(shortcut); @@ -88,15 +79,14 @@ describe("AutotypeShortcutComponent", () => { const invalidShortcuts = [ "Control+1", "Alt+2", - "Shift+3", "Win+4", "Control+!", "Alt+@", - "Shift+#", + "Alt+#", "Win+$", "Control+Space", "Alt+Enter", - "Shift+Tab", + "Control+Tab", "Win+Escape", ]; @@ -111,12 +101,10 @@ describe("AutotypeShortcutComponent", () => { const invalidShortcuts = [ "Control", "Alt", - "Shift", "Win", "Control+Alt", - "Control+Shift", - "Alt+Shift", - "Control+Alt+Shift", + "Control+Win", + "Control+Alt+Win", ]; invalidShortcuts.forEach((shortcut) => { @@ -127,7 +115,7 @@ describe("AutotypeShortcutComponent", () => { }); it("should reject shortcuts with invalid modifier names", () => { - const invalidShortcuts = ["Ctrl+A", "Command+A", "Super+A", "Meta+A", "Cmd+A", "Invalid+A"]; + const invalidShortcuts = ["Ctrl+A", "Command+A", "Meta+A", "Cmd+A", "Invalid+A"]; invalidShortcuts.forEach((shortcut) => { const control = createControl(shortcut); @@ -137,7 +125,7 @@ describe("AutotypeShortcutComponent", () => { }); it("should reject shortcuts with multiple base keys", () => { - const invalidShortcuts = ["Control+A+B", "Alt+Ctrl+Shift"]; + const invalidShortcuts = ["Control+A+B", "Alt+Ctrl+Win"]; invalidShortcuts.forEach((shortcut) => { const control = createControl(shortcut); @@ -148,11 +136,10 @@ describe("AutotypeShortcutComponent", () => { it("should reject shortcuts with more than two modifiers", () => { const invalidShortcuts = [ - "Control+Alt+Shift+A", + "Control+Alt+Win+A", "Control+Alt+Win+B", - "Control+Shift+Win+C", - "Alt+Shift+Win+D", - "Control+Alt+Shift+Win+E", + "Control+Alt+Win+C", + "Alt+Control+Win+D", ]; invalidShortcuts.forEach((shortcut) => { @@ -221,7 +208,7 @@ describe("AutotypeShortcutComponent", () => { }); it("should handle very long strings", () => { - const longString = "Control+Alt+Shift+Win+A".repeat(100); + const longString = "Control+Alt+Win+A".repeat(100); const control = createControl(longString); const result = validator(control); expect(result).toEqual({ invalidShortcut: { message: "Invalid shortcut" } }); @@ -230,7 +217,7 @@ describe("AutotypeShortcutComponent", () => { describe("modifier combinations", () => { it("should accept all possible single modifier combinations", () => { - const modifiers = ["Control", "Alt", "Shift", "Win"]; + const modifiers = ["Control", "Alt", "Win"]; modifiers.forEach((modifier) => { const control = createControl(`${modifier}+A`); @@ -240,14 +227,7 @@ describe("AutotypeShortcutComponent", () => { }); it("should accept all possible two-modifier combinations", () => { - const combinations = [ - "Control+Alt+A", - "Control+Shift+A", - "Control+Win+A", - "Alt+Shift+A", - "Alt+Win+A", - "Shift+Win+A", - ]; + const combinations = ["Control+Alt+A", "Control+Win+A", "Alt+Win+A"]; combinations.forEach((shortcut) => { const control = createControl(shortcut); @@ -257,12 +237,7 @@ describe("AutotypeShortcutComponent", () => { }); it("should reject all three-modifier combinations", () => { - const combinations = [ - "Control+Alt+Shift+A", - "Control+Alt+Win+A", - "Control+Shift+Win+A", - "Alt+Shift+Win+A", - ]; + const combinations = ["Control+Alt+Win+A", "Alt+Control+Win+A", "Win+Alt+Control+A"]; combinations.forEach((shortcut) => { const control = createControl(shortcut); @@ -270,12 +245,6 @@ describe("AutotypeShortcutComponent", () => { expect(result).toEqual({ invalidShortcut: { message: "Invalid shortcut" } }); }); }); - - it("should reject all four modifiers combination", () => { - const control = createControl("Control+Alt+Shift+Win+A"); - const result = validator(control); - expect(result).toEqual({ invalidShortcut: { message: "Invalid shortcut" } }); - }); }); }); }); diff --git a/apps/desktop/src/autofill/components/autotype-shortcut.component.ts b/apps/desktop/src/autofill/components/autotype-shortcut.component.ts index 3c82d8297a1..4e1a0c2108c 100644 --- a/apps/desktop/src/autofill/components/autotype-shortcut.component.ts +++ b/apps/desktop/src/autofill/components/autotype-shortcut.component.ts @@ -77,25 +77,31 @@ export class AutotypeShortcutComponent { } } + // private buildShortcutFromEvent(event: KeyboardEvent): string | null { const hasCtrl = event.ctrlKey; const hasAlt = event.altKey; const hasShift = event.shiftKey; - const hasMeta = event.metaKey; // Windows key on Windows, Command on macOS + const hasSuper = event.metaKey; // Windows key on Windows, Command on macOS - // Require at least one modifier (Control, Alt, Shift, or Super) - if (!hasCtrl && !hasAlt && !hasShift && !hasMeta) { + // Require at least one valid modifier (Control, Alt, Super) + if (!hasCtrl && !hasAlt && !hasSuper) { return null; } const key = event.key; - // Ignore pure modifier keys themselves - if (key === "Control" || key === "Alt" || key === "Shift" || key === "Meta") { + // disallow pure modifier keys themselves + if (key === "Control" || key === "Alt" || key === "Meta") { return null; } - // Accept a single alphabetical letter as the base key + // disallow shift modifier + if (hasShift) { + return null; + } + + // require a single alphabetical letter as the base key const isAlphabetical = typeof key === "string" && /^[a-z]$/i.test(key); if (!isAlphabetical) { return null; @@ -108,10 +114,7 @@ export class AutotypeShortcutComponent { if (hasAlt) { parts.push("Alt"); } - if (hasShift) { - parts.push("Shift"); - } - if (hasMeta) { + if (hasSuper) { parts.push("Super"); } parts.push(key.toUpperCase()); @@ -129,10 +132,9 @@ export class AutotypeShortcutComponent { } // Must include exactly 1-2 modifiers and end with a single letter - // Valid examples: Ctrl+A, Shift+Z, Ctrl+Shift+X, Alt+Shift+Q + // Valid examples: Ctrl+A, Alt+B, Ctrl+Alt+X, Alt+Control+Q, Win+B, Ctrl+Win+A // Allow modifiers in any order, but only 1-2 modifiers total - const pattern = - /^(?=.*\b(Control|Alt|Shift|Win)\b)(?:Control\+|Alt\+|Shift\+|Win\+){1,2}[A-Z]$/i; + const pattern = /^(?=.*\b(Control|Alt|Win)\b)(?:Control\+|Alt\+|Win\+){1,2}[A-Z]$/i; return pattern.test(value) ? null : { invalidShortcut: { message: this.i18nService.t("invalidShortcut") } }; diff --git a/apps/desktop/src/autofill/guards/reactive-vault-guard.ts b/apps/desktop/src/autofill/guards/reactive-vault-guard.ts new file mode 100644 index 00000000000..d16787ef46a --- /dev/null +++ b/apps/desktop/src/autofill/guards/reactive-vault-guard.ts @@ -0,0 +1,42 @@ +import { inject } from "@angular/core"; +import { CanActivateFn, Router } from "@angular/router"; +import { combineLatest, map, switchMap, distinctUntilChanged } from "rxjs"; + +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; + +import { DesktopSettingsService } from "../../platform/services/desktop-settings.service"; + +/** + * Reactive route guard that redirects to the unlocked vault. + * Redirects to vault when unlocked in main window. + */ +export const reactiveUnlockVaultGuard: CanActivateFn = () => { + const router = inject(Router); + const authService = inject(AuthService); + const accountService = inject(AccountService); + const desktopSettingsService = inject(DesktopSettingsService); + + return combineLatest([accountService.activeAccount$, desktopSettingsService.modalMode$]).pipe( + switchMap(([account, modalMode]) => { + if (!account) { + return [true]; + } + + // Monitor when the vault has been unlocked. + return authService.authStatusFor$(account.id).pipe( + distinctUntilChanged(), + map((authStatus) => { + // If vault is unlocked and we're not in modal mode, redirect to vault + if (authStatus === AuthenticationStatus.Unlocked && !modalMode?.isModalModeActive) { + return router.createUrlTree(["/vault"]); + } + + // Otherwise keep user on the lock screen + return true; + }), + ); + }), + ); +}; diff --git a/apps/desktop/src/autofill/main/main-desktop-autotype.service.spec.ts b/apps/desktop/src/autofill/main/main-desktop-autotype.service.spec.ts new file mode 100644 index 00000000000..92802d2e2cf --- /dev/null +++ b/apps/desktop/src/autofill/main/main-desktop-autotype.service.spec.ts @@ -0,0 +1,400 @@ +/* eslint-disable @typescript-eslint/no-unsafe-function-type */ + +import { TestBed } from "@angular/core/testing"; +import { ipcMain, globalShortcut } from "electron"; + +import { autotype } from "@bitwarden/desktop-napi"; +import { LogService } from "@bitwarden/logging"; + +import { WindowMain } from "../../main/window.main"; +import { AutotypeConfig } from "../models/autotype-config"; +import { AutotypeMatchError } from "../models/autotype-errors"; +import { AutotypeVaultData } from "../models/autotype-vault-data"; +import { AUTOTYPE_IPC_CHANNELS } from "../models/ipc-channels"; +import { AutotypeKeyboardShortcut } from "../models/main-autotype-keyboard-shortcut"; + +import { MainDesktopAutotypeService } from "./main-desktop-autotype.service"; + +// Mock electron modules +jest.mock("electron", () => ({ + ipcMain: { + on: jest.fn(), + removeAllListeners: jest.fn(), + }, + globalShortcut: { + register: jest.fn(), + unregister: jest.fn(), + isRegistered: jest.fn(), + }, +})); + +// Mock desktop-napi +jest.mock("@bitwarden/desktop-napi", () => ({ + autotype: { + getForegroundWindowTitle: jest.fn(), + typeInput: jest.fn(), + }, +})); + +// Mock AutotypeKeyboardShortcut +jest.mock("../models/main-autotype-keyboard-shortcut", () => ({ + AutotypeKeyboardShortcut: jest.fn().mockImplementation(() => ({ + set: jest.fn().mockReturnValue(true), + getElectronFormat: jest.fn().mockReturnValue("Control+Alt+B"), + getArrayFormat: jest.fn().mockReturnValue(["Control", "Alt", "B"]), + })), +})); + +describe("MainDesktopAutotypeService", () => { + let service: MainDesktopAutotypeService; + let mockLogService: jest.Mocked; + let mockWindowMain: jest.Mocked; + let ipcHandlers: Map; + + beforeEach(() => { + // Track IPC handlers + ipcHandlers = new Map(); + (ipcMain.on as jest.Mock).mockImplementation((channel: string, handler: Function) => { + ipcHandlers.set(channel, handler); + }); + + // Mock LogService + mockLogService = { + debug: jest.fn(), + error: jest.fn(), + info: jest.fn(), + warning: jest.fn(), + } as any; + + // Mock WindowMain with webContents + mockWindowMain = { + win: { + webContents: { + send: jest.fn(), + }, + }, + } as any; + + // Reset all mocks + jest.clearAllMocks(); + (globalShortcut.isRegistered as jest.Mock).mockReturnValue(false); + (globalShortcut.register as jest.Mock).mockReturnValue(true); + + TestBed.configureTestingModule({ + providers: [ + { provide: LogService, useValue: mockLogService }, + { provide: WindowMain, useValue: mockWindowMain }, + ], + }); + + // Create service manually since it doesn't use Angular DI + service = new MainDesktopAutotypeService(mockLogService, mockWindowMain); + }); + + afterEach(() => { + ipcHandlers.clear(); // Clear handler map + service.dispose(); + }); + + describe("constructor", () => { + it("should create service", () => { + expect(service).toBeTruthy(); + }); + + it("should initialize keyboard shortcut", () => { + expect(service.autotypeKeyboardShortcut).toBeDefined(); + }); + + it("should register IPC handlers", () => { + expect(ipcMain.on).toHaveBeenCalledWith(AUTOTYPE_IPC_CHANNELS.TOGGLE, expect.any(Function)); + expect(ipcMain.on).toHaveBeenCalledWith( + AUTOTYPE_IPC_CHANNELS.CONFIGURE, + expect.any(Function), + ); + expect(ipcMain.on).toHaveBeenCalledWith(AUTOTYPE_IPC_CHANNELS.EXECUTE, expect.any(Function)); + expect(ipcMain.on).toHaveBeenCalledWith( + "autofill.completeAutotypeError", + expect.any(Function), + ); + }); + }); + + describe("TOGGLE handler", () => { + it("should enable autotype when toggle is true", () => { + const toggleHandler = ipcHandlers.get(AUTOTYPE_IPC_CHANNELS.TOGGLE); + + toggleHandler({}, true); + + expect(globalShortcut.register).toHaveBeenCalled(); + expect(mockLogService.debug).toHaveBeenCalledWith("Autotype enabled."); + }); + + it("should disable autotype when toggle is false", () => { + (globalShortcut.isRegistered as jest.Mock).mockReturnValue(true); + const toggleHandler = ipcHandlers.get(AUTOTYPE_IPC_CHANNELS.TOGGLE); + + toggleHandler({}, false); + + expect(globalShortcut.unregister).toHaveBeenCalled(); + expect(mockLogService.debug).toHaveBeenCalledWith("Autotype disabled."); + }); + }); + + describe("CONFIGURE handler", () => { + it("should update keyboard shortcut with valid configuration", () => { + const config: AutotypeConfig = { + keyboardShortcut: ["Control", "Alt", "A"], + }; + + const mockNewShortcut = { + set: jest.fn().mockReturnValue(true), + getElectronFormat: jest.fn().mockReturnValue("Control+Alt+A"), + getArrayFormat: jest.fn().mockReturnValue(["Control", "Alt", "A"]), + }; + (AutotypeKeyboardShortcut as jest.Mock).mockReturnValue(mockNewShortcut); + + const configureHandler = ipcHandlers.get(AUTOTYPE_IPC_CHANNELS.CONFIGURE); + configureHandler({}, config); + + expect(mockNewShortcut.set).toHaveBeenCalledWith(config.keyboardShortcut); + }); + + it("should log error with invalid keyboard shortcut", () => { + const config: AutotypeConfig = { + keyboardShortcut: ["Invalid", "Keys"], + }; + + const mockNewShortcut = { + set: jest.fn().mockReturnValue(false), + getElectronFormat: jest.fn(), + getArrayFormat: jest.fn(), + }; + (AutotypeKeyboardShortcut as jest.Mock).mockReturnValue(mockNewShortcut); + + const configureHandler = ipcHandlers.get(AUTOTYPE_IPC_CHANNELS.CONFIGURE); + configureHandler({}, config); + + expect(mockLogService.error).toHaveBeenCalledWith( + "Configure autotype failed: the keyboard shortcut is invalid.", + ); + }); + + it("should register new shortcut if one already registered", () => { + (globalShortcut.isRegistered as jest.Mock) + .mockReturnValueOnce(true) + .mockReturnValueOnce(true) + .mockReturnValueOnce(false); + + const config: AutotypeConfig = { + keyboardShortcut: ["Control", "Alt", "B"], + }; + + const mockNewShortcut = { + set: jest.fn().mockReturnValue(true), + getElectronFormat: jest.fn().mockReturnValue("Control+Alt+B"), + getArrayFormat: jest.fn().mockReturnValue(["Control", "Alt", "B"]), + }; + (AutotypeKeyboardShortcut as jest.Mock).mockReturnValue(mockNewShortcut); + + const configureHandler = ipcHandlers.get(AUTOTYPE_IPC_CHANNELS.CONFIGURE); + configureHandler({}, config); + + expect(globalShortcut.unregister).toHaveBeenCalled(); + expect(globalShortcut.register).toHaveBeenCalled(); + }); + + it("should not change shortcut if it is the same", () => { + const config: AutotypeConfig = { + keyboardShortcut: ["Control", "Alt", "B"], + }; + + jest + .spyOn(service.autotypeKeyboardShortcut, "getElectronFormat") + .mockReturnValue("Control+Alt+B"); + + (globalShortcut.isRegistered as jest.Mock).mockReturnValue(true); + + const configureHandler = ipcHandlers.get(AUTOTYPE_IPC_CHANNELS.CONFIGURE); + configureHandler({}, config); + + expect(mockLogService.debug).toHaveBeenCalledWith( + "setKeyboardShortcut() called but shortcut is not different from current.", + ); + }); + }); + + describe("EXECUTE handler", () => { + it("should execute autotype with valid vault data", async () => { + const vaultData: AutotypeVaultData = { + username: "testuser", + password: "testpass", + }; + + jest + .spyOn(service.autotypeKeyboardShortcut, "getArrayFormat") + .mockReturnValue(["Control", "Alt", "B"]); + + const executeHandler = ipcHandlers.get(AUTOTYPE_IPC_CHANNELS.EXECUTE); + await executeHandler({}, vaultData); + + expect(autotype.typeInput).toHaveBeenCalledWith(expect.any(Array), ["Control", "Alt", "B"]); + }); + + it("should not execute autotype with empty username", () => { + const vaultData: AutotypeVaultData = { + username: "", + password: "testpass", + }; + + const executeHandler = ipcHandlers.get(AUTOTYPE_IPC_CHANNELS.EXECUTE); + executeHandler({}, vaultData); + + expect(autotype.typeInput).not.toHaveBeenCalled(); + }); + + it("should not execute autotype with empty password", () => { + const vaultData: AutotypeVaultData = { + username: "testuser", + password: "", + }; + + const executeHandler = ipcHandlers.get(AUTOTYPE_IPC_CHANNELS.EXECUTE); + executeHandler({}, vaultData); + + expect(autotype.typeInput).not.toHaveBeenCalled(); + }); + + it("should format input with tab separator", () => { + const mockNewShortcut = { + set: jest.fn().mockReturnValue(true), + getElectronFormat: jest.fn().mockReturnValue("Control+Alt+B"), + getArrayFormat: jest.fn().mockReturnValue(["Control", "Alt", "B"]), + }; + + (AutotypeKeyboardShortcut as jest.Mock).mockReturnValue(mockNewShortcut); + + const vaultData: AutotypeVaultData = { + username: "user", + password: "pass", + }; + + const executeHandler = ipcHandlers.get(AUTOTYPE_IPC_CHANNELS.EXECUTE); + executeHandler({}, vaultData); + + // Verify the input array contains char codes for "user\tpass" + const expectedPattern = "user\tpass"; + const expectedArray = Array.from(expectedPattern).map((c) => c.charCodeAt(0)); + + expect(autotype.typeInput).toHaveBeenCalledWith(expectedArray, ["Control", "Alt", "B"]); + }); + }); + + describe("completeAutotypeError handler", () => { + it("should log autotype match errors", () => { + const matchError: AutotypeMatchError = { + windowTitle: "Test Window", + errorMessage: "No matching vault item", + }; + + const errorHandler = ipcHandlers.get("autofill.completeAutotypeError"); + errorHandler({}, matchError); + + expect(mockLogService.debug).toHaveBeenCalledWith( + "autofill.completeAutotypeError", + "No match for window: Test Window", + ); + expect(mockLogService.error).toHaveBeenCalledWith( + "autofill.completeAutotypeError", + "No matching vault item", + ); + }); + }); + + describe("disableAutotype", () => { + it("should unregister shortcut if registered", () => { + (globalShortcut.isRegistered as jest.Mock).mockReturnValue(true); + + service.disableAutotype(); + + expect(globalShortcut.unregister).toHaveBeenCalled(); + expect(mockLogService.debug).toHaveBeenCalledWith("Autotype disabled."); + }); + + it("should log debug message if shortcut not registered", () => { + (globalShortcut.isRegistered as jest.Mock).mockReturnValue(false); + + service.disableAutotype(); + + expect(globalShortcut.unregister).not.toHaveBeenCalled(); + expect(mockLogService.debug).toHaveBeenCalledWith( + "Autotype is not registered, implicitly disabled.", + ); + }); + }); + + describe("dispose", () => { + it("should remove all IPC listeners", () => { + service.dispose(); + + expect(ipcMain.removeAllListeners).toHaveBeenCalledWith(AUTOTYPE_IPC_CHANNELS.TOGGLE); + expect(ipcMain.removeAllListeners).toHaveBeenCalledWith(AUTOTYPE_IPC_CHANNELS.CONFIGURE); + expect(ipcMain.removeAllListeners).toHaveBeenCalledWith(AUTOTYPE_IPC_CHANNELS.EXECUTE); + }); + + it("should disable autotype", () => { + (globalShortcut.isRegistered as jest.Mock).mockReturnValue(true); + + service.dispose(); + + expect(globalShortcut.unregister).toHaveBeenCalled(); + }); + }); + + describe("enableAutotype (via TOGGLE handler)", () => { + it("should register global shortcut", () => { + const toggleHandler = ipcHandlers.get(AUTOTYPE_IPC_CHANNELS.TOGGLE); + + toggleHandler({}, true); + + expect(globalShortcut.register).toHaveBeenCalledWith("Control+Alt+B", expect.any(Function)); + }); + + it("should not register if already registered", () => { + (globalShortcut.isRegistered as jest.Mock).mockReturnValue(true); + const toggleHandler = ipcHandlers.get(AUTOTYPE_IPC_CHANNELS.TOGGLE); + + toggleHandler({}, true); + + expect(globalShortcut.register).not.toHaveBeenCalled(); + expect(mockLogService.debug).toHaveBeenCalledWith( + "Autotype is already enabled with this keyboard shortcut: Control+Alt+B", + ); + }); + + it("should log error if registration fails", () => { + (globalShortcut.register as jest.Mock).mockReturnValue(false); + const toggleHandler = ipcHandlers.get(AUTOTYPE_IPC_CHANNELS.TOGGLE); + + toggleHandler({}, true); + + expect(mockLogService.error).toHaveBeenCalledWith("Failed to enable Autotype."); + }); + + it("should send window title to renderer on shortcut activation", () => { + (autotype.getForegroundWindowTitle as jest.Mock).mockReturnValue("Notepad"); + + const toggleHandler = ipcHandlers.get(AUTOTYPE_IPC_CHANNELS.TOGGLE); + toggleHandler({}, true); + + // Get the registered callback + const registeredCallback = (globalShortcut.register as jest.Mock).mock.calls[0][1]; + registeredCallback(); + + expect(autotype.getForegroundWindowTitle).toHaveBeenCalled(); + expect(mockWindowMain.win.webContents.send).toHaveBeenCalledWith( + AUTOTYPE_IPC_CHANNELS.LISTEN, + { windowTitle: "Notepad" }, + ); + }); + }); +}); diff --git a/apps/desktop/src/autofill/main/main-desktop-autotype.service.ts b/apps/desktop/src/autofill/main/main-desktop-autotype.service.ts index 4dcf05a4220..ea2bdd1fe12 100644 --- a/apps/desktop/src/autofill/main/main-desktop-autotype.service.ts +++ b/apps/desktop/src/autofill/main/main-desktop-autotype.service.ts @@ -5,51 +5,46 @@ import { LogService } from "@bitwarden/logging"; import { WindowMain } from "../../main/window.main"; import { stringIsNotUndefinedNullAndEmpty } from "../../utils"; +import { AutotypeConfig } from "../models/autotype-config"; import { AutotypeMatchError } from "../models/autotype-errors"; import { AutotypeVaultData } from "../models/autotype-vault-data"; +import { AUTOTYPE_IPC_CHANNELS } from "../models/ipc-channels"; import { AutotypeKeyboardShortcut } from "../models/main-autotype-keyboard-shortcut"; export class MainDesktopAutotypeService { - autotypeKeyboardShortcut: AutotypeKeyboardShortcut; + private autotypeKeyboardShortcut: AutotypeKeyboardShortcut; constructor( private logService: LogService, private windowMain: WindowMain, ) { this.autotypeKeyboardShortcut = new AutotypeKeyboardShortcut(); + + this.registerIpcListeners(); } - init() { - ipcMain.on("autofill.configureAutotype", (event, data) => { - if (data.enabled) { - const newKeyboardShortcut = new AutotypeKeyboardShortcut(); - const newKeyboardShortcutIsValid = newKeyboardShortcut.set(data.keyboardShortcut); - - if (newKeyboardShortcutIsValid) { - this.disableAutotype(); - this.autotypeKeyboardShortcut = newKeyboardShortcut; - this.enableAutotype(); - } else { - this.logService.error( - "Attempting to configure autotype but the shortcut given is invalid.", - ); - } + registerIpcListeners() { + ipcMain.on(AUTOTYPE_IPC_CHANNELS.TOGGLE, (_event, enable: boolean) => { + if (enable) { + this.enableAutotype(); } else { this.disableAutotype(); - - // Deregister the incoming keyboard shortcut if needed - const setCorrectly = this.autotypeKeyboardShortcut.set(data.keyboardShortcut); - if ( - setCorrectly && - globalShortcut.isRegistered(this.autotypeKeyboardShortcut.getElectronFormat()) - ) { - globalShortcut.unregister(this.autotypeKeyboardShortcut.getElectronFormat()); - this.logService.info("Autotype disabled."); - } } }); - ipcMain.on("autofill.completeAutotypeRequest", (_event, vaultData: AutotypeVaultData) => { + ipcMain.on(AUTOTYPE_IPC_CHANNELS.CONFIGURE, (_event, config: AutotypeConfig) => { + const newKeyboardShortcut = new AutotypeKeyboardShortcut(); + const newKeyboardShortcutIsValid = newKeyboardShortcut.set(config.keyboardShortcut); + + if (!newKeyboardShortcutIsValid) { + this.logService.error("Configure autotype failed: the keyboard shortcut is invalid."); + return; + } + + this.setKeyboardShortcut(newKeyboardShortcut); + }); + + ipcMain.on(AUTOTYPE_IPC_CHANNELS.EXECUTE, (_event, vaultData: AutotypeVaultData) => { if ( stringIsNotUndefinedNullAndEmpty(vaultData.username) && stringIsNotUndefinedNullAndEmpty(vaultData.password) @@ -67,30 +62,74 @@ export class MainDesktopAutotypeService { }); } + // Deregister the keyboard shortcut if registered. disableAutotype() { - // Deregister the current keyboard shortcut if needed const formattedKeyboardShortcut = this.autotypeKeyboardShortcut.getElectronFormat(); + if (globalShortcut.isRegistered(formattedKeyboardShortcut)) { globalShortcut.unregister(formattedKeyboardShortcut); - this.logService.info("Autotype disabled."); + this.logService.debug("Autotype disabled."); + } else { + this.logService.debug("Autotype is not registered, implicitly disabled."); } } + dispose() { + ipcMain.removeAllListeners(AUTOTYPE_IPC_CHANNELS.TOGGLE); + ipcMain.removeAllListeners(AUTOTYPE_IPC_CHANNELS.CONFIGURE); + ipcMain.removeAllListeners(AUTOTYPE_IPC_CHANNELS.EXECUTE); + + // Also unregister the global shortcut + this.disableAutotype(); + } + + // Register the current keyboard shortcut if not already registered. private enableAutotype() { + const formattedKeyboardShortcut = this.autotypeKeyboardShortcut.getElectronFormat(); + if (globalShortcut.isRegistered(formattedKeyboardShortcut)) { + this.logService.debug( + "Autotype is already enabled with this keyboard shortcut: " + formattedKeyboardShortcut, + ); + return; + } + const result = globalShortcut.register( this.autotypeKeyboardShortcut.getElectronFormat(), () => { const windowTitle = autotype.getForegroundWindowTitle(); - this.windowMain.win.webContents.send("autofill.listenAutotypeRequest", { + this.windowMain.win.webContents.send(AUTOTYPE_IPC_CHANNELS.LISTEN, { windowTitle, }); }, ); result - ? this.logService.info("Autotype enabled.") - : this.logService.info("Enabling autotype failed."); + ? this.logService.debug("Autotype enabled.") + : this.logService.error("Failed to enable Autotype."); + } + + // Set the keyboard shortcut if it differs from the present one. If + // the keyboard shortcut is set, de-register the old shortcut first. + private setKeyboardShortcut(keyboardShortcut: AutotypeKeyboardShortcut) { + if ( + keyboardShortcut.getElectronFormat() !== this.autotypeKeyboardShortcut.getElectronFormat() + ) { + const registered = globalShortcut.isRegistered( + this.autotypeKeyboardShortcut.getElectronFormat(), + ); + if (registered) { + this.disableAutotype(); + } + this.autotypeKeyboardShortcut = keyboardShortcut; + if (registered) { + this.enableAutotype(); + } + } else { + this.logService.debug( + "setKeyboardShortcut() called but shortcut is not different from current.", + ); + } } private doAutotype(vaultData: AutotypeVaultData, keyboardShortcut: string[]) { diff --git a/apps/desktop/src/autofill/main/main-ssh-agent.service.ts b/apps/desktop/src/autofill/main/main-ssh-agent.service.ts index 595ef778bcf..31196e4cf98 100644 --- a/apps/desktop/src/autofill/main/main-ssh-agent.service.ts +++ b/apps/desktop/src/autofill/main/main-ssh-agent.service.ts @@ -37,7 +37,7 @@ export class MainSshAgentService { init() { // handle sign request passing to UI sshagent - .serve(async (err: Error, sshUiRequest: sshagent.SshUiRequest) => { + .serve(async (err: Error | null, sshUiRequest: sshagent.SshUiRequest): Promise => { // clear all old (> SIGN_TIMEOUT) requests this.requestResponses = this.requestResponses.filter( (response) => response.timestamp > new Date(Date.now() - this.SIGN_TIMEOUT), diff --git a/apps/desktop/src/autofill/modal/credentials/fido2-create.component.html b/apps/desktop/src/autofill/modal/credentials/fido2-create.component.html new file mode 100644 index 00000000000..67fc76aa317 --- /dev/null +++ b/apps/desktop/src/autofill/modal/credentials/fido2-create.component.html @@ -0,0 +1,66 @@ +
    + + +
    + + +

    + {{ "savePasskeyQuestion" | i18n }} +

    +
    + + +
    +
    + + +
    +
    + +
    + {{ "noMatchingLoginsForSite" | i18n }} +
    + +
    +
    + + + + {{ c.subTitle }} + {{ "save" | i18n }} + + + + + + +
    +
    diff --git a/apps/desktop/src/autofill/modal/credentials/fido2-create.component.spec.ts b/apps/desktop/src/autofill/modal/credentials/fido2-create.component.spec.ts new file mode 100644 index 00000000000..dbef860aafe --- /dev/null +++ b/apps/desktop/src/autofill/modal/credentials/fido2-create.component.spec.ts @@ -0,0 +1,240 @@ +import { TestBed } from "@angular/core/testing"; +import { Router } from "@angular/router"; +import { mock, MockProxy } from "jest-mock-extended"; +import { BehaviorSubject, of } from "rxjs"; + +import { AccountService, Account } from "@bitwarden/common/auth/abstractions/account.service"; +import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { mockAccountInfoWith } from "@bitwarden/common/spec"; +import { UserId } from "@bitwarden/common/types/guid"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { DialogService } from "@bitwarden/components"; +import { PasswordRepromptService } from "@bitwarden/vault"; + +import { DesktopAutofillService } from "../../../autofill/services/desktop-autofill.service"; +import { DesktopSettingsService } from "../../../platform/services/desktop-settings.service"; +import { + DesktopFido2UserInterfaceService, + DesktopFido2UserInterfaceSession, +} from "../../services/desktop-fido2-user-interface.service"; + +import { Fido2CreateComponent } from "./fido2-create.component"; + +describe("Fido2CreateComponent", () => { + let component: Fido2CreateComponent; + let mockDesktopSettingsService: MockProxy; + let mockFido2UserInterfaceService: MockProxy; + let mockAccountService: MockProxy; + let mockCipherService: MockProxy; + let mockDesktopAutofillService: MockProxy; + let mockDialogService: MockProxy; + let mockDomainSettingsService: MockProxy; + let mockLogService: MockProxy; + let mockPasswordRepromptService: MockProxy; + let mockRouter: MockProxy; + let mockSession: MockProxy; + let mockI18nService: MockProxy; + + const activeAccountSubject = new BehaviorSubject({ + id: "test-user-id" as UserId, + ...mockAccountInfoWith({ + email: "test@example.com", + name: "Test User", + }), + }); + + beforeEach(async () => { + mockDesktopSettingsService = mock(); + mockFido2UserInterfaceService = mock(); + mockAccountService = mock(); + mockCipherService = mock(); + mockDesktopAutofillService = mock(); + mockDialogService = mock(); + mockDomainSettingsService = mock(); + mockLogService = mock(); + mockPasswordRepromptService = mock(); + mockRouter = mock(); + mockSession = mock(); + mockI18nService = mock(); + + mockFido2UserInterfaceService.getCurrentSession.mockReturnValue(mockSession); + mockAccountService.activeAccount$ = activeAccountSubject; + + await TestBed.configureTestingModule({ + providers: [ + Fido2CreateComponent, + { provide: DesktopSettingsService, useValue: mockDesktopSettingsService }, + { provide: DesktopFido2UserInterfaceService, useValue: mockFido2UserInterfaceService }, + { provide: AccountService, useValue: mockAccountService }, + { provide: CipherService, useValue: mockCipherService }, + { provide: DesktopAutofillService, useValue: mockDesktopAutofillService }, + { provide: DialogService, useValue: mockDialogService }, + { provide: DomainSettingsService, useValue: mockDomainSettingsService }, + { provide: LogService, useValue: mockLogService }, + { provide: PasswordRepromptService, useValue: mockPasswordRepromptService }, + { provide: Router, useValue: mockRouter }, + { provide: I18nService, useValue: mockI18nService }, + ], + }).compileComponents(); + + component = TestBed.inject(Fido2CreateComponent); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + function createMockCiphers(): CipherView[] { + const cipher1 = new CipherView(); + cipher1.id = "cipher-1"; + cipher1.name = "Test Cipher 1"; + cipher1.type = CipherType.Login; + cipher1.login = { + username: "test1@example.com", + uris: [{ uri: "https://example.com", match: null }], + matchesUri: jest.fn().mockReturnValue(true), + get hasFido2Credentials() { + return false; + }, + } as any; + cipher1.reprompt = CipherRepromptType.None; + cipher1.deletedDate = null; + + return [cipher1]; + } + + describe("ngOnInit", () => { + beforeEach(() => { + mockSession.getRpId.mockResolvedValue("example.com"); + Object.defineProperty(mockDesktopAutofillService, "lastRegistrationRequest", { + get: jest.fn().mockReturnValue({ + userHandle: new Uint8Array([1, 2, 3]), + }), + configurable: true, + }); + mockDomainSettingsService.getUrlEquivalentDomains.mockReturnValue(of(new Set())); + }); + + it("should initialize session and set show header to false", async () => { + const mockCiphers = createMockCiphers(); + mockCipherService.getAllDecrypted.mockResolvedValue(mockCiphers); + + await component.ngOnInit(); + + expect(mockFido2UserInterfaceService.getCurrentSession).toHaveBeenCalled(); + expect(component.session).toBe(mockSession); + }); + + it("should show error dialog when no active session found", async () => { + mockFido2UserInterfaceService.getCurrentSession.mockReturnValue(null); + mockDialogService.openSimpleDialog.mockResolvedValue(false); + + await component.ngOnInit(); + + expect(mockDialogService.openSimpleDialog).toHaveBeenCalledWith({ + title: { key: "unableToSavePasskey" }, + content: { key: "closeThisBitwardenWindow" }, + type: "danger", + acceptButtonText: { key: "closeThisWindow" }, + acceptAction: expect.any(Function), + cancelButtonText: null, + }); + }); + }); + + describe("addCredentialToCipher", () => { + beforeEach(() => { + component.session = mockSession; + }); + + it("should add passkey to cipher", async () => { + const cipher = createMockCiphers()[0]; + + await component.addCredentialToCipher(cipher); + + expect(mockSession.notifyConfirmCreateCredential).toHaveBeenCalledWith(true, cipher); + }); + + it("should not add passkey when password reprompt is cancelled", async () => { + const cipher = createMockCiphers()[0]; + cipher.reprompt = CipherRepromptType.Password; + mockPasswordRepromptService.showPasswordPrompt.mockResolvedValue(false); + + await component.addCredentialToCipher(cipher); + + expect(mockSession.notifyConfirmCreateCredential).toHaveBeenCalledWith(false, cipher); + }); + + it("should call openSimpleDialog when cipher already has a fido2 credential", async () => { + const cipher = createMockCiphers()[0]; + Object.defineProperty(cipher.login, "hasFido2Credentials", { + get: jest.fn().mockReturnValue(true), + }); + mockDialogService.openSimpleDialog.mockResolvedValue(true); + + await component.addCredentialToCipher(cipher); + + expect(mockDialogService.openSimpleDialog).toHaveBeenCalledWith({ + title: { key: "overwritePasskey" }, + content: { key: "alreadyContainsPasskey" }, + type: "warning", + }); + expect(mockSession.notifyConfirmCreateCredential).toHaveBeenCalledWith(true, cipher); + }); + + it("should not add passkey when user cancels overwrite dialog", async () => { + const cipher = createMockCiphers()[0]; + Object.defineProperty(cipher.login, "hasFido2Credentials", { + get: jest.fn().mockReturnValue(true), + }); + mockDialogService.openSimpleDialog.mockResolvedValue(false); + + await component.addCredentialToCipher(cipher); + + expect(mockSession.notifyConfirmCreateCredential).toHaveBeenCalledWith(false, cipher); + }); + }); + + describe("confirmPasskey", () => { + beforeEach(() => { + component.session = mockSession; + }); + + it("should confirm passkey creation successfully", async () => { + await component.confirmPasskey(); + + expect(mockSession.notifyConfirmCreateCredential).toHaveBeenCalledWith(true); + }); + + it("should call openSimpleDialog when session is null", async () => { + component.session = null; + mockDialogService.openSimpleDialog.mockResolvedValue(false); + + await component.confirmPasskey(); + + expect(mockDialogService.openSimpleDialog).toHaveBeenCalledWith({ + title: { key: "unableToSavePasskey" }, + content: { key: "closeThisBitwardenWindow" }, + type: "danger", + acceptButtonText: { key: "closeThisWindow" }, + acceptAction: expect.any(Function), + cancelButtonText: null, + }); + }); + }); + + describe("closeModal", () => { + it("should close modal and notify session", async () => { + component.session = mockSession; + + await component.closeModal(); + + expect(mockSession.notifyConfirmCreateCredential).toHaveBeenCalledWith(false); + expect(mockSession.confirmChosenCipher).toHaveBeenCalledWith(null); + }); + }); +}); diff --git a/apps/desktop/src/autofill/modal/credentials/fido2-create.component.ts b/apps/desktop/src/autofill/modal/credentials/fido2-create.component.ts new file mode 100644 index 00000000000..67237bedccd --- /dev/null +++ b/apps/desktop/src/autofill/modal/credentials/fido2-create.component.ts @@ -0,0 +1,219 @@ +import { CommonModule } from "@angular/common"; +import { ChangeDetectionStrategy, Component, OnInit, OnDestroy } from "@angular/core"; +import { RouterModule, Router } from "@angular/router"; +import { combineLatest, map, Observable, Subject, switchMap } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { BitwardenShield, NoResults } from "@bitwarden/assets/svg"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; +import { Fido2Utils } from "@bitwarden/common/platform/services/fido2/fido2-utils"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CipherType } from "@bitwarden/common/vault/enums/cipher-type"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { + DialogService, + BadgeModule, + ButtonModule, + DialogModule, + IconModule, + ItemModule, + SectionComponent, + TableModule, + SectionHeaderComponent, + BitIconButtonComponent, + SimpleDialogOptions, +} from "@bitwarden/components"; +import { PasswordRepromptService } from "@bitwarden/vault"; + +import { DesktopAutofillService } from "../../../autofill/services/desktop-autofill.service"; +import { DesktopSettingsService } from "../../../platform/services/desktop-settings.service"; +import { + DesktopFido2UserInterfaceService, + DesktopFido2UserInterfaceSession, +} from "../../services/desktop-fido2-user-interface.service"; + +@Component({ + standalone: true, + imports: [ + CommonModule, + RouterModule, + SectionHeaderComponent, + BitIconButtonComponent, + TableModule, + JslibModule, + IconModule, + ButtonModule, + DialogModule, + SectionComponent, + ItemModule, + BadgeModule, + ], + templateUrl: "fido2-create.component.html", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class Fido2CreateComponent implements OnInit, OnDestroy { + session?: DesktopFido2UserInterfaceSession = null; + ciphers$: Observable; + private destroy$ = new Subject(); + readonly Icons = { BitwardenShield, NoResults }; + + private get DIALOG_MESSAGES() { + return { + unexpectedErrorShort: { + title: { key: "unexpectedErrorShort" }, + content: { key: "closeThisBitwardenWindow" }, + type: "danger", + acceptButtonText: { key: "closeThisWindow" }, + cancelButtonText: null as null, + acceptAction: async () => this.dialogService.closeAll(), + }, + unableToSavePasskey: { + title: { key: "unableToSavePasskey" }, + content: { key: "closeThisBitwardenWindow" }, + type: "danger", + acceptButtonText: { key: "closeThisWindow" }, + cancelButtonText: null as null, + acceptAction: async () => this.dialogService.closeAll(), + }, + overwritePasskey: { + title: { key: "overwritePasskey" }, + content: { key: "alreadyContainsPasskey" }, + type: "warning", + }, + } as const satisfies Record; + } + + constructor( + private readonly desktopSettingsService: DesktopSettingsService, + private readonly fido2UserInterfaceService: DesktopFido2UserInterfaceService, + private readonly accountService: AccountService, + private readonly cipherService: CipherService, + private readonly desktopAutofillService: DesktopAutofillService, + private readonly dialogService: DialogService, + private readonly domainSettingsService: DomainSettingsService, + private readonly passwordRepromptService: PasswordRepromptService, + private readonly router: Router, + ) {} + + async ngOnInit(): Promise { + this.session = this.fido2UserInterfaceService.getCurrentSession(); + + if (this.session) { + const rpid = await this.session.getRpId(); + this.initializeCiphersObservable(rpid); + } else { + await this.showErrorDialog(this.DIALOG_MESSAGES.unableToSavePasskey); + } + } + + async ngOnDestroy(): Promise { + this.destroy$.next(); + this.destroy$.complete(); + await this.closeModal(); + } + + async addCredentialToCipher(cipher: CipherView): Promise { + const isConfirmed = await this.validateCipherAccess(cipher); + + try { + if (!this.session) { + throw new Error("Missing session"); + } + + this.session.notifyConfirmCreateCredential(isConfirmed, cipher); + } catch { + await this.showErrorDialog(this.DIALOG_MESSAGES.unableToSavePasskey); + return; + } + + await this.closeModal(); + } + + async confirmPasskey(): Promise { + try { + if (!this.session) { + throw new Error("Missing session"); + } + + this.session.notifyConfirmCreateCredential(true); + } catch { + await this.showErrorDialog(this.DIALOG_MESSAGES.unableToSavePasskey); + } + + await this.closeModal(); + } + + async closeModal(): Promise { + await this.desktopSettingsService.setModalMode(false); + await this.accountService.setShowHeader(true); + + if (this.session) { + this.session.notifyConfirmCreateCredential(false); + this.session.confirmChosenCipher(null); + } + + await this.router.navigate(["/"]); + } + + private initializeCiphersObservable(rpid: string): void { + const lastRegistrationRequest = this.desktopAutofillService.lastRegistrationRequest; + + if (!lastRegistrationRequest || !rpid) { + return; + } + + const userHandle = Fido2Utils.bufferToString( + new Uint8Array(lastRegistrationRequest.userHandle), + ); + + this.ciphers$ = combineLatest([ + this.accountService.activeAccount$.pipe(map((a) => a?.id)), + this.domainSettingsService.getUrlEquivalentDomains(rpid), + ]).pipe( + switchMap(async ([activeUserId, equivalentDomains]) => { + if (!activeUserId) { + return []; + } + + try { + const allCiphers = await this.cipherService.getAllDecrypted(activeUserId); + return allCiphers.filter( + (cipher) => + cipher != null && + cipher.type == CipherType.Login && + cipher.login?.matchesUri(rpid, equivalentDomains) && + Fido2Utils.cipherHasNoOtherPasskeys(cipher, userHandle) && + !cipher.deletedDate, + ); + } catch { + await this.showErrorDialog(this.DIALOG_MESSAGES.unexpectedErrorShort); + return []; + } + }), + ); + } + + private async validateCipherAccess(cipher: CipherView): Promise { + if (cipher.login.hasFido2Credentials) { + const overwriteConfirmed = await this.dialogService.openSimpleDialog( + this.DIALOG_MESSAGES.overwritePasskey, + ); + + if (!overwriteConfirmed) { + return false; + } + } + + if (cipher.reprompt) { + return this.passwordRepromptService.showPasswordPrompt(); + } + + return true; + } + + private async showErrorDialog(config: SimpleDialogOptions): Promise { + await this.dialogService.openSimpleDialog(config); + await this.closeModal(); + } +} diff --git a/apps/desktop/src/autofill/modal/credentials/fido2-excluded-ciphers.component.html b/apps/desktop/src/autofill/modal/credentials/fido2-excluded-ciphers.component.html new file mode 100644 index 00000000000..792934deedc --- /dev/null +++ b/apps/desktop/src/autofill/modal/credentials/fido2-excluded-ciphers.component.html @@ -0,0 +1,44 @@ +
    + + +
    + + +

    + {{ "savePasskeyQuestion" | i18n }} +

    +
    + + +
    +
    + +
    + +
    + +
    + {{ "passkeyAlreadyExists" | i18n }} + {{ "applicationDoesNotSupportDuplicates" | i18n }} +
    + +
    +
    +
    +
    diff --git a/apps/desktop/src/autofill/modal/credentials/fido2-excluded-ciphers.component.spec.ts b/apps/desktop/src/autofill/modal/credentials/fido2-excluded-ciphers.component.spec.ts new file mode 100644 index 00000000000..6a465136458 --- /dev/null +++ b/apps/desktop/src/autofill/modal/credentials/fido2-excluded-ciphers.component.spec.ts @@ -0,0 +1,78 @@ +import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { Router } from "@angular/router"; +import { mock, MockProxy } from "jest-mock-extended"; + +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + +import { DesktopSettingsService } from "../../../platform/services/desktop-settings.service"; +import { + DesktopFido2UserInterfaceService, + DesktopFido2UserInterfaceSession, +} from "../../services/desktop-fido2-user-interface.service"; + +import { Fido2ExcludedCiphersComponent } from "./fido2-excluded-ciphers.component"; + +describe("Fido2ExcludedCiphersComponent", () => { + let component: Fido2ExcludedCiphersComponent; + let fixture: ComponentFixture; + let mockDesktopSettingsService: MockProxy; + let mockFido2UserInterfaceService: MockProxy; + let mockAccountService: MockProxy; + let mockRouter: MockProxy; + let mockSession: MockProxy; + let mockI18nService: MockProxy; + + beforeEach(async () => { + mockDesktopSettingsService = mock(); + mockFido2UserInterfaceService = mock(); + mockAccountService = mock(); + mockRouter = mock(); + mockSession = mock(); + mockI18nService = mock(); + + mockFido2UserInterfaceService.getCurrentSession.mockReturnValue(mockSession); + + await TestBed.configureTestingModule({ + imports: [Fido2ExcludedCiphersComponent], + providers: [ + { provide: DesktopSettingsService, useValue: mockDesktopSettingsService }, + { provide: DesktopFido2UserInterfaceService, useValue: mockFido2UserInterfaceService }, + { provide: AccountService, useValue: mockAccountService }, + { provide: Router, useValue: mockRouter }, + { provide: I18nService, useValue: mockI18nService }, + ], + schemas: [NO_ERRORS_SCHEMA], + }).compileComponents(); + + fixture = TestBed.createComponent(Fido2ExcludedCiphersComponent); + component = fixture.componentInstance; + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + describe("ngOnInit", () => { + it("should initialize session", async () => { + await component.ngOnInit(); + + expect(mockFido2UserInterfaceService.getCurrentSession).toHaveBeenCalled(); + expect(component.session).toBe(mockSession); + }); + }); + + describe("closeModal", () => { + it("should close modal and notify session when session exists", async () => { + component.session = mockSession; + + await component.closeModal(); + + expect(mockDesktopSettingsService.setModalMode).toHaveBeenCalledWith(false); + expect(mockAccountService.setShowHeader).toHaveBeenCalledWith(true); + expect(mockSession.notifyConfirmCreateCredential).toHaveBeenCalledWith(false); + expect(mockRouter.navigate).toHaveBeenCalledWith(["/"]); + }); + }); +}); diff --git a/apps/desktop/src/autofill/modal/credentials/fido2-excluded-ciphers.component.ts b/apps/desktop/src/autofill/modal/credentials/fido2-excluded-ciphers.component.ts new file mode 100644 index 00000000000..049771c2252 --- /dev/null +++ b/apps/desktop/src/autofill/modal/credentials/fido2-excluded-ciphers.component.ts @@ -0,0 +1,78 @@ +import { CommonModule } from "@angular/common"; +import { ChangeDetectionStrategy, Component, OnInit, OnDestroy } from "@angular/core"; +import { RouterModule, Router } from "@angular/router"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { BitwardenShield, NoResults } from "@bitwarden/assets/svg"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { + BadgeModule, + ButtonModule, + DialogModule, + IconModule, + ItemModule, + SectionComponent, + TableModule, + SectionHeaderComponent, + BitIconButtonComponent, +} from "@bitwarden/components"; + +import { DesktopSettingsService } from "../../../platform/services/desktop-settings.service"; +import { + DesktopFido2UserInterfaceService, + DesktopFido2UserInterfaceSession, +} from "../../services/desktop-fido2-user-interface.service"; + +@Component({ + standalone: true, + imports: [ + CommonModule, + RouterModule, + SectionHeaderComponent, + BitIconButtonComponent, + TableModule, + JslibModule, + IconModule, + ButtonModule, + DialogModule, + SectionComponent, + ItemModule, + BadgeModule, + ], + templateUrl: "fido2-excluded-ciphers.component.html", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class Fido2ExcludedCiphersComponent implements OnInit, OnDestroy { + session?: DesktopFido2UserInterfaceSession = null; + readonly Icons = { BitwardenShield, NoResults }; + + constructor( + private readonly desktopSettingsService: DesktopSettingsService, + private readonly fido2UserInterfaceService: DesktopFido2UserInterfaceService, + private readonly accountService: AccountService, + private readonly router: Router, + ) {} + + async ngOnInit(): Promise { + this.session = this.fido2UserInterfaceService.getCurrentSession(); + } + + async ngOnDestroy(): Promise { + await this.closeModal(); + } + + async closeModal(): Promise { + // Clean up modal state + await this.desktopSettingsService.setModalMode(false); + await this.accountService.setShowHeader(true); + + // Clean up session state + if (this.session) { + this.session.notifyConfirmCreateCredential(false); + this.session.confirmChosenCipher(null); + } + + // Navigate away + await this.router.navigate(["/"]); + } +} diff --git a/apps/desktop/src/autofill/modal/credentials/fido2-vault.component.html b/apps/desktop/src/autofill/modal/credentials/fido2-vault.component.html new file mode 100644 index 00000000000..ed04993d09f --- /dev/null +++ b/apps/desktop/src/autofill/modal/credentials/fido2-vault.component.html @@ -0,0 +1,37 @@ +
    + + +
    + + +

    {{ "passkeyLogin" | i18n }}

    +
    + +
    +
    + + + + + {{ c.subTitle }} + {{ "select" | i18n }} + + + +
    diff --git a/apps/desktop/src/autofill/modal/credentials/fido2-vault.component.spec.ts b/apps/desktop/src/autofill/modal/credentials/fido2-vault.component.spec.ts new file mode 100644 index 00000000000..70ef4461f6a --- /dev/null +++ b/apps/desktop/src/autofill/modal/credentials/fido2-vault.component.spec.ts @@ -0,0 +1,196 @@ +import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { Router } from "@angular/router"; +import { mock, MockProxy } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { AccountService, Account } from "@bitwarden/common/auth/abstractions/account.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { PasswordRepromptService } from "@bitwarden/vault"; + +import { DesktopSettingsService } from "../../../platform/services/desktop-settings.service"; +import { + DesktopFido2UserInterfaceService, + DesktopFido2UserInterfaceSession, +} from "../../services/desktop-fido2-user-interface.service"; + +import { Fido2VaultComponent } from "./fido2-vault.component"; + +describe("Fido2VaultComponent", () => { + let component: Fido2VaultComponent; + let fixture: ComponentFixture; + let mockDesktopSettingsService: MockProxy; + let mockFido2UserInterfaceService: MockProxy; + let mockCipherService: MockProxy; + let mockAccountService: MockProxy; + let mockLogService: MockProxy; + let mockPasswordRepromptService: MockProxy; + let mockRouter: MockProxy; + let mockSession: MockProxy; + let mockI18nService: MockProxy; + + const mockActiveAccount = { id: "test-user-id", email: "test@example.com" }; + const mockCipherIds = ["cipher-1", "cipher-2", "cipher-3"]; + + beforeEach(async () => { + mockDesktopSettingsService = mock(); + mockFido2UserInterfaceService = mock(); + mockCipherService = mock(); + mockAccountService = mock(); + mockLogService = mock(); + mockPasswordRepromptService = mock(); + mockRouter = mock(); + mockSession = mock(); + mockI18nService = mock(); + + mockAccountService.activeAccount$ = of(mockActiveAccount as Account); + mockFido2UserInterfaceService.getCurrentSession.mockReturnValue(mockSession); + mockSession.availableCipherIds$ = of(mockCipherIds); + mockCipherService.cipherListViews$ = jest.fn().mockReturnValue(of([])); + + await TestBed.configureTestingModule({ + imports: [Fido2VaultComponent], + providers: [ + { provide: DesktopSettingsService, useValue: mockDesktopSettingsService }, + { provide: DesktopFido2UserInterfaceService, useValue: mockFido2UserInterfaceService }, + { provide: CipherService, useValue: mockCipherService }, + { provide: AccountService, useValue: mockAccountService }, + { provide: LogService, useValue: mockLogService }, + { provide: PasswordRepromptService, useValue: mockPasswordRepromptService }, + { provide: Router, useValue: mockRouter }, + { provide: I18nService, useValue: mockI18nService }, + ], + schemas: [NO_ERRORS_SCHEMA], + }).compileComponents(); + + fixture = TestBed.createComponent(Fido2VaultComponent); + component = fixture.componentInstance; + }); + + const mockCiphers: any[] = [ + { + id: "cipher-1", + name: "Test Cipher 1", + type: CipherType.Login, + login: { + username: "test1@example.com", + }, + reprompt: CipherRepromptType.None, + deletedDate: null, + }, + { + id: "cipher-2", + name: "Test Cipher 2", + type: CipherType.Login, + login: { + username: "test2@example.com", + }, + reprompt: CipherRepromptType.None, + deletedDate: null, + }, + { + id: "cipher-3", + name: "Test Cipher 3", + type: CipherType.Login, + login: { + username: "test3@example.com", + }, + reprompt: CipherRepromptType.Password, + deletedDate: null, + }, + ]; + + describe("ngOnInit", () => { + it("should initialize session and load ciphers successfully", async () => { + mockCipherService.cipherListViews$ = jest.fn().mockReturnValue(of(mockCiphers)); + + await component.ngOnInit(); + + expect(mockFido2UserInterfaceService.getCurrentSession).toHaveBeenCalled(); + expect(component.session).toBe(mockSession); + expect(component.cipherIds$).toBe(mockSession.availableCipherIds$); + expect(mockCipherService.cipherListViews$).toHaveBeenCalledWith(mockActiveAccount.id); + }); + + it("should handle when no active session found", async () => { + mockFido2UserInterfaceService.getCurrentSession.mockReturnValue(null); + + await component.ngOnInit(); + + expect(component.session).toBeNull(); + }); + + it("should filter out deleted ciphers", async () => { + const ciphersWithDeleted = [ + ...mockCiphers.slice(0, 1), + { ...mockCiphers[1], deletedDate: new Date() }, + ...mockCiphers.slice(2), + ]; + mockCipherService.cipherListViews$ = jest.fn().mockReturnValue(of(ciphersWithDeleted)); + + await component.ngOnInit(); + await new Promise((resolve) => setTimeout(resolve, 0)); + + let ciphersResult: CipherView[] = []; + component.ciphers$.subscribe((ciphers) => { + ciphersResult = ciphers; + }); + + expect(ciphersResult).toHaveLength(2); + expect(ciphersResult.every((cipher) => !cipher.deletedDate)).toBe(true); + }); + }); + + describe("chooseCipher", () => { + const cipher = mockCiphers[0]; + + beforeEach(() => { + component.session = mockSession; + }); + + it("should choose cipher when access is validated", async () => { + cipher.reprompt = CipherRepromptType.None; + + await component.chooseCipher(cipher); + + expect(mockSession.confirmChosenCipher).toHaveBeenCalledWith(cipher.id, true); + expect(mockRouter.navigate).toHaveBeenCalledWith(["/"]); + }); + + it("should prompt for password when cipher requires reprompt", async () => { + cipher.reprompt = CipherRepromptType.Password; + mockPasswordRepromptService.showPasswordPrompt.mockResolvedValue(true); + + await component.chooseCipher(cipher); + + expect(mockPasswordRepromptService.showPasswordPrompt).toHaveBeenCalled(); + expect(mockSession.confirmChosenCipher).toHaveBeenCalledWith(cipher.id, true); + }); + + it("should not choose cipher when password reprompt is cancelled", async () => { + cipher.reprompt = CipherRepromptType.Password; + mockPasswordRepromptService.showPasswordPrompt.mockResolvedValue(false); + + await component.chooseCipher(cipher); + + expect(mockPasswordRepromptService.showPasswordPrompt).toHaveBeenCalled(); + expect(mockSession.confirmChosenCipher).toHaveBeenCalledWith(cipher.id, false); + }); + }); + + describe("closeModal", () => { + it("should close modal and notify session", async () => { + component.session = mockSession; + + await component.closeModal(); + + expect(mockRouter.navigate).toHaveBeenCalledWith(["/"]); + expect(mockSession.notifyConfirmCreateCredential).toHaveBeenCalledWith(false); + expect(mockSession.confirmChosenCipher).toHaveBeenCalledWith(null); + }); + }); +}); diff --git a/apps/desktop/src/autofill/modal/credentials/fido2-vault.component.ts b/apps/desktop/src/autofill/modal/credentials/fido2-vault.component.ts new file mode 100644 index 00000000000..897e825c53e --- /dev/null +++ b/apps/desktop/src/autofill/modal/credentials/fido2-vault.component.ts @@ -0,0 +1,161 @@ +import { CommonModule } from "@angular/common"; +import { ChangeDetectionStrategy, Component, OnInit, OnDestroy } from "@angular/core"; +import { RouterModule, Router } from "@angular/router"; +import { + firstValueFrom, + map, + combineLatest, + of, + BehaviorSubject, + Observable, + Subject, + takeUntil, +} from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { BitwardenShield } from "@bitwarden/assets/svg"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CipherRepromptType } from "@bitwarden/common/vault/enums"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { + BadgeModule, + ButtonModule, + DialogModule, + DialogService, + IconModule, + ItemModule, + SectionComponent, + TableModule, + BitIconButtonComponent, + SectionHeaderComponent, +} from "@bitwarden/components"; +import { PasswordRepromptService } from "@bitwarden/vault"; + +import { DesktopSettingsService } from "../../../platform/services/desktop-settings.service"; +import { + DesktopFido2UserInterfaceService, + DesktopFido2UserInterfaceSession, +} from "../../services/desktop-fido2-user-interface.service"; + +@Component({ + standalone: true, + imports: [ + CommonModule, + RouterModule, + SectionHeaderComponent, + BitIconButtonComponent, + TableModule, + JslibModule, + IconModule, + ButtonModule, + DialogModule, + SectionComponent, + ItemModule, + BadgeModule, + ], + templateUrl: "fido2-vault.component.html", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class Fido2VaultComponent implements OnInit, OnDestroy { + session?: DesktopFido2UserInterfaceSession = null; + private destroy$ = new Subject(); + private ciphersSubject = new BehaviorSubject([]); + ciphers$: Observable = this.ciphersSubject.asObservable(); + cipherIds$: Observable | undefined; + readonly Icons = { BitwardenShield }; + + constructor( + private readonly desktopSettingsService: DesktopSettingsService, + private readonly fido2UserInterfaceService: DesktopFido2UserInterfaceService, + private readonly cipherService: CipherService, + private readonly accountService: AccountService, + private readonly dialogService: DialogService, + private readonly logService: LogService, + private readonly passwordRepromptService: PasswordRepromptService, + private readonly router: Router, + ) {} + + async ngOnInit(): Promise { + this.session = this.fido2UserInterfaceService.getCurrentSession(); + this.cipherIds$ = this.session?.availableCipherIds$; + await this.loadCiphers(); + } + + async ngOnDestroy(): Promise { + this.destroy$.next(); + this.destroy$.complete(); + } + + async chooseCipher(cipher: CipherView): Promise { + if (!this.session) { + await this.dialogService.openSimpleDialog({ + title: { key: "unexpectedErrorShort" }, + content: { key: "closeThisBitwardenWindow" }, + type: "danger", + acceptButtonText: { key: "closeThisWindow" }, + cancelButtonText: null, + }); + await this.closeModal(); + + return; + } + + const isConfirmed = await this.validateCipherAccess(cipher); + this.session.confirmChosenCipher(cipher.id, isConfirmed); + + await this.closeModal(); + } + + async closeModal(): Promise { + await this.desktopSettingsService.setModalMode(false); + await this.accountService.setShowHeader(true); + + if (this.session) { + this.session.notifyConfirmCreateCredential(false); + this.session.confirmChosenCipher(null); + } + + await this.router.navigate(["/"]); + } + + private async loadCiphers(): Promise { + const activeUserId = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.id)), + ); + + if (!activeUserId) { + return; + } + + // Combine cipher list with optional cipher IDs filter + combineLatest([this.cipherService.cipherListViews$(activeUserId), this.cipherIds$ || of(null)]) + .pipe( + map(([ciphers, cipherIds]) => { + // Filter out deleted ciphers + const activeCiphers = ciphers.filter((cipher) => !cipher.deletedDate); + + // If specific IDs provided, filter by them + if (cipherIds?.length > 0) { + return activeCiphers.filter((cipher) => cipherIds.includes(cipher.id as string)); + } + + return activeCiphers; + }), + takeUntil(this.destroy$), + ) + .subscribe({ + next: (ciphers) => this.ciphersSubject.next(ciphers as CipherView[]), + error: (error: unknown) => this.logService.error("Failed to load ciphers", error), + }); + } + + private async validateCipherAccess(cipher: CipherView): Promise { + if (cipher.reprompt !== CipherRepromptType.None) { + return this.passwordRepromptService.showPasswordPrompt(); + } + + return true; + } +} diff --git a/apps/desktop/src/autofill/models/autotype-config.ts b/apps/desktop/src/autofill/models/autotype-config.ts new file mode 100644 index 00000000000..dda39023c8c --- /dev/null +++ b/apps/desktop/src/autofill/models/autotype-config.ts @@ -0,0 +1,3 @@ +export interface AutotypeConfig { + keyboardShortcut: string[]; +} diff --git a/apps/desktop/src/autofill/models/ipc-channels.ts b/apps/desktop/src/autofill/models/ipc-channels.ts new file mode 100644 index 00000000000..5fea2daf0cf --- /dev/null +++ b/apps/desktop/src/autofill/models/ipc-channels.ts @@ -0,0 +1,9 @@ +export const AUTOTYPE_IPC_CHANNELS = { + INIT: "autofill.initAutotype", + INITIALIZED: "autofill.autotypeIsInitialized", + TOGGLE: "autofill.toggleAutotype", + CONFIGURE: "autofill.configureAutotype", + LISTEN: "autofill.listenAutotypeRequest", + EXECUTION_ERROR: "autofill.autotypeExecutionError", + EXECUTE: "autofill.executeAutotype", +} as const; diff --git a/apps/desktop/src/autofill/models/main-autotype-keyboard-shortcut.ts b/apps/desktop/src/autofill/models/main-autotype-keyboard-shortcut.ts index b26be92585e..8b241ade032 100644 --- a/apps/desktop/src/autofill/models/main-autotype-keyboard-shortcut.ts +++ b/apps/desktop/src/autofill/models/main-autotype-keyboard-shortcut.ts @@ -1,4 +1,14 @@ -import { defaultWindowsAutotypeKeyboardShortcut } from "../services/desktop-autotype.service"; +/** + Electron's representation of modifier keys + +*/ +export const CONTROL_KEY_STR = "Control"; +export const ALT_KEY_STR = "Alt"; +export const SUPER_KEY_STR = "Super"; + +export const VALID_SHORTCUT_MODIFIER_KEYS: string[] = [CONTROL_KEY_STR, ALT_KEY_STR, SUPER_KEY_STR]; + +export const DEFAULT_KEYBOARD_SHORTCUT: string[] = [CONTROL_KEY_STR, ALT_KEY_STR, "B"]; /* This class provides the following: @@ -13,7 +23,7 @@ export class AutotypeKeyboardShortcut { private autotypeKeyboardShortcut: string[]; constructor() { - this.autotypeKeyboardShortcut = defaultWindowsAutotypeKeyboardShortcut; + this.autotypeKeyboardShortcut = DEFAULT_KEYBOARD_SHORTCUT; } /* @@ -51,14 +61,16 @@ export class AutotypeKeyboardShortcut { This private function validates the strArray input to make sure the array contains valid, currently accepted shortcut keys for Windows. - Valid windows shortcut keys: Control, Alt, Super, Shift, letters A - Z - Valid macOS shortcut keys: Control, Alt, Command, Shift, letters A - Z (not yet supported) + Valid shortcut keys: Control, Alt, Super, letters A - Z + Platform specifics: + - On Windows, Super maps to the Windows key. + - On MacOS, Super maps to the Command key. + - On MacOS, Alt maps to the Option key. See Electron keyboard shorcut docs for more info: https://www.electronjs.org/docs/latest/tutorial/keyboard-shortcuts */ #keyboardShortcutIsValid(strArray: string[]) { - const VALID_SHORTCUT_CONTROL_KEYS: string[] = ["Control", "Alt", "Super", "Shift"]; const UNICODE_LOWER_BOUND = 65; // unicode 'A' const UNICODE_UPPER_BOUND = 90; // unicode 'Z' const MIN_LENGTH: number = 2; @@ -77,7 +89,7 @@ export class AutotypeKeyboardShortcut { // Ensure strArray is all modifier keys, and that the last key is a letter for (let i = 0; i < strArray.length; i++) { if (i < strArray.length - 1) { - if (!VALID_SHORTCUT_CONTROL_KEYS.includes(strArray[i])) { + if (!VALID_SHORTCUT_MODIFIER_KEYS.includes(strArray[i])) { return false; } } else { diff --git a/apps/desktop/src/autofill/preload.ts b/apps/desktop/src/autofill/preload.ts index e839ac223b7..f4f5552944c 100644 --- a/apps/desktop/src/autofill/preload.ts +++ b/apps/desktop/src/autofill/preload.ts @@ -5,13 +5,17 @@ import type { autofill } from "@bitwarden/desktop-napi"; import { Command } from "../platform/main/autofill/command"; import { RunCommandParams, RunCommandResult } from "../platform/main/autofill/native-autofill.main"; +import { AutotypeConfig } from "./models/autotype-config"; import { AutotypeMatchError } from "./models/autotype-errors"; import { AutotypeVaultData } from "./models/autotype-vault-data"; +import { AUTOTYPE_IPC_CHANNELS } from "./models/ipc-channels"; export default { runCommand: (params: RunCommandParams): Promise> => ipcRenderer.invoke("autofill.runCommand", params), + listenerReady: () => ipcRenderer.send("autofill.listenerReady"), + listenPasskeyRegistration: ( fn: ( clientId: number, @@ -130,8 +134,29 @@ export default { }, ); }, - configureAutotype: (enabled: boolean, keyboardShortcut: string[]) => { - ipcRenderer.send("autofill.configureAutotype", { enabled, keyboardShortcut }); + listenNativeStatus: ( + fn: (clientId: number, sequenceNumber: number, status: { key: string; value: string }) => void, + ) => { + ipcRenderer.on( + "autofill.nativeStatus", + ( + event, + data: { + clientId: number; + sequenceNumber: number; + status: { key: string; value: string }; + }, + ) => { + const { clientId, sequenceNumber, status } = data; + fn(clientId, sequenceNumber, status); + }, + ); + }, + configureAutotype: (config: AutotypeConfig) => { + ipcRenderer.send(AUTOTYPE_IPC_CHANNELS.CONFIGURE, config); + }, + toggleAutotype: (enable: boolean) => { + ipcRenderer.send(AUTOTYPE_IPC_CHANNELS.TOGGLE, enable); }, listenAutotypeRequest: ( fn: ( @@ -140,7 +165,7 @@ export default { ) => void, ) => { ipcRenderer.on( - "autofill.listenAutotypeRequest", + AUTOTYPE_IPC_CHANNELS.LISTEN, ( _event, data: { @@ -155,11 +180,12 @@ export default { windowTitle, errorMessage: error.message, }; - ipcRenderer.send("autofill.completeAutotypeError", matchError); + ipcRenderer.send(AUTOTYPE_IPC_CHANNELS.EXECUTION_ERROR, matchError); return; } + if (vaultData !== null) { - ipcRenderer.send("autofill.completeAutotypeRequest", vaultData); + ipcRenderer.send(AUTOTYPE_IPC_CHANNELS.EXECUTE, vaultData); } }); }, diff --git a/apps/desktop/src/autofill/services/desktop-autofill.service.ts b/apps/desktop/src/autofill/services/desktop-autofill.service.ts index 18f4652d72a..e5cd85aa7a3 100644 --- a/apps/desktop/src/autofill/services/desktop-autofill.service.ts +++ b/apps/desktop/src/autofill/services/desktop-autofill.service.ts @@ -1,6 +1,8 @@ import { Injectable, OnDestroy } from "@angular/core"; import { Subject, + combineLatest, + debounceTime, distinctUntilChanged, filter, firstValueFrom, @@ -8,10 +10,12 @@ import { mergeMap, switchMap, takeUntil, - EMPTY, + tap, } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { getOptionalUserId } from "@bitwarden/common/auth/services/account.service"; import { DeviceType } from "@bitwarden/common/enums"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; @@ -48,6 +52,9 @@ import type { NativeWindowObject } from "./desktop-fido2-user-interface.service" @Injectable() export class DesktopAutofillService implements OnDestroy { private destroy$ = new Subject(); + private registrationRequest: autofill.PasskeyRegistrationRequest; + private featureFlag?: FeatureFlag; + private isEnabled: boolean = false; constructor( private logService: LogService, @@ -55,41 +62,77 @@ export class DesktopAutofillService implements OnDestroy { private configService: ConfigService, private fido2AuthenticatorService: Fido2AuthenticatorServiceAbstraction, private accountService: AccountService, - private platformUtilsService: PlatformUtilsService, - ) {} + private authService: AuthService, + platformUtilsService: PlatformUtilsService, + ) { + const deviceType = platformUtilsService.getDevice(); + if (deviceType === DeviceType.MacOsDesktop) { + this.featureFlag = FeatureFlag.MacOsNativeCredentialSync; + } + } async init() { - // Currently only supported for MacOS - if (this.platformUtilsService.getDevice() !== DeviceType.MacOsDesktop) { + this.isEnabled = + this.featureFlag && (await this.configService.getFeatureFlag(this.featureFlag)); + if (!this.isEnabled) { return; } this.configService - .getFeatureFlag$(FeatureFlag.MacOsNativeCredentialSync) + .getFeatureFlag$(this.featureFlag) .pipe( distinctUntilChanged(), - switchMap((enabled) => { - if (!enabled) { - return EMPTY; - } - - return this.accountService.activeAccount$.pipe( - map((account) => account?.id), - filter((userId): userId is UserId => userId != null), - switchMap((userId) => this.cipherService.cipherViews$(userId)), + tap((enabled) => (this.isEnabled = enabled)), + filter((enabled) => enabled === true), // Only proceed if feature is enabled + switchMap(() => { + return combineLatest([ + this.accountService.activeAccount$.pipe( + map((account) => account?.id), + filter((userId): userId is UserId => userId != null), + ), + this.authService.activeAccountStatus$, + ]).pipe( + // Only proceed when the vault is unlocked + filter(([, status]) => status === AuthenticationStatus.Unlocked), + // Then get cipher views + switchMap(([userId]) => this.cipherService.cipherViews$(userId)), ); }), - // TODO: This will unset all the autofill credentials on the OS - // when the account locks. We should instead explicilty clear the credentials - // when the user logs out. Maybe by subscribing to the encrypted ciphers observable instead. + debounceTime(100), // just a precaution to not spam the sync if there are multiple changes (we typically observe a null change) + // No filter for empty arrays here - we want to sync even if there are 0 items + filter((cipherViewMap) => cipherViewMap !== null), + mergeMap((cipherViewMap) => this.sync(Object.values(cipherViewMap ?? []))), takeUntil(this.destroy$), ) .subscribe(); + // Listen for sign out to clear credentials + this.authService.activeAccountStatus$ + .pipe( + filter((status) => status === AuthenticationStatus.LoggedOut), + mergeMap(() => this.sync([])), // sync an empty array + takeUntil(this.destroy$), + ) + .subscribe(); + this.listenIpc(); } + async adHocSync(): Promise { + this.logService.debug("Performing AdHoc sync"); + const account = await firstValueFrom(this.accountService.activeAccount$); + const userId = account?.id; + + if (!userId) { + throw new Error("No active user found"); + } + + const cipherViewMap = await firstValueFrom(this.cipherService.cipherViews$(userId)); + this.logService.info("Performing AdHoc sync", Object.values(cipherViewMap ?? [])); + await this.sync(Object.values(cipherViewMap ?? [])); + } + /** Give metadata about all available credentials in the users vault */ async sync(cipherViews: CipherView[]) { const status = await this.status(); @@ -130,6 +173,11 @@ export class DesktopAutofillService implements OnDestroy { })); } + this.logService.info("Syncing autofill credentials", { + fido2Credentials, + passwordCredentials, + }); + const syncResult = await ipc.autofill.runCommand({ namespace: "autofill", command: "sync", @@ -155,107 +203,152 @@ export class DesktopAutofillService implements OnDestroy { }); } + get lastRegistrationRequest() { + return this.registrationRequest; + } + listenIpc() { - ipc.autofill.listenPasskeyRegistration((clientId, sequenceNumber, request, callback) => { - this.logService.warning("listenPasskeyRegistration", clientId, sequenceNumber, request); - this.logService.warning( - "listenPasskeyRegistration2", - this.convertRegistrationRequest(request), - ); + ipc.autofill.listenPasskeyRegistration(async (clientId, sequenceNumber, request, callback) => { + if (!this.isEnabled) { + this.logService.debug( + `listenPasskeyRegistration: Native credential sync feature flag (${this.featureFlag}) is disabled`, + ); + callback(new Error("Native credential sync feature flag is disabled"), null); + return; + } + + this.registrationRequest = request; + + this.logService.debug("listenPasskeyRegistration", clientId, sequenceNumber, request); + this.logService.debug("listenPasskeyRegistration2", this.convertRegistrationRequest(request)); const controller = new AbortController(); - void this.fido2AuthenticatorService - .makeCredential( + + try { + const response = await this.fido2AuthenticatorService.makeCredential( this.convertRegistrationRequest(request), - { windowXy: request.windowXy }, + { windowXy: normalizePosition(request.windowXy) }, controller, - ) - .then((response) => { - callback(null, this.convertRegistrationResponse(request, response)); - }) - .catch((error) => { - this.logService.error("listenPasskeyRegistration error", error); - callback(error, null); - }); + ); + + callback(null, this.convertRegistrationResponse(request, response)); + } catch (error) { + this.logService.error("listenPasskeyRegistration error", error); + callback(error, null); + } }); ipc.autofill.listenPasskeyAssertionWithoutUserInterface( async (clientId, sequenceNumber, request, callback) => { - this.logService.warning( + if (!this.isEnabled) { + this.logService.debug( + `listenPasskeyAssertionWithoutUserInterface: Native credential sync feature flag (${this.featureFlag}) is disabled`, + ); + callback(new Error("Native credential sync feature flag is disabled"), null); + return; + } + + this.logService.debug( "listenPasskeyAssertion without user interface", clientId, sequenceNumber, request, ); - // For some reason the credentialId is passed as an empty array in the request, so we need to - // get it from the cipher. For that we use the recordIdentifier, which is the cipherId. - if (request.recordIdentifier && request.credentialId.length === 0) { - const activeUserId = await firstValueFrom( - this.accountService.activeAccount$.pipe(getOptionalUserId), - ); - if (!activeUserId) { - this.logService.error("listenPasskeyAssertion error", "Active user not found"); - callback(new Error("Active user not found"), null); - return; - } - - const cipher = await this.cipherService.get(request.recordIdentifier, activeUserId); - if (!cipher) { - this.logService.error("listenPasskeyAssertion error", "Cipher not found"); - callback(new Error("Cipher not found"), null); - return; - } - - const decrypted = await this.cipherService.decrypt(cipher, activeUserId); - - const fido2Credential = decrypted.login.fido2Credentials?.[0]; - if (!fido2Credential) { - this.logService.error("listenPasskeyAssertion error", "Fido2Credential not found"); - callback(new Error("Fido2Credential not found"), null); - return; - } - - request.credentialId = Array.from( - new Uint8Array(parseCredentialId(decrypted.login.fido2Credentials?.[0].credentialId)), - ); - } - const controller = new AbortController(); - void this.fido2AuthenticatorService - .getAssertion( - this.convertAssertionRequest(request), - { windowXy: request.windowXy }, + + try { + // For some reason the credentialId is passed as an empty array in the request, so we need to + // get it from the cipher. For that we use the recordIdentifier, which is the cipherId. + if (request.recordIdentifier && request.credentialId.length === 0) { + const activeUserId = await firstValueFrom( + this.accountService.activeAccount$.pipe(getOptionalUserId), + ); + if (!activeUserId) { + this.logService.error("listenPasskeyAssertion error", "Active user not found"); + callback(new Error("Active user not found"), null); + return; + } + + const cipher = await this.cipherService.get(request.recordIdentifier, activeUserId); + if (!cipher) { + this.logService.error("listenPasskeyAssertion error", "Cipher not found"); + callback(new Error("Cipher not found"), null); + return; + } + + const decrypted = await this.cipherService.decrypt(cipher, activeUserId); + + const fido2Credential = decrypted.login.fido2Credentials?.[0]; + if (!fido2Credential) { + this.logService.error("listenPasskeyAssertion error", "Fido2Credential not found"); + callback(new Error("Fido2Credential not found"), null); + return; + } + + request.credentialId = Array.from( + new Uint8Array(parseCredentialId(decrypted.login.fido2Credentials?.[0].credentialId)), + ); + } + + const response = await this.fido2AuthenticatorService.getAssertion( + this.convertAssertionRequest(request, true), + { windowXy: normalizePosition(request.windowXy) }, controller, - ) - .then((response) => { - callback(null, this.convertAssertionResponse(request, response)); - }) - .catch((error) => { - this.logService.error("listenPasskeyAssertion error", error); - callback(error, null); - }); + ); + + callback(null, this.convertAssertionResponse(request, response)); + } catch (error) { + this.logService.error("listenPasskeyAssertion error", error); + callback(error, null); + return; + } }, ); ipc.autofill.listenPasskeyAssertion(async (clientId, sequenceNumber, request, callback) => { - this.logService.warning("listenPasskeyAssertion", clientId, sequenceNumber, request); + if (!this.isEnabled) { + this.logService.debug( + `listenPasskeyAssertion: Native credential sync feature flag (${this.featureFlag}) is disabled`, + ); + callback(new Error("Native credential sync feature flag is disabled"), null); + return; + } + + this.logService.debug("listenPasskeyAssertion", clientId, sequenceNumber, request); const controller = new AbortController(); - void this.fido2AuthenticatorService - .getAssertion( + try { + const response = await this.fido2AuthenticatorService.getAssertion( this.convertAssertionRequest(request), - { windowXy: request.windowXy }, + { windowXy: normalizePosition(request.windowXy) }, controller, - ) - .then((response) => { - callback(null, this.convertAssertionResponse(request, response)); - }) - .catch((error) => { - this.logService.error("listenPasskeyAssertion error", error); - callback(error, null); - }); + ); + + callback(null, this.convertAssertionResponse(request, response)); + } catch (error) { + this.logService.error("listenPasskeyAssertion error", error); + callback(error, null); + } }); + + // Listen for native status messages + ipc.autofill.listenNativeStatus(async (clientId, sequenceNumber, status) => { + if (!this.isEnabled) { + this.logService.debug( + `listenNativeStatus: Native credential sync feature flag (${this.featureFlag}) is disabled`, + ); + return; + } + + this.logService.info("Received native status", status.key, status.value); + if (status.key === "request-sync") { + // perform ad-hoc sync + await this.adHocSync(); + } + }); + + ipc.autofill.listenerReady(); } private convertRegistrationRequest( @@ -277,7 +370,10 @@ export class DesktopAutofillService implements OnDestroy { alg, type: "public-key", })), - excludeCredentialDescriptorList: [], + excludeCredentialDescriptorList: request.excludedCredentials.map((credentialId) => ({ + id: new Uint8Array(credentialId), + type: "public-key" as const, + })), requireResidentKey: true, requireUserVerification: request.userVerification === "required" || request.userVerification === "preferred", @@ -309,18 +405,19 @@ export class DesktopAutofillService implements OnDestroy { request: | autofill.PasskeyAssertionRequest | autofill.PasskeyAssertionWithoutUserInterfaceRequest, + assumeUserPresence: boolean = false, ): Fido2AuthenticatorGetAssertionParams { let allowedCredentials; if ("credentialId" in request) { allowedCredentials = [ { - id: new Uint8Array(request.credentialId), + id: new Uint8Array(request.credentialId).buffer, type: "public-key" as const, }, ]; } else { allowedCredentials = request.allowedCredentials.map((credentialId) => ({ - id: new Uint8Array(credentialId), + id: new Uint8Array(credentialId).buffer, type: "public-key" as const, })); } @@ -333,7 +430,7 @@ export class DesktopAutofillService implements OnDestroy { requireUserVerification: request.userVerification === "required" || request.userVerification === "preferred", fallbackSupported: false, - assumeUserPresence: true, // For desktop assertions, it's safe to assume UP has been checked by OS dialogues + assumeUserPresence, }; } @@ -358,3 +455,13 @@ export class DesktopAutofillService implements OnDestroy { this.destroy$.complete(); } } + +function normalizePosition(position: { x: number; y: number }): { x: number; y: number } { + // Add 100 pixels to the x-coordinate to offset the native OS dialog positioning. + const xPositionOffset = 100; + + return { + x: Math.round(position.x + xPositionOffset), + y: Math.round(position.y), + }; +} diff --git a/apps/desktop/src/autofill/services/desktop-autotype-policy.service.spec.ts b/apps/desktop/src/autofill/services/desktop-autotype-policy.service.spec.ts index 555e6ceef5b..907da2fe85c 100644 --- a/apps/desktop/src/autofill/services/desktop-autotype-policy.service.spec.ts +++ b/apps/desktop/src/autofill/services/desktop-autotype-policy.service.spec.ts @@ -10,6 +10,7 @@ import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { Account, UserId } from "@bitwarden/common/platform/models/domain/account"; +import { mockAccountInfoWith } from "@bitwarden/common/spec"; import { DesktopAutotypeDefaultSettingPolicy } from "./desktop-autotype-policy.service"; @@ -30,9 +31,10 @@ describe("DesktopAutotypeDefaultSettingPolicy", () => { beforeEach(() => { mockAccountSubject = new BehaviorSubject({ id: mockUserId, - email: "test@example.com", - emailVerified: true, - name: "Test User", + ...mockAccountInfoWith({ + email: "test@example.com", + name: "Test User", + }), }); mockFeatureFlagSubject = new BehaviorSubject(true); mockAuthStatusSubject = new BehaviorSubject( diff --git a/apps/desktop/src/autofill/services/desktop-autotype.service.spec.ts b/apps/desktop/src/autofill/services/desktop-autotype.service.spec.ts index 30cc800dd28..000242476ed 100644 --- a/apps/desktop/src/autofill/services/desktop-autotype.service.spec.ts +++ b/apps/desktop/src/autofill/services/desktop-autotype.service.spec.ts @@ -1,6 +1,363 @@ -import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { TestBed } from "@angular/core/testing"; +import { BehaviorSubject } from "rxjs"; -import { getAutotypeVaultData } from "./desktop-autotype.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; +import { DeviceType } from "@bitwarden/common/enums"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { GlobalStateProvider } from "@bitwarden/common/platform/state"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { LogService } from "@bitwarden/logging"; + +import { DesktopAutotypeDefaultSettingPolicy } from "./desktop-autotype-policy.service"; +import { DesktopAutotypeService, getAutotypeVaultData } from "./desktop-autotype.service"; + +describe("DesktopAutotypeService", () => { + let service: DesktopAutotypeService; + + // Mock dependencies + let mockAccountService: jest.Mocked; + let mockAuthService: jest.Mocked; + let mockCipherService: jest.Mocked; + let mockConfigService: jest.Mocked; + let mockGlobalStateProvider: jest.Mocked; + let mockPlatformUtilsService: jest.Mocked; + let mockBillingAccountProfileStateService: jest.Mocked; + let mockDesktopAutotypePolicy: jest.Mocked; + let mockLogService: jest.Mocked; + + // Mock GlobalState objects + let mockAutotypeEnabledState: any; + let mockAutotypeKeyboardShortcutState: any; + + // BehaviorSubjects for reactive state + let autotypeEnabledSubject: BehaviorSubject; + let autotypeKeyboardShortcutSubject: BehaviorSubject; + let activeAccountSubject: BehaviorSubject; + let activeAccountStatusSubject: BehaviorSubject; + let hasPremiumSubject: BehaviorSubject; + let featureFlagSubject: BehaviorSubject; + let autotypeDefaultPolicySubject: BehaviorSubject; + let cipherViewsSubject: BehaviorSubject; + + beforeEach(() => { + // Initialize BehaviorSubjects + autotypeEnabledSubject = new BehaviorSubject(null); + autotypeKeyboardShortcutSubject = new BehaviorSubject(["Control", "Shift", "B"]); + activeAccountSubject = new BehaviorSubject({ id: "user-123" }); + activeAccountStatusSubject = new BehaviorSubject( + AuthenticationStatus.Unlocked, + ); + hasPremiumSubject = new BehaviorSubject(true); + featureFlagSubject = new BehaviorSubject(true); + autotypeDefaultPolicySubject = new BehaviorSubject(false); + cipherViewsSubject = new BehaviorSubject([]); + + // Mock GlobalState objects + mockAutotypeEnabledState = { + state$: autotypeEnabledSubject.asObservable(), + update: jest.fn().mockImplementation(async (configureState, options) => { + const newState = configureState(autotypeEnabledSubject.value, null); + + // Handle shouldUpdate option + if (options?.shouldUpdate && !options.shouldUpdate(autotypeEnabledSubject.value)) { + return autotypeEnabledSubject.value; + } + + autotypeEnabledSubject.next(newState); + return newState; + }), + }; + + mockAutotypeKeyboardShortcutState = { + state$: autotypeKeyboardShortcutSubject.asObservable(), + update: jest.fn().mockImplementation(async (configureState) => { + const newState = configureState(autotypeKeyboardShortcutSubject.value, null); + autotypeKeyboardShortcutSubject.next(newState); + return newState; + }), + }; + + // Mock GlobalStateProvider + mockGlobalStateProvider = { + get: jest.fn().mockImplementation((keyDef) => { + if (keyDef.key === "autotypeEnabled") { + return mockAutotypeEnabledState; + } + if (keyDef.key === "autotypeKeyboardShortcut") { + return mockAutotypeKeyboardShortcutState; + } + }), + } as any; + + // Mock AccountService + mockAccountService = { + activeAccount$: activeAccountSubject.asObservable(), + } as any; + + // Mock AuthService + mockAuthService = { + activeAccountStatus$: activeAccountStatusSubject.asObservable(), + } as any; + + // Mock CipherService + mockCipherService = { + cipherViews$: jest.fn().mockReturnValue(cipherViewsSubject.asObservable()), + } as any; + + // Mock ConfigService + mockConfigService = { + getFeatureFlag$: jest.fn().mockReturnValue(featureFlagSubject.asObservable()), + } as any; + + // Mock PlatformUtilsService + mockPlatformUtilsService = { + getDevice: jest.fn().mockReturnValue(DeviceType.WindowsDesktop), + } as any; + + // Mock BillingAccountProfileStateService + mockBillingAccountProfileStateService = { + hasPremiumFromAnySource$: jest.fn().mockReturnValue(hasPremiumSubject.asObservable()), + } as any; + + // Mock DesktopAutotypeDefaultSettingPolicy + mockDesktopAutotypePolicy = { + autotypeDefaultSetting$: autotypeDefaultPolicySubject.asObservable(), + } as any; + + // Mock LogService + mockLogService = { + error: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + } as any; + + // Mock ipc (global) + global.ipc = { + autofill: { + listenAutotypeRequest: jest.fn(), + configureAutotype: jest.fn(), + toggleAutotype: jest.fn(), + }, + } as any; + + TestBed.configureTestingModule({ + providers: [ + DesktopAutotypeService, + { provide: AccountService, useValue: mockAccountService }, + { provide: AuthService, useValue: mockAuthService }, + { provide: CipherService, useValue: mockCipherService }, + { provide: ConfigService, useValue: mockConfigService }, + { provide: GlobalStateProvider, useValue: mockGlobalStateProvider }, + { provide: PlatformUtilsService, useValue: mockPlatformUtilsService }, + { + provide: BillingAccountProfileStateService, + useValue: mockBillingAccountProfileStateService, + }, + { provide: DesktopAutotypeDefaultSettingPolicy, useValue: mockDesktopAutotypePolicy }, + { provide: LogService, useValue: mockLogService }, + ], + }); + + service = TestBed.inject(DesktopAutotypeService); + }); + + afterEach(() => { + jest.clearAllMocks(); + service.ngOnDestroy(); + }); + + describe("constructor", () => { + it("should create service", () => { + expect(service).toBeTruthy(); + }); + + it("should initialize observables", () => { + expect(service.autotypeEnabledUserSetting$).toBeDefined(); + expect(service.autotypeKeyboardShortcut$).toBeDefined(); + }); + }); + + describe("init", () => { + it("should register autotype request listener on Windows", async () => { + await service.init(); + + expect(global.ipc.autofill.listenAutotypeRequest).toHaveBeenCalled(); + }); + + it("should not initialize on non-Windows platforms", async () => { + mockPlatformUtilsService.getDevice.mockReturnValue(DeviceType.MacOsDesktop); + + await service.init(); + + expect(global.ipc.autofill.listenAutotypeRequest).not.toHaveBeenCalled(); + }); + + it("should configure autotype when keyboard shortcut changes", async () => { + await service.init(); + + // Allow observables to emit + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(global.ipc.autofill.configureAutotype).toHaveBeenCalled(); + }); + + it("should toggle autotype when feature enabled state changes", async () => { + autotypeEnabledSubject.next(true); + + await service.init(); + + // Allow observables to emit + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(global.ipc.autofill.toggleAutotype).toHaveBeenCalled(); + }); + + it("should enable autotype when policy is true and user setting is null", async () => { + autotypeEnabledSubject.next(null); + autotypeDefaultPolicySubject.next(true); + + await service.init(); + + // Allow observables to emit + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(mockAutotypeEnabledState.update).toHaveBeenCalled(); + expect(autotypeEnabledSubject.value).toBe(true); + }); + }); + + describe("setAutotypeEnabledState", () => { + it("should update autotype enabled state", async () => { + await service.setAutotypeEnabledState(true); + + expect(mockAutotypeEnabledState.update).toHaveBeenCalled(); + expect(autotypeEnabledSubject.value).toBe(true); + }); + + it("should not update if value has not changed", async () => { + autotypeEnabledSubject.next(true); + + await service.setAutotypeEnabledState(true); + + // Update was called but shouldUpdate prevented the change + expect(mockAutotypeEnabledState.update).toHaveBeenCalled(); + expect(autotypeEnabledSubject.value).toBe(true); + }); + }); + + describe("setAutotypeKeyboardShortcutState", () => { + it("should update keyboard shortcut state", async () => { + const newShortcut = ["Control", "Alt", "A"]; + + await service.setAutotypeKeyboardShortcutState(newShortcut); + + expect(mockAutotypeKeyboardShortcutState.update).toHaveBeenCalled(); + expect(autotypeKeyboardShortcutSubject.value).toEqual(newShortcut); + }); + }); + + describe("matchCiphersToWindowTitle", () => { + it("should match ciphers with matching apptitle URIs", async () => { + const mockCiphers = [ + { + login: { + username: "user1", + password: "pass1", + uris: [{ uri: "apptitle://notepad" }], + }, + deletedDate: null, + }, + { + login: { + username: "user2", + password: "pass2", + uris: [{ uri: "apptitle://chrome" }], + }, + deletedDate: null, + }, + ]; + + cipherViewsSubject.next(mockCiphers); + + const result = await service.matchCiphersToWindowTitle("Notepad - Document.txt"); + + expect(result).toHaveLength(1); + expect(result[0].login.username).toBe("user1"); + }); + + it("should filter out deleted ciphers", async () => { + const mockCiphers = [ + { + login: { + username: "user1", + password: "pass1", + uris: [{ uri: "apptitle://notepad" }], + }, + deletedDate: new Date(), + }, + ]; + + cipherViewsSubject.next(mockCiphers); + + const result = await service.matchCiphersToWindowTitle("Notepad"); + + expect(result).toHaveLength(0); + }); + + it("should filter out ciphers without username or password", async () => { + const mockCiphers = [ + { + login: { + username: null, + password: "pass1", + uris: [{ uri: "apptitle://notepad" }], + }, + deletedDate: null, + }, + ]; + + cipherViewsSubject.next(mockCiphers); + + const result = await service.matchCiphersToWindowTitle("Notepad"); + + expect(result).toHaveLength(0); + }); + + it("should perform case-insensitive matching", async () => { + const mockCiphers = [ + { + login: { + username: "user1", + password: "pass1", + uris: [{ uri: "apptitle://NOTEPAD" }], + }, + deletedDate: null, + }, + ]; + + cipherViewsSubject.next(mockCiphers); + + const result = await service.matchCiphersToWindowTitle("notepad - document.txt"); + + expect(result).toHaveLength(1); + }); + }); + + describe("ngOnDestroy", () => { + it("should complete destroy subject", () => { + const destroySpy = jest.spyOn(service["destroy$"], "complete"); + + service.ngOnDestroy(); + + expect(destroySpy).toHaveBeenCalled(); + }); + }); +}); describe("getAutotypeVaultData", () => { it("should return vault data when cipher has username and password", () => { diff --git a/apps/desktop/src/autofill/services/desktop-autotype.service.ts b/apps/desktop/src/autofill/services/desktop-autotype.service.ts index 7ee889e7b81..d108577567d 100644 --- a/apps/desktop/src/autofill/services/desktop-autotype.service.ts +++ b/apps/desktop/src/autofill/services/desktop-autotype.service.ts @@ -1,4 +1,17 @@ -import { combineLatest, filter, firstValueFrom, map, Observable, of, switchMap } from "rxjs"; +import { Injectable, OnDestroy } from "@angular/core"; +import { + combineLatest, + concatMap, + distinctUntilChanged, + filter, + firstValueFrom, + map, + Observable, + of, + Subject, + switchMap, + takeUntil, +} from "rxjs"; import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; @@ -15,14 +28,15 @@ import { } from "@bitwarden/common/platform/state"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { LogService } from "@bitwarden/logging"; import { UserId } from "@bitwarden/user-core"; +import { AutotypeConfig } from "../models/autotype-config"; import { AutotypeVaultData } from "../models/autotype-vault-data"; +import { DEFAULT_KEYBOARD_SHORTCUT } from "../models/main-autotype-keyboard-shortcut"; import { DesktopAutotypeDefaultSettingPolicy } from "./desktop-autotype-policy.service"; -export const defaultWindowsAutotypeKeyboardShortcut: string[] = ["Control", "Shift", "B"]; - export const AUTOTYPE_ENABLED = new KeyDefinition( AUTOTYPE_SETTINGS_DISK, "autotypeEnabled", @@ -44,15 +58,24 @@ export const AUTOTYPE_KEYBOARD_SHORTCUT = new KeyDefinition( { deserializer: (b) => b }, ); -export class DesktopAutotypeService { +@Injectable({ + providedIn: "root", +}) +export class DesktopAutotypeService implements OnDestroy { private readonly autotypeEnabledState = this.globalStateProvider.get(AUTOTYPE_ENABLED); private readonly autotypeKeyboardShortcut = this.globalStateProvider.get( AUTOTYPE_KEYBOARD_SHORTCUT, ); + // if the user's account is Premium + private readonly isPremiumAccount$: Observable; + + // The enabled/disabled state from the user settings menu autotypeEnabledUserSetting$: Observable = of(false); - resolvedAutotypeEnabled$: Observable = of(false); - autotypeKeyboardShortcut$: Observable = of(defaultWindowsAutotypeKeyboardShortcut); + + autotypeKeyboardShortcut$: Observable = of(DEFAULT_KEYBOARD_SHORTCUT); + + private destroy$ = new Subject(); constructor( private accountService: AccountService, @@ -63,76 +86,110 @@ export class DesktopAutotypeService { private platformUtilsService: PlatformUtilsService, private billingAccountProfileStateService: BillingAccountProfileStateService, private desktopAutotypePolicy: DesktopAutotypeDefaultSettingPolicy, + private logService: LogService, ) { + this.autotypeEnabledUserSetting$ = this.autotypeEnabledState.state$.pipe( + map((enabled) => enabled ?? false), + distinctUntilChanged(), // Only emit when the boolean result changes + takeUntil(this.destroy$), + ); + + this.isPremiumAccount$ = this.accountService.activeAccount$.pipe( + filter((account): account is Account => !!account), + switchMap((account) => + this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id), + ), + distinctUntilChanged(), // Only emit when the boolean result changes + takeUntil(this.destroy$), + ); + + this.autotypeKeyboardShortcut$ = this.autotypeKeyboardShortcut.state$.pipe( + map((shortcut) => shortcut ?? DEFAULT_KEYBOARD_SHORTCUT), + takeUntil(this.destroy$), + ); + } + + async init() { + // Currently Autotype is only supported for Windows + if (this.platformUtilsService.getDevice() !== DeviceType.WindowsDesktop) { + return; + } + ipc.autofill.listenAutotypeRequest(async (windowTitle, callback) => { const possibleCiphers = await this.matchCiphersToWindowTitle(windowTitle); const firstCipher = possibleCiphers?.at(0); const [error, vaultData] = getAutotypeVaultData(firstCipher); callback(error, vaultData); }); - } - async init() { - this.autotypeEnabledUserSetting$ = this.autotypeEnabledState.state$; - this.autotypeKeyboardShortcut$ = this.autotypeKeyboardShortcut.state$.pipe( - map((shortcut) => shortcut ?? defaultWindowsAutotypeKeyboardShortcut), - ); - - // Currently Autotype is only supported for Windows - if (this.platformUtilsService.getDevice() === DeviceType.WindowsDesktop) { - // If `autotypeDefaultPolicy` is `true` for a user's organization, and the - // user has never changed their local autotype setting (`autotypeEnabledState`), - // we set their local setting to `true` (once the local user setting is changed - // by this policy or the user themselves, the default policy should - // never change the user setting again). - combineLatest([ - this.autotypeEnabledState.state$, - this.desktopAutotypePolicy.autotypeDefaultSetting$, - ]) - .pipe( - map(async ([autotypeEnabledState, autotypeDefaultPolicy]) => { + // If `autotypeDefaultPolicy` is `true` for a user's organization, and the + // user has never changed their local autotype setting (`autotypeEnabledState`), + // we set their local setting to `true` (once the local user setting is changed + // by this policy or the user themselves, the default policy should + // never change the user setting again). + combineLatest([ + this.autotypeEnabledState.state$, + this.desktopAutotypePolicy.autotypeDefaultSetting$, + ]) + .pipe( + concatMap(async ([autotypeEnabledState, autotypeDefaultPolicy]) => { + try { if (autotypeDefaultPolicy === true && autotypeEnabledState === null) { await this.setAutotypeEnabledState(true); } - }), - ) - .subscribe(); + } catch { + this.logService.error("Failed to set Autotype enabled state."); + } + }), + takeUntil(this.destroy$), + ) + .subscribe(); - // autotypeEnabledUserSetting$ publicly represents the value the - // user has set for autotyeEnabled in their local settings. - this.autotypeEnabledUserSetting$ = this.autotypeEnabledState.state$; + // listen for changes in keyboard shortcut settings + this.autotypeKeyboardShortcut$ + .pipe( + concatMap(async (keyboardShortcut) => { + const config: AutotypeConfig = { + keyboardShortcut, + }; + ipc.autofill.configureAutotype(config); + }), + takeUntil(this.destroy$), + ) + .subscribe(); - // resolvedAutotypeEnabled$ represents the final determination if the Autotype - // feature should be on or off. - this.resolvedAutotypeEnabled$ = combineLatest([ - this.autotypeEnabledState.state$, - this.configService.getFeatureFlag$(FeatureFlag.WindowsDesktopAutotype), - this.accountService.activeAccount$.pipe( - map((activeAccount) => activeAccount?.id), - switchMap((userId) => this.authService.authStatusFor$(userId)), - ), - this.accountService.activeAccount$.pipe( - filter((account): account is Account => !!account), - switchMap((account) => - this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id), - ), - ), - ]).pipe( - map( - ([autotypeEnabled, windowsDesktopAutotypeFeatureFlag, authStatus, hasPremium]) => - autotypeEnabled && - windowsDesktopAutotypeFeatureFlag && - authStatus == AuthenticationStatus.Unlocked && - hasPremium, - ), - ); + this.autotypeFeatureEnabled$ + .pipe( + concatMap(async (enabled) => { + ipc.autofill.toggleAutotype(enabled); + }), + takeUntil(this.destroy$), + ) + .subscribe(); + } - combineLatest([this.resolvedAutotypeEnabled$, this.autotypeKeyboardShortcut$]).subscribe( - ([resolvedAutotypeEnabled, autotypeKeyboardShortcut]) => { - ipc.autofill.configureAutotype(resolvedAutotypeEnabled, autotypeKeyboardShortcut); - }, - ); - } + // Returns an observable that represents whether autotype is enabled for the current user. + private get autotypeFeatureEnabled$(): Observable { + return combineLatest([ + // if the user has enabled the setting + this.autotypeEnabledUserSetting$, + // if the feature flag is set + this.configService.getFeatureFlag$(FeatureFlag.WindowsDesktopAutotype), + // if there is an active account with an unlocked vault + this.authService.activeAccountStatus$, + // if the active user's account is Premium + this.isPremiumAccount$, + ]).pipe( + map( + ([settingsEnabled, ffEnabled, authStatus, isPremiumAcct]) => + settingsEnabled && + ffEnabled && + authStatus === AuthenticationStatus.Unlocked && + isPremiumAcct, + ), + distinctUntilChanged(), // Only emit when the boolean result changes + takeUntil(this.destroy$), + ); } async setAutotypeEnabledState(enabled: boolean): Promise { @@ -176,6 +233,11 @@ export class DesktopAutotypeService { return possibleCiphers; } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } } /** diff --git a/apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts b/apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts index 3caf13fa5b7..432448faba3 100644 --- a/apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts +++ b/apps/desktop/src/autofill/services/desktop-fido2-user-interface.service.ts @@ -43,9 +43,7 @@ export type NativeWindowObject = { windowXy?: { x: number; y: number }; }; -export class DesktopFido2UserInterfaceService - implements Fido2UserInterfaceServiceAbstraction -{ +export class DesktopFido2UserInterfaceService implements Fido2UserInterfaceServiceAbstraction { constructor( private authService: AuthService, private cipherService: CipherService, @@ -66,7 +64,7 @@ export class DesktopFido2UserInterfaceService nativeWindowObject: NativeWindowObject, abortController?: AbortController, ): Promise { - this.logService.warning("newSession", fallbackSupported, abortController, nativeWindowObject); + this.logService.debug("newSession", fallbackSupported, abortController, nativeWindowObject); const session = new DesktopFido2UserInterfaceSession( this.authService, this.cipherService, @@ -94,9 +92,11 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi ) {} private confirmCredentialSubject = new Subject(); - private createdCipher: Cipher; - private availableCipherIdsSubject = new BehaviorSubject(null); + private updatedCipher: CipherView; + + private rpId = new BehaviorSubject(null); + private availableCipherIdsSubject = new BehaviorSubject([""]); /** * Observable that emits available cipher IDs once they're confirmed by the UI */ @@ -114,7 +114,7 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi assumeUserPresence, masterPasswordRepromptRequired, }: PickCredentialParams): Promise<{ cipherId: string; userVerified: boolean }> { - this.logService.warning("pickCredential desktop function", { + this.logService.debug("pickCredential desktop function", { cipherIds, userVerification, assumeUserPresence, @@ -123,6 +123,7 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi try { // Check if we can return the credential without user interaction + await this.accountService.setShowHeader(false); if (assumeUserPresence && cipherIds.length === 1 && !masterPasswordRepromptRequired) { this.logService.debug( "shortcut - Assuming user presence and returning cipherId", @@ -136,22 +137,27 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi // make the cipherIds available to the UI. this.availableCipherIdsSubject.next(cipherIds); - await this.showUi("/passkeys", this.windowObject.windowXy); + await this.showUi("/fido2-assertion", this.windowObject.windowXy, false); const chosenCipherResponse = await this.waitForUiChosenCipher(); this.logService.debug("Received chosen cipher", chosenCipherResponse); return { - cipherId: chosenCipherResponse.cipherId, - userVerified: chosenCipherResponse.userVerified, + cipherId: chosenCipherResponse?.cipherId, + userVerified: chosenCipherResponse?.userVerified, }; } finally { // Make sure to clean up so the app is never stuck in modal mode? await this.desktopSettingsService.setModalMode(false); + await this.accountService.setShowHeader(true); } } + async getRpId(): Promise { + return firstValueFrom(this.rpId.pipe(filter((id) => id != null))); + } + confirmChosenCipher(cipherId: string, userVerified: boolean = false): void { this.chosenCipherSubject.next({ cipherId, userVerified }); this.chosenCipherSubject.complete(); @@ -159,7 +165,7 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi private async waitForUiChosenCipher( timeoutMs: number = 60000, - ): Promise<{ cipherId: string; userVerified: boolean } | undefined> { + ): Promise<{ cipherId?: string; userVerified: boolean } | undefined> { try { return await lastValueFrom(this.chosenCipherSubject.pipe(timeout(timeoutMs))); } catch { @@ -174,7 +180,10 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi /** * Notifies the Fido2UserInterfaceSession that the UI operations has completed and it can return to the OS. */ - notifyConfirmNewCredential(confirmed: boolean): void { + notifyConfirmCreateCredential(confirmed: boolean, updatedCipher?: CipherView): void { + if (updatedCipher) { + this.updatedCipher = updatedCipher; + } this.confirmCredentialSubject.next(confirmed); this.confirmCredentialSubject.complete(); } @@ -195,60 +204,79 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi async confirmNewCredential({ credentialName, userName, + userHandle, userVerification, rpId, }: NewCredentialParams): Promise<{ cipherId: string; userVerified: boolean }> { - this.logService.warning( + this.logService.debug( "confirmNewCredential", credentialName, userName, + userHandle, userVerification, rpId, ); + this.rpId.next(rpId); try { - await this.showUi("/passkeys", this.windowObject.windowXy); + await this.showUi("/fido2-creation", this.windowObject.windowXy, false); // Wait for the UI to wrap up const confirmation = await this.waitForUiNewCredentialConfirmation(); if (!confirmation) { return { cipherId: undefined, userVerified: false }; } - // Create the credential - await this.createCredential({ - credentialName, - userName, - rpId, - userHandle: "", - userVerification, - }); - // wait for 10ms to help RXJS catch up(?) - // We sometimes get a race condition from this.createCredential not updating cipherService in time - //console.log("waiting 10ms.."); - //await new Promise((resolve) => setTimeout(resolve, 10)); - //console.log("Just waited 10ms"); - - // Return the new cipher (this.createdCipher) - return { cipherId: this.createdCipher.id, userVerified: userVerification }; + if (this.updatedCipher) { + await this.updateCredential(this.updatedCipher); + return { cipherId: this.updatedCipher.id, userVerified: userVerification }; + } else { + // Create the cipher + const createdCipher = await this.createCipher({ + credentialName, + userName, + rpId, + userHandle, + userVerification, + }); + return { cipherId: createdCipher.id, userVerified: userVerification }; + } } finally { // Make sure to clean up so the app is never stuck in modal mode? await this.desktopSettingsService.setModalMode(false); + await this.accountService.setShowHeader(true); } } - private async showUi(route: string, position?: { x: number; y: number }): Promise { + private async hideUi(): Promise { + await this.desktopSettingsService.setModalMode(false); + await this.router.navigate(["/"]); + } + + private async showUi( + route: string, + position?: { x: number; y: number }, + showTrafficButtons: boolean = false, + disableRedirect?: boolean, + ): Promise { // Load the UI: - await this.desktopSettingsService.setModalMode(true, position); - await this.router.navigate(["/passkeys"]); + await this.desktopSettingsService.setModalMode(true, showTrafficButtons, position); + await this.accountService.setShowHeader(showTrafficButtons); + await this.router.navigate([ + route, + { + "disable-redirect": disableRedirect || null, + }, + ]); } /** - * Can be called by the UI to create a new credential with user input etc. + * Can be called by the UI to create a new cipher with user input etc. * @param param0 */ - async createCredential({ credentialName, userName, rpId }: NewCredentialParams): Promise { + async createCipher({ credentialName, userName, rpId }: NewCredentialParams): Promise { // Store the passkey on a new cipher to avoid replacing something important + const cipher = new CipherView(); cipher.name = credentialName; @@ -267,32 +295,79 @@ export class DesktopFido2UserInterfaceSession implements Fido2UserInterfaceSessi this.accountService.activeAccount$.pipe(map((a) => a?.id)), ); - const encCipher = await this.cipherService.encrypt(cipher, activeUserId); - const createdCipher = await this.cipherService.createWithServer(encCipher); + if (!activeUserId) { + throw new Error("No active user ID found!"); + } - this.createdCipher = createdCipher; + try { + const createdCipher = await this.cipherService.createWithServer(cipher, activeUserId); + const encryptedCreatedCipher = await this.cipherService.encrypt(createdCipher, activeUserId); - return createdCipher; + return encryptedCreatedCipher.cipher; + } catch { + throw new Error("Unable to create cipher"); + } + } + + async updateCredential(cipher: CipherView): Promise { + this.logService.info("updateCredential"); + await firstValueFrom( + this.accountService.activeAccount$.pipe( + map(async (a) => { + if (a) { + await this.cipherService.updateWithServer(cipher, a.id); + } + }), + ), + ); } async informExcludedCredential(existingCipherIds: string[]): Promise { - this.logService.warning("informExcludedCredential", existingCipherIds); + this.logService.debug("informExcludedCredential", existingCipherIds); + + // make the cipherIds available to the UI. + this.availableCipherIdsSubject.next(existingCipherIds); + + await this.accountService.setShowHeader(false); + await this.showUi("/fido2-excluded", this.windowObject.windowXy, false); } async ensureUnlockedVault(): Promise { - this.logService.warning("ensureUnlockedVault"); + this.logService.debug("ensureUnlockedVault"); const status = await firstValueFrom(this.authService.activeAccountStatus$); if (status !== AuthenticationStatus.Unlocked) { - throw new Error("Vault is not unlocked"); + await this.showUi("/lock", this.windowObject.windowXy, true, true); + + let status2: AuthenticationStatus; + try { + status2 = await lastValueFrom( + this.authService.activeAccountStatus$.pipe( + filter((s) => s === AuthenticationStatus.Unlocked), + take(1), + timeout(1000 * 60 * 5), // 5 minutes + ), + ); + } catch (error) { + this.logService.warning("Error while waiting for vault to unlock", error); + } + + if (status2 === AuthenticationStatus.Unlocked) { + await this.router.navigate(["/"]); + } + + if (status2 !== AuthenticationStatus.Unlocked) { + await this.hideUi(); + throw new Error("Vault is not unlocked"); + } } } async informCredentialNotFound(): Promise { - this.logService.warning("informCredentialNotFound"); + this.logService.debug("informCredentialNotFound"); } async close() { - this.logService.warning("close"); + this.logService.debug("close"); } } diff --git a/apps/desktop/src/autofill/services/ssh-agent.service.ts b/apps/desktop/src/autofill/services/ssh-agent.service.ts index d5aed7f3289..e3280f07ede 100644 --- a/apps/desktop/src/autofill/services/ssh-agent.service.ts +++ b/apps/desktop/src/autofill/services/ssh-agent.service.ts @@ -32,8 +32,8 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { CipherType } from "@bitwarden/common/vault/enums"; import { DialogService, ToastService } from "@bitwarden/components"; -import { ApproveSshRequestComponent } from "../../platform/components/approve-ssh-request"; import { DesktopSettingsService } from "../../platform/services/desktop-settings.service"; +import { ApproveSshRequestComponent } from "../components/approve-ssh-request"; import { SshAgentPromptType } from "../models/ssh-agent-setting"; @Injectable({ @@ -46,8 +46,6 @@ export class SshAgentService implements OnDestroy { private authorizedSshKeys: Record = {}; - private isFeatureFlagEnabled = false; - private destroy$ = new Subject(); constructor( @@ -91,14 +89,14 @@ export class SshAgentService implements OnDestroy { filter(({ enabled }) => enabled), map(({ message }) => message), withLatestFrom(this.authService.activeAccountStatus$, this.accountService.activeAccount$), - // This switchMap handles unlocking the vault if it is locked: - // - If the vault is locked, we will wait for it to be unlocked. - // - If the vault is not unlocked within the timeout, we will abort the flow. + // This switchMap handles unlocking the vault if it is not unlocked: + // - If the vault is locked or logged out, we will wait for it to be unlocked: + // - If the vault is not unlocked in within the timeout, we will abort the flow. // - If the vault is unlocked, we will continue with the flow. // switchMap is used here to prevent multiple requests from being processed at the same time, // and will cancel the previous request if a new one is received. switchMap(([message, status, account]) => { - if (status !== AuthenticationStatus.Unlocked) { + if (status !== AuthenticationStatus.Unlocked || account == null) { ipc.platform.focusWindow(); this.toastService.showToast({ variant: "info", @@ -127,7 +125,11 @@ export class SshAgentService implements OnDestroy { throw error; }), - map(() => [message, account.id]), + concatMap(async () => { + // The active account may have switched with account switching during unlock + const updatedAccount = await firstValueFrom(this.accountService.activeAccount$); + return [message, updatedAccount.id] as const; + }), ); } @@ -200,10 +202,6 @@ export class SshAgentService implements OnDestroy { this.accountService.activeAccount$.pipe(skip(1), takeUntil(this.destroy$)).subscribe({ next: (account) => { - if (!this.isFeatureFlagEnabled) { - return; - } - this.authorizedSshKeys = {}; this.logService.info("Active account changed, clearing SSH keys"); ipc.platform.sshAgent @@ -211,20 +209,12 @@ export class SshAgentService implements OnDestroy { .catch((e) => this.logService.error("Failed to clear SSH keys", e)); }, error: (e: unknown) => { - if (!this.isFeatureFlagEnabled) { - return; - } - this.logService.error("Error in active account observable", e); ipc.platform.sshAgent .clearKeys() .catch((e) => this.logService.error("Failed to clear SSH keys", e)); }, complete: () => { - if (!this.isFeatureFlagEnabled) { - return; - } - this.logService.info("Active account observable completed, clearing SSH keys"); this.authorizedSshKeys = {}; ipc.platform.sshAgent @@ -239,10 +229,6 @@ export class SshAgentService implements OnDestroy { ]) .pipe( concatMap(async ([, enabled]) => { - if (!this.isFeatureFlagEnabled) { - return; - } - if (!enabled) { await ipc.platform.sshAgent.clearKeys(); return; diff --git a/apps/desktop/src/billing/app/accounts/premium.component.html b/apps/desktop/src/billing/app/accounts/premium.component.html index d88602bed1e..c5f9722f133 100644 --- a/apps/desktop/src/billing/app/accounts/premium.component.html +++ b/apps/desktop/src/billing/app/accounts/premium.component.html @@ -13,7 +13,7 @@
    • - {{ "premiumSignUpStorage" | i18n }} + {{ "premiumSignUpStorageV2" | i18n: `${storageProvidedGb} GB` }}
    • diff --git a/apps/desktop/src/billing/app/accounts/premium.component.ts b/apps/desktop/src/billing/app/accounts/premium.component.ts index 637969c1a21..4aff0cc03e1 100644 --- a/apps/desktop/src/billing/app/accounts/premium.component.ts +++ b/apps/desktop/src/billing/app/accounts/premium.component.ts @@ -3,6 +3,7 @@ import { Component } from "@angular/core"; import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/billing/components/premium.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -28,6 +29,7 @@ export class PremiumComponent extends BasePremiumComponent { billingAccountProfileStateService: BillingAccountProfileStateService, toastService: ToastService, accountService: AccountService, + billingApiService: BillingApiServiceAbstraction, ) { super( i18nService, @@ -39,6 +41,7 @@ export class PremiumComponent extends BasePremiumComponent { billingAccountProfileStateService, toastService, accountService, + billingApiService, ); } } diff --git a/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.ts b/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.ts index 8e28d3ca614..3a47086b1aa 100644 --- a/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.ts +++ b/apps/desktop/src/key-management/biometrics/renderer-biometrics.service.ts @@ -1,5 +1,7 @@ import { Injectable } from "@angular/core"; +import { firstValueFrom } from "rxjs"; +import { TokenService } from "@bitwarden/common/auth/abstractions/token.service"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { UserId } from "@bitwarden/common/types/guid"; import { UserKey } from "@bitwarden/common/types/key"; @@ -13,6 +15,10 @@ import { DesktopBiometricsService } from "./desktop.biometrics.service"; */ @Injectable() export class RendererBiometricsService extends DesktopBiometricsService { + constructor(private tokenService: TokenService) { + super(); + } + async authenticateWithBiometrics(): Promise { return await ipc.keyManagement.biometric.authenticateWithBiometrics(); } @@ -31,6 +37,10 @@ export class RendererBiometricsService extends DesktopBiometricsService { } async getBiometricsStatusForUser(id: UserId): Promise { + if ((await firstValueFrom(this.tokenService.hasAccessToken$(id))) === false) { + return BiometricsStatus.NotEnabledInConnectedDesktopApp; + } + return await ipc.keyManagement.biometric.getBiometricsStatusForUser(id); } diff --git a/apps/desktop/src/key-management/electron-key.service.spec.ts b/apps/desktop/src/key-management/electron-key.service.spec.ts index cc1d68ed050..c5e010b1766 100644 --- a/apps/desktop/src/key-management/electron-key.service.spec.ts +++ b/apps/desktop/src/key-management/electron-key.service.spec.ts @@ -13,11 +13,13 @@ import { UserKey } from "@bitwarden/common/types/key"; import { BiometricStateService, KdfConfigService } from "@bitwarden/key-management"; import { - makeSymmetricCryptoKey, FakeAccountService, - mockAccountServiceWith, FakeStateProvider, + makeSymmetricCryptoKey, + mockAccountServiceWith, } from "../../../../libs/common/spec"; +// eslint-disable-next-line no-restricted-imports +import { VAULT_TIMEOUT } from "../../../../libs/common/src/key-management/vault-timeout"; import { DesktopBiometricsService } from "./biometrics/desktop.biometrics.service"; import { ElectronKeyService } from "./electron-key.service"; @@ -40,11 +42,13 @@ describe("ElectronKeyService", () => { let accountService: FakeAccountService; let masterPasswordService: FakeMasterPasswordService; - beforeEach(() => { + beforeEach(async () => { accountService = mockAccountServiceWith(mockUserId); masterPasswordService = new FakeMasterPasswordService(); stateProvider = new FakeStateProvider(accountService); + await stateProvider.setUserState(VAULT_TIMEOUT, 10, mockUserId); + keyService = new ElectronKeyService( masterPasswordService, keyGenerationService, @@ -79,38 +83,17 @@ describe("ElectronKeyService", () => { expect(biometricStateService.getBiometricUnlockEnabled).toHaveBeenCalledWith(mockUserId); }); - describe("biometric unlock enabled", () => { - beforeEach(() => { - biometricStateService.getBiometricUnlockEnabled.mockResolvedValue(true); - }); + it("sets biometric key when biometric unlock enabled", async () => { + biometricStateService.getBiometricUnlockEnabled.mockResolvedValue(true); - it("sets null biometric client key half and biometric unlock key when require password on start disabled", async () => { - biometricStateService.getRequirePasswordOnStart.mockResolvedValue(false); + await keyService.setUserKey(userKey, mockUserId); - await keyService.setUserKey(userKey, mockUserId); - - expect(biometricService.setBiometricProtectedUnlockKeyForUser).toHaveBeenCalledWith( - mockUserId, - userKey, - ); - expect(biometricStateService.setEncryptedClientKeyHalf).not.toHaveBeenCalled(); - expect(biometricStateService.getBiometricUnlockEnabled).toHaveBeenCalledWith(mockUserId); - }); - - describe("require password on start enabled", () => { - beforeEach(() => { - biometricStateService.getRequirePasswordOnStart.mockResolvedValue(true); - }); - - it("sets biometric key", async () => { - await keyService.setUserKey(userKey, mockUserId); - - expect(biometricService.setBiometricProtectedUnlockKeyForUser).toHaveBeenCalledWith( - mockUserId, - userKey, - ); - }); - }); + expect(biometricService.setBiometricProtectedUnlockKeyForUser).toHaveBeenCalledWith( + mockUserId, + userKey, + ); + expect(biometricStateService.setEncryptedClientKeyHalf).not.toHaveBeenCalled(); + expect(biometricStateService.getBiometricUnlockEnabled).toHaveBeenCalledWith(mockUserId); }); }); }); diff --git a/apps/desktop/src/key-management/key-connector/remove-password.component.html b/apps/desktop/src/key-management/key-connector/remove-password.component.html deleted file mode 100644 index 5276e00c531..00000000000 --- a/apps/desktop/src/key-management/key-connector/remove-password.component.html +++ /dev/null @@ -1,20 +0,0 @@ -
      -
      -

      {{ "removeMasterPassword" | i18n }}

      -

      {{ "removeMasterPasswordForOrganizationUserKeyConnector" | i18n }}

      -

      {{ "organizationName" | i18n }}:

      -

      {{ organization.name }}

      -

      {{ "keyConnectorDomain" | i18n }}:

      -

      {{ organization.keyConnectorUrl }}

      -
      - - -
      -
      -
      diff --git a/apps/desktop/src/key-management/key-connector/remove-password.component.ts b/apps/desktop/src/key-management/key-connector/remove-password.component.ts deleted file mode 100644 index d9fea9409f8..00000000000 --- a/apps/desktop/src/key-management/key-connector/remove-password.component.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Component } from "@angular/core"; - -import { RemovePasswordComponent as BaseRemovePasswordComponent } from "@bitwarden/key-management-ui"; - -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -@Component({ - selector: "app-remove-password", - templateUrl: "remove-password.component.html", - standalone: false, -}) -export class RemovePasswordComponent extends BaseRemovePasswordComponent {} diff --git a/apps/desktop/src/key-management/lock/services/desktop-lock-component.service.spec.ts b/apps/desktop/src/key-management/lock/services/desktop-lock-component.service.spec.ts index dd21cf883f3..b01e62d2af3 100644 --- a/apps/desktop/src/key-management/lock/services/desktop-lock-component.service.spec.ts +++ b/apps/desktop/src/key-management/lock/services/desktop-lock-component.service.spec.ts @@ -177,6 +177,9 @@ describe("DesktopLockComponentService", () => { enabled: true, biometricsStatus: BiometricsStatus.Available, }, + prf: { + enabled: false, + }, }, ], [ @@ -197,6 +200,9 @@ describe("DesktopLockComponentService", () => { enabled: true, biometricsStatus: BiometricsStatus.Available, }, + prf: { + enabled: false, + }, }, ], [ @@ -218,6 +224,9 @@ describe("DesktopLockComponentService", () => { enabled: false, biometricsStatus: BiometricsStatus.NotEnabledLocally, }, + prf: { + enabled: false, + }, }, ], [ @@ -238,6 +247,9 @@ describe("DesktopLockComponentService", () => { enabled: false, biometricsStatus: BiometricsStatus.HardwareUnavailable, }, + prf: { + enabled: false, + }, }, ], [ @@ -258,6 +270,9 @@ describe("DesktopLockComponentService", () => { enabled: false, biometricsStatus: BiometricsStatus.PlatformUnsupported, }, + prf: { + enabled: false, + }, }, ], ]; diff --git a/apps/desktop/src/key-management/lock/services/desktop-lock-component.service.ts b/apps/desktop/src/key-management/lock/services/desktop-lock-component.service.ts index fc57a3873ef..0b1896f02f9 100644 --- a/apps/desktop/src/key-management/lock/services/desktop-lock-component.service.ts +++ b/apps/desktop/src/key-management/lock/services/desktop-lock-component.service.ts @@ -69,6 +69,9 @@ export class DesktopLockComponentService implements LockComponentService { enabled: biometricsStatus == BiometricsStatus.Available, biometricsStatus: biometricsStatus, }, + prf: { + enabled: false, + }, }; return unlockOpts; diff --git a/apps/desktop/src/key-management/session-timeout/services/desktop-session-timeout-settings-component.service.ts b/apps/desktop/src/key-management/session-timeout/services/desktop-session-timeout-settings-component.service.ts deleted file mode 100644 index 91c8126cdd7..00000000000 --- a/apps/desktop/src/key-management/session-timeout/services/desktop-session-timeout-settings-component.service.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { defer, from, map, Observable } from "rxjs"; - -import { - VaultTimeout, - VaultTimeoutOption, - VaultTimeoutStringType, -} from "@bitwarden/common/key-management/vault-timeout"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { SessionTimeoutSettingsComponentService } from "@bitwarden/key-management-ui"; - -export class DesktopSessionTimeoutSettingsComponentService - implements SessionTimeoutSettingsComponentService -{ - availableTimeoutOptions$: Observable = defer(() => - from(ipc.platform.powermonitor.isLockMonitorAvailable()).pipe( - map((isLockMonitorAvailable) => { - const options: VaultTimeoutOption[] = [ - { name: this.i18nService.t("oneMinute"), value: 1 }, - { name: this.i18nService.t("fiveMinutes"), value: 5 }, - { name: this.i18nService.t("fifteenMinutes"), value: 15 }, - { name: this.i18nService.t("thirtyMinutes"), value: 30 }, - { name: this.i18nService.t("oneHour"), value: 60 }, - { name: this.i18nService.t("fourHours"), value: 240 }, - { name: this.i18nService.t("onIdle"), value: VaultTimeoutStringType.OnIdle }, - { name: this.i18nService.t("onSleep"), value: VaultTimeoutStringType.OnSleep }, - ]; - - if (isLockMonitorAvailable) { - options.push({ - name: this.i18nService.t("onLocked"), - value: VaultTimeoutStringType.OnLocked, - }); - } - - options.push( - { name: this.i18nService.t("onRestart"), value: VaultTimeoutStringType.OnRestart }, - { name: this.i18nService.t("never"), value: VaultTimeoutStringType.Never }, - ); - - return options; - }), - ), - ); - - constructor(private readonly i18nService: I18nService) {} - - onTimeoutSave(_: VaultTimeout): void {} -} diff --git a/apps/desktop/src/key-management/session-timeout/services/desktop-session-timeout-type.service.spec.ts b/apps/desktop/src/key-management/session-timeout/services/desktop-session-timeout-type.service.spec.ts new file mode 100644 index 00000000000..d3ece8842b2 --- /dev/null +++ b/apps/desktop/src/key-management/session-timeout/services/desktop-session-timeout-type.service.spec.ts @@ -0,0 +1,125 @@ +import { + VaultTimeoutNumberType, + VaultTimeoutStringType, +} from "@bitwarden/common/key-management/vault-timeout"; + +import { DesktopSessionTimeoutTypeService } from "./desktop-session-timeout-type.service"; + +describe("DesktopSessionTimeoutTypeService", () => { + let service: DesktopSessionTimeoutTypeService; + let mockIsLockMonitorAvailable: jest.Mock; + + beforeEach(() => { + mockIsLockMonitorAvailable = jest.fn(); + + (global as any).ipc = { + platform: { + powermonitor: { + isLockMonitorAvailable: mockIsLockMonitorAvailable, + }, + }, + }; + + service = new DesktopSessionTimeoutTypeService(); + }); + + describe("isAvailable", () => { + it("should return false for Immediately", async () => { + const result = await service.isAvailable(VaultTimeoutNumberType.Immediately); + + expect(result).toBe(false); + }); + + it.each([ + VaultTimeoutStringType.OnIdle, + VaultTimeoutStringType.OnSleep, + VaultTimeoutStringType.OnRestart, + VaultTimeoutStringType.Never, + VaultTimeoutStringType.Custom, + ])("should return true for always available type: %s", async (timeoutType) => { + const result = await service.isAvailable(timeoutType); + + expect(result).toBe(true); + }); + + it.each([VaultTimeoutNumberType.OnMinute, VaultTimeoutNumberType.EightHours])( + "should return true for numeric timeout type: %s", + async (timeoutType) => { + const result = await service.isAvailable(timeoutType); + + expect(result).toBe(true); + }, + ); + + describe("OnLocked availability", () => { + it("should return true when lock monitor is available", async () => { + mockIsLockMonitorAvailable.mockResolvedValue(true); + + const result = await service.isAvailable(VaultTimeoutStringType.OnLocked); + + expect(result).toBe(true); + expect(mockIsLockMonitorAvailable).toHaveBeenCalled(); + }); + + it("should return false when lock monitor is not available", async () => { + mockIsLockMonitorAvailable.mockResolvedValue(false); + + const result = await service.isAvailable(VaultTimeoutStringType.OnLocked); + + expect(result).toBe(false); + expect(mockIsLockMonitorAvailable).toHaveBeenCalled(); + }); + }); + }); + + describe("getOrPromoteToAvailable", () => { + it.each([ + VaultTimeoutNumberType.OnMinute, + VaultTimeoutStringType.OnIdle, + VaultTimeoutStringType.OnSleep, + VaultTimeoutStringType.OnRestart, + VaultTimeoutStringType.OnLocked, + VaultTimeoutStringType.Never, + VaultTimeoutStringType.Custom, + ])("should return the original type when it is available: %s", async (timeoutType) => { + jest.spyOn(service, "isAvailable").mockResolvedValue(true); + + const result = await service.getOrPromoteToAvailable(timeoutType); + + expect(result).toBe(timeoutType); + expect(service.isAvailable).toHaveBeenCalledWith(timeoutType); + }); + + it("should return OnMinute when Immediately is not available", async () => { + jest.spyOn(service, "isAvailable").mockResolvedValue(false); + + const result = await service.getOrPromoteToAvailable(VaultTimeoutNumberType.Immediately); + + expect(result).toBe(VaultTimeoutNumberType.OnMinute); + expect(service.isAvailable).toHaveBeenCalledWith(VaultTimeoutNumberType.Immediately); + }); + + it("should return OnSleep when OnLocked is not available", async () => { + jest.spyOn(service, "isAvailable").mockResolvedValue(false); + + const result = await service.getOrPromoteToAvailable(VaultTimeoutStringType.OnLocked); + + expect(result).toBe(VaultTimeoutStringType.OnSleep); + expect(service.isAvailable).toHaveBeenCalledWith(VaultTimeoutStringType.OnLocked); + }); + + it.each([ + VaultTimeoutStringType.OnIdle, + VaultTimeoutStringType.OnSleep, + VaultTimeoutNumberType.OnMinute, + 5, + ])("should return OnRestart when type is not available: %s", async (timeoutType) => { + jest.spyOn(service, "isAvailable").mockResolvedValue(false); + + const result = await service.getOrPromoteToAvailable(timeoutType); + + expect(result).toBe(VaultTimeoutStringType.OnRestart); + expect(service.isAvailable).toHaveBeenCalledWith(timeoutType); + }); + }); +}); diff --git a/apps/desktop/src/key-management/session-timeout/services/desktop-session-timeout-type.service.ts b/apps/desktop/src/key-management/session-timeout/services/desktop-session-timeout-type.service.ts new file mode 100644 index 00000000000..1f09e83b0f1 --- /dev/null +++ b/apps/desktop/src/key-management/session-timeout/services/desktop-session-timeout-type.service.ts @@ -0,0 +1,46 @@ +import { SessionTimeoutTypeService } from "@bitwarden/common/key-management/session-timeout"; +import { + isVaultTimeoutTypeNumeric, + VaultTimeout, + VaultTimeoutNumberType, + VaultTimeoutStringType, +} from "@bitwarden/common/key-management/vault-timeout"; + +export class DesktopSessionTimeoutTypeService implements SessionTimeoutTypeService { + async isAvailable(type: VaultTimeout): Promise { + switch (type) { + case VaultTimeoutNumberType.Immediately: + return false; + case VaultTimeoutStringType.OnIdle: + case VaultTimeoutStringType.OnSleep: + case VaultTimeoutStringType.OnRestart: + case VaultTimeoutStringType.Never: + case VaultTimeoutStringType.Custom: + return true; + case VaultTimeoutStringType.OnLocked: + return await ipc.platform.powermonitor.isLockMonitorAvailable(); + default: + if (isVaultTimeoutTypeNumeric(type)) { + return true; + } + break; + } + + return false; + } + + async getOrPromoteToAvailable(type: VaultTimeout): Promise { + const available = await this.isAvailable(type); + if (!available) { + switch (type) { + case VaultTimeoutNumberType.Immediately: + return VaultTimeoutNumberType.OnMinute; + case VaultTimeoutStringType.OnLocked: + return VaultTimeoutStringType.OnSleep; + default: + return VaultTimeoutStringType.OnRestart; + } + } + return type; + } +} diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index 1c6a2bc49c9..61b5da472f1 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Nuwe URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "'n Onverwagte fout het voorgekom." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Iteminligting" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Leer meer" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Funksie Onbeskikbaar" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Volg Ons" }, - "syncVault": { - "message": "Sichroniseer Kluis" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Verander Hoofwagwoord" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GG geÃĢnkripteerde berging vir lÃĒeraanhegsels." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Stuur Kluis Uit" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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": "LÃĒerformaat" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Permanent geskrapte item" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Teruggestelde item" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Alle Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Is u seker u wil hierdie Send skrap?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Kopieer Send-skakel na knipbord", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Hoofwagwoord is verwyder" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Is u seker u wil die “Nooit”-opsie gebruik? Deur u vergrendelopsies op “Nooit” te stel word u kluis se enkripsie op u toestel bewaar. Indien u hierdie opsie gebruik moet u verseker dat u toestel behoorlik beskerm is." }, "vault": { - "message": "Kluis" + "message": "Kluis", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Meld aan met meesterwagwoord" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Invoer data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Invoer fout" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index ca404f4e179..302f53eea43 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "ØąØ§Ø¨Øˇ ØŦØ¯ŲŠØ¯" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "حدØĢ ØŽØˇØŖ ØēŲŠØą Ų…ØĒŲˆŲ‚Øš." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Ų…ØšŲ„ŲˆŲ…Ø§ØĒ Ø§Ų„ØšŲ†ØĩØą" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Ø§ØšØąŲ Ø§Ų„Ų…Ø˛ŲŠØ¯" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Ø§Ų„Ų…ŲŠØ˛ØŠ ØēŲŠØą Ų…ØĒŲˆŲØąØŠ" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "ØĒØ§Ø¨ØšŲ†Ø§" }, - "syncVault": { - "message": "Ų…Ø˛Ø§Ų…Ų†ØŠ Ø§Ų„ØŽØ˛Ø§Ų†ØŠ" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "ØĒØēŲŠŲŠØą ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØąØĻŲŠØŗŲŠØŠ" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 ØŦ؊ØēØ§Ø¨Ø§ŲŠØĒ ŲˆØ­Ø¯ØŠ ØĒØŽØ˛ŲŠŲ† Ų…Ø´ŲØąØŠ Ų„Ų…ØąŲŲ‚Ø§ØĒ Ø§Ų„Ų…Ų„ŲØ§ØĒ." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "ØŽŲŠØ§ØąØ§ØĒ ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨ØŽØˇŲˆØĒŲŠŲ† Ø§Ų„Ų…Ų…Ų„ŲˆŲƒØŠ Ų…ØĢŲ„ YubiKey ؈ Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "ØĒØĩØ¯ŲŠØą Ų…Ų†" }, - "exportVault": { - "message": "ØĒØĩØ¯ŲŠØą Ø§Ų„ØŽØ˛Ø§Ų†ØŠ" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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": "Øĩ؊ØēØŠ Ø§Ų„Ų…Ų„Ų" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "ØĒŲ… Ø­Ø°Ų Ø§Ų„ØšŲ†ØĩØą Ø¨Ø´ŲƒŲ„ داØĻŲ…" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Ø§Ų„ØšŲ†ØĩØą Ø§Ų„Ų…ØŗØĒؚاد" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "ŲƒŲ„ Ø§Ų„ØĨØąØŗØ§Ų„Ø§ØĒ", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø­Ø°Ų Ų‡Ø°Ø§ Ø§Ų„ØĨØąØŗØ§Ų„ØŸ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Ų†ØŗØŽ ØąØ§Ø¨Øˇ ØĨØąØŗØ§Ų„ ØĨŲ„Ų‰ Ø§Ų„Ø­Ø§ŲØ¸ØŠ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "ØĒŲ…ØĒ ØĨØ˛Ø§Ų„ØŠ ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØąØĻŲŠØŗŲŠØŠ." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Ų‡Ų„ ØŖŲ†ØĒ Ų…ØĒØŖŲƒØ¯ Ų…Ų† ØŖŲ†Ųƒ ØĒØąŲŠØ¯ Ø§ØŗØĒØŽØ¯Ø§Ų… ØŽŲŠØ§Øą \"Ų…ØˇŲ„Ų‚Ø§\"؟ ØĨؚداد ØŽŲŠØ§ØąØ§ØĒ Ø§Ų„Ų‚ŲŲ„ ØĨŲ„Ų‰ \"Ų…ØˇŲ„Ų‚Ø§\" ŲŠØŽØ˛Ų† ؅؁ØĒاح ØĒØ´ŲŲŠØą Ø§Ų„Ų…ØŗØĒŲˆØ¯Øš Ø§Ų„ØŽØ§Øĩ Ø¨Ųƒ ØšŲ„Ų‰ ØŦŲ‡Ø§Ø˛Ųƒ. ØĨذا ŲƒŲ†ØĒ ØĒØŗØĒØŽØ¯Ų… Ų‡Ø°Ø§ Ø§Ų„ØŽŲŠØ§ØąØŒ ؊ØŦب ØŖŲ† ØĒØĒØŖŲƒØ¯ Ų…Ų† Ø§Ų„Ø­ŲØ§Ø¸ ØšŲ„Ų‰ Ø­Ų…Ø§ŲŠØŠ ØŦŲ‡Ø§Ø˛Ųƒ Ø¨Ø´ŲƒŲ„ ØĩØ­ŲŠØ­." }, "vault": { - "message": "Ø§Ų„ØŽØ˛Ø§Ų†ØŠ" + "message": "Ø§Ų„ØŽØ˛Ø§Ų†ØŠ", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "ØĒØŗØŦŲŠŲ„ Ø§Ų„Ø¯ØŽŲˆŲ„ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… ŲƒŲ„Ų…ØŠ Ø§Ų„Ų…ØąŲˆØą Ø§Ų„ØąØĻŲŠØŗŲŠØŠ" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Ø§Ų„Ø§ØŗŲ… Ø§Ų„Ø¨Ø¯ŲŠŲ„ Ų„Ų„Ų†ØˇØ§Ų‚" }, - "importData": { - "message": "Ø§ØŗØĒŲŠØąØ§Ø¯ Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "ØŽØˇØŖ ؁؊ Ø§Ų„Ø§ØŗØĒŲŠØąØ§Ø¯" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "ØĒŲ… Ø­ŲØ¸ Ø§Ų„Ų…Ų„Ų ØšŲ„Ų‰ Ø§Ų„ØŦŲ‡Ø§Ø˛. ØĨØ¯Ø§ØąØŠ Ų…Ų† ØĒŲ†Ø˛ŲŠŲ„Ø§ØĒ ØŦŲ‡Ø§Ø˛Ųƒ." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index 55c2bdcd677..d9709cf308f 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "Send, bu tarixdə həmişəlik silinəcək.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "PaylaÅŸÄąlacaq fayl" + }, + "hideTextByDefault": { + "message": "Mətni ilkin olaraq gizlət" + }, + "hideYourEmail": { + "message": "E-poçt ÃŧnvanÄąnÄąz baxanlardan gizlədilsin." + }, + "limitSendViews": { + "message": "BaxÄąÅŸlarÄą limitlə" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ baxÄąÅŸ qaldÄą", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "Limitə çatdÄąqdan sonra bu Send-ə heç kim baxa bilməz.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Özəl not" + }, + "sendDetails": { + "message": "Send detallarÄą", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "AlÄącÄąlarÄąn bu \"Send\"ə erişməsi ÃŧçÃŧn ixtiyari bir parol əlavə edin.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "PaylaÅŸÄąlacaq mətn" + }, + "newItemHeaderTextSend": { + "message": "Yeni Send mətni", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "Yeni Send faylÄą", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Send mətninə dÃŧzəliş et", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Send faylÄąna dÃŧzəliş et", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Bu Send-i həmişəlik silmək istədiyinizə əminsiniz?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "Yeni", + "description": "for adding new items" + }, "newUri": { "message": "Yeni URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Qoşma əlavə et" }, + "itemsTransferred": { + "message": "Elementlər kÃļçÃŧrÃŧldÃŧ" + }, + "fixEncryption": { + "message": "Şifrələməni dÃŧzəlt" + }, + "fixEncryptionTooltip": { + "message": "Bu fayl, kÃļhnə bir şifrələmə Ãŧsulunu istifadə edir." + }, + "attachmentUpdated": { + "message": "Qoşma gÃŧncəllənib" + }, "maxFileSizeSansPunctuation": { "message": "Maksimal fayl həcmi 500 MB-dÄąr" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "GÃļzlənilməz bir səhv baş verdi." }, + "unexpectedErrorShort": { + "message": "GÃļzlənilməz xəta" + }, + "closeThisBitwardenWindow": { + "message": "Bu Bitwarden pəncərəsini bağlayÄąb yenidən sÄąnayÄąn." + }, "itemInformation": { "message": "Element məlumatlarÄą" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Daha ətraflÄą" }, + "migrationsFailed": { + "message": "Şifrələmə ayarlarÄąnÄą gÃŧncəlləyərkən bir xəta baş verdi." + }, + "updateEncryptionSettingsTitle": { + "message": "Şifrələmə ayarlarÄąnÄązÄą gÃŧncəlləyin" + }, + "updateEncryptionSettingsDesc": { + "message": "TÃļvsiyə edilən yeni şifrələmə ayarlarÄą, hesabÄąnÄązÄąn təhlÃŧkəsizliyini artÄąracaq. İndi gÃŧncəlləmək ÃŧçÃŧn ana parolunuzu daxil edin." + }, + "confirmIdentityToContinue": { + "message": "Davam etmək ÃŧçÃŧn kimliyinizi təsdiqləyin" + }, + "enterYourMasterPassword": { + "message": "Ana parolunuzu daxil edin" + }, + "updateSettings": { + "message": "AyarlarÄą gÃŧncəllə" + }, "featureUnavailable": { "message": "Özəllik əlçatmazdÄąr" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Bizi izləyin" }, - "syncVault": { - "message": "Seyfi sinxronlaşdÄąr" + "syncNow": { + "message": "İndi sinxr." }, "changeMasterPass": { "message": "Ana parolu dəyişdir" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "Fayl qoşmalarÄą ÃŧçÃŧn 1 GB şifrələnmiş saxlama sahəsi." }, + "premiumSignUpStorageV2": { + "message": "Fayl qoşmalarÄą ÃŧçÃŧn $SIZE$ şifrələnmiş anbar sahəsi.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "YubiKey və Duo kimi mÃŧlkiyyətçi iki addÄąmlÄą giriş seçimləri." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Buradan xaricə kÃļçÃŧr" }, - "exportVault": { - "message": "Seyfi xaricə kÃļçÃŧr" + "exportNoun": { + "message": "Xaricə kÃļçÃŧrmə", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Xaricə kÃļçÃŧr", + "description": "The verb 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Äą" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Element birdəfəlik silindi" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Element bərpa edildi" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Əlaqə məlumatlarÄą" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "BÃŧtÃŧn \"Send\"lər", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Bu \"Send\"i silmək istədiyinizə əminsiniz?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "\"Send\" keçidini kopyala", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Send keçidini lÃļvhəyə kopyala", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2437,7 +2574,7 @@ "message": "Ana parolunuz təşkilatÄąnÄązdakÄą siyasətlərdən birinə və ya bir neçəsinə uyğun gəlmir. Seyfə erişmək ÃŧçÃŧn ana parolunuzu indi gÃŧncəlləməlisiniz. Davam etsəniz, hazÄąrkÄą seansdan Ã§ÄąxÄąÅŸ etmiş və təkrar giriş etməli olacaqsÄąnÄąz. Digər cihazlardakÄą aktiv seanslar bir saata qədər aktiv qalmağa davam edə bilər." }, "changePasswordWarning": { - "message": "Parolunuzu dəyişdirdikdən sonra yeni parolunuzla giriş etməli olacaqsÄąnÄąz. Digər cihazlardakÄą aktiv seanslar bir saat ərzində Ã§ÄąxÄąÅŸ sonlandÄąrÄąlacaq." + "message": "Parolunuzu dəyişdirdikdən sonra yeni parolunuzla giriş etməli olacaqsÄąnÄąz. Digər cihazlardakÄą aktiv sessiyalar bir saat ərzində sonlandÄąrÄąlacaq." }, "accountRecoveryUpdateMasterPasswordSubtitle": { "message": "HesabÄąn geri qaytarÄąlmasÄą prosesini tamamlamaq ÃŧçÃŧn ana parolunuzu dəyişdirin." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Ana parol silindi." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "AşağıdakÄą təşkilatlarÄąn Ãŧzvləri ÃŧçÃŧn artÄąq ana parol tələb olunmur. LÃŧtfən aşağıdakÄą domeni təşkilatÄąnÄązÄąn inzibatÃ§ÄąsÄą ilə təsdiqləyin." - }, "organizationName": { "message": "Təşkilat adÄą" }, @@ -2991,7 +3125,8 @@ "message": "\"Heç vaxt\"i seçmək istədiyinizə əminsiniz? Kilid seçimini \"Heç vaxt\" olaraq ayarlasanÄąz, seyfinizin şifrələmə açarÄą cihazÄąnÄązda saxlanÄąlacaq. Bu seçimi istifadə etsəniz, cihazÄąnÄązÄą daha yaxÅŸÄą mÃŧhafizə etdiyinizə əmin olmalÄąsÄąnÄąz." }, "vault": { - "message": "Seyf" + "message": "Seyf", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Ana parolla giriş et" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Domen ləqəbi" }, - "importData": { - "message": "Veriləri daxilə kÃļçÃŧr", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Daxilə kÃļçÃŧrmə xətasÄą" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Fayl cihazda saxlanÄąldÄą. Endirilənləri cihazÄąnÄązdan idarə edin." }, + "importantNotice": { + "message": "Vacib bildiriş" + }, + "setupTwoStepLogin": { + "message": "İki addÄąmlÄą girişi qur" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden, 2025-ci ilin Fevral ayÄąndan etibarən yeni cihazlardan gələn girişləri doğrulamaq ÃŧçÃŧn hesabÄąnÄązÄąn e-poçtuna bir kod gÃļndərəcək." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "HesabÄąnÄązÄą qorumaq ÃŧçÃŧn alternativ bir yol kimi iki addÄąmlÄą girişi qura və ya e-poçtunuzu erişə biləcəyiniz bir e-poçtla dəyişdirə bilərsiniz." + }, + "remindMeLater": { + "message": "Daha sonra xatÄąrlat" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "$EMAIL$ e-poçtunuza gÃŧvənli şəkildə erişə bilirsiniz?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Xeyr, bilmirəm" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Bəli, e-poçtuma gÃŧvənli şəkildə erişə bilirəm" + }, + "turnOnTwoStepLogin": { + "message": "İki addÄąmlÄą girişi işə sal" + }, + "changeAcctEmail": { + "message": "HesabÄąn e-poçtunu dəyişdir" + }, + "passkeyLogin": { + "message": "Keçid açarÄą ilə giriş edilsin?" + }, + "savePasskeyQuestion": { + "message": "Keçid açarÄą saxlanÄąlsÄąn?" + }, + "saveNewPasskey": { + "message": "Yeni giriş kimi saxla" + }, + "savePasskeyNewLogin": { + "message": "Keçid açarÄąnÄą yeni bir giriş olaraq saxla" + }, + "noMatchingLoginsForSite": { + "message": "Bu sayt ÃŧçÃŧn uyuşan giriş məlumatÄą yoxdur" + }, + "overwritePasskey": { + "message": "Keçid açarÄąnÄąn Ãŧzərinə yazÄąlsÄąn?" + }, + "unableToSavePasskey": { + "message": "Keçid açarÄą saxlanÄąla bilmir" + }, + "alreadyContainsPasskey": { + "message": "Bu elementdə artÄąq bir keçid açarÄą var. HazÄąrkÄą keçid açarÄąnÄąn Ãŧzərinə yazmaq istədiyinizə əminsiniz?" + }, + "passkeyAlreadyExists": { + "message": "Bu tətbiq ÃŧçÃŧn bir keçid açarÄą artÄąq mÃļvcuddur." + }, + "applicationDoesNotSupportDuplicates": { + "message": "Bu tətbiq, təkrarlarÄą dəstəkləmir." + }, + "closeThisWindow": { + "message": "Bu pəncərəni bağla" + }, "allowScreenshots": { "message": "EkranÄą çəkməyə icazə ver" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Bu giriş risk altÄąndadÄąr və bir veb sayt əskikdir. Daha gÃŧclÃŧ təhlÃŧkəsizlik ÃŧçÃŧn bir veb sayt əlavə edin və parolu dəyişdirin." }, + "vulnerablePassword": { + "message": "Zəifliyi olan parol." + }, + "changeNow": { + "message": "İndi dəyişdir" + }, "missingWebsite": { "message": "Əskik veb sayt" }, @@ -3906,10 +4112,16 @@ "message": "Send ilə həssas məlumatlar əmniyyətdədir", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "Heç bir axtarÄąÅŸ nəticəsi qayÄątmadÄą" + }, "sendsBodyNoItems": { "message": "İstənilən platformada fayllarÄą və veriləri hər kəslə gÃŧvənli şəkildə paylaÅŸÄąn. MəlumatlarÄąnÄąz, ifşa olunmamaq ÃŧçÃŧn ucdan-uca şifrələnmiş qalacaq.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Filtrləri təmizləyin və ya başqa bir axtarÄąÅŸ terminini sÄąnayÄąn" + }, "generatorNudgeTitle": { "message": "Cəld parol yaradÄąn" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Yazma qÄąsayolu" }, - "editAutotypeShortcutDescription": { - "message": "AşağıdakÄą dəyişdiricilərdən birini və ya ikisini daxil edin: Ctrl, Alt, Win və ya Shift və bir hərf." + "editAutotypeKeyboardModifiersDescription": { + "message": "AşağıdakÄą dəyişdiricilərdən birini və ya ikisini daxil edin: Ctrl, Alt, Win və bir hərf." }, "invalidShortcut": { "message": "YararsÄąz qÄąsayol" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Arxivdən Ã§Äąxart" }, + "archived": { + "message": "Arxivləndi" + }, "itemsInArchive": { "message": "Arxivdəki elementlər" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Elementi arxivlə" }, - "archiveItemConfirmDesc": { - "message": "Arxivlənmiş elementlər Ãŧmumi axtarÄąÅŸ nəticələrindən və avto-doldurma təkliflərindən xaric ediləcək. Bu elementi arxivləmək istədiyinizə əminsiniz?" + "archiveItemDialogContent": { + "message": "Arxivləndikdən sonra, bu element axtarÄąÅŸ nəticələrindən və avto-doldurma təkliflərindən xaric ediləcək." + }, + "unArchiveAndSave": { + "message": "Arxivdən Ã§Äąxart və saxla" + }, + "restartPremium": { + "message": "\"Premium\"u yenidən başlat" + }, + "premiumSubscriptionEnded": { + "message": "Premium abunəliyiniz bitdi" + }, + "premiumSubscriptionEndedDesc": { + "message": "Arxivinizə təkrar erişmək ÃŧçÃŧn Premium abunəliyinizi yenidən başladÄąn. Təkrar başlatmazdan əvvəl arxivlənmiş elementin detallarÄąna dÃŧzəliş etsəniz, həmin element seyfinizə daÅŸÄąnacaq." + }, + "itemRestored": { + "message": "Element bərpa edildi" }, "zipPostalCodeLabel": { "message": "ZIP / Poçt kodu" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "Və daha çoxu!" }, - "planDescPremium": { - "message": "Tam onlayn təhlÃŧkəsizlik" + "advancedOnlineSecurity": { + "message": "QabaqcÄąl onlayn təhlÃŧkəsizlik" }, "upgradeToPremium": { "message": "\"Premium\"a yÃŧksəlt" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "TəşkilatÄąnÄąz, artÄąq Bitwarden-ə giriş etmək ÃŧçÃŧn ana parol istifadə etmir. Davam etmək ÃŧçÃŧn təşkilatÄą və domeni doğrulayÄąn." + }, + "continueWithLogIn": { + "message": "Giriş etməyə davam" + }, + "doNotContinue": { + "message": "Davam etmə" + }, + "domain": { + "message": "Domen" + }, + "keyConnectorDomainTooltip": { + "message": "Bu domen, hesabÄąnÄązÄąn şifrələmə açarlarÄąnÄą saxlayacaq, ona gÃļrə də, bu domenə gÃŧvəndiyinizə əmin olun. Əmin deyilsinizsə, adminizlə əlaqə saxlayÄąn." + }, + "verifyYourOrganization": { + "message": "Giriş etmək ÃŧçÃŧn təşkilatÄąnÄązÄą doğrulayÄąn" + }, + "organizationVerified": { + "message": "Təşkilat doğrulandÄą" + }, + "domainVerified": { + "message": "Domen doğrulandÄą" + }, + "leaveOrganizationContent": { + "message": "TəşkilatÄąnÄązÄą doğrulamasanÄąz, təşkilata erişiminiz ləğv ediləcək." + }, + "leaveNow": { + "message": "İndi tərk et" + }, + "verifyYourDomainToLogin": { + "message": "Giriş etmək ÃŧçÃŧn domeninizi doğrulayÄąn" + }, + "verifyYourDomainDescription": { + "message": "Giriş prosesini davam etdirmək ÃŧçÃŧn bu domeni doğrulayÄąn." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Giriş prosesini davam etdirmək ÃŧçÃŧn bu təşkilatÄą və domeni doğrulayÄąn." + }, "sessionTimeoutSettingsAction": { "message": "Vaxt bitmə əməliyyatÄą" }, "sessionTimeoutHeader": { "message": "Sessiya vaxt bitməsi" + }, + "resizeSideNavigation": { + "message": "Yan naviqasiyanÄą yeni. ÃļlçÃŧləndir" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Bu ayar, təşkilatÄąnÄąz tərəfindən idarə olunur." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "TəşkilatÄąnÄąz, maksimum seyf bitmə vaxtÄąnÄą $HOURS$ saat $MINUTES$ dəqiqə olaraq ayarladÄą.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "TəşkilatÄąnÄąz, seansÄąn ilkin bitmə vaxtÄąnÄą Sistem kilidi aÃ§Äąlanda olaraq təyin etdi." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "TəşkilatÄąnÄąz, seansÄąn ilkin bitmə vaxtÄąnÄą Yenidən başladÄąlanda olaraq təyin etdi." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maksimum bitmə vaxtÄą $HOURS$ saat $MINUTES$ dəqiqə dəyərini aşa bilməz", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Yenidən başladÄąlanda" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Vaxt bitmə əməliyyatÄąnÄązÄą dəyişdirmək ÃŧçÃŧn bir kilid açma Ãŧsulu qurun" + }, + "upgrade": { + "message": "YÃŧksəlt" + }, + "leaveConfirmationDialogTitle": { + "message": "Tərk etmək istədiyinizə əminsiniz?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Rədd cavabÄą versəniz, fərdi elementləriniz hesabÄąnÄązda qalacaq, paylaÅŸÄąlan elementlərə və təşkilat Ãļzəlliklərinə erişimi itirəcəksiniz." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Erişimi təkrar qazanmaq ÃŧçÃŧn admininizlə əlaqə saxlayÄąn." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Tərk et: $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Seyfimi necə idarə edim?" + }, + "transferItemsToOrganizationTitle": { + "message": "Elementləri bura kÃļçÃŧr: $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$, təhlÃŧkəsizlik və riayətlilik ÃŧçÃŧn bÃŧtÃŧn elementlərin təşkilata aid olmasÄąnÄą tələb edir. Elementlərinizin sahibliyini transfer etmək ÃŧçÃŧn qəbul edin.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Transferi qəbul et" + }, + "declineAndLeave": { + "message": "Rədd et və tərk et" + }, + "whyAmISeeingThis": { + "message": "Bunu niyə gÃļrÃŧrəm?" } } diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index b2e4db47b32..d4d304ab257 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "ĐĐžĐ˛Ņ‹ URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "ĐĐ´ĐąŅ‹ĐģĐ°ŅŅ ĐŊĐĩŅ‡Đ°ĐēаĐŊĐ°Ņ ĐŋаĐŧŅ‹ĐģĐēа." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "ЗвĐĩҁ҂ĐēŅ– ай ŅĐģĐĩĐŧĐĩĐŊ҆Đĩ" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "ДавĐĩĐ´Đ°Ņ†Ņ†Đ° йОĐģҌ҈" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Đ¤ŅƒĐŊĐēŅ†Ņ‹Ņ ĐŊĐĩĐ´Đ°ŅŅ‚ŅƒĐŋĐŊа" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "ĐĄĐ°Ņ‡Ņ‹Ņ†Đĩ Са ĐŊаĐŧŅ–" }, - "syncVault": { - "message": "ĐĄŅ–ĐŊŅ…Ņ€Đ°ĐŊŅ–ĐˇĐ°Đ˛Đ°Ņ†ŅŒ ŅŅ…ĐžĐ˛Ņ–ŅˆŅ‡Đ°" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "ЗĐŧŅĐŊŅ–Ņ†ŅŒ Đ°ŅĐŊĐžŅžĐŊŅ‹ ĐŋĐ°Ņ€ĐžĐģҌ" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 ГБ ĐˇĐ°ŅˆŅ‹Ņ„Ņ€Đ°Đ˛Đ°ĐŊĐ°ĐŗĐ° ŅŅ…ĐžĐ˛Ņ–ŅˆŅ‡Đ° Đ´ĐģŅ даĐģŅƒŅ‡Đ°ĐŊҋ҅ Ņ„Đ°ĐšĐģĐ°Ņž." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "ĐŸŅ€Đ°ĐŋҀҋĐĩŅ‚Đ°Ņ€ĐŊŅ‹Ņ Đ˛Đ°Ņ€Ņ‹ŅĐŊ҂ҋ Đ´Đ˛ŅƒŅ…ŅŅ‚Đ°ĐŋĐŊĐ°ĐŗĐ° ŅžĐ˛Đ°Ņ…ĐžĐ´Ņƒ, Ņ‚Đ°ĐēŅ–Ņ ŅĐē YubiKey, FIDO U2F Ņ– Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Đ­ĐēҁĐŋĐ°Ņ€Ņ‚Đ°Đ˛Đ°Ņ†ŅŒ ŅŅ…ĐžĐ˛Ņ–ŅˆŅ‡Đ°" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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": "Đ¤Đ°Ņ€ĐŧĐ°Ņ‚ Ņ„Đ°ĐšĐģа" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Đ­ĐģĐĩĐŧĐĩĐŊŅ‚ Đ˛Ņ‹Đ´Đ°ĐģĐĩĐŊŅ‹ ĐŊĐ°ĐˇĐ°ŅžŅŅ‘Đ´Ņ‹" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Đ­ĐģĐĩĐŧĐĩĐŊŅ‚ адĐŊĐžŅžĐģĐĩĐŊŅ‹" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "ĐŖŅĐĩ Send'Ņ‹", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Đ’Ņ‹ ŅĐ°ĐŋŅ€Đ°ŅžĐ´Ņ‹ Ņ…ĐžŅ‡Đ°Ņ†Đĩ Đ˛Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ ĐŗŅŅ‚Ņ‹ Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "ĐĄĐēаĐŋŅ–ŅĐ˛Đ°Ņ†ŅŒ ҁĐŋĐ°ŅŅ‹ĐģĐē҃ ĐŊа Send ҃ ĐąŅƒŅ„ĐĩŅ€ айĐŧĐĩĐŊ҃", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "ĐŅĐŊĐžŅžĐŊŅ‹ ĐŋĐ°Ņ€ĐžĐģҌ Đ˛Ņ‹Đ´Đ°ĐģĐĩĐŊŅ‹." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Đ’Ņ‹ ŅĐ°ĐŋŅ€Đ°ŅžĐ´Ņ‹ Ņ…ĐžŅ‡Đ°Ņ†Đĩ адĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ĐąĐģаĐēŅ–Ņ€ĐžŅžĐē҃ ŅŅ…ĐžĐ˛Ņ–ŅˆŅ‡Đ°? ĐŸŅ€Ņ‹ĐˇĐŊĐ°Ņ‡Ņ‹ŅžŅˆŅ‹ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁ ĐąĐģаĐēŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊŅ \"ĐŅ–ĐēĐžĐģŅ–\", ĐēĐģŅŽŅ‡ ŅˆŅ‹Ņ„Ņ€Đ°Đ˛Đ°ĐŊĐŊŅ ĐąŅƒĐ´ĐˇĐĩ ĐˇĐ°Ņ…ĐžŅžĐ˛Đ°Ņ†Ņ†Đ° ĐŊа Đ˛Đ°ŅˆĐ°Đš ĐŋҀҋĐģадСĐĩ. КаĐģŅ– Đ˛Ņ‹ Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°Đĩ҆Đĩ ĐŗŅŅ‚Ņ‹ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁ, Đ˛Ņ‹ ĐŋĐ°Đ˛Ņ–ĐŊĐŊŅ‹ ĐąŅ‹Ņ†ŅŒ ҃ĐŋŅŅžĐŊĐĩĐŊŅ‹ Ņž ҂ҋĐŧ, ŅˆŅ‚Đž Đ˛Đ°ŅˆĐ° ĐŋҀҋĐģада ĐŊадСĐĩĐšĐŊа Đ°ĐąĐ°Ņ€ĐžĐŊĐĩĐŊа." }, "vault": { - "message": "ĐĄŅ…ĐžĐ˛Ņ–ŅˆŅ‡Đ°" + "message": "ĐĄŅ…ĐžĐ˛Ņ–ŅˆŅ‡Đ°", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "ĐŖĐ˛Đ°ĐšŅŅ†Ņ– С Đ°ŅĐŊĐžŅžĐŊŅ‹Đŧ ĐŋĐ°Ņ€ĐžĐģĐĩĐŧ" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index ad03c2cc023..692a98d249f 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "ИСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐĩŅ‚Đž ҉Đĩ ĐąŅŠĐ´Đĩ ĐžĐēĐžĐŊŅ‡Đ°Ņ‚ĐĩĐģĐŊĐž Đ¸ĐˇŅ‚Ņ€Đ¸Ņ‚Đž ĐŊа Ņ‚Đ°ĐˇĐ¸ Đ´Đ°Ņ‚Đ°.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "ФаКĐģ Са ҁĐŋОдĐĩĐģŅĐŊĐĩ" + }, + "hideTextByDefault": { + "message": "ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа Ņ‚ĐĩĐēŅŅ‚Đ° ĐŋĐž ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ" + }, + "hideYourEmail": { + "message": "ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа Đ’Đ°ŅˆĐ°Ņ‚Đ° Đĩ-ĐŋĐžŅ‰Đ° ĐžŅ‚ ĐŋĐžĐģŅƒŅ‡Đ°Ņ‚ĐĩĐģĐ¸Ņ‚Đĩ." + }, + "limitSendViews": { + "message": "ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡Đ°Đ˛Đ°ĐŊĐĩ ĐŊа ĐŋŅ€ĐĩĐŗĐģĐĩĐļдаĐŊĐ¸ŅŅ‚Đ°" + }, + "limitSendViewsCount": { + "message": "ĐžŅŅ‚Đ°Đ˛Đ°Ņ‚ $ACCESSCOUNT$ ĐŋŅ€ĐĩĐŗĐģĐĩĐļдаĐŊĐ¸Ņ", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "НиĐēОК ĐŊŅĐŧа да ĐŧĐžĐļĐĩ да ĐŋŅ€ĐĩĐŗĐģĐĩĐļда Ņ‚ĐžĐ˛Đ° ИСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐĩ ҁĐģĐĩĐ´ Đ´ĐžŅŅ‚Đ¸ĐŗĐ°ĐŊĐĩ ĐŊа ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩĐŊиĐĩŅ‚Đž.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Đ›Đ¸Ņ‡ĐŊа ĐąĐĩĐģĐĩĐļĐēа" + }, + "sendDetails": { + "message": "ĐŸĐžĐ´Ņ€ĐžĐąĐŊĐžŅŅ‚Đ¸ Са ИСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐĩŅ‚Đž", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "ДобавĐĩŅ‚Đĩ ĐŊĐĩĐˇĐ°Đ´ŅŠĐģĐļĐ¸Ņ‚ĐĩĐģĐŊа ĐŋĐ°Ņ€ĐžĐģа, ҁ ĐēĐžŅŅ‚Đž ĐŋĐžĐģŅƒŅ‡Đ°Ņ‚ĐĩĐģĐ¸Ņ‚Đĩ да иĐŧĐ°Ņ‚ Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž Ņ‚ĐžĐ˛Đ° ИСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐĩ.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "ĐĸĐĩĐēҁ҂ Са ҁĐŋОдĐĩĐģŅĐŊĐĩ" + }, + "newItemHeaderTextSend": { + "message": "Ново Ņ‚ĐĩĐēŅŅ‚ĐžĐ˛Đž ИСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐĩ", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "Ново Ņ„Đ°ĐšĐģОвО ИСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐĩ", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Ņ‚ĐĩĐēŅŅ‚ĐžĐ˛ĐžŅ‚Đž ИСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐĩ", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Đ ĐĩдаĐēŅ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа Ņ„Đ°ĐšĐģĐžĐ˛ĐžŅ‚Đž ИСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐĩ", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "ĐĐ°Đ¸ŅŅ‚Đ¸ĐŊа Đģи Đ¸ŅĐēĐ°Ņ‚Đĩ да Đ¸ĐˇŅ‚Ņ€Đ¸ĐĩŅ‚Đĩ СавиĐŊĐ°ĐŗĐ¸ Ņ‚ĐžĐ˛Đ° ИСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐĩ?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "Ново", + "description": "for adding new items" + }, "newUri": { "message": "Нов Đ°Đ´Ņ€Đĩҁ" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Đ”ĐžĐąĐ°Đ˛ŅĐŊĐĩ ĐŊа ĐŋŅ€Đ¸ĐēĐ°Ņ‡ĐĩĐŊ Ņ„Đ°ĐšĐģ" }, + "itemsTransferred": { + "message": "ЕĐģĐĩĐŧĐĩĐŊŅ‚Đ¸Ņ‚Đĩ ŅĐ° ĐŋŅ€ĐĩŅ…Đ˛ŅŠŅ€ĐģĐĩĐŊи" + }, + "fixEncryption": { + "message": "ПоĐŋŅ€Đ°Đ˛ŅĐŊĐĩ ĐŊа ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐĩŅ‚Đž" + }, + "fixEncryptionTooltip": { + "message": "ĐĸОСи Ņ„Đ°ĐšĐģ иСĐŋĐžĐģСва ĐžŅŅ‚Đ°Ņ€ŅĐģ ĐŧĐĩŅ‚ĐžĐ´ ĐŊа ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐĩ." + }, + "attachmentUpdated": { + "message": "ĐŸŅ€Đ¸ĐēĐ°Ņ‡ĐĩĐŊĐ¸ŅŅ‚ Ņ„Đ°ĐšĐģ Đĩ аĐēŅ‚ŅƒĐ°ĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊ" + }, "maxFileSizeSansPunctuation": { "message": "МаĐēŅĐ¸ĐŧаĐģĐŊĐ¸ŅŅ‚ Ņ€Đ°ĐˇĐŧĐĩŅ€ ĐŊа Ņ„Đ°ĐšĐģа Đĩ 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "НĐĩĐžŅ‡Đ°ĐēваĐŊа ĐŗŅ€Đĩ҈Đēа." }, + "unexpectedErrorShort": { + "message": "НĐĩĐžŅ‡Đ°ĐēваĐŊа ĐŗŅ€Đĩ҈Đēа" + }, + "closeThisBitwardenWindow": { + "message": "Đ—Đ°Ņ‚Đ˛ĐžŅ€ĐĩŅ‚Đĩ ĐŋŅ€ĐžĐˇĐžŅ€ĐĩŅ†Đ° ĐŊа Đ‘Đ¸Ņ‚ŅƒĐžŅ€Đ´ĐĩĐŊ и ĐžĐŋĐ¸Ņ‚Đ°ĐšŅ‚Đĩ ĐžŅ‚ĐŊОвО." + }, "itemInformation": { "message": "ХвĐĩĐ´ĐĩĐŊĐ¸Ņ Са ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "ĐĐ°ŅƒŅ‡ĐĩŅ‚Đĩ ĐŋОвĐĩ҇Đĩ" }, + "migrationsFailed": { + "message": "Đ’ŅŠĐˇĐŊиĐēĐŊа ĐŗŅ€Đĩ҈Đēа ĐŋŅ€Đ¸ ОйĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩŅ‚Đž ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ Са ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐĩ." + }, + "updateEncryptionSettingsTitle": { + "message": "ОбĐŊОвĐĩŅ‚Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ ŅĐ¸ Са ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐĩ" + }, + "updateEncryptionSettingsDesc": { + "message": "ĐĐžĐ˛Đ¸Ņ‚Đĩ ĐŋŅ€ĐĩĐŋĐžŅ€ŅŠŅ‡Đ°ĐŊи ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи Са ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐĩ ҉Đĩ ĐŋĐžĐ´ĐžĐąŅ€ŅŅ‚ ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚Ņ‚Đ° ĐŊа аĐēĐ°ŅƒĐŊŅ‚Đ° Ви. Đ’ŅŠĐ˛ĐĩĐ´ĐĩŅ‚Đĩ ĐŗĐģавĐŊĐ°Ņ‚Đ° ŅĐ¸ ĐŋĐ°Ņ€ĐžĐģа, Са да ĐŗĐ¸ ОйĐŊĐžĐ˛Đ¸Ņ‚Đĩ ҁĐĩĐŗĐ°." + }, + "confirmIdentityToContinue": { + "message": "ĐŸĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩŅ‚Đĩ ŅĐ°ĐŧĐžĐģĐ¸Ņ‡ĐŊĐžŅŅ‚Ņ‚Đ° ŅĐ¸, Са да ĐŋŅ€ĐžĐ´ŅŠĐģĐļĐ¸Ņ‚Đĩ" + }, + "enterYourMasterPassword": { + "message": "Đ’ŅŠĐ˛ĐĩĐ´ĐĩŅ‚Đĩ ĐŗĐģавĐŊĐ°Ņ‚Đ° ŅĐ¸ ĐŋĐ°Ņ€ĐžĐģа" + }, + "updateSettings": { + "message": "ОбĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ" + }, "featureUnavailable": { "message": "Đ¤ŅƒĐŊĐēŅ†Đ¸ŅŅ‚Đ° Đĩ ĐŊĐĩĐ´ĐžŅŅ‚ŅŠĐŋĐŊа" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "ĐĄĐģĐĩĐ´Đ˛Đ°ĐšŅ‚Đĩ ĐŊи" }, - "syncVault": { - "message": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩ" + "syncNow": { + "message": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐĩ ҁĐĩĐŗĐ°" }, "changeMasterPass": { "message": "ĐŸŅ€ĐžĐŧŅĐŊа ĐŊа ĐŗĐģавĐŊĐ°Ņ‚Đ° ĐŋĐ°Ņ€ĐžĐģа" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 ГБ ĐŋŅ€ĐžŅŅ‚Ņ€Đ°ĐŊŅŅ‚Đ˛Đž Са Ņ„Đ°ĐšĐģОвĐĩ, ĐēĐžĐ¸Ņ‚Đž ҁĐĩ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°Ņ‚." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ ĐŋŅ€ĐžŅŅ‚Ņ€Đ°ĐŊŅŅ‚Đ˛Đž Са Ņ„Đ°ĐšĐģОвĐĩ, ĐēĐžĐ¸Ņ‚Đž ҁĐĩ ŅˆĐ¸Ņ„Ņ€Đ¸Ņ€Đ°Ņ‚.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Đ§Đ°ŅŅ‚ĐŊĐž Đ´Đ˛ŅƒŅŅ‚ĐĩĐŋĐĩĐŊĐŊĐž ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ ҇ҀĐĩС YubiKey и Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "ИСĐŊĐ°ŅŅĐŊĐĩ ĐžŅ‚" }, - "exportVault": { - "message": "ИСĐŊĐ°ŅŅĐŊĐĩ ĐŊа ҂ҀĐĩĐˇĐžŅ€Đ°" + "exportNoun": { + "message": "ИСĐŊĐ°ŅŅĐŊĐĩ", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "ИСĐŊĐ°ŅŅĐŊĐĩ", + "description": "The verb 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": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚ ĐŊа Ņ„Đ°ĐšĐģа" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "ЗаĐŋĐ¸ŅŅŠŅ‚ Đĩ Đ¸ĐˇŅ‚Ņ€Đ¸Ņ‚ ĐžĐēĐžĐŊŅ‡Đ°Ņ‚ĐĩĐģĐŊĐž" }, + "archivedItemRestored": { + "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐ¸ŅŅ‚ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ Đĩ Đ˛ŅŠĐˇŅŅ‚Đ°ĐŊОвĐĩĐŊ" + }, "restoredItem": { "message": "ЗаĐŋĐ¸ŅŅŠŅ‚ Đĩ Đ˛ŅŠĐˇŅŅ‚Đ°ĐŊОвĐĩĐŊ" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Са ĐēĐžĐŊŅ‚Đ°ĐēŅ‚" }, + "send": { + "message": "ИСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐĩ", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Đ’ŅĐ¸Ņ‡Đēи иСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐ¸Ņ", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да Đ¸ĐˇŅ‚Ņ€Đ¸ĐĩŅ‚Đĩ Ņ‚ĐžĐ˛Đ° иСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐĩ?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "КоĐŋĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ˛Ņ€ŅŠĐˇĐēĐ°Ņ‚Đ° ĐēҊĐŧ ИСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐĩŅ‚Đž", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "КоĐŋĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ˛Ņ€ŅŠĐˇĐēĐ°Ņ‚Đ° ĐēҊĐŧ иСĐŋŅ€Đ°Ņ‰Đ°ĐŊĐĩŅ‚Đž", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "ГĐģавĐŊĐ°Ņ‚Đ° ĐŋĐ°Ņ€ĐžĐģа Đĩ ĐŋŅ€ĐĩĐŧĐ°Ņ…ĐŊĐ°Ņ‚Đ°." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "За ҇ĐģĐĩĐŊОвĐĩŅ‚Đĩ ĐŊа ҁĐģĐĩĐ´ĐŊĐ°Ņ‚Đ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ вĐĩ҇Đĩ ĐŊĐĩ ҁĐĩ Đ¸ĐˇĐ¸ŅĐēва ĐŗĐģавĐŊа ĐŋĐ°Ņ€ĐžĐģа. ĐŸĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩŅ‚Đĩ Đ´ĐžĐŧĐĩĐšĐŊа ĐŋĐž-Đ´ĐžĐģ҃ ҁ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° ĐŊа ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° ŅĐ¸." - }, "organizationName": { "message": "ИĐŧĐĩ ĐŊа ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ°" }, @@ -2991,7 +3125,8 @@ "message": "ĐŖĐ˛ĐĩŅ€ĐĩĐŊи Đģи ҁ҂Đĩ, ҇Đĩ Đ¸ŅĐēĐ°Ņ‚Đĩ да СададĐĩŅ‚Đĩ ŅŅ‚ĐžĐšĐŊĐžŅŅ‚ „НиĐēĐžĐŗĐ°â€œ? ĐĸОва вОди Đ´Đž ŅŅŠŅ…Ņ€Đ°ĐŊĐĩĐŊиĐĩ ĐŊа ŅˆĐ¸Ņ„Ņ€Đ¸Ņ€Đ°Ņ‰Đ¸Ņ ĐēĐģŅŽŅ‡ Са ҂ҀĐĩĐˇĐžŅ€Đ° Đ˛ŅŠĐ˛ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛ĐžŅ‚Đž ви. АĐēĐž иСĐŋĐžĐģĐˇĐ˛Đ°Ņ‚Đĩ Ņ‚Đ°ĐˇĐ¸ Đ˛ŅŠĐˇĐŧĐžĐļĐŊĐžŅŅ‚, Đĩ ĐŧĐŊĐžĐŗĐž ваĐļĐŊĐž да иĐŧĐ°Ņ‚Đĩ ĐŊадĐģĐĩĐļĐŊа ĐˇĐ°Ņ‰Đ¸Ņ‚Đ° ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛ĐžŅ‚Đž ŅĐ¸." }, "vault": { - "message": "ĐĸŅ€ĐĩĐˇĐžŅ€" + "message": "ĐĸŅ€ĐĩĐˇĐžŅ€", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "ВĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ ҁ ĐŗĐģавĐŊĐ°Ņ‚Đ° ĐŋĐ°Ņ€ĐžĐģа" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "ĐŸŅĐĩвдОĐŊиĐŧĐĩĐŊ Đ´ĐžĐŧĐĩĐšĐŊ" }, - "importData": { - "message": "ВĐŊĐ°ŅŅĐŊĐĩ ĐŊа даĐŊĐŊи", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ вĐŊĐ°ŅŅĐŊĐĩŅ‚Đž" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "ФаКĐģŅŠŅ‚ Đĩ СаĐŋаСĐĩĐŊ ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛ĐžŅ‚Đž. МоĐļĐĩŅ‚Đĩ да ĐŗĐž ĐŊаĐŧĐĩŅ€Đ¸Ņ‚Đĩ в ĐŧŅŅŅ‚ĐžŅ‚Đž Са ŅĐ˛Đ°ĐģŅĐŊĐ¸Ņ ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛ĐžŅ‚Đž." }, + "importantNotice": { + "message": "ВаĐļĐŊĐž ŅŅŠĐžĐąŅ‰ĐĩĐŊиĐĩ" + }, + "setupTwoStepLogin": { + "message": "ĐĐ°ŅŅ‚Ņ€ĐžĐšŅ‚Đĩ Đ´Đ˛ŅƒŅŅ‚ĐĩĐŋĐĩĐŊĐŊĐž ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Đ‘Đ¸Ņ‚ŅƒĐžŅ€Đ´ĐĩĐŊ ҉Đĩ иСĐŋŅ€Đ°Ņ‚Đ¸ ĐēОд Đ´Đž Đĩ-ĐŋĐžŅ‰Đ°Ņ‚Đ° Ви, Са ĐŋĐžŅ‚Đ˛ŅŠŅ€ĐļдаваĐŊĐĩ ĐŊа вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩŅ‚Đž ĐžŅ‚ ĐŊОви ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°. ĐĸОва ҉Đĩ СаĐŋĐžŅ‡ĐŊĐĩ ĐžŅ‚ Ņ„ĐĩĐ˛Ņ€ŅƒĐ°Ņ€Đ¸ 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "МоĐļĐĩŅ‚Đĩ да ĐŊĐ°ŅŅ‚Ņ€ĐžĐ¸Ņ‚Đĩ Đ´Đ˛ŅƒŅŅ‚ĐĩĐŋĐĩĐŊĐŊĐž ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ, ĐēĐ°Ņ‚Đž Ņ€Đ°ĐˇĐģĐ¸Ņ‡ĐĩĐŊ ĐŧĐĩŅ‚ĐžĐ´ ĐŊа ĐˇĐ°Ņ‰Đ¸Ņ‚Đ°, иĐģи аĐēĐž Đĩ ĐŊĐĩĐžĐąŅ…ĐžĐ´Đ¸ĐŧĐž да ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ Đĩ-ĐŋĐžŅ‰Đ°Ņ‚Đ° ŅĐ¸ ҁ Ņ‚Đ°Đēава, Đ´Đž ĐēĐžŅŅ‚Đž иĐŧĐ°Ņ‚Đĩ Đ´ĐžŅŅ‚ŅŠĐŋ." + }, + "remindMeLater": { + "message": "НаĐŋĐžĐŧĐŊĐĩŅ‚Đĩ Đŧи ĐŋĐž-ĐēҊҁĐŊĐž" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "ИĐŧĐ°Ņ‚Đĩ Đģи ŅĐ¸ĐŗŅƒŅ€ĐĩĐŊ Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž Đĩ-ĐŋĐžŅ‰Đ°Ņ‚Đ° ŅĐ¸ – $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "НĐĩ, ĐŊŅĐŧаĐŧ" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Да, иĐŧаĐŧ Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž Đĩ-ĐŋĐžŅ‰Đ°Ņ‚Đ° ŅĐ¸" + }, + "turnOnTwoStepLogin": { + "message": "ВĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŊа Đ´Đ˛ŅƒŅŅ‚ĐĩĐŋĐĩĐŊĐŊĐžŅ‚Đž ŅƒĐ´ĐžŅŅ‚ĐžĐ˛ĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ" + }, + "changeAcctEmail": { + "message": "ĐŸŅ€ĐžĐŧŅĐŊа ĐŊа Đĩ-ĐŋĐžŅ‰Đ°Ņ‚Đ°" + }, + "passkeyLogin": { + "message": "ВĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ ҁҊҁ ҁĐĩĐēŅ€ĐĩŅ‚ĐĩĐŊ ĐēĐģŅŽŅ‡?" + }, + "savePasskeyQuestion": { + "message": "Да ҁĐĩ СаĐŋаСи ĐŊа ҁĐĩĐēŅ€ĐĩŅ‚ĐŊĐ¸ŅŅ‚ ĐēĐģŅŽŅ‡?" + }, + "saveNewPasskey": { + "message": "ЗаĐŋаСваĐŊĐĩ ĐēĐ°Ņ‚Đž ĐŊОв ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ Са вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ" + }, + "savePasskeyNewLogin": { + "message": "ЗаĐŋаСваĐŊĐĩ ĐŊа ҁĐĩĐēŅ€ĐĩŅ‚ĐŊĐ¸Ņ ĐēĐģŅŽŅ‡ ĐēĐ°Ņ‚Đž ĐŊОв ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ Са вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ" + }, + "noMatchingLoginsForSite": { + "message": "ĐŅĐŧа СаĐŋĐ¸ŅĐ¸ Са вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ ĐžŅ‚ĐŗĐžĐ˛Đ°Ņ€ŅŅ‰Đ¸ ĐŊа Ņ‚ĐžĐˇĐ¸ ҃ĐĩĐą ŅĐ°ĐšŅ‚" + }, + "overwritePasskey": { + "message": "Да ҁĐĩ СаĐŧĐĩĐŊи Đģи ҁĐĩĐēŅ€ĐĩŅ‚ĐŊĐ¸ŅŅ‚ ĐēĐģŅŽŅ‡?" + }, + "unableToSavePasskey": { + "message": "ĐĄĐĩĐēŅ€ĐĩŅ‚ĐŊĐ¸ŅŅ‚ ĐēĐģŅŽŅ‡ ĐŊĐĩ ĐŧĐžĐļĐĩ да ĐąŅŠĐ´Đĩ СаĐŋаСĐĩĐŊ" + }, + "alreadyContainsPasskey": { + "message": "ĐĸОСи ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ вĐĩ҇Đĩ ŅŅŠĐ´ŅŠŅ€Đļа ҁĐĩĐēŅ€ĐĩŅ‚ĐĩĐŊ ĐēĐģŅŽŅ‡. ĐĐ°Đ¸ŅŅ‚Đ¸ĐŊа Đģи Đ¸ŅĐēĐ°Ņ‚Đĩ да СаĐŧĐĩĐŊĐ¸Ņ‚Đĩ Ņ‚ĐĩĐēŅƒŅ‰Đ¸Ņ ҁĐĩĐēŅ€ĐĩŅ‚ĐĩĐŊ ĐēĐģŅŽŅ‡?" + }, + "passkeyAlreadyExists": { + "message": "За Ņ‚ĐžĐ˛Đ° ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ вĐĩ҇Đĩ ŅŅŠŅ‰ĐĩŅŅ‚Đ˛ŅƒĐ˛Đ° ҁĐĩĐēŅ€ĐĩŅ‚ĐĩĐŊ ĐēĐģŅŽŅ‡." + }, + "applicationDoesNotSupportDuplicates": { + "message": "ĐĸОва ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ĐŊĐĩ ĐŋĐžĐ´Đ´ŅŠŅ€Đļа Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Đ¸." + }, + "closeThisWindow": { + "message": "Đ—Đ°Ņ‚Đ˛ĐžŅ€ĐĩŅ‚Đĩ Ņ‚ĐžĐˇĐ¸ ĐŋŅ€ĐžĐˇĐžŅ€Đĩ҆" + }, "allowScreenshots": { "message": "ПозвоĐģŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐˇĐ°ŅĐŊĐĩĐŧаĐŊĐĩŅ‚Đž ĐŊа ĐĩĐēŅ€Đ°ĐŊа" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "ĐĸОСи ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ Са вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ Đĩ в Ņ€Đ¸ŅĐē и в ĐŊĐĩĐŗĐž ĐģиĐŋŅĐ˛Đ° ҃ĐĩĐą ŅĐ°ĐšŅ‚. ДобавĐĩŅ‚Đĩ ҃ĐĩĐą ŅĐ°ĐšŅ‚ и ҁĐŧĐĩĐŊĐĩŅ‚Đĩ ĐŋĐ°Ņ€ĐžĐģĐ°Ņ‚Đ°, Са ĐŋĐž-Đ´ĐžĐąŅ€Đ° ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚." }, + "vulnerablePassword": { + "message": "ĐŖŅĐˇĐ˛Đ¸Đŧа ĐŋĐ°Ņ€ĐžĐģа." + }, + "changeNow": { + "message": "ĐŸŅ€ĐžĐŧŅĐŊа ҁĐĩĐŗĐ°" + }, "missingWebsite": { "message": "ЛиĐŋŅĐ˛Đ°Ņ‰ ҃ĐĩĐą ŅĐ°ĐšŅ‚" }, @@ -3906,10 +4112,16 @@ "message": "ИСĐŋŅ€Đ°Ņ‰Đ°ĐšŅ‚Đĩ Ņ‡ŅƒĐ˛ŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģĐŊа иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ ŅĐ¸ĐŗŅƒŅ€ĐŊĐž", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "ĐŅĐŧа Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž" + }, "sendsBodyNoItems": { "message": "ĐĄĐŋОдĐĩĐģŅĐšŅ‚Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊĐž Ņ„Đ°ĐšĐģОвĐĩ и даĐŊĐŊи ҁ Đ˛ŅĐĩĐēĐ¸ĐŗĐž, ĐŋŅ€ĐĩС Đ˛ŅŅĐēа ŅĐ¸ŅŅ‚ĐĩĐŧа. ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸ŅŅ‚Đ° Ви ҉Đĩ ĐąŅŠĐ´Đĩ ĐˇĐ°Ņ‰Đ¸Ņ‚ĐĩĐŊа ҁ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐĩ ĐžŅ‚ ĐēŅ€Đ°Đš Đ´Đž ĐēŅ€Đ°Đš, а видиĐŧĐžŅŅ‚Ņ‚Đ° Ņ ҉Đĩ ĐąŅŠĐ´Đĩ ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩĐŊа.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Đ˜ĐˇŅ‡Đ¸ŅŅ‚ĐĩŅ‚Đĩ Ņ„Đ¸ĐģŅ‚Ņ€Đ¸Ņ‚Đĩ иĐģи ĐžĐŋĐ¸Ņ‚Đ°ĐšŅ‚Đĩ да Ņ‚ŅŠŅ€ŅĐ¸Ņ‚Đĩ ĐŊĐĩŅ‰Đž Đ´Ņ€ŅƒĐŗĐž" + }, "generatorNudgeTitle": { "message": "ĐĄŅŠĐˇĐ´Đ°Đ˛Đ°ĐšŅ‚Đĩ ĐŋĐ°Ņ€ĐžĐģи ĐąŅŠŅ€ĐˇĐž" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "КоĐŧйиĐŊĐ°Ņ†Đ¸Ņ Са Đ˛ŅŠĐ˛ĐĩĐļдаĐŊĐĩ" }, - "editAutotypeShortcutDescription": { - "message": "ИСĐŋĐžĐģĐˇĐ˛Đ°ĐšŅ‚Đĩ ĐĩдиĐŊ иĐģи ĐŋОвĐĩ҇Đĩ ĐžŅ‚ ĐŧĐžĐ´Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžŅ€Đ¸Ņ‚Đĩ Ctrl, Alt, Win иĐģи Shift, СаĐĩĐ´ĐŊĐž ҁ ĐŊŅĐēĐžŅ ĐąŅƒĐēва." + "editAutotypeKeyboardModifiersDescription": { + "message": "ИСĐŋĐžĐģĐˇĐ˛Đ°ĐšŅ‚Đĩ ĐĩдиĐŊ иĐģи ĐŋОвĐĩ҇Đĩ ĐžŅ‚ ĐŧĐžĐ´Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžŅ€Đ¸Ņ‚Đĩ Ctrl, Alt иĐģи Win, СаĐĩĐ´ĐŊĐž ҁ ĐŊŅĐēĐžŅ ĐąŅƒĐēва." }, "invalidShortcut": { "message": "НĐĩĐŋŅ€Đ°Đ˛Đ¸ĐģĐŊа ĐēĐžĐŧйиĐŊĐ°Ņ†Đ¸Ņ" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "ИСваĐļдаĐŊĐĩ ĐžŅ‚ Đ°Ņ€Ņ…Đ¸Đ˛Đ°" }, + "archived": { + "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐž" + }, "itemsInArchive": { "message": "ЕĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ в Đ°Ņ€Ņ…Đ¸Đ˛Đ°" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ°" }, - "archiveItemConfirmDesc": { - "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐ¸Ņ‚Đĩ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ŅĐ° иСĐēĐģŅŽŅ‡ĐĩĐŊи ĐžŅ‚ ĐžĐąŅ‰Đ¸Ņ‚Đĩ Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸ ĐŋŅ€Đ¸ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ и ĐžŅ‚ ĐŋŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊĐ¸ŅŅ‚Đ° Са Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋĐžĐŋҊĐģваĐŊĐĩ. ĐĐ°Đ¸ŅŅ‚Đ¸ĐŊа Đģи Đ¸ŅĐēĐ°Ņ‚Đĩ да Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°Ņ‚Đĩ Ņ‚ĐžĐˇĐ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚?" + "archiveItemDialogContent": { + "message": "ĐĄĐģĐĩĐ´ ĐēĐ°Ņ‚Đž ĐąŅŠĐ´Đĩ Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊ, Ņ‚ĐžĐˇĐ¸ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ ĐŊŅĐŧа да ҁĐĩ ĐŋĐžĐēаСва в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐŋŅ€Đ¸ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ, ĐŊĐ¸Ņ‚Đž в ĐŋŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊĐ¸ŅŅ‚Đ° Са Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐŋĐžĐŋҊĐģваĐŊĐĩ." + }, + "unArchiveAndSave": { + "message": "Đ Đ°ĐˇĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ и СаĐŋаСваĐŊĐĩ" + }, + "restartPremium": { + "message": "ПодĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐŋĐģĐ°Ņ‚ĐĩĐŊĐ¸Ņ айОĐŊаĐŧĐĩĐŊŅ‚" + }, + "premiumSubscriptionEnded": { + "message": "Đ’Đ°ŅˆĐ¸ŅŅ‚ айОĐŊаĐŧĐĩĐŊŅ‚ Са ĐŋĐģĐ°Ņ‚ĐĩĐŊĐ¸Ņ ĐŋĐģаĐŊ Đĩ ĐŋŅ€Đ¸ĐēĐģŅŽŅ‡Đ¸Đģ" + }, + "premiumSubscriptionEndedDesc": { + "message": "АĐēĐž Đ¸ŅĐēĐ°Ņ‚Đĩ ĐžŅ‚ĐŊОвО да ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚Đĩ Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž Đ°Ņ€Ņ…Đ¸Đ˛Đ° ŅĐ¸, Ņ‚Ņ€ŅĐąĐ˛Đ° да ĐŋОдĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐŋĐģĐ°Ņ‚ĐĩĐŊĐ¸Ņ ŅĐ¸ айОĐŊаĐŧĐĩĐŊŅ‚. АĐēĐž Ņ€ĐĩдаĐēŅ‚Đ¸Ņ€Đ°Ņ‚Đĩ даĐŊĐŊĐ¸Ņ‚Đĩ Са Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚ ĐŋŅ€Đĩди ĐŋОдĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩŅ‚Đž, Ņ‚ĐžĐš ҉Đĩ ĐąŅŠĐ´Đĩ Đ˛ŅŠŅ€ĐŊĐ°Ņ‚ в ҂ҀĐĩĐˇĐžŅ€Đ°." + }, + "itemRestored": { + "message": "ЗаĐŋĐ¸ŅŅŠŅ‚ ĐąĐĩ Đ˛ŅŠĐˇŅŅ‚Đ°ĐŊОвĐĩĐŊ" }, "zipPostalCodeLabel": { "message": "ĐŸĐžŅ‰ĐĩĐŊҁĐēи ĐēОд" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "И ĐžŅ‰Đĩ!" }, - "planDescPremium": { - "message": "ĐŸŅŠĐģĐŊа ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚ в ИĐŊŅ‚ĐĩŅ€ĐŊĐĩŅ‚" + "advancedOnlineSecurity": { + "message": "Đ Đ°ĐˇŅˆĐ¸Ņ€ĐĩĐŊа ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚ в ИĐŊŅ‚ĐĩŅ€ĐŊĐĩŅ‚" }, "upgradeToPremium": { "message": "ĐĐ°Đ´ĐŗŅ€Đ°Đ´ĐĩŅ‚Đĩ Đ´Đž ПĐģĐ°Ņ‚ĐĩĐŊĐ¸Ņ ĐŋĐģаĐŊ" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Đ’Đ°ŅˆĐ°Ņ‚Đ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ вĐĩ҇Đĩ ĐŊĐĩ иСĐŋĐžĐģСва ĐŗĐģавĐŊи ĐŋĐ°Ņ€ĐžĐģи Са вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩ в Đ‘Đ¸Ņ‚ŅƒĐžŅ€Đ´ĐĩĐŊ. За да ĐŋŅ€ĐžĐ´ŅŠĐģĐļĐ¸Ņ‚Đĩ, ĐŋĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩŅ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° и Đ´ĐžĐŧĐĩĐšĐŊа." + }, + "continueWithLogIn": { + "message": "ĐŸŅ€ĐžĐ´ŅŠĐģĐļаваĐŊĐĩ ҁ вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩŅ‚Đž" + }, + "doNotContinue": { + "message": "НĐĩ ĐŋŅ€ĐžĐ´ŅŠĐģĐļаваĐŧ" + }, + "domain": { + "message": "ДоĐŧĐĩĐšĐŊ" + }, + "keyConnectorDomainTooltip": { + "message": "ĐĸОСи Đ´ĐžĐŧĐĩĐšĐŊ ҉Đĩ ŅŅŠŅ…Ņ€Đ°ĐŊŅĐ˛Đ° ĐēĐģŅŽŅ‡ĐžĐ˛ĐĩŅ‚Đĩ Са ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐĩ ĐŊа аĐēĐ°ŅƒĐŊŅ‚Đ° Ви, Ņ‚Đ°Đēа ҇Đĩ ҁĐĩ ŅƒĐ˛ĐĩŅ€ĐĩŅ‚Đĩ, ҇Đĩ Đŧ҃ иĐŧĐ°Ņ‚Đĩ дОвĐĩŅ€Đ¸Đĩ. АĐēĐž иĐŧĐ°Ņ‚Đĩ ҁҊĐŧĐŊĐĩĐŊĐ¸Ņ, ŅĐ˛ŅŠŅ€ĐļĐĩŅ‚Đĩ ҁĐĩ ҁ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° ŅĐ¸." + }, + "verifyYourOrganization": { + "message": "ĐŸĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩŅ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° ŅĐ¸, Са да ҁĐĩ вĐŋĐ¸ŅˆĐĩŅ‚Đĩ" + }, + "organizationVerified": { + "message": "ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° Đĩ ĐŋĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩĐŊа" + }, + "domainVerified": { + "message": "ДоĐŧĐĩĐšĐŊŅŠŅ‚ Đĩ ĐŋĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩĐŊ" + }, + "leaveOrganizationContent": { + "message": "АĐēĐž ĐŊĐĩ ĐŋĐžŅ‚Đ˛ŅŠŅ€Đ´Đ¸Ņ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ°, Đ´ĐžŅŅ‚ŅŠĐŋŅŠŅ‚ Ви Đ´Đž ĐŊĐĩŅ ҉Đĩ ĐąŅŠĐ´Đĩ ĐŋŅ€ĐĩŅƒŅŅ‚Đ°ĐŊОвĐĩĐŊ." + }, + "leaveNow": { + "message": "НаĐŋ҃ҁĐēаĐŊĐĩ ҁĐĩĐŗĐ°" + }, + "verifyYourDomainToLogin": { + "message": "ĐŸĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩŅ‚Đĩ Đ´ĐžĐŧĐĩĐšĐŊа ŅĐ¸, Са да ҁĐĩ вĐŋĐ¸ŅˆĐĩŅ‚Đĩ" + }, + "verifyYourDomainDescription": { + "message": "За да ĐŋŅ€ĐžĐ´ŅŠĐģĐļĐ¸Ņ‚Đĩ ҁ вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩŅ‚Đž, ĐŋĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩŅ‚Đĩ Ņ‚ĐžĐˇĐ¸ Đ´ĐžĐŧĐĩĐšĐŊ." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "За да ĐŋŅ€ĐžĐ´ŅŠĐģĐļĐ¸Ņ‚Đĩ ҁ вĐŋĐ¸ŅĐ˛Đ°ĐŊĐĩŅ‚Đž, ĐŋĐžŅ‚Đ˛ŅŠŅ€Đ´ĐĩŅ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° и Đ´ĐžĐŧĐĩĐšĐŊа." + }, "sessionTimeoutSettingsAction": { "message": "ДĐĩĐšŅŅ‚Đ˛Đ¸Đĩ ĐŋŅ€Đ¸ Đ¸ĐˇŅ‚Đ¸Ņ‡Đ°ĐŊĐĩŅ‚Đž ĐŊа Đ˛Ņ€ĐĩĐŧĐĩŅ‚Đž Са Đ´ĐžŅŅ‚ŅŠĐŋ" }, "sessionTimeoutHeader": { "message": "Đ˜ĐˇŅ‚Đ¸Ņ‡Đ°ĐŊĐĩ ĐŊа Đ˛Ņ€ĐĩĐŧĐĩŅ‚Đž Са ҁĐĩŅĐ¸ŅŅ‚Đ°" + }, + "resizeSideNavigation": { + "message": "ĐŸŅ€ĐĩĐžŅ€Đ°ĐˇĐŧĐĩŅ€ŅĐ˛Đ°ĐŊĐĩ ĐŊа ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ‡ĐŊĐ°Ņ‚Đ° ĐŊĐ°Đ˛Đ¸ĐŗĐ°Ņ†Đ¸Ņ" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "ĐĸаСи ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēа ҁĐĩ ҃ĐŋŅ€Đ°Đ˛ĐģŅĐ˛Đ° ĐžŅ‚ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° Ви." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° Ви Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐ¸Đģа ĐŧаĐēŅĐ¸ĐŧаĐģĐŊĐžŅ‚Đž Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩ Са Đ´ĐžŅŅ‚ŅŠĐŋ ĐŊа [%1$i] Ņ‡Đ°Ņ(а) и [%2$i] ĐŧиĐŊŅƒŅ‚Đ¸.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° Ви Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐ¸Đģа ŅŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐŊĐžŅ‚Đž Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩ Са Đ´ĐžŅŅ‚ŅŠĐŋ да ĐąŅŠĐ´Đĩ Đ´Đž СаĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŊа ŅĐ¸ŅŅ‚ĐĩĐŧĐ°Ņ‚Đ°." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ° Ви Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐ¸Đģа ŅŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐŊĐžŅ‚Đž Ņ€Đ°ĐˇŅ€Đĩ҈ĐĩĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩ Са Đ´ĐžŅŅ‚ŅŠĐŋ да ĐąŅŠĐ´Đĩ Đ´Đž Ņ€ĐĩŅŅ‚Đ°Ņ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "МаĐēŅĐ¸ĐŧаĐģĐŊĐžŅ‚Đž Đ˛Ņ€ĐĩĐŧĐĩ ĐŊа Đ´ĐžŅŅ‚ŅŠĐŋ ĐŊĐĩ ĐŧĐžĐļĐĩ да ĐŋŅ€ĐĩĐ˛Đ¸ŅˆĐ°Đ˛Đ° $HOURS$ Ņ‡Đ°Ņ(а) и $MINUTES$ ĐŧиĐŊŅƒŅ‚Đ¸", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "ĐŸŅ€Đ¸ Ņ€ĐĩŅŅ‚Đ°Ņ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Đ—Đ°Đ´Đ°ĐšŅ‚Đĩ ĐŧĐĩŅ‚ĐžĐ´ Са ĐžŅ‚ĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ, Са да ĐŧĐžĐļĐĩ да ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸ĐĩŅ‚Đž ĐŋŅ€Đ¸ Đ¸ĐˇŅ‚Đ¸Ņ‡Đ°ĐŊĐĩ ĐŊа Đ˛Ņ€ĐĩĐŧĐĩŅ‚Đž Са Đ´ĐžŅŅ‚ŅŠĐŋ" + }, + "upgrade": { + "message": "ĐĐ°Đ´ĐŗŅ€Đ°ĐļдаĐŊĐĩ" + }, + "leaveConfirmationDialogTitle": { + "message": "ĐĐ°Đ¸ŅŅ‚Đ¸ĐŊа Đģи Đ¸ŅĐēĐ°Ņ‚Đĩ да ĐŊаĐŋ҃ҁĐŊĐĩŅ‚Đĩ?" + }, + "leaveConfirmationDialogContentOne": { + "message": "АĐēĐž ĐžŅ‚ĐēаĐļĐĩŅ‚Đĩ, Đ’Đ°ŅˆĐ¸Ņ‚Đĩ ŅĐžĐąŅŅ‚Đ˛ĐĩĐŊи ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ҉Đĩ ĐžŅŅ‚Đ°ĐŊĐ°Ņ‚ в аĐēĐ°ŅƒĐŊŅ‚Đ° Ви, ĐŊĐž ҉Đĩ ĐˇĐ°ĐŗŅƒĐąĐ¸Ņ‚Đĩ Đ´ĐžŅŅ‚ŅŠĐŋ Đ´Đž ҁĐŋОдĐĩĐģĐĩĐŊĐ¸Ņ‚Đĩ ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ и Ņ„ŅƒĐŊĐēŅ†Đ¸ĐžĐŊаĐģĐŊĐžŅŅ‚Đ¸Ņ‚Đĩ ĐŊа ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ°." + }, + "leaveConfirmationDialogContentTwo": { + "message": "ĐĄĐ˛ŅŠŅ€ĐļĐĩŅ‚Đĩ ҁĐĩ ҁ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€, Са да ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚Đĩ Đ´ĐžŅŅ‚ŅŠĐŋ ĐžŅ‚ĐŊОвО." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "НаĐŋ҃ҁĐēаĐŊĐĩ ĐŊа $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "КаĐē да ҃ĐŋŅ€Đ°Đ˛ĐģŅĐ˛Đ°Đŧ ҂ҀĐĩĐˇĐžŅ€Đ° ŅĐ¸?" + }, + "transferItemsToOrganizationTitle": { + "message": "ĐŸŅ€ĐĩŅ…Đ˛ŅŠŅ€ĐģŅĐŊĐĩ ĐŊа ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ ĐēҊĐŧ $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ Đ¸ĐˇĐ¸ŅĐēва Đ˛ŅĐ¸Ņ‡Đēи ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸ да ŅŅ‚Đ°ĐŊĐ°Ņ‚ ĐŋŅ€Đ¸Ņ‚ĐĩĐļаĐŊиĐĩ ĐŊа ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ°, Са ĐŋĐž-Đ´ĐžĐąŅ€Đ° ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚ и ŅŅŠĐ˛ĐŧĐĩŅŅ‚Đ¸ĐŧĐžŅŅ‚. ИСйĐĩŅ€ĐĩŅ‚Đĩ, ҇Đĩ ĐŋŅ€Đ¸ĐĩĐŧĐ°Ņ‚Đĩ, Са да ĐŋŅ€ĐĩŅ…Đ˛ŅŠŅ€ĐģĐ¸Ņ‚Đĩ ŅĐžĐąŅŅ‚Đ˛ĐĩĐŊĐžŅŅ‚Ņ‚Đ° ĐŊа ĐĩĐģĐĩĐŧĐĩĐŊŅ‚Đ¸Ņ‚Đĩ ŅĐ¸ ĐēҊĐŧ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŅ‚Đ°.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "ĐŸŅ€Đ¸ĐĩĐŧаĐŊĐĩ ĐŊа ĐŋŅ€ĐĩŅ…Đ˛ŅŠŅ€ĐģŅĐŊĐĩŅ‚Đž" + }, + "declineAndLeave": { + "message": "ĐžŅ‚ĐēаСваĐŊĐĩ и ĐŊаĐŋ҃ҁĐēаĐŊĐĩ" + }, + "whyAmISeeingThis": { + "message": "Đ—Đ°Ņ‰Đž виĐļдаĐŧ Ņ‚ĐžĐ˛Đ°?" } } diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index d6c61c1ab51..97e67f9541f 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "āύāϤ⧁āύ URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "āĻāĻ•āϟāĻŋ āĻ…āĻĒā§āϰāĻ¤ā§āϝāĻžāĻļāĻŋāϤ āĻ¤ā§āϰ⧁āϟāĻŋ āϘāĻŸā§‡āϛ⧇āĨ¤" }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "āĻŦāĻ¸ā§āϤ⧁ āϤāĻĨā§āϝ" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "āφāϰāĻ“ āϜāĻžāύ⧁āύ" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "āĻŦ⧈āĻļāĻŋāĻˇā§āĻŸā§āϝ āĻ…āύ⧁āĻĒāϞāĻŦā§āϧ" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow us" }, - "syncVault": { - "message": "āĻ­āĻ˛ā§āϟ āϏāĻŋāĻ™ā§āĻ• āĻ•āϰ⧁āύ" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "āĻŽā§‚āϞ āĻĒāĻžāϏāĻ“āϝāĻŧāĻžāĻ°ā§āĻĄ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "āĻĢāĻžāχāϞ āϏāĻ‚āϝ⧁āĻ•ā§āϤāĻŋāϰ āϜāĻ¨ā§āϝ ā§§ āϜāĻŋāĻŦāĻŋ āĻāύāĻ•ā§āϰāĻŋāĻĒā§āĻŸā§‡āĻĄ āĻ¸ā§āĻĨāĻžāύāĨ¤" }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "āĻ­āĻ˛ā§āϟ āϰāĻĢāϤāĻžāύāĻŋ" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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": "āĻĢāĻžāχāϞ⧇āϰ āϧāϰāĻŖ" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "āĻŦāĻ¸ā§āϤ⧁āϟāĻŋ āĻ¸ā§āĻĨāĻžāϝāĻŧā§€āĻ­āĻžāĻŦ⧇ āĻŽā§āϛ⧇ āĻĢ⧇āϞāĻž āĻšā§Ÿā§‡āϛ⧇" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "āĻŦāĻ¸ā§āϤ⧁ āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰāĻ•ā§ƒāϤ" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index 569f1072c4b..49bb2b6559a 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Novi URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Neočekivana greÅĄka se dogodila." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Informacije o stavki" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Saznajte viÅĄe" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Funkcija nije dostupna" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Pratite nas" }, - "syncVault": { - "message": "Sinhronizujte trezor sada" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Promijenite glavnu lozinku" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB ÅĄifriranog prostora za pohranu podataka." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Izvezi trezor" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Svi Send-ovi", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Sigurno ÅželiÅĄ izbrisati ovaj Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Kopiraj link Send-a", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Uvoz podataka", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "GreÅĄka pri uvozu" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index de468f1e8b3..79bdbb7c881 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Nova URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Afig adjunt" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "La mida màxima del fitxer Ês de 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "S'ha produït un error inesperat." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "InformaciÃŗ de l'element" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "MÊs informaciÃŗ" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Característica no disponible" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Seguiu-nos" }, - "syncVault": { - "message": "Sincronitza la caixa forta" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Canvia la contrasenya mestra" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB d'emmagatzematge xifrat per als fitxers adjunts." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Opcions propietàries de doble factor com ara YubiKey i Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Exporta des de" }, - "exportVault": { - "message": "Exporta caixa forta" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Element suprimit definitivament" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Element restaurat" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "InformaciÃŗ de contacte" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Tots els Send", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Esteu segur que voleu suprimir aquest Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copia l'enllaç Send al porta-retalls", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "S'ha suprimit la contrasenya mestra." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Ja no cal contrasenya mestra per als membres de la segÃŧent organitzaciÃŗ. Confirmeu el domini segÃŧent amb l'administrador de l'organitzaciÃŗ." - }, "organizationName": { "message": "Nom de l'organitzaciÃŗ" }, @@ -2991,7 +3125,8 @@ "message": "Esteu segur que voleu utilitzar l'opciÃŗ \"Mai\"? En configurar les opcions de bloqueig a \"Mai\" s'emmagatzema la clau de xifratge de la vostra caixa forta al vostre dispositiu. Si utilitzeu aquesta opciÃŗ, heu d'assegurar-vos que conserveu el dispositiu degudament protegit." }, "vault": { - "message": "Caixa forta" + "message": "Caixa forta", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Inici de sessiÃŗ amb contrasenya mestra" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Domini d'àlies" }, - "importData": { - "message": "Importa dades", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Error d'importaciÃŗ" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Permet capturar la pantalla" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index c02dbabbc93..33469dc30dc 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "Tento Send bude trvale smazÃĄn v určenÊ datum.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "Soubor ke sdílení" + }, + "hideTextByDefault": { + "message": "Ve vÃŊchozím nastavení skrÃŊt text" + }, + "hideYourEmail": { + "message": "Skryje VaÅĄi e-mailovou adresu před zobrazením." + }, + "limitSendViews": { + "message": "Omezit zobrazení" + }, + "limitSendViewsCount": { + "message": "ZbÃŊvÃĄ $ACCESSCOUNT$ zobrazení", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "Po dosaÅžení limitu nebude nikdo moci zobrazit tento Send.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "SoukromÃĄ poznÃĄmka" + }, + "sendDetails": { + "message": "Podrobnosti Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "PřidÃĄ volitelnÊ heslo pro příjemce pro přístup k tomuto Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text ke sdílení" + }, + "newItemHeaderTextSend": { + "message": "NovÃŊ textovÃŊ Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "NovÃŊ Send se soubory", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Upravit textovÃŊ Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Upravit Send se soubory", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Opravdu chcete tento Send trvale smazat?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "NovÃĄ", + "description": "for adding new items" + }, "newUri": { "message": "NovÃĄ URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Přidat přílohu" }, + "itemsTransferred": { + "message": "PřevedenÊ poloÅžky" + }, + "fixEncryption": { + "message": "Opravit ÅĄifrovÃĄní" + }, + "fixEncryptionTooltip": { + "message": "Tento soubor pouŞívÃĄ zastaralou ÅĄifrovací metodu." + }, + "attachmentUpdated": { + "message": "Příloha byla aktualizovÃĄna" + }, "maxFileSizeSansPunctuation": { "message": "MaximÃĄlní velikost souboru je 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Vyskytla se neočekÃĄvanÃĄ chyba." }, + "unexpectedErrorShort": { + "message": "NeočekÃĄvanÃĄ chyba" + }, + "closeThisBitwardenWindow": { + "message": "Zavřete toto okno Bitwardenu a zkuste to znovu." + }, "itemInformation": { "message": "Informace o poloÅžce" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Dozvědět se více" }, + "migrationsFailed": { + "message": "DoÅĄlo k chybě při aktualizaci nastavení ÅĄifrovÃĄní." + }, + "updateEncryptionSettingsTitle": { + "message": "Aktualizovat nastavení ÅĄifrovÃĄní" + }, + "updateEncryptionSettingsDesc": { + "message": "NovÊ doporučenÊ nastavení ÅĄifrovÃĄní zlepÅĄÃ­ bezpečnost VaÅĄeho Ãēčtu. Pokud chcete aktualizovat nyní, zadejte hlavní heslo." + }, + "confirmIdentityToContinue": { + "message": "Pro pokračovÃĄní potvrďte svou identitu" + }, + "enterYourMasterPassword": { + "message": "Zadejte svÊ hlavní heslo" + }, + "updateSettings": { + "message": "Aktualizovat nastavení" + }, "featureUnavailable": { "message": "Funkce není dostupnÃĄ" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Sledujte nÃĄs" }, - "syncVault": { - "message": "Synchronizovat trezor" + "syncNow": { + "message": "Synchronizovat nyní" }, "changeMasterPass": { "message": "Změnit hlavní heslo" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB ÅĄifrovanÊho uloÅžiÅĄtě pro přílohy." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ ÅĄifrovanÊho ÃēloÅžiÅĄtě pro přílohy.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Volby proprietÃĄlních dvoufÃĄzovÃŊch přihlÃĄÅĄení jako je YubiKey a Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Exportovat z" }, - "exportVault": { - "message": "Exportovat trezor" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Exportovat", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "PoloÅžka byla trvale smazÃĄna" }, + "archivedItemRestored": { + "message": "ArchivovanÃĄ poloÅžka byla obnovena" + }, "restoredItem": { "message": "PoloÅžka byla obnovena" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "VÅĄechny Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Opravdu chcete smazat tento Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Kopírovat odkaz pro Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Zkopírovat odkaz Send do schrÃĄnky", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Hlavní heslo bylo odebrÃĄno" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Hlavní heslo jiÅž není vyÅžadovÃĄno pro členy nÃĄsledující organizace. Potvrďte níŞe uvedenou domÊnu u sprÃĄvce VaÅĄÃ­ organizace." - }, "organizationName": { "message": "NÃĄzev organizace" }, @@ -2991,7 +3125,8 @@ "message": "Opravdu chcete pouŞít volbu \"Nikdy\"? Nastavením volby uzamčení na \"Nikdy\" bude ÅĄifrovací klíč k trezoru uloÅžen přímo ve VaÅĄem zařízení. Pokud tuto moÅžnost pouÅžijete, měli byste VaÅĄe zařízení Å™ÃĄdně zabezpečit a chrÃĄnit." }, "vault": { - "message": "Trezor" + "message": "Trezor", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "PřihlÃĄsit se pomocí hlavního hesla" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "DomÊna aliasu" }, - "importData": { - "message": "Importovat data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Chyba importu" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Soubor byl uloÅžen. MůŞete jej nalÊzt ve staÅženÊ sloÅžce v zařízení." }, + "importantNotice": { + "message": "DůleÅžitÊ upozornění" + }, + "setupTwoStepLogin": { + "message": "Nastavit dvoufÃĄzovÊ přihlÃĄÅĄení" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden odeÅĄle kÃŗd na e-mail VaÅĄeho Ãēčtu pro ověření přihlÃĄÅĄení z novÃŊch zařízení počínaje Ãēnorem 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "DvoufÃĄzovÊ přihlÃĄÅĄení můŞete nastavit jako alternativní způsob ochrany VaÅĄeho Ãēčtu nebo změnit svůj e-mail na ten, k němuÅž můŞete přistupovat." + }, + "remindMeLater": { + "message": "Připomenout později" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "MÃĄte spolehlivÃŊ přístup ke svÊmu e-mailu $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Ne, nemÃĄm" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ano, ke svÊmu e-mailu mÃĄm přístup" + }, + "turnOnTwoStepLogin": { + "message": "Zapnout dvoufÃĄzovÊ přihlÃĄÅĄení" + }, + "changeAcctEmail": { + "message": "Změnit e-mail Ãēčtu" + }, + "passkeyLogin": { + "message": "PřihlÃĄsit se pomocí přístupovÊho klíče?" + }, + "savePasskeyQuestion": { + "message": "UloÅžit přístupovÃŊ klíč?" + }, + "saveNewPasskey": { + "message": "UloÅžit jako novÊ přihlaÅĄovací Ãēdaje" + }, + "savePasskeyNewLogin": { + "message": "UloÅžit přístupovÃŊ klíč jako novÊ přihlÃĄÅĄení" + }, + "noMatchingLoginsForSite": { + "message": "ÅŊÃĄdnÊ odpovídající přihlaÅĄovací Ãēdaje pro tento web" + }, + "overwritePasskey": { + "message": "Přepsat přístupovÃŊ klíč?" + }, + "unableToSavePasskey": { + "message": "Nelze uloÅžit přístupovÃŊ klíč" + }, + "alreadyContainsPasskey": { + "message": "Tato poloÅžka jiÅž obsahuje přístupovÃŊ klíč. Jste si jisti, Åže chcete přepsat aktuÃĄlní přístupovÃŊ klíč?" + }, + "passkeyAlreadyExists": { + "message": "PřístupovÃŊ klíč pro tuto aplikaci jiÅž existuje." + }, + "applicationDoesNotSupportDuplicates": { + "message": "Tato aplikace nepodporuje duplikÃĄty." + }, + "closeThisWindow": { + "message": "Zavřít toto okno" + }, "allowScreenshots": { "message": "Povolit zÃĄznam obrazovky" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Tyto přihlaÅĄovací Ãēdaje jsou ohroÅženÊ a chybí jim webovÃĄ strÃĄnka. Přidejte webovou strÃĄnku a změňte heslo pro větÅĄÃ­ bezpečnost." }, + "vulnerablePassword": { + "message": "ZranitelnÊ heslo." + }, + "changeNow": { + "message": "Změnit nyní" + }, "missingWebsite": { "message": "Chybějící webovÃĄ strÃĄnka" }, @@ -3906,10 +4112,16 @@ "message": "Posílejte citlivÊ informace bezpečně", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "Nebyly vrÃĄceny ÅžÃĄdnÊ vÃŊsledky hledÃĄní" + }, "sendsBodyNoItems": { "message": "Sdílejte bezpečně soubory a data s kÃŊmkoli na libovolnÊ platformě. VaÅĄe informace zůstanou ÅĄifrovÃĄny a zÃĄroveň omezují expozici.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "VymaÅžte filtry nebo zkuste jinÃŊ hledanÃŊ vÃŊraz" + }, "generatorNudgeTitle": { "message": "RychlÊ vytvoření hesla" }, @@ -4128,7 +4340,7 @@ "typeShortcut": { "message": "Napsat zkratku" }, - "editAutotypeShortcutDescription": { + "editAutotypeKeyboardModifiersDescription": { "message": "Zahrňte jeden nebo dva z nÃĄsledujících modifikÃĄtorů: Ctrl, Alt, Win nebo Shift a písmeno." }, "invalidShortcut": { @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Odebrat z archivu" }, + "archived": { + "message": "ArchivovÃĄno" + }, "itemsInArchive": { "message": "PoloÅžky v archivu" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archivovat poloÅžku" }, - "archiveItemConfirmDesc": { - "message": "ArchivovanÊ poloÅžky jsou vyloučeny z obecnÃŊch vÃŊsledků vyhledÃĄvÃĄní a z nÃĄvrhů automatickÊho vyplňovÃĄní. Jste si jisti, Åže chcete tuto poloÅžku archivovat?" + "archiveItemDialogContent": { + "message": "Jakmile bude tato poloÅžka archivovÃĄna, bude vyloučena z vÃŊsledků vyhledÃĄvÃĄní a z nÃĄvrhů automatickÊho vyplňovÃĄní." + }, + "unArchiveAndSave": { + "message": "Odebrat z archivu a uloÅžit" + }, + "restartPremium": { + "message": "Restartovat Premium" + }, + "premiumSubscriptionEnded": { + "message": "VaÅĄe předplatnÊ Premium skončilo" + }, + "premiumSubscriptionEndedDesc": { + "message": "Chcete-li získat přístup k VaÅĄemu archivu, restartujte předplatnÊ Premium. Pokud upravíte detaily archivovanÊ poloÅžky před restartovÃĄním, bude přesunuta zpět do VaÅĄeho trezoru." + }, + "itemRestored": { + "message": "PoloÅžka byla obnovena" }, "zipPostalCodeLabel": { "message": "ZIP / PSČ" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "A jeÅĄtě více!" }, - "planDescPremium": { - "message": "Dokončit online zabezpečení" + "advancedOnlineSecurity": { + "message": "PokročilÊ zabezpečení online" }, "upgradeToPremium": { "message": "Aktualizovat na Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "VaÅĄe organizace jiÅž k přihlÃĄÅĄení do Bitwardenu nepouŞívÃĄ hlavní hesla. Chcete-li pokračovat, ověřte organizaci a domÊnu." + }, + "continueWithLogIn": { + "message": "Pokračovat s přihlÃĄÅĄením" + }, + "doNotContinue": { + "message": "Nepokračovat" + }, + "domain": { + "message": "DomÊna" + }, + "keyConnectorDomainTooltip": { + "message": "Tato domÊna uloŞí ÅĄifrovací klíče VaÅĄeho Ãēčtu, takÅže se ujistěte, Åže jí věříte. Pokud si nejste jisti, kontaktujte VaÅĄeho sprÃĄvce." + }, + "verifyYourOrganization": { + "message": "Ověřte svou organizaci pro přihlÃĄÅĄení" + }, + "organizationVerified": { + "message": "Organizace byla ověřena" + }, + "domainVerified": { + "message": "DomÊna byla ověřena" + }, + "leaveOrganizationContent": { + "message": "Pokud neověříte svou organizaci, VÃĄÅĄ přístup k organizaci bude zruÅĄen." + }, + "leaveNow": { + "message": "Opustit hned" + }, + "verifyYourDomainToLogin": { + "message": "Ověřte svou domÊnu pro přihlÃĄÅĄení" + }, + "verifyYourDomainDescription": { + "message": "Chcete-li pokračovat v přihlÃĄÅĄení, ověřte tuto domÊnu." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Chcete-li pokračovat v přihlÃĄÅĄení, ověřte organizaci a domÊnu." + }, "sessionTimeoutSettingsAction": { "message": "Akce vyprÅĄení časovÊho limitu" }, "sessionTimeoutHeader": { "message": "ČasovÃŊ limit relace" + }, + "resizeSideNavigation": { + "message": "Změnit velikost boční navigace" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Tato nastavení je spravovÃĄno VaÅĄÃ­ organizací." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "VaÅĄe organizace nastavila maximÃĄlní časovÃŊ limit relace na $HOURS$ hodin a $MINUTES$ minut.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "VaÅĄe organizace nastavila vÃŊchozí časovÃŊ limit relace na Při uzamknutí systÊmu." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "VaÅĄe organizace nastavila vÃŊchozí časovÃŊ limit relace na Při restartu." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "MaximÃĄlní časovÃŊ limit nesmí překročit $HOURS$ hodin a $MINUTES$ minut", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Při restartu" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Nastavte metodu odemknutí, abyste změnili akci při vyprÅĄení časovÊho limitu" + }, + "upgrade": { + "message": "Aktualizovat" + }, + "leaveConfirmationDialogTitle": { + "message": "Opravdu chcete odejít?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Odmítnutím zůstanou VaÅĄe osobní poloÅžky ve VaÅĄem Ãēčtu, ale ztratíte přístup ke sdílenÃŊm poloÅžkÃĄm a funkcím organizace." + }, + "leaveConfirmationDialogContentTwo": { + "message": "ObraÅĨte se na svÊho sprÃĄvce, abyste znovu získali přístup." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Opustit $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Jak mohu spravovat svůj trezor?" + }, + "transferItemsToOrganizationTitle": { + "message": "PřenÊst poloÅžky do $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ vyÅžaduje, aby byly vÅĄechny poloÅžky vlastněny organizací z důvodu bezpečnosti a shody. Klepnutím na tlačítko pro převod vlastnictví VaÅĄich poloÅžek.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Přijmout převod" + }, + "declineAndLeave": { + "message": "Odmítnout a odejít" + }, + "whyAmISeeingThis": { + "message": "Proč se mi toto zobrazuje?" } } diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index 25b52fcc101..f67a3dbe406 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "New URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Item information" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Feature unavailable" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow us" }, - "syncVault": { - "message": "Sync vault" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Change master password" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index 1d135a533f2..2e8c4b72911 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Ny URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "En uventet fejl opstod." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Emneinformation" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "LÃĻs mere" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Funktion utilgÃĻngelig" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Følg os" }, - "syncVault": { - "message": "Synk boks" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Skift hovedadgangskode" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB krypteret lagerplads til filvedhÃĻftninger." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "ProprietÃĻre totrins-login muligheder, sÃĨsom YubiKey og Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "EksportÊr fra" }, - "exportVault": { - "message": "EksportÊr boks" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Emne slettet permanent" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Emne gendannet" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Alle Send", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Sikker pÃĨ, at du vil slette denne Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "KopiÊr Send-link til udklipsholder", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Hovedadgangskode fjernet." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Sikker pÃĨ, at indstillingen \"Aldrig\" skal bruges? SÃĻttes lÃĨseindstillinger til \"Aldrig\", gemmes din bokskrypteringsnøgle pÃĨ enheden. Bruges denne indstilling, sÃĨ sørg for at holde din enhed ordentligt beskyttet." }, "vault": { - "message": "Boks" + "message": "Boks", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log ind med hovedadgangskoden" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "AliasdomÃĻne" }, - "importData": { - "message": "Dataimport", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Importfejl" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Fil gemt pÃĨ enheden. HÃĨndtÊr fra enhedens downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index 2f8daec5b68..1d15a27d505 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "Das Send wird an diesem Datum dauerhaft gelÃļscht.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "Zu teilende Datei" + }, + "hideTextByDefault": { + "message": "Text standardmäßig ausblenden" + }, + "hideYourEmail": { + "message": "Verberge deine E-Mail-Adresse vor Betrachtern." + }, + "limitSendViews": { + "message": "Ansichten begrenzen" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ Ansichten Ãŧbrig", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "Nach Erreichen des Limits kann niemand mehr dieses Send sehen.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private Notiz" + }, + "sendDetails": { + "message": "Send-Details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "FÃŧge ein optionales Passwort hinzu, mit dem Empfänger auf dieses Send zugreifen kÃļnnen.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Zu teilender Text" + }, + "newItemHeaderTextSend": { + "message": "Neues Text-Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "Neues Datei-Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Text-Send bearbeiten", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Datei-Send bearbeiten", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Bist du sicher, dass du dieses Send dauerhaft lÃļschen mÃļchtest?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "Neu", + "description": "for adding new items" + }, "newUri": { "message": "Neue URL" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Anhang hinzufÃŧgen" }, + "itemsTransferred": { + "message": "Einträge wurden Ãŧbertragen" + }, + "fixEncryption": { + "message": "VerschlÃŧsselung reparieren" + }, + "fixEncryptionTooltip": { + "message": "Diese Datei verwendet eine veraltete VerschlÃŧsselungsmethode." + }, + "attachmentUpdated": { + "message": "Anhang aktualisiert" + }, "maxFileSizeSansPunctuation": { "message": "Die maximale DateigrÃļße beträgt 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Ein unerwarteter Fehler ist aufgetreten." }, + "unexpectedErrorShort": { + "message": "Unerwarteter Fehler" + }, + "closeThisBitwardenWindow": { + "message": "Schließe dieses Bitwarden-Fenster und versuche es erneut." + }, "itemInformation": { "message": "Eintragsinformationen" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Mehr erfahren" }, + "migrationsFailed": { + "message": "Beim Aktualisieren der VerschlÃŧsselungseinstellungen ist ein Fehler aufgetreten." + }, + "updateEncryptionSettingsTitle": { + "message": "Aktualisiere deine VerschlÃŧsselungseinstellungen" + }, + "updateEncryptionSettingsDesc": { + "message": "Die neuen empfohlenen VerschlÃŧsselungseinstellungen verbessern deine Kontosicherheit. Gib dein Master-Passwort ein, um sie zu aktualisieren." + }, + "confirmIdentityToContinue": { + "message": "Bestätige deine Identität, um fortzufahren" + }, + "enterYourMasterPassword": { + "message": "Gib dein Master-Passwort ein" + }, + "updateSettings": { + "message": "Einstellungen aktualisieren" + }, "featureUnavailable": { "message": "Funktion nicht verfÃŧgbar" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Folge uns" }, - "syncVault": { - "message": "Tresor synchronisieren" + "syncNow": { + "message": "Jetzt synchronisieren" }, "changeMasterPass": { "message": "Master-Passwort ändern" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB verschlÃŧsselter Speicherplatz fÃŧr Dateianhänge." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ verschlÃŧsselter Speicher fÃŧr Dateianhänge.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietäre Optionen fÃŧr die Zwei-Faktor Authentifizierung wie YubiKey und Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export aus" }, - "exportVault": { - "message": "Tresor exportieren" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Exportieren", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Eintrag dauerhaft gelÃļscht" }, + "archivedItemRestored": { + "message": "Archivierter Eintrag wiederhergestellt" + }, "restoredItem": { "message": "Eintrag wiederhergestellt" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Kontaktinformationen" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Alle Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Bist du sicher, dass du dieses Send lÃļschen mÃļchtest?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Send-Link kopieren", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Send-Link in Zwischenablage kopieren", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2562,7 +2699,7 @@ } }, "vaultCustomTimeoutMinimum": { - "message": "Das minimal benutzerdefinierte Timeout beträgt 1 Minute." + "message": "Minimale benutzerdefinierte Timeout-Zeit beträgt 1 Minute." }, "inviteAccepted": { "message": "Einladung angenommen" @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master-Passwort entfernt" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "FÃŧr Mitglieder der folgenden Organisation ist kein Master-Passwort mehr erforderlich. Bitte bestätige die folgende Domain bei deinem Organisations-Administrator." - }, "organizationName": { "message": "Name der Organisation" }, @@ -2991,7 +3125,8 @@ "message": "Bist du sicher, dass du die Option \"Nie\" verwenden mÃļchtest? Durch das Setzen der Sperroptionen zu \"Nie\" wird der VerschlÃŧsselungscode deines Tresors auf deinem Gerät gespeichert. Wenn du diese Option verwendest, solltest du sicherstellen, dass dein Gerät ausreichend geschÃŧtzt ist." }, "vault": { - "message": "Tresor" + "message": "Tresor", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Mit Master-Passwort anmelden" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias-Domain" }, - "importData": { - "message": "Daten importieren", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Importfehler" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Datei auf Gerät gespeichert. Greife darauf Ãŧber die Downloads deines Geräts zu." }, + "importantNotice": { + "message": "Wichtiger Hinweis" + }, + "setupTwoStepLogin": { + "message": "Zwei-Faktor-Authentifizierung einrichten" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Ab Februar 2025 wird Bitwarden einen Code an deine Konto-E-Mail-Adresse senden, um Anmeldungen von neuen Geräten zu verifizieren." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Du kannst die Zwei-Faktor-Authentifizierung als eine alternative Methode einrichten, um dein Konto zu schÃŧtzen, oder deine E-Mail-Adresse zu einer anderen ändern, auf die du zugreifen kannst." + }, + "remindMeLater": { + "message": "Erinnere mich später" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Hast du zuverlässigen Zugriff auf deine E-Mail-Adresse $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nein, habe ich nicht" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ja, ich kann zuverlässig auf meine E-Mails zugreifen" + }, + "turnOnTwoStepLogin": { + "message": "Zwei-Faktor-Authentifizierung aktivieren" + }, + "changeAcctEmail": { + "message": "E-Mail-Adresse des Kontos ändern" + }, + "passkeyLogin": { + "message": "Mit Passkey anmelden?" + }, + "savePasskeyQuestion": { + "message": "Passkey speichern?" + }, + "saveNewPasskey": { + "message": "Als neue Zugangsdaten speichern" + }, + "savePasskeyNewLogin": { + "message": "Passkey als neue Zugangsdaten speichern" + }, + "noMatchingLoginsForSite": { + "message": "Keine passenden Zugangsdaten fÃŧr diese Seite" + }, + "overwritePasskey": { + "message": "Passkey Ãŧberschreiben?" + }, + "unableToSavePasskey": { + "message": "Passkey konnte nicht gespeichert werden" + }, + "alreadyContainsPasskey": { + "message": "Dieser Eintrag enthält bereits einen Passkey. Bist du sicher, dass du den aktuellen Passkey Ãŧberschreiben mÃļchtest?" + }, + "passkeyAlreadyExists": { + "message": "FÃŧr diese Anwendung existiert bereits ein Passkey." + }, + "applicationDoesNotSupportDuplicates": { + "message": "Diese Anwendung unterstÃŧtzt keine mehrfachen Instanzen." + }, + "closeThisWindow": { + "message": "Dieses Fenster schließen" + }, "allowScreenshots": { "message": "Bildschirmaufnahme erlauben" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Diese Zugangsdaten sind gefährdet und es fehlt eine Website. FÃŧge eine Website hinzu und ändere das Passwort fÃŧr mehr Sicherheit." }, + "vulnerablePassword": { + "message": "Gefährdetes Passwort." + }, + "changeNow": { + "message": "Jetzt ändern" + }, "missingWebsite": { "message": "Fehlende Website" }, @@ -3906,10 +4112,16 @@ "message": "Sensible Informationen sicher versenden", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "Keine Suchergebnisse gefunden" + }, "sendsBodyNoItems": { "message": "Teile Dateien und Daten sicher mit jedem auf jeder Plattform. Deine Informationen bleiben Ende-zu-Ende-verschlÃŧsselt, während die Verbreitung begrenzt wird.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Filter lÃļschen oder es mit einem anderen Suchbegriff versuchen" + }, "generatorNudgeTitle": { "message": "PasswÃļrter schnell erstellen" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Autotype-TastaturkÃŧrzel" }, - "editAutotypeShortcutDescription": { - "message": "FÃŧge einen oder zwei der folgenden Modifikatoren ein: Strg, Alt, Win oder Umschalttaste, sowie einen Buchstaben." + "editAutotypeKeyboardModifiersDescription": { + "message": "FÃŧge einen oder zwei der folgenden Modifikatoren ein: Strg, Alt, Win und einen Buchstaben." }, "invalidShortcut": { "message": "UngÃŧltiges TastaturkÃŧrzel" @@ -4165,7 +4377,10 @@ "description": "Verb" }, "unArchive": { - "message": "Nicht mehr archivieren" + "message": "Wiederherstellen" + }, + "archived": { + "message": "Archiviert" }, "itemsInArchive": { "message": "Einträge im Archiv" @@ -4177,7 +4392,7 @@ "message": "Archivierte Einträge werden hier angezeigt und von allgemeinen Suchergebnissen sowie Auto-AusfÃŧllen-Vorschlägen ausgeschlossen." }, "itemWasSentToArchive": { - "message": "Eintrag wurde ins Archiv verschoben" + "message": "Eintrag wurde archiviert" }, "itemWasUnarchived": { "message": "Eintrag wird nicht mehr archiviert" @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Eintrag archivieren" }, - "archiveItemConfirmDesc": { - "message": "Archivierte Einträge werden von allgemeinen Suchergebnissen und Auto-AusfÃŧllen-Vorschlägen ausgeschlossen. Bist du sicher, dass du diesen Eintrag archivieren mÃļchtest?" + "archiveItemDialogContent": { + "message": "Nach der Archivierung wird dieser Eintrag aus den Suchergebnissen und Auto-AusfÃŧllen-Vorschlägen ausgeschlossen." + }, + "unArchiveAndSave": { + "message": "Nicht mehr archivieren und speichern" + }, + "restartPremium": { + "message": "Premium neu starten" + }, + "premiumSubscriptionEnded": { + "message": "Dein Premium-Abonnement ist abgelaufen" + }, + "premiumSubscriptionEndedDesc": { + "message": "Starte dein Premium-Abonnement neu, um den Zugriff auf dein Archiv wiederherzustellen. Wenn du die Details fÃŧr einen archivierten Eintrag vor dem Neustart bearbeitest, wird er wieder zurÃŧck in deinen Tresor verschoben." + }, + "itemRestored": { + "message": "Eintrag wurde wiederhergestellt" }, "zipPostalCodeLabel": { "message": "PLZ / Postleitzahl" @@ -4201,27 +4431,161 @@ "message": "Integrierter Authenticator" }, "secureFileStorage": { - "message": "Sicherer Dateispeicher" + "message": "Sichere Dateispeicherung" }, "emergencyAccess": { "message": "Notfallzugriff" }, "breachMonitoring": { - "message": "Datendiebstahl-Überwachung" + "message": "Datenleck-Überwachung" }, "andMoreFeatures": { - "message": "Und mehr!" + "message": "Und vieles mehr!" }, - "planDescPremium": { - "message": "Umfassende Online-Sicherheit" + "advancedOnlineSecurity": { + "message": "Erweiterte Online-Sicherheit" }, "upgradeToPremium": { - "message": "Upgrade auf Premium" + "message": "Auf Premium upgraden" + }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Deine Organisation verwendet keine Master-PasswÃļrter mehr, um sich bei Bitwarden anzumelden. Verifiziere die Organisation und Domain, um fortzufahren." + }, + "continueWithLogIn": { + "message": "Mit der Anmeldung fortfahren" + }, + "doNotContinue": { + "message": "Nicht fortfahren" + }, + "domain": { + "message": "Domain" + }, + "keyConnectorDomainTooltip": { + "message": "Diese Domain speichert die VerschlÃŧsselungsschlÃŧssel deines Kontos. Stelle daher sicher, dass du ihr vertraust. Wenn du dir nicht sicher bist, wende dich an deinen Administrator." + }, + "verifyYourOrganization": { + "message": "Verifiziere deine Organisation, um dich anzumelden" + }, + "organizationVerified": { + "message": "Organisation verifiziert" + }, + "domainVerified": { + "message": "Domain verifiziert" + }, + "leaveOrganizationContent": { + "message": "Wenn du deine Organisation nicht verifizierst, wird dein Zugriff auf die Organisation widerrufen." + }, + "leaveNow": { + "message": "Jetzt verlassen" + }, + "verifyYourDomainToLogin": { + "message": "Verifiziere deine Domain, um dich anzumelden" + }, + "verifyYourDomainDescription": { + "message": "Verifiziere diese Domain, um mit der Anmeldung fortzufahren." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Um mit der Anmeldung fortzufahren, verifiziere die Organisation und Domain." }, "sessionTimeoutSettingsAction": { "message": "Timeout-Aktion" }, "sessionTimeoutHeader": { "message": "Sitzungs-Timeout" + }, + "resizeSideNavigation": { + "message": "GrÃļße der Seitennavigation ändern" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Diese Einstellung wird von deiner Organisation verwaltet." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Deine Organisation hat das maximale Sitzungs-Timeout auf $HOURS$ Stunde(n) und $MINUTES$ Minute(n) festgelegt.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Deine Organisation hat das Standard-Sitzungs-Timeout auf \"Wenn System gesperrt\" gesetzt." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Deine Organisation hat das Standard-Sitzungs-Timeout auf \"Beim Neustart der App\" gesetzt." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Das maximale Timeout darf $HOURS$ Stunde(n) und $MINUTES$ Minute(n) nicht Ãŧberschreiten", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Beim Neustart der App" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Stell eine Entsperrmethode ein, um deine Timeout-Aktion zu ändern" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Bist du sicher, dass du gehen willst?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Wenn du ablehnst, bleiben deine persÃļnlichen Einträge in deinem Konto erhalten, aber du wirst den Zugriff auf geteilte Einträge und Organisationsfunktionen verlieren." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Kontaktiere deinen Administrator, um wieder Zugriff zu erhalten." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "$ORGANIZATION$ verlassen", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Wie kann ich meinen Tresor verwalten?" + }, + "transferItemsToOrganizationTitle": { + "message": "Einträge zu $ORGANIZATION$ Ãŧbertragen", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ erfordert zur Sicherheit und Compliance, dass alle Einträge der Organisation gehÃļren. Klicke auf Akzeptieren, um den Besitz deiner Einträge zu Ãŧbertragen.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Übertragung annehmen" + }, + "declineAndLeave": { + "message": "Ablehnen und verlassen" + }, + "whyAmISeeingThis": { + "message": "Warum wird mir das angezeigt?" } } diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index 0b869c1e02f..c5e1a0d98dc 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "ΝέÎŋ URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Î ĪÎŋĪƒÎ¸ÎŽÎēΡ ĪƒĪ…ÎŊΡÎŧÎŧέÎŊÎŋĪ…" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "ΤÎŋ ÎŧÎ­ÎŗÎšĪƒĪ„Îŋ ÎŧÎ­ÎŗÎĩθÎŋĪ‚ ÎąĪĪ‡ÎĩίÎŋĪ… ÎĩίÎŊιΚ 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Î ÎąĪÎŋĪ…ĪƒÎšÎŦĪƒĪ„ÎˇÎēÎĩ έÎŊÎą ÎŧΡ ÎąÎŊÎąÎŧÎĩÎŊΌÎŧÎĩÎŊÎŋ ĪƒĪ†ÎŦÎģÎŧÎą." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "ΠÎģÎˇĪÎŋΆÎŋĪÎ¯ÎĩĪ‚ ÎąÎŊĪ„ÎšÎēÎĩΚÎŧέÎŊÎŋĪ…" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "ΜÎŦθÎĩĪ„Îĩ Ī€ÎĩĪÎšĪƒĪƒĪŒĪ„ÎĩĪÎą" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Μη Î´ÎšÎąÎ¸Î­ĪƒÎšÎŧΡ ÎģÎĩÎšĪ„ÎŋĪ…ĪÎŗÎ¯Îą" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "ΑÎēÎŋÎģÎŋĪ…Î¸ÎŽĪƒĪ„Îĩ ÎŧÎąĪ‚" }, - "syncVault": { - "message": "ÎŖĪ…ÎŗĪ‡ĪÎŋÎŊÎšĪƒÎŧĪŒĪ‚ ÎēĪĪĪ€Ī„ÎˇĪ‚" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "ΑÎģÎģÎąÎŗÎŽ ÎēĪĪÎšÎŋĪ… ÎēĪ‰Î´ÎšÎēÎŋĪ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB Îē΁΅΀΄ÎŋÎŗĪÎąĪ†ÎˇÎŧέÎŊÎŋ ÎąĪ€ÎŋθΡÎēÎĩĪ…Ī„ÎšÎēΌ Ī‡ĪŽĪÎŋ ÎŗÎšÎą ĪƒĪ…ÎŊΡÎŧÎŧέÎŊÎą ÎąĪĪ‡ÎĩÎ¯Îą." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Î™Î´ÎšĪŒÎēĪ„ÎˇĪ„ÎĩĪ‚ ÎĩĪ€ÎšÎģÎŋÎŗÎ­Ī‚ ĪƒĪÎŊδÎĩĪƒÎˇĪ‚ Î´ĪÎŋ βΡÎŧÎŦ΄ΉÎŊ, ĪŒĪ€Ī‰Ī‚ Ī„Îŋ YubiKey ÎēιΚ Ī„Îŋ Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Î•ÎžÎąÎŗĪ‰ÎŗÎŽ ÎąĪ€ĪŒ" }, - "exportVault": { - "message": "Î•ÎžÎąÎŗĪ‰ÎŗÎŽ ÎēĪĪĪ€Ī„ÎˇĪ‚" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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": "Î¤ĪĪ€ÎŋĪ‚ ÎąĪĪ‡ÎĩίÎŋĪ…" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "ΤÎŋ ÎąÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎŋ Î´ÎšÎąÎŗĪÎŦĪ†ÎˇÎēÎĩ ÎŋĪÎšĪƒĪ„ÎšÎēÎŦ" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "ΤÎŋ ÎąÎŊĪ„ÎšÎēÎĩίÎŧÎĩÎŊÎŋ ÎĩĪ€ÎąÎŊÎąĪ†Î­ĪÎ¸ÎˇÎēÎĩ" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "ÎŖĪ„ÎŋÎšĪ‡ÎĩÎ¯Îą ÎĩĪ€ÎšÎēÎŋΚÎŊΉÎŊÎ¯ÎąĪ‚" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "ΌÎģÎą Ī„Îą Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Î•Î¯ĪƒĪ„Îĩ βέβιΚÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą Î´ÎšÎąÎŗĪÎŦΈÎĩĪ„Îĩ Ī„Îŋ Send;", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "ΑÎŊĪ„ÎšÎŗĪÎąĪ†ÎŽ ĪƒĪ…ÎŊÎ´Î­ĪƒÎŧÎŋĪ… Send ĪƒĪ„Îŋ Ī€ĪĪŒĪ‡ÎĩÎšĪÎŋ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Ο ÎēĪĪÎšÎŋĪ‚ ÎēĪ‰Î´ÎšÎēĪŒĪ‚ ÎąĪ†ÎąÎšĪÎ­Î¸ÎˇÎēÎĩ" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Î•Î¯ĪƒĪ„Îĩ βέβιΚÎŋΚ ĪŒĪ„Îš θέÎģÎĩĪ„Îĩ ÎŊÎą Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšÎŽĪƒÎĩĪ„Îĩ Ī„ÎˇÎŊ ÎĩĪ€ÎšÎģÎŋÎŗÎŽ \"ΠÎŋĪ„Î­\"; Ο ÎŋĪÎšĪƒÎŧĪŒĪ‚ ΄ΉÎŊ ÎĩĪ€ÎšÎģÎŋÎŗĪŽÎŊ ÎēÎģÎĩÎšÎ´ĪŽÎŧÎąĪ„ÎŋĪ‚ ΃Îĩ \"ΠÎŋĪ„Î­\" ÎąĪ€ÎŋθΡÎēÎĩĪÎĩΚ Ī„Îŋ ÎēÎģÎĩΚδί Îē΁΅΀΄ÎŋÎŗĪÎŦĪ†ÎˇĪƒÎˇĪ‚ Ī„ÎŋĪ… Î¸ÎˇĪƒÎąĪ…/ÎēίÎŋĪ… ĪƒÎąĪ‚ ĪƒĪ„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ ĪƒÎąĪ‚. ΕÎŦÎŊ Ī‡ĪÎˇĪƒÎšÎŧÎŋĪ€ÎŋÎšÎŽĪƒÎĩĪ„Îĩ ÎąĪ…Ī„ÎŽÎŊ Ī„ÎˇÎŊ ÎĩĪ€ÎšÎģÎŋÎŗÎŽ, θι Ī€ĪÎ­Ī€ÎĩΚ ÎŊÎą Î´ÎšÎąĪƒĪ†ÎąÎģÎ¯ĪƒÎĩĪ„Îĩ ĪŒĪ„Îš θι Î´ÎšÎąĪ„ÎˇĪÎĩÎ¯Ī„Îĩ Ī„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ ĪƒÎąĪ‚ ÎēÎąĪ„ÎŦÎģÎģΡÎģÎą ΀΁ÎŋĪƒĪ„ÎąĪ„ÎĩĪ…ÎŧέÎŊΡ." }, "vault": { - "message": "ÎšĪĪĪ€Ī„Îˇ" + "message": "ÎšĪĪĪ€Ī„Îˇ", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "ÎŖĪ…ÎŊδÎĩθÎĩÎ¯Ī„Îĩ ÎŧÎĩ Ī„ÎŋÎŊ ÎēĪĪÎšÎŋ ÎēĪ‰Î´ÎšÎēΌ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "ΨÎĩĪ…Î´ĪŽÎŊĪ…ÎŧÎŋ Ī„ÎŋÎŧέι" }, - "importData": { - "message": "Î•ÎšĪƒÎąÎŗĪ‰ÎŗÎŽ δÎĩδÎŋÎŧέÎŊΉÎŊ", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "ÎŖĪ†ÎŦÎģÎŧÎą ÎēÎąĪ„ÎŦ Ī„ÎˇÎŊ ÎĩÎšĪƒÎąÎŗĪ‰ÎŗÎŽ" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "ΤÎŋ ÎąĪĪ‡ÎĩίÎŋ ÎąĪ€ÎŋθΡÎēÎĩĪĪ„ÎˇÎēÎĩ ĪƒĪ„Îˇ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽ. Î”ÎšÎąĪ‡ÎĩÎ¯ĪÎšĪƒÎˇ ÎąĪ€ĪŒ Ī„ÎšĪ‚ ÎģÎŽĪˆÎĩÎšĪ‚ Ī„ÎˇĪ‚ ĪƒĪ…ĪƒÎēÎĩĪ…ÎŽĪ‚ ĪƒÎąĪ‚." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Î“ĪÎŽÎŗÎŋĪÎˇ δΡÎŧΚÎŋĪ…ĪÎŗÎ¯Îą ÎēĪ‰Î´ÎšÎēĪŽÎŊ Ī€ĪĪŒĪƒÎ˛ÎąĪƒÎˇĪ‚" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 6bef882d970..0ce98b8c62b 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "New URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Item information" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Feature unavailable" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow us" }, - "syncVault": { - "message": "Sync vault" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Change master password" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3324,7 +3459,7 @@ "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." }, - "trustUser":{ + "trustUser": { "message": "Trust user" }, "inputRequired": { @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3877,6 +4077,12 @@ }, "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." + }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" }, "missingWebsite": { "message": "Missing website" @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector":{ + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified":{ + "message": "Organization verified" + }, + "domainVerified":{ + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index 16af69361c6..7b46a4d928e 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "New URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Item information" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Feature unavailable" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow us" }, - "syncVault": { - "message": "Sync vault" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Change master password" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organisation. Please confirm the domain below with your organisation administrator." - }, "organizationName": { "message": "Organisation name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organisation is no longer using master passwords to log into Bitwarden. To continue, verify the organisation and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organisation to log in" + }, + "organizationVerified": { + "message": "Organisation verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organisation, your access to the organisation will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organisation and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organisation." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organisation has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organisation has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organisation has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organisation features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ is requiring all items to be owned by the organisation for security and compliance. Click accept to transfer ownership of your items.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index c6f1253bb59..42428ea649c 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "New URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Item information" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Feature unavailable" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow us" }, - "syncVault": { - "message": "Sync vault" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Change master password" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Permanently deleted item" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Restored item" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organisation. Please confirm the domain below with your organisation administrator." - }, "organizationName": { "message": "Organisation name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "PIN" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organisation is no longer using master passwords to log into Bitwarden. To continue, verify the organisation and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organisation to log in" + }, + "organizationVerified": { + "message": "Organisation verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organisation, your access to the organisation will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organisation and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organisation." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organisation has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organisation has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organisation has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organisation features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ is requiring all items to be owned by the organisation for security and compliance. Click accept to transfer ownership of your items.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index 28a9f3b8bce..d4a9938e7b1 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Nova URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Informo de ero" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Lerni pli" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "La funkcio nedisponeblas" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Sekvu nin" }, - "syncVault": { - "message": "Speguli la trezorejon" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Ŝanĝi la ĉefan pasvorton" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Elporti el" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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": "Dosierformato" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "La ero poreterne forviŝiĝis" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "La ero restariĝis" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Ĉiuj Send'oj", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Ĉu vi certas, ke vi volas forigi tiun Send'on?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "La ĉefa pasvorto foriĝis" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Nomo de la organizo" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Trezorejo" + "message": "Trezorejo", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Saluti per la ĉefa pasvorto" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Enporti datumon", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Enporti eraron" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "La dosiero konserviĝis en la aparato. La elŝutojn administru de via aparato." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index 9966fa1064c..7620ed56bc1 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -70,7 +70,7 @@ } }, "noEditPermissions": { - "message": "You don't have permission to edit this item" + "message": "No tienes permiso para editar este elemento" }, "welcomeBack": { "message": "Bienvenido de nuevo" @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Nueva URI" }, @@ -706,10 +774,22 @@ "message": "El adjunto se ha guardado." }, "addAttachment": { - "message": "Add attachment" + "message": "AÃąadir adjunto" + }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Corregir cifrado" + }, + "fixEncryptionTooltip": { + "message": "El archivo estÃĄ usando un mÊtodo de cifrado obsoleto." + }, + "attachmentUpdated": { + "message": "Adjunto actualizado" }, "maxFileSizeSansPunctuation": { - "message": "Maximum file size is 500 MB" + "message": "El tamaÃąo mÃĄximo del archivo es de 500 MB" }, "file": { "message": "Archivo" @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Ha ocurrido un error inesperado." }, + "unexpectedErrorShort": { + "message": "Error inesperado" + }, + "closeThisBitwardenWindow": { + "message": "Cierra esta ventana de Bitwarden e intÊntalo de nuevo." + }, "itemInformation": { "message": "InformaciÃŗn del elemento" }, @@ -1039,7 +1125,7 @@ "message": "Debes aÃąadir o bien la URL del servidor base, o al menos un entorno personalizado." }, "selfHostedEnvMustUseHttps": { - "message": "URLs must use HTTPS." + "message": "Las URLs deben usar HTTPS." }, "customEnvironment": { "message": "Entorno personalizado" @@ -1093,6 +1179,24 @@ "learnMore": { "message": "MÃĄs informaciÃŗn" }, + "migrationsFailed": { + "message": "Se ha producido un error al actualizar los ajustes de cifrado." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirma tu identidad para continuar" + }, + "enterYourMasterPassword": { + "message": "Introduce tu contraseÃąa maestra" + }, + "updateSettings": { + "message": "Actualizar ajustes" + }, "featureUnavailable": { "message": "Característica no disponible" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Síguenos" }, - "syncVault": { - "message": "Sincronizar caja fuerte" + "syncNow": { + "message": "Sincronizar ahora" }, "changeMasterPass": { "message": "Cambiar contraseÃąa maestra" @@ -1232,7 +1336,7 @@ "message": "ContraseÃąa maestra no vÃĄlida" }, "invalidMasterPasswordConfirmEmailAndHost": { - "message": "Invalid master password. Confirm your email is correct and your account was created on $HOST$.", + "message": "ContraseÃąa maestra incorrecta. Confirma que tu correo electrÃŗnico es correcto y que tu cuenta fue creada en $HOST$.", "placeholders": { "host": { "content": "$1", @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1GB de espacio en disco cifrado." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Opciones de inicio de sesiÃŗn con autenticaciÃŗn de dos pasos propietarios como YubiKey y Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Exportar desde" }, - "exportVault": { - "message": "Exportar caja fuerte" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Elemento eliminado de forma permanente" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Elemento restaurado" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "InformaciÃŗn de contacto" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Todos los Send", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "ÂŋEstÃĄ seguro de que quiere eliminar este envío?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copiar el enlace del Send al portapapeles", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "ContraseÃąa maestra eliminada." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Ya no es necesaria una contraseÃąa maestra para los miembros de la siguiente organizaciÃŗn. Por favor, confirma el dominio que aparece a continuaciÃŗn con el administrador de tu organizaciÃŗn." - }, "organizationName": { "message": "Nombre de la organizaciÃŗn" }, @@ -2991,7 +3125,8 @@ "message": "ÂŋEstÃĄ seguro de que quieres usar la opciÃŗn \"Nunca\"? Al ajustar las opciones de bloqueo a \"Nunca\", la clave de cifrado de su caja fuerte se guardarÃĄ en tu dispositivo. Si usas esta opciÃŗn, asegÃērate de mantener tu dispositivo debidamente protegido." }, "vault": { - "message": "Caja fuerte" + "message": "Caja fuerte", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Iniciar sesiÃŗn con contraseÃąa maestra" @@ -3089,18 +3224,18 @@ "message": "You denied a login attempt from another device. If this was you, try to log in with the device again." }, "webApp": { - "message": "Web app" + "message": "AplicaciÃŗn web" }, "mobile": { - "message": "Mobile", + "message": "MÃŗvil", "description": "Mobile app" }, "extension": { - "message": "Extension", + "message": "ExtensiÃŗn", "description": "Browser extension/addon" }, "desktop": { - "message": "Desktop", + "message": "Escritorio", "description": "Desktop app" }, "cli": { @@ -3111,7 +3246,7 @@ "description": "Software Development Kit" }, "server": { - "message": "Server" + "message": "Servidor" }, "loginRequest": { "message": "Login request" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias de dominio" }, - "importData": { - "message": "Importar datos", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Error de importaciÃŗn" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Aviso importante" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "RecuÊrdame mÃĄs tarde" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "ÂŋTienes acceso fiable a tu correo electrÃŗnico, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, no lo tengo" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "ÂŋIniciar sesiÃŗn con clave de acceso?" + }, + "savePasskeyQuestion": { + "message": "ÂŋGuardar clave de acceso?" + }, + "saveNewPasskey": { + "message": "Guardar como nuevo inicio de sesiÃŗn" + }, + "savePasskeyNewLogin": { + "message": "Guardar clave de acceso como nuevo inicio de sesiÃŗn" + }, + "noMatchingLoginsForSite": { + "message": "No hay inicios de sesiÃŗn coincidentes para este sitio" + }, + "overwritePasskey": { + "message": "ÂŋSobrescribir clave de acceso?" + }, + "unableToSavePasskey": { + "message": "No se puede guardar la clave de acceso" + }, + "alreadyContainsPasskey": { + "message": "Este elemento ya contiene una clave de acceso. ÂŋEstÃĄs seguro de que quieres sobrescribir la clave de acceso actual?" + }, + "passkeyAlreadyExists": { + "message": "Ya existe una clave de acceso para esta aplicaciÃŗn." + }, + "applicationDoesNotSupportDuplicates": { + "message": "Esta aplicaciÃŗn no soporta duplicados." + }, + "closeThisWindow": { + "message": "Cerrar esta ventana" + }, "allowScreenshots": { "message": "Permitir captura de pantalla" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Envía informaciÃŗn sensible de forma segura", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Comparte archivos y datos de forma segura con cualquiera, en cualquier plataforma. Tu informaciÃŗn permanecerÃĄ encriptada de extremo a extremo, limitando su exposiciÃŗn.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Crear contraseÃąas rÃĄpidamente" }, @@ -4117,10 +4329,10 @@ } }, "showMore": { - "message": "Show more" + "message": "Mostrar mÃĄs" }, "showLess": { - "message": "Show less" + "message": "Mostrar menos" }, "enableAutotypeDescription": { "message": "Bitwarden no valida las ubicaciones de entrada, asegÃērate de que estÃĄs en la ventana y en el capo correctos antes de usar el atajo." @@ -4128,24 +4340,24 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { - "message": "Invalid shortcut" + "message": "Atajo invÃĄlido" }, "moreBreadcrumbs": { "message": "More 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." }, "next": { - "message": "Next" + "message": "Siguiente" }, "confirmKeyConnectorDomain": { "message": "Confirm Key Connector domain" }, "confirm": { - "message": "Confirm" + "message": "Confirmar" }, "enableAutotypeShortcutPreview": { "message": "Enable autotype shortcut (Feature Preview)" @@ -4154,18 +4366,21 @@ "message": "Be sure you are in the correct field before using the shortcut to avoid filling data into the wrong place." }, "editShortcut": { - "message": "Edit shortcut" + "message": "Editar atajo" }, "archiveNoun": { - "message": "Archive", + "message": "Archivo", "description": "Noun" }, "archiveVerb": { - "message": "Archive", + "message": "Archivar", "description": "Verb" }, "unArchive": { - "message": "Unarchive" + "message": "Desarchivar" + }, + "archived": { + "message": "Archived" }, "itemsInArchive": { "message": "Items in archive" @@ -4185,17 +4400,32 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { - "message": "ZIP / Postal code" + "message": "ZIP / CÃŗdigo postal" }, "cardNumberLabel": { - "message": "Card number" + "message": "NÃēmero de tarjeta" }, "upgradeNow": { - "message": "Upgrade now" + "message": "Actualizar ahora" }, "builtInAuthenticator": { "message": "Built-in authenticator" @@ -4204,24 +4434,158 @@ "message": "Secure file storage" }, "emergencyAccess": { - "message": "Emergency access" + "message": "Acceso de emergencia" }, "breachMonitoring": { "message": "Breach monitoring" }, "andMoreFeatures": { - "message": "And more!" + "message": "ÂĄY mÃĄs!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { - "message": "Upgrade to Premium" + "message": "Actualizar a Premium" + }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continuar con el inicio de sesiÃŗn" + }, + "doNotContinue": { + "message": "No continuar" + }, + "domain": { + "message": "Dominio" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verifica tu organizaciÃŗn para iniciar sesiÃŗn" + }, + "organizationVerified": { + "message": "OrganizaciÃŗn verificada" + }, + "domainVerified": { + "message": "Dominio verificado" + }, + "leaveOrganizationContent": { + "message": "Si no verificas tu organizaciÃŗn, tu acceso a la organizaciÃŗn serÃĄ revocado." + }, + "leaveNow": { + "message": "Salir ahora" + }, + "verifyYourDomainToLogin": { + "message": "Verifica tu dominio para iniciar sesiÃŗn" + }, + "verifyYourDomainDescription": { + "message": "Para continuar con el inicio de sesiÃŗn, verifica este dominio." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Para continuar con el inicio de sesiÃŗn, verifica la organizaciÃŗn y el dominio." }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Al reiniciar" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index d85c52bb763..f82a0c90f5d 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Uus URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Tekkis ootamatu viga." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Kirje andmed" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Loe edasi" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Funktsioon pole saadaval" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Jälgi meid" }, - "syncVault": { - "message": "SÃŧnkroniseeri hoidla" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Muuda Ãŧlemparooli" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB ulatuses krÃŧpteeritud salvestusruum." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Eraomanduses kaheastmelise logimise valikud, nagu näiteks YubiKey ja Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Ekspordi asukohast" }, - "exportVault": { - "message": "Ekspordi hoidla" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Kirje on jäädavalt kustutatud" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Kirje on taastatud" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "KÃĩik Sendid", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Soovid tÃĩesti selle Sendi kustutada?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Kopeeri Sendi link lÃĩikelauale", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Ülemparool on eemaldatud." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Oled kindel, et soovid kasutada valikut \"Mitte kunagi\"? Sellega talletatakse sinu hoidla krÃŧpteerimise vÃĩtit seadme mälus. Peaksid olema väga hoolas ja kindel, et seade on ohutu ja selles ei ole pahavara." }, "vault": { - "message": "Hoidla" + "message": "Hoidla", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Logi sisse Ãŧlemparooliga" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Varidomeen" }, - "importData": { - "message": "Impordi andmed", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "TÃĩrge importimisel" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Fail salvestatud. Halda oma seadmesse allalaaditud faile." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index 36401df0078..91c0b13c266 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "URI berria" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Ustekabeko akatsa gertatu da." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Elementuaren informazioa" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Gehiago ikasi" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Ezaugarria ez dago erabilgarri" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Jarraitu gaitzazu" }, - "syncVault": { - "message": "Sinkronizatu kutxa gotorra" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Aldatu pasahitz nagusia" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "Eranskinentzako 1GB-eko zifratutako biltegia." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Esportatu kutxa gotorra" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Elementua betirako ezabatua" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Elementua berreskuratua" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Send guztiak", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Ziur al zaude Send hau ezabatu nahi duzula?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Kopiatu Send esteka arbelean", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Pasahitz nagusia ezabatua." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Ziur zaude \"Inoiz ez\" aukera erabili nahi duzula? Zure blokeo aukerak \"Inoiz ez\" bezala konfiguratzeak kutxa gotorraren zifratze-gakoa gailuan gordetzen du. Aukera hau erabiltzen baduzu, gailua behar bezala babestuta duzula ziurtatu behar duzu." }, "vault": { - "message": "Kutxa gotorra" + "message": "Kutxa gotorra", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Hasi saioa pasahitz nagusiarekin" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index ac0e83dd44d..3d800cc4417 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Ų†Ø´Ø§Ų†ÛŒ Ø§ÛŒŲ†ØĒØąŲ†ØĒی ØŦدید" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Ø§ŲØ˛ŲˆØ¯Ų† ŲžÛŒŲˆØŗØĒ" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "حداڊØĢØą Ø­ØŦŲ… ŲØ§ÛŒŲ„ ÛĩÛ°Û° Ų…Ú¯Ø§Ø¨Ø§ÛŒØĒ Ø§ØŗØĒ" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "یڊ ØŽØˇØ§ÛŒ ØēÛŒØą Ų…Ų†ØĒØ¸ØąŲ‡ ØąØŽ Ø¯Ø§Ø¯Ų‡ Ø§ØŗØĒ." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Ø§ØˇŲ„Ø§ØšØ§ØĒ Ų…ŲˆØąØ¯" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "بیشØĒØą Ø¨Ø¯Ø§Ų†ÛŒØ¯" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "ŲˆÛŒÚ˜Ú¯ÛŒ Ų…ŲˆØŦŲˆØ¯ Ų†ÛŒØŗØĒ" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "īģŖīēŽ īē­īē īēŠīģ§īē’īēŽīģ īŽīģ¨īģ´īēĒ" }, - "syncVault": { - "message": "Ų‡Ų…Ú¯Ø§Ų…â€ŒØŗØ§Ø˛ÛŒ Ú¯Ø§ŲˆØĩŲ†Ø¯ŲˆŲ‚" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "ØĒØēÛŒÛŒØą ÚŠŲ„Ų…Ų‡ ØšØ¨ŲˆØą اØĩŲ„ÛŒ" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "Ûą گیگابایØĒ ؁Øļای Ø°ØŽÛŒØąŲ‡â€ŒØŗØ§Ø˛ÛŒ ØąŲ…Ø˛Ų†Ú¯Ø§ØąÛŒ Ø´Ø¯Ų‡ Ø¨ØąØ§ÛŒ ŲžØąŲˆŲ†Ø¯Ų‡â€ŒŲ‡Ø§ÛŒ ŲžÛŒŲˆØŗØĒ." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Ú¯Ø˛ÛŒŲ†Ų‡â€ŒŲ‡Ø§ÛŒ ŲˆØąŲˆØ¯ اØļØ§ŲÛŒ Ø¯Ųˆ Ų…ØąØ­Ų„Ų‡â€ŒØ§ÛŒ Ų…Ø§Ų†Ų†Ø¯ YubiKey ؈ Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Ø¨ØąŲˆŲ† ØąÛŒØ˛ÛŒ Ø§Ø˛" }, - "exportVault": { - "message": "Ø¨ØąŲˆŲ† ØąÛŒØ˛ÛŒ Ú¯Ø§ŲˆØĩŲ†Ø¯ŲˆŲ‚" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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": "ŲØąŲ…ØĒ ŲžØąŲˆŲ†Ø¯Ų‡" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Ų…ŲˆØąØ¯ Ø¨ØąØ§ÛŒ Ų‡Ų…ÛŒØ´Ų‡ Ø­Ø°Ų شد" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Ų…ŲˆØąØ¯ Ø¨Ø§Ø˛ÛŒØ§Ø¨ÛŒ شد" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Ø§ØˇŲ„Ø§ØšØ§ØĒ ØĒŲ…Ø§Øŗ" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Ų‡Ų…Ų‡ Ø§ØąØŗØ§Ų„â€ŒŲ‡Ø§", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Øĸیا Ų…ØˇŲ…ØĻŲ† Ų‡ØŗØĒید ÚŠŲ‡ Ų…ÛŒâ€ŒØŽŲˆØ§Ų‡ÛŒØ¯ Ø§ÛŒŲ† Ø§ØąØŗØ§Ų„ ØąØ§ Ø­Ø°Ų ÚŠŲ†ÛŒØ¯ØŸ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "ÚŠŲžÛŒ ŲžÛŒŲˆŲ†Ø¯ Ø§ØąØŗØ§Ų„ Ø¨Ų‡ Ø­Ø§ŲØ¸Ų‡ Ų…ŲˆŲ‚ØĒ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "ÚŠŲ„Ų…Ų‡ ØšØ¨ŲˆØą اØĩŲ„ÛŒ Ø­Ø°Ų شد" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Ø¨ØąØ§ÛŒ اؚØļای ØŗØ§Ø˛Ų…Ø§Ų† Ø˛ÛŒØąØŒ ÚŠŲ„Ų…Ų‡ ØšØ¨ŲˆØą اØĩŲ„ÛŒ Ø¯ÛŒÚ¯Øą Ų„Ø§Ø˛Ų… Ų†ÛŒØŗØĒ. Ų„ØˇŲØ§Ų‹ Ø¯Ø§Ų…Ų†Ų‡ Ø˛ÛŒØą ØąØ§ با Ų…Ø¯ÛŒØą ØŗØ§Ø˛Ų…Ø§Ų† ØŽŲˆØ¯ ØĒØŖÛŒÛŒØ¯ ÚŠŲ†ÛŒØ¯." - }, "organizationName": { "message": "Ų†Ø§Ų… ØŗØ§Ø˛Ų…Ø§Ų†" }, @@ -2991,7 +3125,8 @@ "message": "Øĸیا ØŦØ¯Ø§Ų‹ Ų…ÛŒâ€ŒØŽŲˆØ§Ų‡ÛŒØ¯ Ø§Ø˛ Ú¯Ø˛ÛŒŲ†Ų‡ \"Ų‡ØąÚ¯Ø˛\" Ø§ØŗØĒŲØ§Ø¯Ų‡ ÚŠŲ†ÛŒØ¯ØŸ ØĒŲ†Ø¸ÛŒŲ… ÚŠØąØ¯Ų† Ú¯Ø˛ÛŒŲ†Ų‡ ؂؁؄ Ø¨Ų‡ \"Ų‡ØąÚ¯Ø˛\" ÚŠŲ„ÛŒØ¯Ų‡Ø§ÛŒ ØąŲ…Ø˛Ų†Ú¯Ø§ØąÛŒ Ú¯Ø§ŲˆØĩŲ†Ø¯ŲˆŲ‚ØĒØ§Ų† ØąØ§ Ø¨Øą ØąŲˆÛŒ Ø¯ØŗØĒÚ¯Ø§Ų‡ Ø´Ų…Ø§ Ø°ØŽÛŒØąŲ‡ ØŽŲˆØ§Ų‡Ø¯ ÚŠØąØ¯. Ø§Ú¯Øą Ø§Ø˛ Ø§ÛŒŲ† Ú¯Ø˛ÛŒŲ†Ų‡ Ø§ØŗØĒŲØ§Ø¯Ų‡ Ų…ÛŒâ€ŒÚŠŲ†ÛŒØ¯ باید Ø§ØˇŲ…ÛŒŲ†Ø§Ų† داشØĒŲ‡ باشید ÚŠŲ‡ Ø¯ØŗØĒÚ¯Ø§Ų‡ Ø´Ų…Ø§ ÚŠØ§Ų…Ų„Ø§ Ų…Ø­Ø§ŲØ¸ØĒ Ø´Ø¯Ų‡ Ø§ØŗØĒ." }, "vault": { - "message": "Ú¯Ø§ŲˆØĩŲ†Ø¯ŲˆŲ‚" + "message": "Ú¯Ø§ŲˆØĩŲ†Ø¯ŲˆŲ‚", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "با ÚŠŲ„Ų…Ų‡ ØšØ¨ŲˆØą اØĩŲ„ÛŒ ŲˆØ§ØąØ¯ Ø´ŲˆÛŒØ¯" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Ø¯Ø§Ų…Ų†Ų‡ Ų…ØŗØĒØšØ§Øą" }, - "importData": { - "message": "Ø¯ØąŲˆŲ† ØąÛŒØ˛ÛŒ Ø¯Ø§Ø¯Ų‡", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "ØŽØˇØ§ÛŒ Ø¯ØąŲˆŲ† ØąÛŒØ˛ÛŒ" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "ŲžØąŲˆŲ†Ø¯Ų‡ Ø¯Øą Ø¯ØŗØĒÚ¯Ø§Ų‡ Ø°ØŽÛŒØąŲ‡ شد. Ø§Ø˛ ب؎ش Ø¨Ø§ØąÚ¯ÛŒØąÛŒâ€ŒŲ‡Ø§ÛŒ Ø¯ØŗØĒÚ¯Ø§Ų‡ ØŽŲˆØ¯ Ų…Ø¯ÛŒØąÛŒØĒ ÚŠŲ†ÛŒØ¯." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "اØŦØ§Ø˛Ų‡ ØļØ¨Øˇ ØĩŲØ­Ų‡" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Ø§ÛŒŲ† ŲˆØąŲˆØ¯ Ø¯Øą Ų…ØšØąØļ ØŽØˇØą Ø§ØŗØĒ ؈ ŲØ§Ų‚Ø¯ ŲˆØ¨â€ŒØŗØ§ÛŒØĒ Ų…ÛŒâ€ŒØ¨Ø§Ø´Ø¯. Ø¨ØąØ§ÛŒ Ø§Ų…Ų†ÛŒØĒ بیشØĒØąØŒ یڊ ŲˆØ¨â€ŒØŗØ§ÛŒØĒ اØļØ§ŲŲ‡ ÚŠŲ†ÛŒØ¯ ؈ ØąŲ…Ø˛ ØšØ¨ŲˆØą ØąØ§ ØĒØēÛŒÛŒØą Ø¯Ų‡ÛŒØ¯." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "ŲˆØ¨â€ŒØŗØ§ÛŒØĒ ؈ØŦŲˆØ¯ Ų†Ø¯Ø§ØąØ¯" }, @@ -3906,10 +4112,16 @@ "message": "Ø§ØˇŲ„Ø§ØšØ§ØĒ Ø­ØŗØ§Øŗ ØąØ§ Ø¨Ų‡â€ŒØĩŲˆØąØĒ Ø§ÛŒŲ…Ų† Ø§ØąØŗØ§Ų„ ÚŠŲ†ÛŒØ¯", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "ŲžØąŲˆŲ†Ø¯Ų‡â€ŒŲ‡Ø§ ؈ Ø¯Ø§Ø¯Ų‡â€ŒŲ‡Ø§ÛŒ ØŽŲˆØ¯ ØąØ§ Ø¨Ų‡â€ŒØĩŲˆØąØĒ Ø§Ų…Ų† با Ų‡Øą ÚŠØŗÛŒØŒ Ø¯Øą Ų‡Øą ŲžŲ„ØĒŲØąŲ…ÛŒ Ø¨Ų‡ اشØĒØąØ§ÚŠ Ø¨Ú¯Ø°Ø§ØąÛŒØ¯. Ø§ØˇŲ„Ø§ØšØ§ØĒ Ø´Ų…Ø§ Ø¯Øą Ø­ÛŒŲ† اشØĒØąØ§ÚŠâ€ŒÚ¯Ø°Ø§ØąÛŒ Ø¨Ų‡â€ŒØˇŲˆØą ÚŠØ§Ų…Ų„ ØąŲ…Ø˛Ú¯Ø°Ø§ØąÛŒ Ø§Ų†ØĒŲ‡Ø§ Ø¨Ų‡ Ø§Ų†ØĒŲ‡Ø§ Ø¨Ø§Ų‚ÛŒ ØŽŲˆØ§Ų‡Ø¯ Ų…Ø§Ų†Ø¯ ؈ Ų…ÛŒØ˛Ø§Ų† Ø§ŲØ´Ø§ Ų…Ø­Ø¯ŲˆØ¯ Ų…ÛŒâ€ŒØ´ŲˆØ¯.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "ØŗØ§ØŽØĒ ØŗØąÛŒØš ÚŠŲ„Ų…Ø§ØĒ ØšØ¨ŲˆØą" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "ØĒØ§ÛŒŲž Ų…ÛŒØ§Ų†Ø¨Øą" }, - "editAutotypeShortcutDescription": { - "message": "Ø´Ø§Ų…Ų„ یڊ یا Ø¯Ųˆ Ų…ŲˆØąØ¯ Ø§Ø˛ ÚŠŲ„ÛŒØ¯Ų‡Ø§ÛŒ ØĒØēÛŒÛŒØąØ¯Ų‡Ų†Ø¯Ų‡ Ø˛ÛŒØą: Ctrl، Alt، Win یا Shift ؈ یڊ Ø­ØąŲ." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Ų…ÛŒØ§Ų†Ø¨Øą Ų†Ø§Ų…ØšØĒØ¨Øą" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "ØŽØ§ØąØŦ ÚŠØąØ¯Ų† Ø§Ø˛ Ø¨Ø§ÛŒÚ¯Ø§Ų†ÛŒ" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "ØĸیØĒŲ…â€ŒŲ‡Ø§ÛŒ Ų…ŲˆØŦŲˆØ¯ Ø¯Øą Ø¨Ø§ÛŒÚ¯Ø§Ų†ÛŒ" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Ø¨Ø§ÛŒÚ¯Ø§Ų†ÛŒ ØĸیØĒŲ…" }, - "archiveItemConfirmDesc": { - "message": "ØĸیØĒŲ…â€ŒŲ‡Ø§ÛŒ Ø¨Ø§ÛŒÚ¯Ø§Ų†ÛŒâ€ŒØ´Ø¯Ų‡ Ø§Ø˛ Ų†ØĒایØŦ ØŦØŗØĒØŦŲˆÛŒ ØšŲ…ŲˆŲ…ÛŒ ؈ ŲžÛŒØ´Ų†Ų‡Ø§Ø¯ Ų‡Ø§ ŲžØą ÚŠØąØ¯Ų† ØŽŲˆØ¯ÚŠØ§Øą Ø­Ø°Ų Ų…ÛŒâ€ŒØ´ŲˆŲ†Ø¯. Øĸیا Ų…ØˇŲ…ØĻŲ† Ų‡ØŗØĒید ÚŠŲ‡ Ų…ÛŒâ€ŒØŽŲˆØ§Ų‡ÛŒØ¯ Ø§ÛŒŲ† ØĸیØĒŲ… ØąØ§ Ø¨Ø§ÛŒÚ¯Ø§Ų†ÛŒ ÚŠŲ†ÛŒØ¯ØŸ" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ڊد ŲžØŗØĒی" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "؈ بیشØĒØą!" }, - "planDescPremium": { - "message": "Ø§Ų…Ų†ÛŒØĒ ØĸŲ†Ų„Ø§ÛŒŲ† ÚŠØ§Ų…Ų„" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Ø§ØąØĒŲ‚Ø§ Ø¨Ų‡ Ų†ØŗØŽŲ‡ ŲžØąŲ…ÛŒŲˆŲ…" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Ø§Ų‚Ø¯Ø§Ų… ŲˆŲ‚ŲŲ‡ Ø˛Ų…Ø§Ų†ÛŒ" }, "sessionTimeoutHeader": { "message": "ŲˆŲ‚ŲŲ‡ Ø˛Ų…Ø§Ų†ÛŒ Ų†Ø´ØŗØĒ" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index e2952659d03..13bf67e8987 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Uusi URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Tapahtui odottamaton virhe." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Kohteen tiedot" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Lue lisää" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Ominaisuus ei ole käytettävissä" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Seuraa meitä" }, - "syncVault": { - "message": "Synkronoi holvi" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Vaihda pääsalasana" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 Gt salattua tallennustilaa tiedostoliitteille." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Kaksivaiheisen kirjautumisen erikoisvaihtoehdot, kuten YubiKey ja Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Vie lähteestä" }, - "exportVault": { - "message": "Vie holvi" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Kohde poistettiin pysyvästi" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Kohde palautettiin" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Yhteystiedot" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Kaikki Sendit", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Haluatko varmasti poistaa Sendin?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Kopioi Send-linkki leikepÃļydälle", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Pääsalasana poistettiin" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Haluatko varmasti käyttää asetusta \"Ei koskaan\"? Se tallentaa holvisi salausavaimen laitteellesi. Jos käytät asetusta, varmista, että laite on suojattu hyvin." }, "vault": { - "message": "Holvi" + "message": "Holvi", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Kirjaudu pääsalasanalla" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Aliaksen verkkotunnus" }, - "importData": { - "message": "Tuo tietoja", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Tuontivirhe" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Tiedosto tallennettiin laitteelle. Hallitse sitä laitteesi latauksista." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Salli kuvankaappaus" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index 6eaa5577807..4898f63f9ff 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Bagong URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Isang hindi inaasahang error ang naganap." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Impormasyon ng item" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Matuto nang higit pa" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Hindi magagamit ang tampok" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Sundin mo kami" }, - "syncVault": { - "message": "Vault ng Sync" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Palitan ang master password" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB naka encrypt na imbakan para sa mga attachment ng file." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "I-export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanenteng tinanggal" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item na nai-restore" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Lahat ng Mga Padala", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Sigurado ka bang gusto mo na i-delete ang Ipadala na ito?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Kopyahin Ipadala ang link sa clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Tinanggal ang password ng master" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Sigurado ka bang gusto mong gamitin ang opsyon na \"Never\" Ang pagtatakda ng iyong mga pagpipilian sa lock sa \"Hindi kailanman\" ay nag iimbak ng key ng pag encrypt ng iyong vault sa iyong aparato. Kung gagamitin mo ang pagpipiliang ito dapat mong tiyakin na pinapanatili mong protektado nang maayos ang iyong aparato." }, "vault": { - "message": "Ayos" + "message": "Ayos", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Mag-login gamit ang pangunahing password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index 6cca98444b8..2aa6ffc959e 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Nouvel URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Ajouter une pièce jointe" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "La taille maximale des fichiers est de 500 Mo" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Une erreur inattendue est survenue." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Informations sur l'ÊlÊment" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "En savoir plus" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "FonctionnalitÊ non disponible" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Suivez-nous" }, - "syncVault": { - "message": "Synchroniser le coffre" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Changer le mot de passe principal" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 Go de stockage chiffrÊ pour les fichiers joints." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Options de connexion propriÊtaires à deux facteurs telles que YubiKey et Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Exporter depuis" }, - "exportVault": { - "message": "Exporter le coffre" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "ÉlÊment supprimÊ dÊfinitivement" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "ÉlÊment restaurÊ" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Informations de contact" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Tous les Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Êtes-vous sÃģr de vouloir supprimer ce Send ?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copier le lien du Send dans le presse-papier", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Mot de passe principal supprimÊ" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Un mot de passe maÃŽtre n'est plus requis pour les membres de l'organisation suivante. Veuillez confirmer le domaine ci-dessous avec l'administrateur de votre organisation." - }, "organizationName": { "message": "Nom de l'organisation" }, @@ -2991,7 +3125,8 @@ "message": "Êtes-vous sÃģr de vouloir utiliser l'option \"Jamais\" ? DÊfinir le verrouillage sur \"Jamais\" stocke la clÊ de chiffrement de votre coffre sur votre appareil. Si vous utilisez cette option, vous devez vous assurer de correctement protÊger votre appareil." }, "vault": { - "message": "Coffre" + "message": "Coffre", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Se connecter avec le mot de passe principal" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Domaine de l'alias" }, - "importData": { - "message": "Importer des donnÊes", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Erreur lors de l'importation" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Fichier enregistrÊ sur l'appareil. GÊrez à partir des tÊlÊchargements de votre appareil." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Autoriser les captures d'Êcran" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Cet identifiant est à risques et manque un site web. Ajoutez un site web et changez le mot de passe pour une meilleure sÊcuritÊ." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Site Web manquant" }, @@ -3906,10 +4112,16 @@ "message": "Envoyez des informations sensibles, en toute sÊcuritÊ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Partagez des fichiers et des donnÊes en toute sÊcuritÊ avec n'importe qui, sur n'importe quelle plateforme. Vos informations resteront chiffrÊes de bout en bout tout en limitant l'exposition.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "CrÊer rapidement des mots de passe" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Saisir le raccourci" }, - "editAutotypeShortcutDescription": { - "message": "Inclure un ou deux des modificateurs suivants : Ctrl, Alt, Win, ou Shift, et une lettre." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Raccourci invalide" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "DÊsarchiver" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "ÉlÊments dans l'archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archiver l'ÊlÊment" }, - "archiveItemConfirmDesc": { - "message": "Les ÊlÊments archivÊs sont exclus des rÊsultats de recherche gÊnÊraux et des suggestions de remplissage automatique. Êtes-vous sÃģr de vouloir archiver cet ÊlÊment ?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Code postal" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "Et encore plus !" }, - "planDescPremium": { - "message": "SÊcuritÊ en ligne complète" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Mettre à niveau vers Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Action à l’expiration" }, "sessionTimeoutHeader": { "message": "DÊlai d'expiration de la session" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index d607bb8d097..c4a52a24d6f 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "New URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Item information" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Feature unavailable" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow us" }, - "syncVault": { - "message": "Sync vault" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Change master password" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index 868cd9ccbc5..26de7e9688a 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "כ×Ēוב×Ē ×—×“×Š×”" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "×”×•×Ą×Ŗ ×Ļרופה" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "גודל הקוב×Ĩ המרבי הוא 500MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "איר×ĸה שגיאה לא ×Ļפויה." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "מיד×ĸ ×ĸל הפריט" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "למיד×ĸ × ×•×Ą×Ŗ" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "יכול×Ē ×–×• לא זמינה" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "×ĸקוב אחרינו" }, - "syncVault": { - "message": "סנכרון כספ×Ē" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "×”×—×œ×Ŗ סיסמה ראשי×Ē" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 ג'יגה של מקום אחסון מו×Ļפן ×ĸבור קב×Ļים מ×Ļורפים." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "אפשרויו×Ē ×›× ×™×Ą×” דו־שלבי×Ē ×§× ×™×™× ×™×•×Ē ×›×’×•×Ÿ YubiKey ו־Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "יי×Ļוא מ־" }, - "exportVault": { - "message": "י×Ļוא כספ×Ē" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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": "×Ēבני×Ē ×§×•×‘×Ĩ" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "הפריט נמחק ל×Ļמי×Ēו×Ē" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "הפריט שוחזר" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "פרטי איש ק׊ר" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "כל הסÖĩנְדים", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "האם א×Ēה בטוח שבר×Ļונך למחוק ץÖĩנְד זה?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "ה×ĸ×Ē×§ קישור ץÖĩנְד ללוח", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "הסיסמה הראשי×Ē ×”×•×Ą×¨×”" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "סיסמה ראשי×Ē ××™× ×” נדרש×Ē ×ĸוד ×ĸבור חברים בארגון הבא. נא לאשר א×Ē ×”×“×•×ž×™×™×Ÿ שלהלן ×ĸם מנהל הארגון שלך." - }, "organizationName": { "message": "׊ם הארגון" }, @@ -2991,7 +3125,8 @@ "message": "האם א×Ēה בטוח שבר×Ļונך להש×Ēמ׊ באפשרו×Ē \"ל×ĸולם לא\"? הגדר×Ē ××¤×Š×¨×•×™×•×Ē ×”× ×ĸילה שלך ל\"ל×ĸולם לא\" מאחסנ×Ē ××Ē ×ž×¤×Ēח הה×Ļפנה של הכספ×Ē ×Š×œ×š במכשיר שלך. אם א×Ēה מ׊×Ēמ׊ באפשרו×Ē ×–×• ×ĸליך לוודא ׊א×Ēה שומר ×ĸל המכשיר שלך מוגן כראוי." }, "vault": { - "message": "כספ×Ē" + "message": "כספ×Ē", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "כניסה ×ĸם סיסמה ראשי×Ē" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "דומיין כינוי" }, - "importData": { - "message": "ייבא × ×Ēונים", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "שגיא×Ē ×™×™×‘×•×" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "הקוב×Ĩ נ׊מר למכשיר. נהל מהורדו×Ē ×”×ž×›×Š×™×¨ שלך." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "אפ׊ר לכיד×Ē ×ž×Ą×š" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "כניסה זו נמ×Ļא×Ē ×‘×Ą×™×›×•×Ÿ וחסר בה א×Ēר אינטרנט. ×”×•×Ą×Ŗ א×Ēר אינטרנט ושנה א×Ē ×”×Ą×™×Ą×ž×” לאבטחה חזקה יו×Ēר." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "לא נמ×Ļא א×Ēר אינטרנט" }, @@ -3906,10 +4112,16 @@ "message": "שלח מיד×ĸ רגיש באופן בטוח", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "׊×Ē×Ŗ קב×Ļים ונ×Ēונים באופן מאובטח ×ĸם כל אחד, בכל פלטפורמה. המיד×ĸ שלך יישאר מו×Ļפן מק×Ļה־לק×Ļה ×Ēוך הגבל×Ē ×—×Š×™×¤×”.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "×Ļור סיסמאו×Ē ×‘×ž×”×™×¨×•×Ē" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "הקלד קי×Ļור דרך" }, - "editAutotypeShortcutDescription": { - "message": "כלול אחד או שניים ממקשי ה×Ļי׍ות הבאים: Ctrl, Alt, Win, או Shift, ואו×Ē." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "קי×Ļור דרך לא חוקי" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "הסר מהארכיון" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "פריטים בארכיון" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "ה×ĸבר פריט לארכיון" }, - "archiveItemConfirmDesc": { - "message": "פריטים בארכיון מוחרגים מ×Ēו×Ļאו×Ē ×—×™×¤×•×Š כללי וה×Ļ×ĸו×Ē ×œ×ž×™×œ×•×™ אוטומטי. האם א×Ēה בטוח שבר×Ļונך לה×ĸביר פריט זה לארכיון?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "מיקוד" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "ו×ĸוד!" }, - "planDescPremium": { - "message": "השלם אבטחה מקוונ×Ē" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "שדרג לפרימיום" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "פ×ĸול×Ē ×¤×Ą×§ זמן" }, "sessionTimeoutHeader": { "message": "פץק זמן להפ×ĸלה" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index 2ab323eedc9..30390b1e445 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "New URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Item information" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Feature unavailable" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow us" }, - "syncVault": { - "message": "Sync vault" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Change master password" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index 0f7a8185118..5331eae2d14 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "Send će na ovaj datum biti trajno izbrisan.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "Datoteka za dijeljenje" + }, + "hideTextByDefault": { + "message": "Zadano sakrij tekst" + }, + "hideYourEmail": { + "message": "Sakriti adresu e-poÅĄte od primatelja." + }, + "limitSendViews": { + "message": "Ograniči broj pogleda" + }, + "limitSendViewsCount": { + "message": "Preostalo pogleda: $ACCESSCOUNT$", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "Nakon dosegnutog broja, nitko neće moći pogledati Send.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Privatna biljeÅĄka" + }, + "sendDetails": { + "message": "Detalji Senda", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Dodaj neobaveznu lozinku za pristup ovom Sendu.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Tekst za dijeljenje" + }, + "newItemHeaderTextSend": { + "message": "Novi teksutalni Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "Novi datotečni Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Uredi tekstualni Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Uredi datotečni Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Sigurno ÅželiÅĄ trajno izbrisati ovaj Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "Novo", + "description": "for adding new items" + }, "newUri": { "message": "Novi URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Dodaj privitak" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Privitak aÅžuriran" + }, "maxFileSizeSansPunctuation": { "message": "Najveća veličina datoteke je 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "DoÅĄlo je do neočekivane pogreÅĄke." }, + "unexpectedErrorShort": { + "message": "Neočekivana greÅĄka" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Informacije o stavci" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Saznaj viÅĄe" }, + "migrationsFailed": { + "message": "Dogodila se greÅĄka pri aÅžuriranju postavki ÅĄifriranja." + }, + "updateEncryptionSettingsTitle": { + "message": "AÅžuriraj svoje postakve ÅĄifriranja" + }, + "updateEncryptionSettingsDesc": { + "message": "Nove preporučene postavke ÅĄifriranja poboljÅĄat će sigurnost tvojeg računa. Za aÅžuriranje, unesi svoju glavnu lozinku." + }, + "confirmIdentityToContinue": { + "message": "Za nastavak, potvrdi svoj identitet" + }, + "enterYourMasterPassword": { + "message": "Unesi svoju glavnu lozinku" + }, + "updateSettings": { + "message": "AÅžuriraj postavke" + }, "featureUnavailable": { "message": "Značajka nije dostupna" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Prati nas" }, - "syncVault": { - "message": "Sinkronizraj trezor" + "syncNow": { + "message": "Sinkroniziraj" }, "changeMasterPass": { "message": "Promjeni glavnu lozinku" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB ÅĄifriranog prostora za pohranu podataka." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ ÅĄifriranog prostora za privitke.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Mogućnosti za prijavu u dva koraka kao ÅĄto su YubiKey i Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Izvezi iz" }, - "exportVault": { - "message": "Izvezi trezor" + "exportNoun": { + "message": "Izvoz", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Izvoz", + "description": "The verb form of the word Export" + }, + "importNoun": { + "message": "Uvoz", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Uvoz", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Format datoteke" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Stavka trajno izbrisana" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Stavka vraćena" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Kontaktne informacije" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Svi Sendovi", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Sigurno ÅželiÅĄ izbrisati ovaj Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Kopiraj vezu na Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Kopiraj vezu na Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Glavna lozinka uklonjena." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Glavna lozinka viÅĄe nije obavezna za članove ove organizacije. Provjeri prikazanu domenu sa svojim administratorom." - }, "organizationName": { "message": "Naziv Organizacije" }, @@ -2991,7 +3125,8 @@ "message": "Sigurno ÅželiÅĄ koristiti opciju „Nikada”? Postavljanje opcija zaključavanja na „Nikada” pohranjuje ÅĄifru tvojeg trezora na tvom uređaju. Ako koristiÅĄ ovu opciju, trebalo bi osigurati da je uređaj pravilno zaÅĄtićen." }, "vault": { - "message": "Trezor" + "message": "Trezor", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Prijava glavnom lozinkom" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domene" }, - "importData": { - "message": "Uvezi podatke", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "GreÅĄka prilikom uvoza" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Datoteka spremljena na uređaj. Upravljaj u preuzimanjima svog uređaja." }, + "importantNotice": { + "message": "VaÅžna napomena" + }, + "setupTwoStepLogin": { + "message": "Postavi dvostruku autentifikaciju" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden će, počevÅĄi od veljače 2025., za provjeru prijava s novih uređaja poslati kôd na e-poÅĄtu tvog računa." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Prijavu dvostrukom autentifikacijom moÅžeÅĄ postaviti kao alternativni način zaÅĄtite svog računa ili promijeni svoju e-poÅĄtu u onu kojoj moÅžeÅĄ pristupiti." + }, + "remindMeLater": { + "message": "Podsjeti me kasnije" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "ImaÅĄ li pouzdan pristup svojoj e-poÅĄti: $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Ne, nemam" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Da, pouzdano mogu pristupiti svojoj e-poÅĄti" + }, + "turnOnTwoStepLogin": { + "message": "Uključi prijavu dvostrukom autentifikacijom" + }, + "changeAcctEmail": { + "message": "Promjeni e-poÅĄtu računa" + }, + "passkeyLogin": { + "message": "Prijava pristupnim ključem?" + }, + "savePasskeyQuestion": { + "message": "Spremi pristupni ključ?" + }, + "saveNewPasskey": { + "message": "Spremi novu prijavu" + }, + "savePasskeyNewLogin": { + "message": "Spremi pristupni ključ kao novu prijavu" + }, + "noMatchingLoginsForSite": { + "message": "Nema prijava za ovu web stranicu" + }, + "overwritePasskey": { + "message": "PrebriÅĄi pristupni ključ?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "Ova stavka već sadrÅži pristupni ključ. Sigurno ÅželiÅĄ prebrisati trenutni pristupni ključ?" + }, + "passkeyAlreadyExists": { + "message": "Za ovu aplikaciju već postoji pristupni ključ." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Zatvori ovaj prozor" + }, "allowScreenshots": { "message": "Dozvoli snimanje zaslona" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Ova prijava je ugroÅžena i nedostaje joj web-stranica. Dodaj web-stranicu i promijeni lozinku za veću sigurnost." }, + "vulnerablePassword": { + "message": "Ranjiva lozinka." + }, + "changeNow": { + "message": "Promijeni sada" + }, "missingWebsite": { "message": "Nedostaje web-stranica" }, @@ -3906,10 +4112,16 @@ "message": "Sigurno poÅĄalji osjetljive podatke", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "Nema rezultata pretrage" + }, "sendsBodyNoItems": { "message": "Sigurno dijeli datoteke i podatke s bilo kime, na bilo kojoj platformi. Tvoji podaci ostaju kriptirani uz ograničenje izloÅženosti.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Očisti filtre ili pokuÅĄaj s drugačijom pretragom" + }, "generatorNudgeTitle": { "message": "Brzo stvori lozinke" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Vrsta prečaca" }, - "editAutotypeShortcutDescription": { - "message": "Uključi jedan ili dva modifikatora: Ctrl, Alt, Win ili Shift i slovo." + "editAutotypeKeyboardModifiersDescription": { + "message": "Uključi jedan ili dva modifikatora: Ctrl, Alt, Win i slovo." }, "invalidShortcut": { "message": "NevaÅžeći prečac" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "PoniÅĄti arhiviranje" }, + "archived": { + "message": "Arhivirano" + }, "itemsInArchive": { "message": "Stavke u arhivi" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Arhiviraj stavku" }, - "archiveItemConfirmDesc": { - "message": "Arhivirane stavke biti će izuzete iz rezultata općih pretraga i preporuka auto-ispune. Sigurno ÅželiÅĄ arhivirati?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Ponovno Pokreni Premium" + }, + "premiumSubscriptionEnded": { + "message": "Toja Premium pretplata je zavrÅĄila" + }, + "premiumSubscriptionEndedDesc": { + "message": "Za ponovni pristup svojoj arhivi, ponovno pokreni Premium pretplatu. Ako urediÅĄ detalje arhivirane stavke prije ponovnog pokretanja, ona će biti vraćena u tvoj trezor." + }, + "itemRestored": { + "message": "Stavka je vraćena" }, "zipPostalCodeLabel": { "message": "PoÅĄtanski broj" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "I viÅĄe!" }, - "planDescPremium": { - "message": "DovrÅĄi online sigurnost" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": " Nadogradi na Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domena" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domena potvrđena" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Radnja nakon isteka" }, "sessionTimeoutHeader": { "message": "Istek sesije" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Ovom postavkom upravlja tvoja organizacija." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Kod ponovnog pokretanja" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Postavi metodu otključavanja za promjenu radnje nakon isteka vremenskog ograničenja trezora" + }, + "upgrade": { + "message": "Nadogradnja" + }, + "leaveConfirmationDialogTitle": { + "message": "Sigurno ÅželiÅĄ napustiti?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Prihvati prijenos" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "ZaÅĄto ovo vidim?" } } diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index 9a6dd787f8c..49a034cb6d5 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "A Send vÊglegesen tÃļrÃļlve lesz ebben az időpontban.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "MegosztandÃŗ fÃĄjl" + }, + "hideTextByDefault": { + "message": "SzÃļveg elrejtÊse alapÊrtelmezetten" + }, + "hideYourEmail": { + "message": "SajÃĄt email cím elrejtÊse a megtekintÊsek elől." + }, + "limitSendViews": { + "message": "MegtekintÊsek korlÃĄtozÃĄsa" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ megtekintÊs maradt.", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "Senki sem tudja megtekinteni ezt a Send elemet a korlÃĄt elÊrÊse utÃĄn.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "SzemÊlyes jegyzet" + }, + "sendDetails": { + "message": "Send rÊszletek", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Adjunk meg egy opcionÃĄlis jelszÃŗt a címzetteknek a Send elÊrÊsÊhez.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "MegosztandÃŗ szÃļveg" + }, + "newItemHeaderTextSend": { + "message": "Új szÃļveges Send elem", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "Új fÃĄjl Send elem", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Send szÃļveg szerkesztÊse", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Send fÃĄjl szerkesztÊse", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Biztosan vÊglegesen tÃļrlÊsre kerÃŧljÃļn ez a Send elem?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "Új", + "description": "for adding new items" + }, "newUri": { "message": "Új URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "MellÊklet hozzÃĄadÃĄsa" }, + "itemsTransferred": { + "message": "Az elemek ÃĄtvitelre kerÃŧltek." + }, + "fixEncryption": { + "message": "TitkosítÃĄs javítÃĄsa" + }, + "fixEncryptionTooltip": { + "message": "Ez a fÃĄjl elavult titkosítÃĄsi mÃŗdszert hasznÃĄl." + }, + "attachmentUpdated": { + "message": "A mellÊklet frissítÊsre kerÃŧlt." + }, "maxFileSizeSansPunctuation": { "message": "A maximÃĄlis fÃĄjlmÊret 500 MB." }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "VÃĄratlan hiba tÃļrtÊnt." }, + "unexpectedErrorShort": { + "message": "VÃĄratlan hiba" + }, + "closeThisBitwardenWindow": { + "message": "ZÃĄrjuk be ezt a Bitwarden ablakot Ês prÃŗbÃĄljuk Ãējra." + }, "itemInformation": { "message": "Elem informÃĄciÃŗ" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "TovÃĄbbi informÃĄciÃŗ" }, + "migrationsFailed": { + "message": "Hiba tÃļrtÊnt a titkosítÃĄsi beÃĄllítÃĄsok frissítÊsekor." + }, + "updateEncryptionSettingsTitle": { + "message": "A titkosítÃĄsi beÃĄllítÃĄsok frissítÊse" + }, + "updateEncryptionSettingsDesc": { + "message": "Az Ãēj ajÃĄnlott titkosítÃĄsi beÃĄllítÃĄsok javítjÃĄk a fiÃŗk biztonsÃĄgÃĄt. Adjuk meg a mesterjelszÃŗt a frissítÊshez most." + }, + "confirmIdentityToContinue": { + "message": "A folytatÃĄshoz meg kell erősíteni a szemÊlyazonossÃĄgot." + }, + "enterYourMasterPassword": { + "message": "MesterjelszÃŗ megadÃĄsa" + }, + "updateSettings": { + "message": "BeÃĄllítÃĄsok frissítÊse" + }, "featureUnavailable": { "message": "Ez a funkciÃŗ nem Êrhető el." }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "KÃļvetÊs" }, - "syncVault": { - "message": "SzÊf szinkronizÃĄlÃĄsa" + "syncNow": { + "message": "SzinkronizÃĄlÃĄs most" }, "changeMasterPass": { "message": "MesterjelszÃŗ mÃŗdosítÃĄsa" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB titkosított fÃĄjlmellÊklet tÃĄrhely." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ titkosított tÃĄrhely a fÃĄjlmellÊkletekhez.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "SajÃĄt kÊtlÊpcsős bejelentkezÊsi lehetősÊgek mint a YubiKey Ês a Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "ExportÃĄlÃĄs innen:" }, - "exportVault": { - "message": "SzÊf exportÃĄlÃĄsa" + "exportNoun": { + "message": "ExportÃĄlÃĄs", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "ExportÃĄlÃĄs", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Elem vÊgleges tÃļrlÊse" }, + "archivedItemRestored": { + "message": "Az archivÃĄlt elem visszaÃĄllítÃĄsra kerÃŧlt." + }, "restoredItem": { "message": "VisszaÃĄllított elem" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "ElÊrhetősÊgi adatok" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Összes kÃŧldÊs", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Biztosan tÃļrlÊsre kerÃŧljÃļn ez a kÃŧldÊs?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Send hivatkozÃĄs mÃĄsolÃĄsa", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "HivatkozÃĄs kÃŧldÊse mÃĄsolÃĄs a vÃĄgÃŗlapra", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "A mesterjelszÃŗ eltÃĄvolítÃĄsra kerÃŧlt." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A kÃļvetkező szervezet tagjai szÃĄmÃĄra mÃĄr nincs szÃŧksÊg mesterjelszÃŗra. ErősítsÃŧk meg az alÃĄbbi tartomÃĄnyt a szervezet adminisztrÃĄtorÃĄval." - }, "organizationName": { "message": "Szervezet neve" }, @@ -2991,7 +3125,8 @@ "message": "Biztosan szeretnÊnk hasznÃĄlni a \"Soha\" opciÃŗt? A zÃĄrolÃĄsi opciÃŗk \"Soha\" ÊrtÊkre ÃĄllítÃĄsa a szÊf titkosítÃĄsi kulcsÃĄt az eszkÃļzÃļn tÃĄrolja. Ennek az opciÃŗnak a hasznÃĄlatakor cÊlszerÅą az eszkÃļz megfelelő vÊdettsÊgÊt biztosítani." }, "vault": { - "message": "SzÊf" + "message": "SzÊf", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "BejelentkezÊs mesterjelszÃŗval" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Áldomain" }, - "importData": { - "message": "Adatok importÃĄlÃĄsa", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "ImportÃĄlÃĄsi hiba" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "A fÃĄjl mentÊsre kerÃŧlt az eszkÃļzre. KezeljÃŧk az eszkÃļzről a letÃļltÊseket." }, + "importantNotice": { + "message": "Fontos megjegyzÊs" + }, + "setupTwoStepLogin": { + "message": "KÊtlÊpÊses bejelentkezÊs beÃŧzemelÊse" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "A Bitwarden 2025 februÃĄrjÃĄtÃŗl kÃŗdot kÃŧld a fiÃŗkhoz tartozÃŗ email címre, amellyel ellenőrizhetők az Ãēj eszkÃļzÃļkről tÃļrtÊnő bejelentkezÊsek." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "A fiÃŗk vÊdelmÊnek alternatív mÃŗdjakÊnt beÃĄllíthatunk kÊtlÊpÊses bejelentkezÊst vagy mÃŗdosíthatjuk az email címet egy elÊrhetőre." + }, + "remindMeLater": { + "message": "EmlÊkeztetÊs kÊsőbb" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "MegbízhatÃŗ a hozzÃĄfÊrÊs $EMAIL$ email címhez?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nem, nem Êrem el" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Igen, megbízhatÃŗan hozzÃĄfÊrek az email címhez" + }, + "turnOnTwoStepLogin": { + "message": "KÊtlÊpÊses bejelentkezÊs bekapcsolÃĄsa" + }, + "changeAcctEmail": { + "message": "FiÃŗk email cím megvÃĄltoztatÃĄsa" + }, + "passkeyLogin": { + "message": "BejelentkezÊs belÊpőkulccsal?" + }, + "savePasskeyQuestion": { + "message": "BelÊpőkulcs mentÊse?" + }, + "saveNewPasskey": { + "message": "MentÊs Ãēj bejelentkezÊskÊnt" + }, + "savePasskeyNewLogin": { + "message": "BelÊpőkulcs mentÊse Ãēj bejelentkezÊskÊnt" + }, + "noMatchingLoginsForSite": { + "message": "Nincsenek egyező bejelentkezÊsek ehhez a webhelyhez." + }, + "overwritePasskey": { + "message": "BelÊpőkulcs felÃŧlírÃĄsa?" + }, + "unableToSavePasskey": { + "message": "Nem lehet menteni a belÊpőkulcsot." + }, + "alreadyContainsPasskey": { + "message": "Ez az elem mÃĄr tartalmaz egy belÊpőkulcsot. Biztosan felÃŧlírÃĄsra kerÃŧljÃļn az aktuÃĄlis belÊpőkulcs?" + }, + "passkeyAlreadyExists": { + "message": "Ehhez az alkalmazÃĄshoz mÃĄr lÊtezik belÊpőkulcs." + }, + "applicationDoesNotSupportDuplicates": { + "message": "Ez az alkalmazÃĄs nem tÃĄmogatja a mÃĄsolatokat." + }, + "closeThisWindow": { + "message": "Ezen ablak bezÃĄrÃĄsa" + }, "allowScreenshots": { "message": "KÊpernyőrÃļgzítÊs engedÊlyezÊse" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Ez a bejelentkezÊs veszÊlyben van Ês hiÃĄnyzik egy webhely. Adjunk hozzÃĄ egy webhelyet Ês mÃŗdosítsuk a jelszÃŗt az erősebb biztonsÃĄg ÊrdekÊben." }, + "vulnerablePassword": { + "message": "A jelszÃŗ sÊrÃŧlÊkeny." + }, + "changeNow": { + "message": "MÃŗdosítÃĄs most" + }, "missingWebsite": { "message": "HiÃĄnyzÃŗ webhely" }, @@ -3906,10 +4112,16 @@ "message": "ÉrzÊkeny informÃĄciÃŗt kÃŧldÊse biztonsÃĄgosan", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "Nincsenek visszakapott keresÊsi eredmÊnyek." + }, "sendsBodyNoItems": { "message": "FÃĄjlok vagy adatok megosztÃĄsa biztonsÃĄgosan bÃĄrkivel, bÃĄrmilyen platformon. Az informÃĄciÃŗk titkosítva maradnak a vÊgpontokon, korlÃĄtozva a kitettsÊget.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "TÃļrÃļljÃŧk a szÅąrőket vagy prÃŗbÃĄlkozzunk mÃĄsik keresÊsi kifejezÊssel." + }, "generatorNudgeTitle": { "message": "Jelszavak gyors lÊtrehozÃĄsa" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Tartalmazzon egyet vagy kettőt a kÃļvetkező mÃŗdosítÃŗk kÃļzÃŧl: Ctrl, Alt, Win Ês egy betÅą." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "VisszavÊtel archívumbÃŗl" }, + "archived": { + "message": "ArchivÃĄlva" + }, "itemsInArchive": { "message": "Archívum elemek szÃĄma" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Elem archivÃĄlÃĄsa" }, - "archiveItemConfirmDesc": { - "message": "Az archivÃĄlt elemek ki vannak zÃĄrva az ÃĄltalÃĄnos keresÊsi eredmÊnyekből Ês az automatikus kitÃļltÊsi javaslatokbÃŗl. Biztosan archivÃĄlni szeretnÊnk ezt az elemet?" + "archiveItemDialogContent": { + "message": "Az archivÃĄlÃĄs utÃĄn ez az elem kizÃĄrÃĄsra kerÃŧl a keresÊsi eredmÊnyekből Ês az automatikus kitÃļltÊsi javaslatokbÃŗl." + }, + "unArchiveAndSave": { + "message": "ArchivÃĄlÃĄs visszavonÃĄsa Ês mentÊs" + }, + "restartPremium": { + "message": "PrÊmium előfizetÊs ÃējraindítÃĄsa" + }, + "premiumSubscriptionEnded": { + "message": "A PrÊmium előfizetÊs vÊget Êrt." + }, + "premiumSubscriptionEndedDesc": { + "message": "Az archívumhoz hozzÃĄfÊrÊs visszaszerzÊsÊhez indítsuk Ãējra a PrÊmium előfizetÊst. Ha az ÃējraindítÃĄs előtt szerkesztjÃŧk egy archivÃĄlt elem adatait, akkor az visszakerÃŧl a szÊfbe." + }, + "itemRestored": { + "message": "Az elem visszaÃĄllítÃĄsra kerÃŧlt." }, "zipPostalCodeLabel": { "message": "IrÃĄnyítÃŗszÃĄm" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "És mÊg tÃļbb!" }, - "planDescPremium": { - "message": "Teljes kÃļrÅą online biztonsÃĄg" + "advancedOnlineSecurity": { + "message": "Bővített online biztonsÃĄg" }, "upgradeToPremium": { "message": "ÁttÊrÊs PrÊmium csomagra" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "A szervezet mÃĄr nem hasznÃĄl mesterjelszavakat a Bitwardenbe bejelentkezÊshez. A folytatÃĄshoz ellenőrizzÃŧk a szervezetet Ês a tartomÃĄnyt." + }, + "continueWithLogIn": { + "message": "FolytatÃĄs bejelentkezÊssel" + }, + "doNotContinue": { + "message": "Nincs folytatÃĄs" + }, + "domain": { + "message": "TartomÃĄny" + }, + "keyConnectorDomainTooltip": { + "message": "Ez a tartomÃĄny tÃĄrolja a fiÃŗk titkosítÃĄsi kulcsait, ezÊrt győződjÃŧnk meg rÃŗla, hogy megbízunk-e benne. Ha nem vagyunk biztos benne, ÊrdeklődjÃŧnk adminisztrÃĄtornÃĄl." + }, + "verifyYourOrganization": { + "message": "Szervezet ellenőrzÊse a bejelentkezÊshez" + }, + "organizationVerified": { + "message": "A szervezet ellenőrzÊsre kerÃŧlt." + }, + "domainVerified": { + "message": "A tartomÃĄny ellenőrzÊsre kerÃŧlt." + }, + "leaveOrganizationContent": { + "message": "Ha nem ellenőrizzÃŧk a szervezetet, a szervezethez hozzÃĄfÊrÊs visszavonÃĄsra kerÃŧl." + }, + "leaveNow": { + "message": "ElhagyÃĄs most" + }, + "verifyYourDomainToLogin": { + "message": "TartomÃĄny ellenőrzÊse a bejelentkezÊshez" + }, + "verifyYourDomainDescription": { + "message": "A bejelentkezÊs folytatÃĄsÃĄhoz ellenőrizzÃŧk ezt a tartomÃĄnyt." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "A bejelentkezÊs folytatÃĄsÃĄhoz ellenőrizzÃŧk a szervezetet Ês a tartomÃĄnyt." + }, "sessionTimeoutSettingsAction": { "message": "IdőkifutÃĄsi mÅąvelet" }, "sessionTimeoutHeader": { "message": "Munkamenet időkifutÃĄs" + }, + "resizeSideNavigation": { + "message": "OldalnavigÃĄciÃŗ ÃĄtmÊretezÊs" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Ezt a beÃĄllítÃĄst a szervezet lezeli." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "A szervezet a munkamenet maximÃĄlis munkamenet időkifutÃĄsÃĄt $HOURS$ ÃŗrÃĄra Ês $MINUTES$ percre ÃĄllította be.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "A szervezet az alapÊrtelmezett munkamenet időkifutÃĄstr RendszerzÃĄr be ÊrtÊkre ÃĄllította." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "A szervezet a maximÃĄlis munkamenet időkifutÃĄst ÚjraindítÃĄskor ÊrtÊkre ÃĄllította." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "A maximÃĄlis időtÃēllÊpÊs nem haladhatja meg a $HOURS$ Ãŗra Ês $MINUTES$ perc ÊrtÊket.", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "ÚjraindítÃĄskor" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Állítsunk be egy feloldÃĄsi mÃŗdot a szÊf időkifutÃĄsi mÅąveletÊnek mÃŗdosítÃĄsÃĄhoz." + }, + "upgrade": { + "message": "ÁttÊrÊs" + }, + "leaveConfirmationDialogTitle": { + "message": "Biztosan szeretnÊnk kilÊpni?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Az elutasítÃĄssal a szemÊlyes elemek a fiÃŗkban maradnak, de elveszítjÃŧk hozzÃĄfÊrÊst a megosztott elemekhez Ês a szervezeti funkciÃŗkhoz." + }, + "leaveConfirmationDialogContentTwo": { + "message": "LÊpjÃŧnk kapcsolatba az adminisztrÃĄtorral a hozzÃĄfÊrÊs visszaszerzÊsÊÊrt." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "$ORGANIZATION$ elhagyÃĄsa", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Hogyan kezeljem a szÊfet?" + }, + "transferItemsToOrganizationTitle": { + "message": "Elemek ÃĄtvitele $ORGANIZATION$ szervezethez", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ megkÃļveteli, hogy minden elem a szervezet tulajdonÃĄban legyen a biztonsÃĄg Ês a megfelelősÊg ÊrdekÊben. KattintÃĄs az elfogadÃĄsra az elemek tulajdonjogÃĄnak ÃĄtruhÃĄzÃĄsÃĄhoz.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Átvitel elfogadÃĄsa" + }, + "declineAndLeave": { + "message": "ElutasítÃĄs Ês kilÊpÊs" + }, + "whyAmISeeingThis": { + "message": "MiÊrt lÃĄthatÃŗ ez?" } } diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index 188ee153da1..fe02d601974 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "URl Baru" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Terjadi kesalahan yang tak diduga." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Informasi Item" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Pelajari lebih lanjut" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Fitur Tidak Tersedia" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Ikuti Kami" }, - "syncVault": { - "message": "Sinkronisasi Brankas" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Ubah Kata Sandi Utama" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB penyimpanan berkas yang dienkripsi." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Ekspor Brankas" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item Terhapus Permanen" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item Yang Dipulihkan" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Semua Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Anda yakin ingin menghapus Send ini?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Salin Send tautan ke papan klip", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Sandi utama dihapus" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Brankas" + "message": "Brankas", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index 8caf4982356..b7e225de77e 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "Il Send sarà eliminato definitivamente in questa data.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File da condividere" + }, + "hideTextByDefault": { + "message": "Nascondi il testo per impostazione predefinita" + }, + "hideYourEmail": { + "message": "Nascondi il tuo indirizzo email ai visualizzatori." + }, + "limitSendViews": { + "message": "Limita le visualizzazioni" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ visualizzazioni rimaste", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "Nessuno potrà visualizzare il Send dopo il raggiungimento del limite.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Nota privata" + }, + "sendDetails": { + "message": "Dettagli del Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Richiedi ai destinatari una password per aprire questo Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Testo da condividere" + }, + "newItemHeaderTextSend": { + "message": "Nuovo Send (testo)", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "Nuovo Send (file)", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Modifica Send testuale", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Modifica Send con file", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Vuoi davvero eliminare questo Send? L'azione è irreversibile.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "Nuovo", + "description": "for adding new items" + }, "newUri": { "message": "Nuovo URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Aggiungi allegato" }, + "itemsTransferred": { + "message": "Elementi trasferiti" + }, + "fixEncryption": { + "message": "Correggi la crittografia" + }, + "fixEncryptionTooltip": { + "message": "Questo file usa un metodo di crittografia obsoleto." + }, + "attachmentUpdated": { + "message": "Allegato aggiornato" + }, "maxFileSizeSansPunctuation": { "message": "La dimensione massima consentita è 500 MB." }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Si è verificato un errore imprevisto." }, + "unexpectedErrorShort": { + "message": "Errore inaspettato" + }, + "closeThisBitwardenWindow": { + "message": "Chiudi questa finestra di Bitwarden e riprova." + }, "itemInformation": { "message": "Informazioni sull'elemento" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Ulteriori informazioni" }, + "migrationsFailed": { + "message": "Si è verificato un errore durante l'aggiornamento delle impostazioni di cifratura." + }, + "updateEncryptionSettingsTitle": { + "message": "Aggiorna le impostazioni di crittografia" + }, + "updateEncryptionSettingsDesc": { + "message": "Le nuove impostazioni di crittografia consigliate miglioreranno la sicurezza del tuo account. Inserisci la tua password principale per aggiornare." + }, + "confirmIdentityToContinue": { + "message": "Conferma la tua identità per continuare" + }, + "enterYourMasterPassword": { + "message": "Inserisci la tua password principale" + }, + "updateSettings": { + "message": "Aggiorna impostazioni" + }, "featureUnavailable": { "message": "Funzionalità non disponibile" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Seguici" }, - "syncVault": { - "message": "Sincronizza cassaforte" + "syncNow": { + "message": "Sincronizza" }, "changeMasterPass": { "message": "Cambia password principale" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB di spazio di archiviazione crittografato per gli allegati." }, + "premiumSignUpStorageV2": { + "message": "Archivio crittografato di $SIZE$ per allegati.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Opzioni di verifica in due passaggi proprietarie come YubiKey e Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Esporta da" }, - "exportVault": { - "message": "Esporta cassaforte" + "exportNoun": { + "message": "Esporta", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Esporta", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Elemento eliminato definitivamente" }, + "archivedItemRestored": { + "message": "Elemento estratto dall'archivio" + }, "restoredItem": { "message": "Elemento ripristinato" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Informazioni di contatto" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Tutti i Send", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Sei sicuro di voler eliminare questo Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copia link del Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copia il link al Send negli appunti", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Password principale rimossa" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "La password principale non è piÚ richiesta per i membri dell'organizzazione. Per favore, conferma il dominio qui sotto con l'amministratore." - }, "organizationName": { "message": "Nome dell'organizzazione" }, @@ -2991,7 +3125,8 @@ "message": "Sei sicuro di voler usare l'opzione \"Mai\"? Impostare le opzioni di blocco su \"Mai\" salverà la chiave di crittografia della cassaforte sul tuo dispositivo. Se usi questa opzione, assicurati di mantenere il tuo dispositivo adeguatamente protetto." }, "vault": { - "message": "Cassaforte" + "message": "Cassaforte", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Accedi con password principale" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Dominio alias" }, - "importData": { - "message": "Importa dati", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Errore di importazione" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File salvato sul dispositivo. Gestisci dai download del dispositivo." }, + "importantNotice": { + "message": "Avviso importante" + }, + "setupTwoStepLogin": { + "message": "Imposta l'accesso in due passaggi" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden invierà un codice all'email del tuo account per verificare gli accessi da nuovi dispositivi." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Puoi impostare l'accesso in due passaggi per proteggere il tuo account, oppure scegliere una email alla quale hai accesso." + }, + "remindMeLater": { + "message": "Ricordamelo in seguito" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Confermi di poter accedere all'email $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "SÃŦ, ho accesso all'email" + }, + "turnOnTwoStepLogin": { + "message": "Attiva l'accesso in due passaggi" + }, + "changeAcctEmail": { + "message": "Cambia l'email dell'account" + }, + "passkeyLogin": { + "message": "Vuoi accedere con la passkey?" + }, + "savePasskeyQuestion": { + "message": "Vuoi salvare la passkey?" + }, + "saveNewPasskey": { + "message": "Salva come nuovo login" + }, + "savePasskeyNewLogin": { + "message": "Salva la passkey come nuovo elemento" + }, + "noMatchingLoginsForSite": { + "message": "Nessun login salvato per questa pagina" + }, + "overwritePasskey": { + "message": "Vuoi sovrascrivere la passkey?" + }, + "unableToSavePasskey": { + "message": "Impossibile salvare la passkey" + }, + "alreadyContainsPasskey": { + "message": "Questo elemento contiene già una passkey. Vuoi sovrascriverla?" + }, + "passkeyAlreadyExists": { + "message": "Esiste già una passkey per questa applicazione." + }, + "applicationDoesNotSupportDuplicates": { + "message": "Questa applicazione non supporta duplicati." + }, + "closeThisWindow": { + "message": "Chiudi questa finestra" + }, "allowScreenshots": { "message": "Permetti cattura dello schermo" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Questo login è a rischio e non contiene un sito web. Aggiungi un sito web e cambia la password per maggiore sicurezza." }, + "vulnerablePassword": { + "message": "Password vulnerabile." + }, + "changeNow": { + "message": "Cambiala subito" + }, "missingWebsite": { "message": "Sito Web mancante" }, @@ -3906,10 +4112,16 @@ "message": "Invia informazioni sensibili in modo sicuro", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "Nessun risultato" + }, "sendsBodyNoItems": { "message": "Condividi facilmente file e dati con chiunque, su qualsiasi piattaforma. Le tue informazioni saranno crittografate end-to-end per la massima sicurezza.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Elimina i filtri di ricerca o prova con altri termini" + }, "generatorNudgeTitle": { "message": "Crea rapidamente password sicure" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Premi i tasti da impostare per la scorciatoia" }, - "editAutotypeShortcutDescription": { - "message": "Includi uno o due dei seguenti modificatori: Ctrl, Alt, Win, o Shift, piÚ una lettera." + "editAutotypeKeyboardModifiersDescription": { + "message": "Includi uno o due tasti modificatori a scelta tra Ctrl, Alt e Win." }, "invalidShortcut": { "message": "Scorciatoia non valida" @@ -4165,7 +4377,10 @@ "description": "Verb" }, "unArchive": { - "message": "Rimuovi dall'Archivio" + "message": "Togli dall'archivio" + }, + "archived": { + "message": "Archiviato" }, "itemsInArchive": { "message": "Elementi archiviati" @@ -4174,19 +4389,34 @@ "message": "Nessun elemento nell'archivio" }, "noItemsInArchiveDesc": { - "message": "Gli elementi archiviati appariranno qui e saranno esclusi dai risultati di ricerca e dall'auto-riempimento." + "message": "Gli elementi archiviati appariranno qui e saranno esclusi dai risultati di ricerca e dal riempimento automatico." }, "itemWasSentToArchive": { "message": "Elemento archiviato" }, "itemWasUnarchived": { - "message": "Elemento rimosso dall'archivio" + "message": "Elemento estratto dall'archivio" }, "archiveItem": { "message": "Archivia elemento" }, - "archiveItemConfirmDesc": { - "message": "Gli elementi archiviati sono esclusi dai risultati di ricerca e dall'auto-riempimento. Vuoi davvero archiviare questo elemento?" + "archiveItemDialogContent": { + "message": "Una volta archiviato, questo elemento sarà escluso dai risultati di ricerca e dai suggerimenti del completamento automatico." + }, + "unArchiveAndSave": { + "message": "Togli dall'archivio e salva" + }, + "restartPremium": { + "message": "Riavvia Premium" + }, + "premiumSubscriptionEnded": { + "message": "Il tuo abbonamento Premium è terminato" + }, + "premiumSubscriptionEndedDesc": { + "message": "Per recuperare l'accesso al tuo archivio, riavvia il tuo abbonamento Premium. Se modifichi i dettagli di un elemento archiviato prima del riavvio, sarà spostato nella tua cassaforte." + }, + "itemRestored": { + "message": "L'elemento è stato ripristinato" }, "zipPostalCodeLabel": { "message": "CAP / codice postale" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "E molto altro!" }, - "planDescPremium": { - "message": "Sicurezza online completa" + "advancedOnlineSecurity": { + "message": "Sicurezza online avanzata" }, "upgradeToPremium": { "message": "Aggiorna a Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "La tua organizzazione non utilizza piÚ le password principali per accedere a Bitwarden. Per continuare, verifica l'organizzazione e il dominio." + }, + "continueWithLogIn": { + "message": "Accedi e continua" + }, + "doNotContinue": { + "message": "Non continuare" + }, + "domain": { + "message": "Dominio" + }, + "keyConnectorDomainTooltip": { + "message": "Questo dominio memorizzerà le chiavi di crittografia del tuo account, quindi assicurati di impostarlo come affidabile. Se non hai la certezza che lo sia, verifica con l'amministratore." + }, + "verifyYourOrganization": { + "message": "Verifica la tua organizzazione per accedere" + }, + "organizationVerified": { + "message": "Organizzazione verificata" + }, + "domainVerified": { + "message": "Dominio verificato" + }, + "leaveOrganizationContent": { + "message": "Se non verifichi l'organizzazione, il tuo accesso sarà revocato." + }, + "leaveNow": { + "message": "Abbandona" + }, + "verifyYourDomainToLogin": { + "message": "Verifica il tuo dominio per accedere" + }, + "verifyYourDomainDescription": { + "message": "Per continuare con l'accesso, verifica questo dominio." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Per continuare con l'accesso, verifica l'organizzazione e il dominio." + }, "sessionTimeoutSettingsAction": { "message": "Azione al timeout" }, "sessionTimeoutHeader": { "message": "Timeout della sessione" + }, + "resizeSideNavigation": { + "message": "Ridimensiona la navigazione laterale" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Questa impostazione è gestita dalla tua organizzazione." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "La tua organizzazione ha impostato $HOURS$ ora/e e $MINUTES$ minuto/i come durata massima della sessione.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "In base alle impostazioni della tua organizzazione, la sessione terminerà al blocco del dispositivo." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "In base alle impostazioni della tua organizzazione, la sessione terminerà al riavvio dell'applicazione." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "La durata della sessione non puÃ˛ superare $HOURS$ ora/e e $MINUTES$ minuto/i", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Al riavvio" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Imposta un metodo di sblocco per modificare l'azione al timeout" + }, + "upgrade": { + "message": "Aggiorna" + }, + "leaveConfirmationDialogTitle": { + "message": "Vuoi davvero abbandonare?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Se rifiuti, tutti gli elementi esistenti resteranno nel tuo account, ma perderai l'accesso agli oggetti condivisi e alle funzioni organizzative." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contatta il tuo amministratore per recuperare l'accesso." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Abbandona $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Come si gestisce la cassaforte?" + }, + "transferItemsToOrganizationTitle": { + "message": "Trasferisci gli elementi in $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ richiede che tutti gli elementi siano di proprietà dell'organizzazione per motivi di conformità e sicurezza. Clicca su 'Accetta' per trasferire la proprietà.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accetta il trasferimento" + }, + "declineAndLeave": { + "message": "Rifiuta e abbandona" + }, + "whyAmISeeingThis": { + "message": "PerchÊ vedo questo avviso?" } } diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index ca50828b12c..b97a6614325 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "新しい URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "ä爿œŸã›ãŦエナãƒŧがį™ēį”Ÿã—ãžã—ãŸã€‚" }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "ã‚ĸã‚¤ãƒ†ãƒ æƒ…å ą" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "čŠŗį´°æƒ…å ą" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "ã‚ĩãƒŧãƒ“ã‚šãŒåˆŠį”¨ã§ããžã›ã‚“" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "フりロãƒŧ" }, - "syncVault": { - "message": "äŋįŽĄåēĢぎ同期" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "マ゚ã‚ŋãƒŧパ゚ワãƒŧドぎ変更" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1GB ãŽæš—åˇåŒ–ã•ã‚ŒãŸãƒ•ã‚Ąã‚¤ãƒĢ゚トãƒŦãƒŧジ。" }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "YubiKey、Duo ãĒおぎプロプナイエã‚ŋãƒĒãĒ2æŽĩ階čĒč¨ŧã‚Ēãƒ—ã‚ˇãƒ§ãƒŗã€‚" }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "エク゚ポãƒŧト元" }, - "exportVault": { - "message": "äŋįŽĄåēĢぎエク゚ポãƒŧト" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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": "ãƒ•ã‚Ąã‚¤ãƒĢåŊĸåŧ" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "厌全ãĢ削除されたã‚ĸイテム" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "ãƒĒ゚トã‚ĸされたã‚ĸイテム" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "すずãĻぎ Send", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "こぎ Send を削除しãĻもよろしいですか?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Send ãƒĒãƒŗã‚¯ã‚’ã‚¯ãƒĒップボãƒŧドãĢã‚ŗãƒ”ãƒŧ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "マ゚ã‚ŋãƒŧパ゚ワãƒŧドを削除しぞした。" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "æœŦåŊ“ãĢ「ãĒし」ã‚Ēãƒ—ã‚ˇãƒ§ãƒŗã‚’äŊŋį”¨ã—ãžã™ã‹īŧŸãƒ­ãƒƒã‚¯ã‚Ēãƒ—ã‚ˇãƒ§ãƒŗã‚’ã€ŒãĒし」ãĢč¨­åŽšã™ã‚‹ã¨ã€äŋįŽĄåēĢãŽæš—åˇåŒ–ã‚­ãƒŧがデバイ゚ãĢäŋå­˜ã•れぞす。 こぎã‚Ēãƒ—ã‚ˇãƒ§ãƒŗã‚’äŊŋį”¨ã™ã‚‹å ´åˆã¯ã€ãƒ‡ãƒã‚¤ã‚šã‚’éŠåˆ‡ãĢäŋč­ˇã™ã‚‹åŋ…čĻãŒã‚ã‚Šãžã™ã€‚" }, "vault": { - "message": "äŋįŽĄåēĢ" + "message": "äŋįŽĄåēĢ", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "マ゚ã‚ŋãƒŧパ゚ワãƒŧãƒ‰ã§ãƒ­ã‚°ã‚¤ãƒŗ" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "エイãƒĒã‚ĸã‚šãƒ‰ãƒĄã‚¤ãƒŗ" }, - "importData": { - "message": "デãƒŧã‚ŋãŽã‚¤ãƒŗãƒãƒŧト", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "ã‚¤ãƒŗãƒãƒŧト エナãƒŧ" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "ãƒ•ã‚Ąã‚¤ãƒĢをデバイ゚ãĢäŋå­˜ã—ぞした。デバイ゚ぎダã‚Ļãƒŗãƒ­ãƒŧãƒ‰ã§įŽĄį†ã§ããžã™ã€‚" }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "゚クãƒĒãƒŧãƒŗã‚ˇãƒ§ãƒƒãƒˆã‚’č¨ąå¯" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index 9337286d3fd..4923df5e18b 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "ახალი URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Item information" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "გაიგეთ მეáƒĸი" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Feature unavailable" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow us" }, - "syncVault": { - "message": "Sync vault" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Change master password" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "ქაáƒĒავი" + "message": "ქაáƒĒავი", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "მონაáƒĒემების შემოáƒĸანა", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "შემოáƒĸანის შეáƒĒდომა" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index d607bb8d097..c4a52a24d6f 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "New URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Item information" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Feature unavailable" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow us" }, - "syncVault": { - "message": "Sync vault" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Change master password" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index d1375efee8c..60b38ad181a 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "ā˛šāŗŠā˛¸ ā˛¯āŗā˛†ā˛°āŗā˛" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "➅➍ā˛ŋā˛°āŗ€ā˛•āŗā˛ˇā˛ŋ➤ ā˛Ļ⺋➎ ➏➂➭ā˛ĩā˛ŋ➏ā˛ŋā˛Ļāŗ†." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "ā˛ā˛Ÿā˛‚ ā˛Žā˛žā˛šā˛ŋ➤ā˛ŋ" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "ā˛‡ā˛¨āŗā˛¨ā˛ˇāŗā˛Ÿāŗ ➤ā˛ŋ➺ā˛ŋ➝ā˛ŋ➰ā˛ŋ" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "ā˛ĩ⺈ā˛ļā˛ŋā˛ˇāŗā˛Ÿāŗā˛¯ ā˛˛ā˛­āŗā˛¯ā˛ĩā˛ŋā˛˛āŗā˛˛" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "ā˛¨ā˛Žāŗā˛Žā˛¨āŗā˛¨āŗ ➅➍⺁➏➰ā˛ŋ➏ā˛ŋ" }, - "syncVault": { - "message": "➏ā˛ŋā˛‚ā˛•āŗ ā˛ĩā˛žā˛˛āŗā˛Ÿāŗ" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "ā˛Žā˛žā˛¸āŗā˛Ÿā˛°āŗ ā˛Ēā˛žā˛¸āŗā˛ĩā˛°āŗā˛Ąāŗ ā˛Ŧā˛Ļā˛˛ā˛žā˛¯ā˛ŋ➏ā˛ŋ" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "ā˛Ģāŗˆā˛˛āŗ ā˛˛ā˛—ā˛¤āŗā˛¤āŗā˛—ā˛ŗā˛ŋā˛—ā˛žā˛—ā˛ŋ 1 ➜ā˛ŋā˛Ŧā˛ŋ ā˛Žā˛¨āŗâ€Œā˛•āŗā˛°ā˛ŋā˛Ēāŗā˛Ÿāŗ ā˛Žā˛žā˛Ąā˛ŋā˛Ļ ā˛¸ā˛‚ā˛—āŗā˛°ā˛š." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "➰ā˛Ģāŗā˛¤āŗ ā˛ĩā˛žā˛˛āŗā˛Ÿāŗ" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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": "ā˛•ā˛Ąā˛¤ā˛Ļ ā˛Žā˛žā˛Ļ➰ā˛ŋ" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "ā˛ļā˛žā˛ļāŗā˛ĩ➤ā˛ĩā˛žā˛—ā˛ŋ ➅➺ā˛ŋā˛¸ā˛˛ā˛žā˛Ļ ā˛ā˛Ÿā˛‚" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "ā˛ā˛Ÿā˛‚ ā˛…ā˛¨āŗā˛¨āŗ ā˛Žā˛°āŗā˛¸āŗā˛Ĩā˛žā˛Ēā˛ŋā˛¸ā˛˛ā˛žā˛—ā˛ŋā˛Ļāŗ†" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "ā˛Žā˛˛āŗā˛˛ā˛ž ā˛•ā˛ŗāŗā˛šā˛ŋā˛¸āŗā˛¤āŗā˛¤ā˛Ļāŗ†", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "➈ ā˛•ā˛ŗāŗā˛šā˛ŋ➏⺁ā˛ĩā˛ŋā˛•āŗ†ā˛¯ā˛¨āŗā˛¨āŗ ➅➺ā˛ŋ➏➞⺁ ➍⺀ā˛ĩ⺁ ā˛–ā˛šā˛ŋ➤ā˛ĩā˛žā˛—ā˛ŋ ā˛Ŧ➝➏⺁ā˛ĩā˛ŋā˛°ā˛ž?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "➍➕➞ā˛ŋ➏ā˛ŋ ā˛•āŗā˛˛ā˛ŋā˛Ēāŗâ€Œā˛Ŧāŗ‹ā˛°āŗā˛Ąāŗâ€Œā˛—āŗ† ➞ā˛ŋā˛‚ā˛•āŗ ā˛•ā˛ŗāŗā˛šā˛ŋ➏ā˛ŋ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index 2e40b8d7f23..9bbe324505b 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "냈 URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "ė˜ˆę¸°ėš˜ ëĒģ한 똤ëĨ˜ę°€ ë°œėƒí–ˆėŠĩ니다." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "항ëĒŠ ė •ëŗ´" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "더 ė•Œė•„ëŗ´ę¸°" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "기ëŠĨ ė‚ŦėšŠí•  눘 ė—†ėŒ" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "íŒ”ëĄœėš°í•˜ę¸°" }, - "syncVault": { - "message": "ëŗ´ę´€í•¨ 동기화" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "ë§ˆėŠ¤í„° 비밀번호 ëŗ€ę˛Ŋ" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1GBė˜ ė•”í˜¸í™”ëœ 파ėŧ ė €ėžĨė†Œ." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "ëŗ´ę´€í•¨ ë‚´ëŗ´ë‚´ę¸°" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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": "파ėŧ í˜•ė‹" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "똁ęĩŦ렁ėœŧ로 ė‚­ė œëœ 항ëĒŠ" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "ëŗĩė›ëœ 항ëĒŠ" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "ëĒ¨ë“  Send", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "ė •ë§ ė´ SendëĨŧ ė‚­ė œí•˜ė‹œę˛ ėŠĩ니까?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Send 링íŦëĨŧ 클ëĻŊëŗ´ë“œė— ëŗĩė‚Ŧ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "ë§ˆėŠ¤í„° 비밀번호가 ė œęą°ë˜ė—ˆėŠĩ니다." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "ė •ë§ \"ėž ęˇ¸ė§€ ė•ŠėŒ\" ė˜ĩė…˜ė„ ė‚ŦėšŠí•˜ė‹œę˛ ėŠĩ니까? ėž ę¸ˆ ė˜ĩė…˜ė„ \"ėž ęˇ¸ė§€ ė•ŠėŒ\"ėœŧ로 ė„¤ė •í•˜ëŠ´ ė‚ŦėšŠėž ëŗ´ę´€í•¨ė˜ ė•”í˜¸í™” 키ëĨŧ ė‚ŦėšŠėžė˜ 揰揰뗐 ëŗ´ę´€í•Šë‹ˆë‹¤. ė´ ė˜ĩė…˜ė„ ė‚ŦėšŠí•˜ę¸° 렄뗐 ė‚ŦėšŠėžė˜ 기기가 ėž˜ ëŗ´í˜¸ë˜ė–´ ėžˆëŠ” ėƒíƒœė¸ė§€ í™•ė¸í•˜ė‹­ė‹œė˜¤." }, "vault": { - "message": "ëŗ´ę´€í•¨" + "message": "ëŗ´ę´€í•¨", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "ë§ˆėŠ¤í„° 비밀번호로 ëĄœęˇ¸ė¸" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "ë°ė´í„° ę°€ė ¸ė˜¤ę¸°", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/lt/messages.json b/apps/desktop/src/locales/lt/messages.json index 16f328d6240..0b7f6613f83 100644 --- a/apps/desktop/src/locales/lt/messages.json +++ b/apps/desktop/src/locales/lt/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Naujas URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "ÄŽvyko netikėta klaida." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Elemento informacija" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "SuÅžinoti daugiau" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Funkcija nepasiekiama" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Sekite mus" }, - "syncVault": { - "message": "Sinchronizuoti saugyklą" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Keisti pagrindinį slaptaÅžodį" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB uÅžÅĄifruotos vietos diske failÅŗ prisegimams." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Patentuotos dviejÅŗ ÅžingsniÅŗ prisijungimo parinktys, tokios kaip YubiKey ir Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Eksportuoti saugyklą" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Elementas iÅĄtrintas visam laikui" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Elementas atkurtas" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Visi Sendai", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Ar tikrai norite iÅĄtrinti ÅĄÄ¯ Sendą?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Kopijuoti Sendą į iÅĄkarpinę", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Pagrindinis slaptaÅžodis paÅĄalintas" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Ar jÅĢs tikrai norite naudoti \"Niekada\" pasirinkimą? Nustačius savo uÅžraktą į \"Niekada\", saugyklos ÅĄifravimo raktas bus laikomas jÅĢsÅŗ įrenginyje. Jei norite naudotis ÅĄiuo pasirinkimu, uÅžtikrinkite savo įrenginio saugą." }, "vault": { - "message": "Saugykla" + "message": "Saugykla", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Prisijungti su pagrindiniu slaptaÅžodÅžiu" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domenas" }, - "importData": { - "message": "Importuoti duomenis", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Importavimo klaida" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index 7800a4e9024..8775b608c61 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "Send ÅĄajā datumā tiks neatgriezeniski izdzēsts.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "Datne, ko kopÄĢgot" + }, + "hideTextByDefault": { + "message": "Pēc noklusējuma paslēpt tekstu" + }, + "hideYourEmail": { + "message": "Paslēpt e-pasta adresi no apskatÄĢtājiem." + }, + "limitSendViews": { + "message": "IerobeÅžot skatÄĢjumus" + }, + "limitSendViewsCount": { + "message": "AtlikuÅĄi $ACCESSCOUNT$ skatÄĢjumi", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "Neviens nevar apskatÄĢt ÅĄo Send pēc tam, kad ir sasniegts ierobeÅžojums.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Personiska piezÄĢme" + }, + "sendDetails": { + "message": "Informācija par Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Pēc izvēles var pievienot paroli, lai saņēmēji varētu piekÄŧÅĢt ÅĄim Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "KopÄĢgojamais teksts" + }, + "newItemHeaderTextSend": { + "message": "Jauns teksta Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "Jauns datnes Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Labot teksta Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Labot datnes Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Vai tieÅĄÄm neatgriezeniski izdzēst ÅĄo Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "Jauns", + "description": "for adding new items" + }, "newUri": { "message": "Jauns URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Pievienot pielikumu" }, + "itemsTransferred": { + "message": "Vienumi pārcelti" + }, + "fixEncryption": { + "message": "Salabot ÅĄifrÄ“ÅĄanu" + }, + "fixEncryptionTooltip": { + "message": "Å ÄĢ datne izmanto novecojuÅĄu ÅĄifrÄ“ÅĄanas veidu." + }, + "attachmentUpdated": { + "message": "Pielikums atjaunināts" + }, "maxFileSizeSansPunctuation": { "message": "Lielākais pieÄŧaujamais datnes izmērs ir 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "AtgadÄĢjās neparedzēta kÄŧÅĢda." }, + "unexpectedErrorShort": { + "message": "Neparedzēta kÄŧÅĢda" + }, + "closeThisBitwardenWindow": { + "message": "Å is Bitwarden logs jāaizver un jāmēĪina vēlreiz." + }, "itemInformation": { "message": "Vienuma informācija" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Uzzināt vairāk" }, + "migrationsFailed": { + "message": "AtgadÄĢjās kÄŧÅĢda ÅĄifrÄ“ÅĄanas iestatÄĢjumu atjauninÄÅĄanā." + }, + "updateEncryptionSettingsTitle": { + "message": "Atjaunini savus ÅĄifrÄ“ÅĄanas iestatÄĢjumus" + }, + "updateEncryptionSettingsDesc": { + "message": "Jaunie ieteicamie ÅĄifrÄ“ÅĄanas iestatÄĢjumi uzlabos Tava konta droÅĄÄĢbu. Jāievada sava galvenā parole, lai atjauninātu tagad." + }, + "confirmIdentityToContinue": { + "message": "Jāapliecina sava identitāte, lai turpinātu" + }, + "enterYourMasterPassword": { + "message": "Jāievada sava galvenā parole" + }, + "updateSettings": { + "message": "Atjaunināt IestatÄĢjumus" + }, "featureUnavailable": { "message": "Iespēja nav pieejama" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Sekot mums" }, - "syncVault": { - "message": "Sinhronizēt glabātavu" + "syncNow": { + "message": "Sinhronizēt tÅĢlÄĢt" }, "changeMasterPass": { "message": "MainÄĢt galveno paroli" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB ÅĄifrētas krātuves datņu pielikumiem." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ ÅĄifrētas krātuves datņu pielikumiem.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Tādas slēgtā pirmavota divpakāpju pieteikÅĄanās iespējas kā YubiKey un Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "IzgÅĢt no" }, - "exportVault": { - "message": "IzgÅĢt glabātavas saturu" + "exportNoun": { + "message": "IzgÅĢÅĄana", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "IzgÅĢt", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Vienums neatgriezeniski izdzēsts" }, + "archivedItemRestored": { + "message": "ArhÄĢva vienums atjaunots" + }, "restoredItem": { "message": "Vienums atjaunots" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Saziņas informācija" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Visi Send", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Vai tieÅĄÄm izdzēst ÅĄo Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Ievietot Send saiti starpliktuvē", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Ievietot Send saiti starpliktuvē", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Galvenā parole tika noņemta." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Galvenā parole vairs nav nepiecieÅĄama turpmāk minētās apvienÄĢbas dalÄĢbniekiem. LÅĢgums saskaņot zemāk esoÅĄo domēnu ar savas apvienÄĢbas pārvaldÄĢtāju." - }, "organizationName": { "message": "ApvienÄĢbas nosaukums" }, @@ -2991,7 +3125,8 @@ "message": "Vai tieÅĄÄm izmantot uzstādÄĢjumu \"Nekad\"? Uzstādot aizslēgÅĄanas iespēju uz \"Nekad\", ÅĄifrÄ“ÅĄanas atslēga tiek glabāta ierÄĢcē. Ja ÅĄÄĢ iespēja tiek izmantota, jāpārliecinās, ka ierÄĢce tiek pienācÄĢgi aizsargāta." }, "vault": { - "message": "Glabātava" + "message": "Glabātava", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Pieteikties ar galveno paroli" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Aizstājdomēns" }, - "importData": { - "message": "Ievietot datus", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "IevietoÅĄanas kÄŧÅĢda" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Datne saglabāta ierÄĢcē. Tā ir atrodama ierÄĢces lejupielāŞu mapē." }, + "importantNotice": { + "message": "SvarÄĢgs paziņojums" + }, + "setupTwoStepLogin": { + "message": "IestatÄĢt divpakāpju pieteikÅĄanos" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden, sākot ar 2025. gada februāri, nosÅĢtÄĢs kodu uz konta e-pasta adresi, lai apliecinātu pieteikÅĄanos jaunās ierÄĢcēs." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Var iestatÄĢt divpakāpju pieteikÅĄanos kā citu veidu, kā aizsargāt savu kontu, vai iestatÄĢt savu e-pasta adresi uz tādu, kurai ir piekÄŧuve." + }, + "remindMeLater": { + "message": "Atgādināt man vēlāk" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Vai ir uzticama piekÄŧuve savai e-pasta adresei $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nē, nav" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Jā, varu uzticami piekÄŧÅĢt savam e-pastam" + }, + "turnOnTwoStepLogin": { + "message": "Ieslēgt divpakāpju pieteikÅĄanos" + }, + "changeAcctEmail": { + "message": "MainÄĢt konta e-pasta adresi" + }, + "passkeyLogin": { + "message": "Pieteikties ar piekÄŧuves atslēgu?" + }, + "savePasskeyQuestion": { + "message": "Saglabāt piekÄŧuves atslēgu?" + }, + "saveNewPasskey": { + "message": "Saglabāt kā jaunu pieteikÅĄanās vienumu" + }, + "savePasskeyNewLogin": { + "message": "Saglabāt piekÄŧuves atslēgu kā jaunu pieteikÅĄanās vienumu" + }, + "noMatchingLoginsForSite": { + "message": "Å ai vietnei nav atbilstoÅĄu pieteikÅĄanās vietnumu" + }, + "overwritePasskey": { + "message": "PārrakstÄĢt piekÄŧuves atslēgu?" + }, + "unableToSavePasskey": { + "message": "Nevar saglabāt piekÄŧuves atslēgu" + }, + "alreadyContainsPasskey": { + "message": "Å is vienums jau satur piekÄŧuves atslēgu. Vai tieÅĄÄm pārrakstÄĢt paÅĄreizējo piekÄŧuves atslēgu?" + }, + "passkeyAlreadyExists": { + "message": "Å ai lietotnei jau pastāv piekÄŧuves atslēga." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Aizvērt ÅĄo logu" + }, "allowScreenshots": { "message": "AtÄŧaut ekrāna tverÅĄanu" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Å is pieteikÅĄanās vienums ir pakÄŧauts riskam, un tam nav norādÄĢta tÄĢmekÄŧvietne. Lielākai droÅĄÄĢbai jāpievieno tÄĢmekÄŧvietne un jānomaina parole." }, + "vulnerablePassword": { + "message": "Ievainojama parole." + }, + "changeNow": { + "message": "MainÄĢt tagad" + }, "missingWebsite": { "message": "Nav norādÄĢta tÄĢmekÄŧvietne" }, @@ -3906,10 +4112,16 @@ "message": "DroÅĄÄ veidā nosÅĢti jÅĢtÄĢgu informāciju", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "Nekas netika atrasts" + }, "sendsBodyNoItems": { "message": "KopÄĢgo datnes un datus droÅĄÄ veidā ar ikvienu jebkurā platformā! Tava informācija paliks pilnÄĢbā ÅĄifrēta, vienlaikus ierobeÅžojot riskantumu.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "JānotÄĢra atsijātāji vai jāmēĪina cits meklÄ“ÅĄanas vaicājums" + }, "generatorNudgeTitle": { "message": "Ātra paroÄŧu izveidoÅĄana" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "IevadÄĢt ÄĢsinājumtaustiņus" }, - "editAutotypeShortcutDescription": { - "message": "JāiekÄŧauj viens vai divi no ÅĄiem taustiņiem - Ctrl, Alt, Win vai Shift - un burts." + "editAutotypeKeyboardModifiersDescription": { + "message": "JāiekÄŧauj viens vai divi no ÅĄiem taustiņiem: Ctrl, Alt, Win un burts." }, "invalidShortcut": { "message": "NederÄĢgi ÄĢsinājumtaustiņi" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Atcelt arhivÄ“ÅĄanu" }, + "archived": { + "message": "Arhivēts" + }, "itemsInArchive": { "message": "Vienumi arhÄĢvā" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Arhivēt vienumu" }, - "archiveItemConfirmDesc": { - "message": "Arhivētie vienumi netiek iekÄŧauti vispārējās meklÄ“ÅĄanas iznākumos un automātiskās aizpildes ieteikumos. Vai tieÅĄÄm ahrivēt ÅĄo vienumu?" + "archiveItemDialogContent": { + "message": "Pēc ievietoÅĄanas arhÄĢvā ÅĄis vienums netiks iekÄŧauts meklÄ“ÅĄanas iznākumā un automātiskās aizpildes ieteikumos." + }, + "unArchiveAndSave": { + "message": "Atcelt arhivÄ“ÅĄanu un saglabāt" + }, + "restartPremium": { + "message": "Atsākt Premium" + }, + "premiumSubscriptionEnded": { + "message": "Tavs Premium abonements beidzās" + }, + "premiumSubscriptionEndedDesc": { + "message": "Lai atgÅĢtu piekÄŧuvi savam arhÄĢvam, jāatsāk Premium abonements. Ja labosi arhivēta vienuma informāciju pirms atsākÅĄanas, tas tiks pārvietots atpakaÄŧ Tavā glabātavā." + }, + "itemRestored": { + "message": "Vienums tika atjaunots" }, "zipPostalCodeLabel": { "message": "ZIP / Pasta indekss" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "Un vēl!" }, - "planDescPremium": { - "message": "PilnÄĢga droÅĄÄĢba tieÅĄsaistē" + "advancedOnlineSecurity": { + "message": "Izvērsta tieÅĄsaistes droÅĄÄĢba" }, "upgradeToPremium": { "message": "Uzlabot uz Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "ApvienÄĢba vairs neizmanto galvenās paroles, lai pieteiktos Bitwarden. Lai turpinātu, jāapliecina apvienÄĢba un domēns." + }, + "continueWithLogIn": { + "message": "Turpināt ar pieteikÅĄanos" + }, + "doNotContinue": { + "message": "Neturpināt" + }, + "domain": { + "message": "Domēns" + }, + "keyConnectorDomainTooltip": { + "message": "Å ajā domēnā tiks glabātas konta ÅĄifrÄ“ÅĄanas atslēgas, tādēÄŧ jāpārliecinās par uzticamÄĢbu. Ja nav pārliecÄĢbas, jāsazinās ar savu pārvaldÄĢtāju." + }, + "verifyYourOrganization": { + "message": "Jāapliecina apvienÄĢba, lai pieteiktos" + }, + "organizationVerified": { + "message": "ApvienÄĢba apliecināta" + }, + "domainVerified": { + "message": "Domēns ir apliecināts" + }, + "leaveOrganizationContent": { + "message": "Ja neapliecināsi apvienÄĢbu, tiks atsaukta piekÄŧuve tai." + }, + "leaveNow": { + "message": "Pamest tagad" + }, + "verifyYourDomainToLogin": { + "message": "Jāapliecina domēns, lai pieteiktos" + }, + "verifyYourDomainDescription": { + "message": "Lai turpinātu pieteikÅĄanos, jāapliecina ÅĄis domēns." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Lai turpinātu pieteikÅĄanos, jāapliecina apvienÄĢba un domēns." + }, "sessionTimeoutSettingsAction": { "message": "Noildzes darbÄĢba" }, "sessionTimeoutHeader": { "message": "Sesijas noildze" + }, + "resizeSideNavigation": { + "message": "MainÄĢt sānu pārvietoÅĄanās joslas izmēru" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Å o iestatÄĢjumu pārvalda apvienÄĢba." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Uzlabot" + }, + "leaveConfirmationDialogTitle": { + "message": "Vai tieÅĄÄm pamest?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Pēc noraidÄĢÅĄanas personÄĢgie vienumi paliks Tavā kontā, bet Tu zaudēsi piekÄŧvuvi kopÄĢgotajiem vienumiem un apvienÄĢbu iespējām." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Jāsazinās ar savu pārvaldÄĢtāju, lai atgÅĢtu piekÄŧuvi." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Pamest $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Kā es varu pārvaldÄĢt savu glabātavu?" + }, + "transferItemsToOrganizationTitle": { + "message": "Pārcelt vienumus uz $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index 29e3cefee0c..76cf594b54a 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Novi link" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "DoÅĄlo je do neočekivane greÅĄke." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Informacija o stavki" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Saznaj viÅĄe" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Funkcija nije dostupna" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Prati nas" }, - "syncVault": { - "message": "Sinhronizacija trezora" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Promjena glavne lozinke" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB ÅĄifrovanog skladiÅĄta za priloge datoteka." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Izvezi trezor" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index 662ce9a1fc6..f8131201ff9 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "ā´Ēāĩā´¤ā´ŋā´¯ URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "ā´’ā´°āĩ ā´…ā´Ēāĩā´°ā´¤āĩ€ā´•āĩā´ˇā´ŋā´¤ ā´Ēā´ŋā´ļā´•āĩ ⴏⴂⴭā´ĩā´ŋⴚāĩā´šāĩ." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "ā´ĩā´ŋā´ĩā´°ā´‚" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "ā´•āĩ‚ā´Ÿāĩā´¤āĩŊ ā´…ā´ąā´ŋā´¯āĩā´•" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "ā´¸ā´ĩā´ŋā´ļāĩ‡ā´ˇā´¤ ⴞⴭāĩā´¯ā´Žā´˛āĩā´˛" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "ā´žā´™āĩā´™ā´ŗāĩ† ā´Ēā´ŋā´¨āĩā´¤āĩā´Ÿā´°āĩā´•" }, - "syncVault": { - "message": "ā´ĩā´žāĩžā´Ÿāĩ ā´¸ā´Žā´¨āĩā´ĩā´¯ā´ŋā´Ēāĩā´Ēā´ŋā´•āĩā´•āĩā´•" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "ā´Ēāĩā´°ā´žā´Ĩā´Žā´ŋā´• ā´Ēā´žā´¸āĩâ€Œā´ĩāĩ‡ā´Ąāĩ ā´Žā´žā´ąāĩā´ąāĩā´•" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "ā´Ģā´¯āĩŊ ā´…ā´ąāĩā´ąā´žā´šāĩā´šāĩā´Žāĩ†ā´¨āĩā´ąāĩā´•āĩžā´•āĩā´•ā´žā´¯ā´ŋ 1 GB ā´Žāĩģā´•āĩā´°ā´ŋā´Ēāĩā´ąāĩā´ąāĩā´šāĩ†ā´¯āĩâ€Œā´¤ ā´¸āĩā´ąāĩā´ąāĩ‹ā´ąāĩ‡ā´œāĩ." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "ā´ĩā´žāĩžā´Ÿāĩ ā´Žā´•āĩā´¸āĩā´Ēāĩ‹āĩŧⴟāĩ" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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": "ā´Ģā´¯āĩŊ ā´Ģāĩ‹āĩŧā´Žā´žā´ąāĩā´ąāĩ" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "ā´Žā´¨āĩā´¨āĩ†ā´¨āĩā´¨āĩ‡ā´•āĩā´•āĩā´Žā´žā´¯ā´ŋ ā´¨āĩ€ā´•āĩā´•ā´‚ ⴚāĩ†ā´¯āĩā´¤ ⴇⴍⴂ" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "ā´ĩāĩ€ā´Ŗāĩā´Ÿāĩ†ā´Ÿāĩā´¤āĩā´¤ ⴇⴍⴙāĩā´™āĩž" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "ā´Žā´˛āĩā´˛ā´žā´‚ Send-ā´•āĩž", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index d607bb8d097..c4a52a24d6f 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "New URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Item information" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Feature unavailable" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow us" }, - "syncVault": { - "message": "Sync vault" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Change master password" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index bcbd26cede3..14d7ff6d701 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "New URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Item information" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Feature unavailable" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow us" }, - "syncVault": { - "message": "Sync vault" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Change master password" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index 42fb6d479c0..884aa9ac345 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Ny URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "En uventet feil har oppstÃĨtt." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Objektsinformasjon" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "LÃĻr mer" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Funksjonen er utilgjengelig" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Følg oss" }, - "syncVault": { - "message": "Synkroniser hvelvet" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Endre hovedpassordet" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB med kryptert fillagring." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Eksporter fra" }, - "exportVault": { - "message": "Eksporter hvelvet" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Slett objektet permanent" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Gjenopprettet objekt" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Kontaktinformasjon" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Alle Send-er", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Er du sikker pÃĨ at du vil slette denne Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Kopier Send-lenke til utklippstavlen", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Hovedpassordet er fjernet." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organisasjonens navn" }, @@ -2991,7 +3125,8 @@ "message": "Er du sikker pÃĨ at du vil bruke alternativet ÂĢAldriÂģ? Ved ÃĨ angi lÃĨsemulighetene til ÂĢAldriÂģ lagres hvelvets krypteringsnøkkel pÃĨ enheten. Hvis du bruker dette alternativet, bør du sørge for at du holder enheten forsvarlig beskyttet." }, "vault": { - "message": "Hvelv" + "message": "Hvelv", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Logg inn med hovedpassord" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias-domene" }, - "importData": { - "message": "Importer data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Importeringsfeil" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Tillat skjermklipp/-opptak" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index cce8f6a2ba5..6440aa9db29 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "ā¤¨ā¤¯ā¤žā¤ URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Item information" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Feature unavailable" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow us" }, - "syncVault": { - "message": "Sync vault" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Change master password" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index 82b51b018c5..56ef39ec97a 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "Op deze datum wordt de Send definitief verwijderd.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "Bestand om te delen" + }, + "hideTextByDefault": { + "message": "Tekst standaard verbergen" + }, + "hideYourEmail": { + "message": "Je e-mailadres voor ontvangers verbergen." + }, + "limitSendViews": { + "message": "Weergaven limiteren" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ weergaven over", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "Niemand kan deze Send weergeven als de limiet is bereikt.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "PrivÊnotitie" + }, + "sendDetails": { + "message": "Send-details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Voeg een optioneel wachtwoord toe voor ontvangers om toegang te krijgen tot deze Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Te delen tekst" + }, + "newItemHeaderTextSend": { + "message": "Nieuwe Send (tekst)", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "Nieuwe Send (bestand)", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Send (tekst) bewerken", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Send (bestand) bewerken", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Weet je zeker dat je deze Send permanent wil verwijderen?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "Nieuw", + "description": "for adding new items" + }, "newUri": { "message": "Nieuwe URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Bijlage toevoegen" }, + "itemsTransferred": { + "message": "Items overgedragen" + }, + "fixEncryption": { + "message": "Versleuteling repareren" + }, + "fixEncryptionTooltip": { + "message": "Dit bestand gebruikt een verouderde versleutelingsmethode." + }, + "attachmentUpdated": { + "message": "Bijlagen bijgewerkt" + }, "maxFileSizeSansPunctuation": { "message": "Maximale bestandsgrootte is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Er is een onverwachte fout opgetreden." }, + "unexpectedErrorShort": { + "message": "Onverwachte fout" + }, + "closeThisBitwardenWindow": { + "message": "Sluit dit Bitwarden-venster en probeer het opnieuw." + }, "itemInformation": { "message": "Item-informatie" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Meer informatie" }, + "migrationsFailed": { + "message": "Er is een fout opgetreden bij het bijwerken van de versleutelingsinstellingen." + }, + "updateEncryptionSettingsTitle": { + "message": "Je versleutelingsinstellingen bijwerken" + }, + "updateEncryptionSettingsDesc": { + "message": "De nieuwe aanbevolen versleutelingsinstellingen verbeteren de beveiliging van je account. Voer je hoofdwachtwoord in om nu bij te werken." + }, + "confirmIdentityToContinue": { + "message": "Bevestig je identiteit om door te gaan" + }, + "enterYourMasterPassword": { + "message": "Voer je hoofdwachtwoord in" + }, + "updateSettings": { + "message": "Instellingen bijwerken" + }, "featureUnavailable": { "message": "Functionaliteit niet beschikbaar" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Volg ons" }, - "syncVault": { - "message": "Kluis synchroniseren" + "syncNow": { + "message": "Nu synchroniseren" }, "changeMasterPass": { "message": "Hoofdwachtwoord wijzigen" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB versleutelde opslag voor bijlagen." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ versleutelde opslag voor bijlagen.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Eigen opties voor tweestapsaanmelding zoals YubiKey en Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Exporteren vanuit" }, - "exportVault": { - "message": "Kluis exporteren" + "exportNoun": { + "message": "Exporteren", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Exporteren", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Definitief verwijderd item" }, + "archivedItemRestored": { + "message": "Gearchiveerd item hersteld" + }, "restoredItem": { "message": "Hersteld item" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact informatie" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Alle Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Weet je zeker dat je deze Send wilt verwijderen?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Send-koppeling kopiÃĢren", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Send-link naar klembord kopiÃĢren", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Hoofdwachtwoord verwijderd." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Voor leden van de volgende organisatie is een hoofdwachtwoord niet langer nodig. Bevestig het domein hieronder met de beheerder van je organisatie." - }, "organizationName": { "message": "Organisatienaam" }, @@ -2991,7 +3125,8 @@ "message": "Weet je zeker dat je de optie \"Nooit\" wilt gebruiken? De vergrendelingsoptie \"Nooit\" bewaart de sleutel van je kluis op je apparaat. Als je deze optie gebruikt, moet je ervoor zorgen dat je je apparaat naar behoren beschermt." }, "vault": { - "message": "Kluis" + "message": "Kluis", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Inloggen met je hoofdwachtwoord" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Aliasdomein" }, - "importData": { - "message": "Data importeren", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Fout bij importeren" }, @@ -3631,7 +3762,7 @@ "message": "Import directly from browser" }, "browserProfile": { - "message": "Browser Profile" + "message": "Browserprofiel" }, "seeDetailedInstructions": { "message": "Zie gedetailleerde instructies op onze helpsite op", @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Bestand op apparaat opgeslagen. Beheer vanaf de downloads op je apparaat." }, + "importantNotice": { + "message": "Belangrijke melding" + }, + "setupTwoStepLogin": { + "message": "Tweestapsaanmelding instellen" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Vanaf februari 2025 stuurt Bitwarden een code naar het e-mailadres van je account om inloggen op nieuwe apparaten te verifiÃĢren." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Je kunt tweestapsaanmelding instellen als een alternatieve manier om je account te beschermen of je e-mailadres te veranderen naar een waar je toegang toe hebt." + }, + "remindMeLater": { + "message": "Herinner me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Heb je betrouwbare toegang tot je e-mail, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nee, dat heb ik niet" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ja, ik heb betrouwbare toegang tot mijn e-mail" + }, + "turnOnTwoStepLogin": { + "message": "Tweestapsaanmelding inschakelen" + }, + "changeAcctEmail": { + "message": "E-mailadres van het account veranderen" + }, + "passkeyLogin": { + "message": "Inloggen met passkey?" + }, + "savePasskeyQuestion": { + "message": "Passkey opslaan?" + }, + "saveNewPasskey": { + "message": "Opslaan als nieuwe login" + }, + "savePasskeyNewLogin": { + "message": "Passkey als nieuwe login opslaan" + }, + "noMatchingLoginsForSite": { + "message": "Geen overeenkomende logins voor deze site" + }, + "overwritePasskey": { + "message": "Passkey overschrijven?" + }, + "unableToSavePasskey": { + "message": "Kon passkey niet opslaan" + }, + "alreadyContainsPasskey": { + "message": "Dit item heeft al een passkey. Weet je zeker dat je de huidige passkey wilt overschrijven?" + }, + "passkeyAlreadyExists": { + "message": "Er bestaat al een passkey voor deze applicatie." + }, + "applicationDoesNotSupportDuplicates": { + "message": "Deze applicatie ondersteunt geen duplicaten." + }, + "closeThisWindow": { + "message": "Sluit dit venster" + }, "allowScreenshots": { "message": "Schermopname toestaan" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Deze login is in gevaar en mist een website. Voeg een website toe en verander het wachtwoord voor een sterkere beveiliging." }, + "vulnerablePassword": { + "message": "Kwetsbaar wachtwoord." + }, + "changeNow": { + "message": "Nu wijzigen" + }, "missingWebsite": { "message": "Ontbrekende website" }, @@ -3906,10 +4112,16 @@ "message": "Gevoelige informatie veilig versturen", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "Geen resultaten teruggekregen" + }, "sendsBodyNoItems": { "message": "Deel bestanden en gegevens veilig met iedereen, op elk platform. Je informatie blijft end-to-end versleuteld terwijl en blootstelling beperkt.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Wis filters of probeer een andere zoekterm" + }, "generatorNudgeTitle": { "message": "Snel wachtwoorden maken" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Typ de sneltoets" }, - "editAutotypeShortcutDescription": { - "message": "Voeg een of twee van de volgende toetsen toe: Ctrl, Alt, Win of Shift, en een letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Voeg een of twee van de volgende toetsen toe: Ctrl, Alt, Win en een letter." }, "invalidShortcut": { "message": "Ongeldige sneltoets" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Dearchiveren" }, + "archived": { + "message": "Gearchiveerd" + }, "itemsInArchive": { "message": "Items in archief" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Item archiveren" }, - "archiveItemConfirmDesc": { - "message": "Gearchiveerde items worden uitgesloten van algemene zoekresultaten en automatische invulsuggesties. Weet je zeker dat je dit item wilt archiveren?" + "archiveItemDialogContent": { + "message": "Eenmaal gearchiveerd wordt dit item uitgesloten van zoekresultaten en suggesties voor automatisch invullen." + }, + "unArchiveAndSave": { + "message": "Dearchiveren en opslaan" + }, + "restartPremium": { + "message": "Premium herstarten" + }, + "premiumSubscriptionEnded": { + "message": "Je Premium-abonnement is afgelopen" + }, + "premiumSubscriptionEndedDesc": { + "message": "Herstart je Premium-abonnement om toegang tot je archief te krijgen. Als je de details wijzigt voor een gearchiveerd item voor het opnieuw opstarten, zal het terug naar je kluis worden verplaatst." + }, + "itemRestored": { + "message": "Item is hersteld" }, "zipPostalCodeLabel": { "message": "Postcode" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "En meer!" }, - "planDescPremium": { - "message": "Online beveiliging voltooien" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Opwaarderen naar Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Je organisatie maakt niet langer gebruik van hoofdwachtwoorden om in te loggen op Bitwarden. Controleer de organisatie en het domein om door te gaan." + }, + "continueWithLogIn": { + "message": "Doorgaan met inloggen" + }, + "doNotContinue": { + "message": "Niet verder gaan" + }, + "domain": { + "message": "Domein" + }, + "keyConnectorDomainTooltip": { + "message": "Dit domein zal de encryptiesleutels van je account opslaan, dus zorg ervoor dat je het vertrouwt. Als je het niet zeker weet, controleer dan bij je beheerder." + }, + "verifyYourOrganization": { + "message": "Verifieer je organisatie om in te loggen" + }, + "organizationVerified": { + "message": "Organisatie geverifieerd" + }, + "domainVerified": { + "message": "Domein geverifieerd" + }, + "leaveOrganizationContent": { + "message": "Als je je organisatie niet verifieert, wordt je toegang tot de organisatie ingetrokken." + }, + "leaveNow": { + "message": "Nu verlaten" + }, + "verifyYourDomainToLogin": { + "message": "Verifieer je domein om in te loggen" + }, + "verifyYourDomainDescription": { + "message": "Bevestig dit domein om verder te gaan met inloggen." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Bevestig organisatie en domein om verder te gaan met inloggen." + }, "sessionTimeoutSettingsAction": { "message": "Time-out actie" }, "sessionTimeoutHeader": { "message": "Sessietime-out" + }, + "resizeSideNavigation": { + "message": "Formaat zijnavigatie wijzigen" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Deze instelling wordt beheerd door je organisatie." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Je organisatie heeft de maximale sessietime-out ingesteld op $HOURS$ uur en $MINUTES$ minuten.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Je organisatie heeft de standaard sessietime-out ingesteld op Systeem vergrendelen." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Je organisatie heeft de standaard sessietime-out ingesteld op Herstarten applicatie." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximale time-out kan niet langer zijn dan $HOURS$ uur en $MINUTES$ minu(u)t(en)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Herstarten" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Stel een ontgrendelingsmethode in om je kluis time-out actie te wijzigen" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Weet je zeker dat je wilt verlaten?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Door te weigeren, blijven je persoonlijke items in je account, maar verlies je toegang tot gedeelde items en organisatiefunctionaliteit." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Neem contact op met je beheerder om weer toegang te krijgen." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "$ORGANIZATION$ verlaten", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Hoe beheer ik mijn kluis?" + }, + "transferItemsToOrganizationTitle": { + "message": "Items overdragen aan $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ vereist dat alle items eigendom zijn van de organisatie voor veiligheid en naleving. Klik op accepteren voor het overdragen van eigendom van je items.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Overdacht accepteren" + }, + "declineAndLeave": { + "message": "Weigeren en verlaten" + }, + "whyAmISeeingThis": { + "message": "Waarom zie ik dit?" } } diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index 08567979e8b..d7e50fc314d 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Ny URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Det har oppstÃĨtt ein feil." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Elementinformasjon" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "LÃĻr meir" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Funksjon er utilgjengeleg" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Følg oss" }, - "syncVault": { - "message": "Synkroniser kvelven" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Endre hovudpassord" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Alle Send-ar", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Er du sikker pÃĨ at du vil fjerne denne Send-en?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Kopier Send-lenke til utklippstavle", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index 4ca05acaac5..1751612508c 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "New URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Item information" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Feature unavailable" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow us" }, - "syncVault": { - "message": "Sync vault" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Change master password" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index c05e7f05cb1..6bd454f5aad 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "W tym dniu wysyłka zostanie trwale usunięta.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "Plik wysyłki" + }, + "hideTextByDefault": { + "message": "Ukryj domyślnie tekst wysyłki" + }, + "hideYourEmail": { + "message": "Ukryj mÃŗj adres e-mail przed odbiorcami." + }, + "limitSendViews": { + "message": "Maksymalna liczba wyświetleń" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "Po osiągnięciu limitu wysyłka będzie niedostępna.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Prywatna notatka" + }, + "sendDetails": { + "message": "SzczegÃŗÅ‚y wysyłki", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Zabezpiecz wysyłkę opcjonalnym hasłem.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Tekst wysyłki" + }, + "newItemHeaderTextSend": { + "message": "Nowa wysyłka tekstu", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "Nowa wysyłka pliku", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edytuj tekst wysyłki", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edytuj plik wysyłki", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Czy na pewno chcesz usunąć trwale wysyłkę?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "Nowy element", + "description": "for adding new items" + }, "newUri": { "message": "Nowy URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Dodaj załącznik" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maksymalny rozmiar pliku to 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Wystąpił nieoczekiwany błąd." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Informacje o elemencie" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Dowiedz się więcej" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Funkcja jest niedostępna" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Obserwuj nas" }, - "syncVault": { - "message": "Synchronizuj sejf" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Zmień hasło gÅ‚Ãŗwne" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB miejsca na zaszyfrowane załączniki." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Specjalne opcje logowania dwustopniowego, takie jak YubiKey i Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Eksportuj z" }, - "exportVault": { - "message": "Eksportuj sejf" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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 pliku" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Element został trwale usunięty" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Element został przywrÃŗcony" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Informacje kontaktowe" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Wszystkie wysyłki", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Czy na pewno chcesz usunąć wysyłkę?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Kopiuj link wysyłki", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Kopiuj link wysyłki do schowka", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Hasło gÅ‚Ãŗwne zostało usunięte" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Hasło gÅ‚Ãŗwne nie jest juÅŧ wymagane dla członkÃŗw następującej organizacji. PotwierdÅē poniÅŧszą domenę z administratorem organizacji." - }, "organizationName": { "message": "Nazwa organizacji" }, @@ -2991,7 +3125,8 @@ "message": "Czy na pewno chcesz uÅŧyć opcji „Nigdy”? Ustawienie blokady na „Nigdy” spowoduje przechowywanie klucza szyfrowania sejfu na urządzeniu. Upewnij się, Åŧe urządzenie jest odpowiednio chronione." }, "vault": { - "message": "Sejf" + "message": "Sejf", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Logowanie hasłem gÅ‚Ãŗwnym" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Domena aliasu" }, - "importData": { - "message": "Importuj dane", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Błąd importowania" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Plik zapisany na urządzeniu. Zarządzaj plikiem na swoim urządzeniu." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Przypomnij pÃŗÅēniej" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Zmień adres e-mail konta" + }, + "passkeyLogin": { + "message": "Zalogować się kluczem dostępu?" + }, + "savePasskeyQuestion": { + "message": "Zapisać klucz dostępu?" + }, + "saveNewPasskey": { + "message": "Zapisz jako nowy dane logowania" + }, + "savePasskeyNewLogin": { + "message": "Zapisz klucz dostępu jako nowe dane logowania" + }, + "noMatchingLoginsForSite": { + "message": "Brak pasujących danych logowania dla tej strony" + }, + "overwritePasskey": { + "message": "Zastąpić klucz dostępu?" + }, + "unableToSavePasskey": { + "message": "Nie moÅŧna zapisać klucza dostępu" + }, + "alreadyContainsPasskey": { + "message": "Element zawiera juÅŧ klucz dostępu. Czy na pewno chcesz zastąpić obecny klucz dostępu?" + }, + "passkeyAlreadyExists": { + "message": "Klucz dostępu dla tej aplikacji juÅŧ istnieje." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Zezwalaj na wykonywanie zrzutÃŗw ekranu" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Dane logowania są zagroÅŧone i nie zawierają strony internetowej. Dodaj stronę internetową i zmień hasło." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Zmień teraz" + }, "missingWebsite": { "message": "Brak strony internetowej" }, @@ -3906,10 +4112,16 @@ "message": "Wysyłaj poufne informacje w bezpieczny sposÃŗb", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "Brak pasujących elementÃŗw" + }, "sendsBodyNoItems": { "message": "Udostępniaj pliki i teksty kaÅŧdemu na dowolnej platformie. Informacje będę szyfrowane end-to-end, zapewniając poufność.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Wyczyść filtry lub uÅŧyj innej frazy" + }, "generatorNudgeTitle": { "message": "Szybkie tworzenie haseł" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Rodzaj skrÃŗtu" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "SkrÃŗt jest nieprawidłowy" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Usuń z archiwum" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Elementy w archiwum" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archiwizuj element" }, - "archiveItemConfirmDesc": { - "message": "Zarchiwizowane elementy są wykluczone z wynikÃŗw wyszukiwania i sugestii autouzupełniania. Czy na pewno chcesz archiwizować element?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "Kod pocztowy" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Zmień rozmiar nawigacji bocznej" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index 288f8e42616..e48942b409a 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "O Send serÃĄ apagado permanentemente nesta data.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "Arquivo para compartilhar" + }, + "hideTextByDefault": { + "message": "Ocultar texto por padrÃŖo" + }, + "hideYourEmail": { + "message": "Ocultar seu endereço de e-mail dos visualizadores." + }, + "limitSendViews": { + "message": "Limitar visualizaçÃĩes" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ visualizaçÃĩes restantes", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "NinguÊm poderÃĄ visualizar este Send depois que o limite foi atingido.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "AnotaÃ§ÃŖo privada" + }, + "sendDetails": { + "message": "Detalhes do Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Adicione uma senha opcional para que os destinatÃĄrios acessem este Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Texto para compartilhar" + }, + "newItemHeaderTextSend": { + "message": "Novo Send de texto", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "Novo Send de arquivo", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Editar Send de texto", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Editar Send de arquivo", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Tem certeza de que quer apagar este Send permanentemente?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "Criar", + "description": "for adding new items" + }, "newUri": { "message": "Novo URI" }, @@ -287,7 +355,7 @@ "description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'" }, "contactCSToAvoidDataLossPart2": { - "message": "para evitar a perca adicional dos dados.", + "message": "para evitar a perca de dados adicionais.", "description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'" }, "january": { @@ -470,7 +538,7 @@ "message": "Use campos ocultos para dados sensíveis como senhas" }, "checkBoxHelpText": { - "message": "Use caixas de seleÃ§ÃŖo se gostaria de preencher automaticamente a caixa de seleÃ§ÃŖo de um formulÃĄrio, como um lembrar e-mail" + "message": "Use caixas de seleÃ§ÃŖo se gostaria de preencher a caixa de seleÃ§ÃŖo de um formulÃĄrio, como um lembrar e-mail" }, "linkedHelpText": { "message": "Use um campo vinculado quando vocÃĒ estiver experienciando problemas com o preenchimento automÃĄtico em um site específico." @@ -708,6 +776,18 @@ "addAttachment": { "message": "Adicionar anexo" }, + "itemsTransferred": { + "message": "Itens transferidos" + }, + "fixEncryption": { + "message": "Corrigir criptografia" + }, + "fixEncryptionTooltip": { + "message": "Este arquivo estÃĄ usando um mÊtodo de criptografia desatualizado." + }, + "attachmentUpdated": { + "message": "Anexo atualizado" + }, "maxFileSizeSansPunctuation": { "message": "O tamanho mÃĄximo do arquivo Ê de 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Ocorreu um erro inesperado." }, + "unexpectedErrorShort": { + "message": "Erro inesperado" + }, + "closeThisBitwardenWindow": { + "message": "Feche esta janela do Bitwarden e tente novamente." + }, "itemInformation": { "message": "InformaçÃĩes do item" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Saiba mais" }, + "migrationsFailed": { + "message": "Ocorreu um erro ao atualizar as configuraçÃĩes de criptografia." + }, + "updateEncryptionSettingsTitle": { + "message": "Atualize suas configuraçÃĩes de criptografia" + }, + "updateEncryptionSettingsDesc": { + "message": "As novas configuraçÃĩes de criptografia recomendadas melhorarÃŖo a segurança da sua conta. Digite sua senha principal para atualizar agora." + }, + "confirmIdentityToContinue": { + "message": "Confirme sua identidade para continuar" + }, + "enterYourMasterPassword": { + "message": "Digite sua senha principal" + }, + "updateSettings": { + "message": "Atualizar configuraçÃĩes" + }, "featureUnavailable": { "message": "Recurso indisponível" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Siga-nos" }, - "syncVault": { - "message": "Sincronizar cofre" + "syncNow": { + "message": "Sincronizar agora" }, "changeMasterPass": { "message": "Alterar senha principal" @@ -1247,19 +1351,19 @@ "message": "AutenticaÃ§ÃŖo em duas etapas" }, "vaultTimeoutHeader": { - "message": "Tempo limite do cofre" + "message": "Limite de tempo do cofre" }, "vaultTimeout": { - "message": "Tempo limite do cofre" + "message": "Limite de tempo do cofre" }, "vaultTimeout1": { - "message": "Tempo limite" + "message": "Limite de tempo" }, "vaultTimeoutAction1": { - "message": "AÃ§ÃŖo do tempo limite" + "message": "AÃ§ÃŖo do limite de tempo" }, "vaultTimeoutDesc": { - "message": "Escolha quando o seu cofre executarÃĄ a aÃ§ÃŖo do tempo limite do cofre." + "message": "Escolha quando o seu cofre executarÃĄ a aÃ§ÃŖo do limite de tempo do cofre." }, "immediately": { "message": "Imediatamente" @@ -1490,11 +1594,20 @@ "premiumSignUpStorage": { "message": "1 GB de armazenamento criptografado para anexos de arquivos." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ de armazenamento criptografado para anexos de arquivo.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "OpçÃĩes proprietÃĄrias de autenticaÃ§ÃŖo em duas etapas como YubiKey e Duo." }, "premiumSignUpReports": { - "message": "Higiene de senha, saÃēde da conta, e relatÃŗrios de brechas de dados para manter o seu cofre seguro." + "message": "RelatÃŗrios de higiene de senha, saÃēde da conta, e vazamentos de dados para manter o seu cofre seguro." }, "premiumSignUpTotp": { "message": "Gerador de cÃŗdigos de verificaÃ§ÃŖo TOTP (2FA) para credenciais no seu cofre." @@ -1650,7 +1763,7 @@ "message": "Confira se a senha foi exposta." }, "passwordExposed": { - "message": "Esta senha foi exposta $VALUE$ vez(es) em brechas de dados. VocÃĒ deve alterÃĄ-la.", + "message": "Esta senha foi exposta $VALUE$ vez(es) em vazamentos de dados. VocÃĒ deve alterÃĄ-la.", "placeholders": { "value": { "content": "$1", @@ -1659,7 +1772,7 @@ } }, "passwordSafe": { - "message": "Esta senha nÃŖo foi encontrada em brechas de dados conhecidas. Deve ser segura de usar." + "message": "Esta senha nÃŖo foi encontrada em vazamentos de dados conhecidos. Deve ser segura de usar." }, "baseDomain": { "message": "Domínio de base", @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Exportar de" }, - "exportVault": { - "message": "Exportar cofre" + "exportNoun": { + "message": "ExportaÃ§ÃŖo", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Exportar", + "description": "The verb 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" @@ -1780,7 +1906,7 @@ "message": "Confirmar exportaÃ§ÃŖo do cofre" }, "exportWarningDesc": { - "message": "Esta exportaÃ§ÃŖo contÊm os dados do seu cofre em um formato nÃŖo criptografado. VocÃĒ nÃŖo deve armazenar ou enviar o arquivo exportado por canais inseguros (como e-mail). Apague o arquivo imediatamente apÃŗs terminar de usÃĄ-lo." + "message": "Esta exportaÃ§ÃŖo contÊm os dados do seu cofre em um formato sem criptografia. VocÃĒ nÃŖo deve armazenar ou enviar o arquivo exportado por canais inseguros (como e-mail). Apague o arquivo imediatamente apÃŗs terminar de usÃĄ-lo." }, "encExportKeyWarningDesc": { "message": "Esta exportaÃ§ÃŖo criptografa seus dados usando a chave de criptografia da sua conta. Se vocÃĒ rotacionar a chave de criptografia da sua conta, vocÃĒ deve exportar novamente, jÃĄ que vocÃĒ nÃŖo serÃĄ capaz de descriptografar este arquivo de exportaÃ§ÃŖo." @@ -1935,7 +2061,7 @@ "message": "Uma ou mais políticas da organizaÃ§ÃŖo estÃŖo afetando as configuraçÃĩes do seu gerador." }, "vaultTimeoutAction": { - "message": "AÃ§ÃŖo do tempo limite do cofre" + "message": "AÃ§ÃŖo do limite de tempo do cofre" }, "vaultTimeoutActionLockDesc": { "message": "A senha principal ou outro mÊtodo de desbloqueio Ê necessÃĄrio para acessar seu cofre novamente." @@ -1944,7 +2070,7 @@ "message": "ReautenticaÃ§ÃŖo Ê necessÃĄria para acessar seu cofre novamente." }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Configure um mÊtodo de desbloqueio para alterar a aÃ§ÃŖo do tempo limite do cofre." + "message": "Configure um mÊtodo de desbloqueio para alterar a aÃ§ÃŖo do limite de tempo do cofre." }, "lock": { "message": "Bloquear", @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item apagado para sempre" }, + "archivedItemRestored": { + "message": "Item arquivado restaurado" + }, "restoredItem": { "message": "Item restaurado" }, @@ -1973,10 +2102,10 @@ "message": "Apagar para sempre" }, "vaultTimeoutLogOutConfirmation": { - "message": "Ao desconectar-se, todo o seu acesso ao cofre serÃĄ removido e serÃĄ necessÃĄrio autenticaÃ§ÃŖo on-line apÃŗs o período do tempo limite. Tem certeza que quer usar esta configuraÃ§ÃŖo?" + "message": "Ao desconectar-se, todo o seu acesso ao cofre serÃĄ removido e serÃĄ necessÃĄrio autenticaÃ§ÃŖo on-line apÃŗs o período do limite de tempo. Tem certeza que quer usar esta configuraÃ§ÃŖo?" }, "vaultTimeoutLogOutConfirmationTitle": { - "message": "ConfirmaÃ§ÃŖo de aÃ§ÃŖo do tempo limite" + "message": "ConfirmaÃ§ÃŖo de aÃ§ÃŖo do limite de tempo" }, "enterpriseSingleSignOn": { "message": "AutenticaÃ§ÃŖo Ãēnica empresarial" @@ -2088,16 +2217,16 @@ } }, "policyInEffectUppercase": { - "message": "ContÊm um ou mais caracteres em maiÃēsculo" + "message": "Conter um ou mais caracteres em maiÃēsculo" }, "policyInEffectLowercase": { - "message": "ContÊm um ou mais caracteres em minÃēsculo" + "message": "Conter um ou mais caracteres em minÃēsculo" }, "policyInEffectNumbers": { - "message": "ContÊm um ou mais nÃēmeros" + "message": "Conter um ou mais nÃēmeros" }, "policyInEffectSpecial": { - "message": "ContÊm um ou mais dos seguintes caracteres especiais $CHARS$", + "message": "Conter um ou mais dos seguintes caracteres especiais $CHARS$", "placeholders": { "chars": { "content": "$1", @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "InformaçÃĩes de contato" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Todos os Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Tem certeza que quer apagar este Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copiar link do Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copiar link do Send para a ÃĄrea de transferÃĒncia", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2504,7 +2641,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "As políticas da sua organizaÃ§ÃŖo estÃŖo afetando o tempo limite do seu cofre. O mÃĄximo permitido do tempo limite do cofre Ê $HOURS$ hora(s) e $MINUTES$ minuto(s). A aÃ§ÃŖo de tempo limite do seu cofre estÃĄ configurada para $ACTION$.", + "message": "As políticas da sua organizaÃ§ÃŖo estÃŖo afetando o limite de tempo do seu cofre. O mÃĄximo permitido do limite de tempo do cofre Ê $HOURS$ hora(s) e $MINUTES$ minuto(s). A aÃ§ÃŖo de limite de tempo do seu cofre estÃĄ configurada para $ACTION$.", "placeholders": { "hours": { "content": "$1", @@ -2521,7 +2658,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "As políticas da sua organizaÃ§ÃŖo configuraram a aÃ§ÃŖo do tempo limite do seu cofre para $ACTION$.", + "message": "As políticas da sua organizaÃ§ÃŖo configuraram a aÃ§ÃŖo do limite de tempo do seu cofre para $ACTION$.", "placeholders": { "action": { "content": "$1", @@ -2530,13 +2667,13 @@ } }, "vaultTimeoutTooLarge": { - "message": "O tempo limite do seu cofre excede as restriçÃĩes definidas por sua organizaÃ§ÃŖo." + "message": "O limite de tempo do seu cofre excede as restriçÃĩes definidas por sua organizaÃ§ÃŖo." }, "vaultTimeoutPolicyAffectingOptions": { - "message": "Os requisitos das políticas corporativas foram aplicados às suas opçÃĩes de tempo limite" + "message": "Os requisitos das políticas corporativas foram aplicados às suas opçÃĩes de limite de tempo" }, "vaultTimeoutPolicyInEffect": { - "message": "As políticas da sua organizaÃ§ÃŖo configuraram o seu mÃĄximo permitido do tempo limite do cofre para $HOURS$ hora(s) e $MINUTES$ minuto(s).", + "message": "As políticas da sua organizaÃ§ÃŖo configuraram o seu mÃĄximo permitido do limite de tempo do cofre para $HOURS$ hora(s) e $MINUTES$ minuto(s).", "placeholders": { "hours": { "content": "$1", @@ -2549,7 +2686,7 @@ } }, "vaultTimeoutPolicyMaximumError": { - "message": "O tempo limite excede a restriÃ§ÃŖo definida pela sua organizaÃ§ÃŖo: mÃĄximo de $HOURS$ hora(s) e $MINUTES$ minuto(s)", + "message": "O limite de tempo excede a restriÃ§ÃŖo definida pela sua organizaÃ§ÃŖo: mÃĄximo de $HOURS$ hora(s) e $MINUTES$ minuto(s)", "placeholders": { "hours": { "content": "$1", @@ -2562,7 +2699,7 @@ } }, "vaultCustomTimeoutMinimum": { - "message": "O mínimo do tempo limite personalizado Ê de 1 minuto." + "message": "O mínimo do limite de tempo personalizado Ê de 1 minuto." }, "inviteAccepted": { "message": "Convite aceito" @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Senha principal removida" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Uma senha principal nÃŖo Ê mais necessÃĄria para membros da seguinte organizaÃ§ÃŖo. Confirme o domínio abaixo com o administrador da sua organizaÃ§ÃŖo." - }, "organizationName": { "message": "Nome da organizaÃ§ÃŖo" }, @@ -2991,7 +3125,8 @@ "message": "VocÃĒ tem certeza que deseja usar a opÃ§ÃŖo \"Nunca\"? Ao usar o \"Nunca\", a chave de criptografia do seu cofre Ê armazenada no seu dispositivo. Se vocÃĒ usar esta opÃ§ÃŖo, deve garantir que mantÊm seu dispositivo devidamente protegido." }, "vault": { - "message": "Cofre" + "message": "Cofre", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Conectar-se com senha principal" @@ -3073,7 +3208,7 @@ } }, "loginRequestApprovedForEmailOnDevice": { - "message": "SolicitaÃ§ÃŖo de autenticaÃ§ÃŖo aprovada para $EMAIL$ em $DEVICE$", + "message": "SolicitaÃ§ÃŖo de acesso aprovada para $EMAIL$ em $DEVICE$", "placeholders": { "email": { "content": "$1", @@ -3086,7 +3221,7 @@ } }, "youDeniedLoginAttemptFromAnotherDevice": { - "message": "VocÃĒ negou uma tentativa de autenticaÃ§ÃŖo por outro dispositivo. Se foi vocÃĒ, tente conectar-se com o dispositivo novamente." + "message": "VocÃĒ negou uma tentativa de acesso de outro dispositivo. Se era vocÃĒ, tente se conectar com o dispositivo novamente." }, "webApp": { "message": "Aplicativo web" @@ -3114,7 +3249,7 @@ "message": "Servidor" }, "loginRequest": { - "message": "SolicitaÃ§ÃŖo de autenticaÃ§ÃŖo" + "message": "SolicitaÃ§ÃŖo de acesso" }, "deviceType": { "message": "Tipo do dispositivo" @@ -3198,7 +3333,7 @@ "message": "Senha fraca identificada e encontrada em um vazamento de dados. Use uma senha forte e Ãēnica para proteger a sua conta. Tem certeza de que deseja usar essa senha?" }, "checkForBreaches": { - "message": "Conferir vazamentos de dados conhecidos por esta senha" + "message": "Conferir se esta senha vazou ao pÃēblico" }, "loggedInExclamation": { "message": "Conectado!" @@ -3292,7 +3427,7 @@ "message": "Problemas para acessar?" }, "loginApproved": { - "message": "AutenticaÃ§ÃŖo aprovada" + "message": "Acesso aprovado" }, "userEmailMissing": { "message": "E-mail do usuÃĄrio ausente" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Domínio de alias" }, - "importData": { - "message": "Importar dados", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Erro ao importar" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Arquivo salvo no dispositivo. Gerencie a partir das transferÃĒncias do seu dispositivo." }, + "importantNotice": { + "message": "Aviso importante" + }, + "setupTwoStepLogin": { + "message": "Configurar autenticaÃ§ÃŖo em duas etapas" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "O Bitwarden enviarÃĄ um cÃŗdigo no e-mail da sua conta para verificar o acesso de novos dispositivos começando em fevereiro de 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "VocÃĒ pode configurar a autenticaÃ§ÃŖo em duas etapas como um mÊtodo alternativo de proteÃ§ÃŖo da sua conta, ou vocÃĒ pode alterar o seu e-mail para um que possa acessar." + }, + "remindMeLater": { + "message": "Lembre-me depois" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "VocÃĒ tem acesso adequado ao seu e-mail, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "NÃŖo tenho" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Sim, consigo acessar meu e-mail adequadamente" + }, + "turnOnTwoStepLogin": { + "message": "Ativar autenticaÃ§ÃŖo em duas etapas" + }, + "changeAcctEmail": { + "message": "Alterar e-mail da conta" + }, + "passkeyLogin": { + "message": "Conectar-se com chave de acesso?" + }, + "savePasskeyQuestion": { + "message": "Salvar chave de acesso?" + }, + "saveNewPasskey": { + "message": "Salvar como nova credencial" + }, + "savePasskeyNewLogin": { + "message": "Salvar chave de acesso como nova credencial" + }, + "noMatchingLoginsForSite": { + "message": "Nenhuma credencial correspondente para este site" + }, + "overwritePasskey": { + "message": "Substituir chave de acesso?" + }, + "unableToSavePasskey": { + "message": "NÃŖo Ê possível salvar a chave de acesso" + }, + "alreadyContainsPasskey": { + "message": "Este item jÃĄ contÊm uma chave de acesso. Tem certeza que deseja substituir a atual?" + }, + "passkeyAlreadyExists": { + "message": "Uma chave de acesso jÃĄ existe para este aplicativo." + }, + "applicationDoesNotSupportDuplicates": { + "message": "Este aplicativo nÃŖo suporta duplicatas." + }, + "closeThisWindow": { + "message": "Fechar esta janela" + }, "allowScreenshots": { "message": "Permitir captura de tela" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Esta credencial estÃĄ em risco e estÃĄ sem um site. Adicione um site e altere a senha para segurança melhor." }, + "vulnerablePassword": { + "message": "Senha vulnerÃĄvel." + }, + "changeNow": { + "message": "Alterar agora" + }, "missingWebsite": { "message": "Site ausente" }, @@ -3906,10 +4112,16 @@ "message": "Envie informaçÃĩes sensíveis com segurança", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "Nenhum resultado de busca" + }, "sendsBodyNoItems": { "message": "Compartilhe dados e arquivos com segurança com qualquer pessoa, em qualquer plataforma. Suas informaçÃĩes permanecerÃŖo criptografadas de ponta a ponta, limitando a exposiÃ§ÃŖo.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Limpe os filtros ou tente outro termo de busca" + }, "generatorNudgeTitle": { "message": "Crie senhas de forma rÃĄpida" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Atalho de digitaÃ§ÃŖo" }, - "editAutotypeShortcutDescription": { - "message": "Inclua um ou dois dos seguintes modificadores: Ctrl, Alt, Win, ou Shift, e uma letra." + "editAutotypeKeyboardModifiersDescription": { + "message": "Inclua um ou dois dos seguintes modificadores: Ctrl, Alt, Win, e uma letra." }, "invalidShortcut": { "message": "Atalho invÃĄlido" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Desarquivar" }, + "archived": { + "message": "Arquivados" + }, "itemsInArchive": { "message": "Itens no arquivo" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Arquivar item" }, - "archiveItemConfirmDesc": { - "message": "Itens arquivados sÃŖo excluídos dos resultados gerais de busca e das sugestÃĩes de preenchimento automÃĄtico. Tem certeza de que deseja arquivar este item?" + "archiveItemDialogContent": { + "message": "Ao arquivar, o item serÃĄ excluído dos resultados de busca e sugestÃĩes de preenchimento automÃĄtico." + }, + "unArchiveAndSave": { + "message": "Desarquivar e salvar" + }, + "restartPremium": { + "message": "Retomar Premium" + }, + "premiumSubscriptionEnded": { + "message": "Sua assinatura Premium terminou" + }, + "premiumSubscriptionEndedDesc": { + "message": "Para recuperar o seu acesso ao seu arquivo, retoma sua assinatura Premium. Se editar detalhes de um item arquivado antes de retomar, ele serÃĄ movido de volta para o seu cofre." + }, + "itemRestored": { + "message": "O item foi restaurado" }, "zipPostalCodeLabel": { "message": "CEP / CÃŗdigo postal" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "E mais!" }, - "planDescPremium": { - "message": "Segurança on-line completa" + "advancedOnlineSecurity": { + "message": "Segurança on-line avançada" }, "upgradeToPremium": { "message": "Faça upgrade para o Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "A sua organizaÃ§ÃŖo nÃŖo estÃĄ mais usando senhas principais para se conectar ao Bitwarden. Para continuar, verifique a organizaÃ§ÃŖo e o domínio." + }, + "continueWithLogIn": { + "message": "Continuar acessando" + }, + "doNotContinue": { + "message": "NÃŖo continuar" + }, + "domain": { + "message": "Domínio" + }, + "keyConnectorDomainTooltip": { + "message": "Este domínio armazenarÃĄ as chaves de criptografia da sua conta, entÃŖo certifique-se que confia nele. Se nÃŖo tiver certeza, verifique com o seu administrador." + }, + "verifyYourOrganization": { + "message": "Verifique sua organizaÃ§ÃŖo para se conectar" + }, + "organizationVerified": { + "message": "OrganizaÃ§ÃŖo verificada" + }, + "domainVerified": { + "message": "Domínio verificado" + }, + "leaveOrganizationContent": { + "message": "Se vocÃĒ nÃŖo verificar a sua organizaÃ§ÃŖo, o seu acesso à organizaÃ§ÃŖo serÃĄ revogado." + }, + "leaveNow": { + "message": "Sair agora" + }, + "verifyYourDomainToLogin": { + "message": "Verifique seu domínio para se conectar" + }, + "verifyYourDomainDescription": { + "message": "Para continuar se conectando, verifique este domínio." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Para continuar se conectando, verifique a organizaÃ§ÃŖo e o domínio." + }, "sessionTimeoutSettingsAction": { - "message": "AÃ§ÃŖo do tempo limite" + "message": "AÃ§ÃŖo do limite de tempo" }, "sessionTimeoutHeader": { - "message": "Tempo limite da sessÃŖo" + "message": "Limite de tempo da sessÃŖo" + }, + "resizeSideNavigation": { + "message": "Redimensionar navegaÃ§ÃŖo lateral" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Esta configuraÃ§ÃŖo Ê gerenciada pela sua organizaÃ§ÃŖo." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "A sua organizaÃ§ÃŖo configurou o limite de tempo mÃĄximo da sessÃŖo para $HOURS$ hora(s) e $MINUTES$ minuto(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "A sua organizaÃ§ÃŖo configurou o limite de tempo padrÃŖo da sessÃŖo para ser no bloqueio do sistema." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "A sua organizaÃ§ÃŖo configurou o limite de tempo padrÃŖo da sessÃŖo para ser no reinício." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "O limite de tempo mÃĄximo nÃŖo pode exceder $HOURS$ hora(s) e $MINUTES$ minuto(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "No reinício" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Configure um mÊtodo de desbloqueio para alterar a aÃ§ÃŖo do limite de tempo" + }, + "upgrade": { + "message": "Fazer upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Tem certeza de que quer sair?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Se recusar, seus itens pessoais continuarÃŖo na sua conta, mas vocÃĒ perderÃĄ o acesso aos itens compartilhados e os recursos de organizaÃ§ÃŖo." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Entre em contato com o seu administrador para recuperar o acesso." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Sair de $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Como gerencio meu cofre?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transferir itens para $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ exige que todos os itens sejam propriedade da organizaÃ§ÃŖo por segurança e conformidade. Clique em aceitar para transferir a propriedade dos seus itens.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Aceitar transferÃĒncia" + }, + "declineAndLeave": { + "message": "Recusar e sair" + }, + "whyAmISeeingThis": { + "message": "Por que estou vendo isso?" } } diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index de0427ddab0..2eee6006d30 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "O Send serÃĄ permanentemente eliminado nesta data.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "Ficheiro a partilhar" + }, + "hideTextByDefault": { + "message": "Ocultar texto por predefiniÃ§ÃŖo" + }, + "hideYourEmail": { + "message": "Oculte o seu endereço de e-mail dos visualizadores." + }, + "limitSendViews": { + "message": "Limitar visualizaçÃĩes" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ visualizaçÃĩes restantes", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "NinguÊm poderÃĄ ver este Send depois de o limite ser atingido.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Nota privada" + }, + "sendDetails": { + "message": "Detalhes do Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Adicione uma palavra-passe opcional para os destinatÃĄrios acederem a este Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Texto a partilhar" + }, + "newItemHeaderTextSend": { + "message": "Novo Send de texto", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "Novo Send de ficheiro", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Editar Send de texto", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Editar Send de ficheiro", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Tem a certeza de que pretende eliminar permanentemente este Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "Novo", + "description": "for adding new items" + }, "newUri": { "message": "Novo URI" }, @@ -532,7 +600,7 @@ "message": "Eliminar anexo" }, "deleteItemConfirmation": { - "message": "Tem a certeza de que pretende eliminar este item?" + "message": "Pretende realmente mover este item para o lixo?" }, "deletedItem": { "message": "Item movido para o lixo" @@ -708,6 +776,18 @@ "addAttachment": { "message": "Adicionar anexo" }, + "itemsTransferred": { + "message": "Itens transferidos" + }, + "fixEncryption": { + "message": "Corrigir encriptaÃ§ÃŖo" + }, + "fixEncryptionTooltip": { + "message": "Este ficheiro estÃĄ a utilizar um mÊtodo de encriptaÃ§ÃŖo desatualizado." + }, + "attachmentUpdated": { + "message": "Anexo atualizado" + }, "maxFileSizeSansPunctuation": { "message": "O tamanho mÃĄximo do ficheiro Ê de 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Ocorreu um erro inesperado." }, + "unexpectedErrorShort": { + "message": "Erro inesperado" + }, + "closeThisBitwardenWindow": { + "message": "Feche esta janela do Bitwarden e tente novamente." + }, "itemInformation": { "message": "InformaçÃĩes do item" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Saber mais" }, + "migrationsFailed": { + "message": "Ocorreu um erro ao atualizar as definiçÃĩes de encriptaÃ§ÃŖo." + }, + "updateEncryptionSettingsTitle": { + "message": "Atualize as suas definiçÃĩes de encriptaÃ§ÃŖo" + }, + "updateEncryptionSettingsDesc": { + "message": "As novas definiçÃĩes de encriptaÃ§ÃŖo recomendadas irÃŖo melhorar a segurança da sua conta. Introduza a sua palavra-passe mestra para atualizar agora." + }, + "confirmIdentityToContinue": { + "message": "Confirme a sua identidade para continuar" + }, + "enterYourMasterPassword": { + "message": "Introduza a sua palavra-passe mestra" + }, + "updateSettings": { + "message": "Atualizar definiçÃĩes" + }, "featureUnavailable": { "message": "Funcionalidade indisponível" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Siga-nos" }, - "syncVault": { - "message": "Sincronizar cofre" + "syncNow": { + "message": "Sincronizar agora" }, "changeMasterPass": { "message": "Alterar palavra-passe mestra" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB de armazenamento encriptado para anexos de ficheiros." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ de armazenamento encriptado para anexos de ficheiros.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "OpçÃĩes proprietÃĄrias de verificaÃ§ÃŖo de dois passos, como YubiKey e Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Exportar de" }, - "exportVault": { - "message": "Exportar cofre" + "exportNoun": { + "message": "ExportaÃ§ÃŖo", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Exportar", + "description": "The verb 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 ficheiro" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item eliminado permanentemente" }, + "archivedItemRestored": { + "message": "Item arquivado restaurado" + }, "restoredItem": { "message": "Item restaurado" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "InformaçÃĩes de contacto" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Todos os Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Tem a certeza de que pretende eliminar este Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copiar link do Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copiar o link do Send para a ÃĄrea de transferÃĒncia", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Palavra-passe mestra removida" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "JÃĄ nÃŖo Ê necessÃĄria uma palavra-passe mestra para os membros da seguinte organizaÃ§ÃŖo. Por favor, confirme o domínio abaixo com o administrador da sua organizaÃ§ÃŖo." - }, "organizationName": { "message": "Nome da organizaÃ§ÃŖo" }, @@ -2991,7 +3125,8 @@ "message": "Tem a certeza de que deseja utilizar a opÃ§ÃŖo \"Nunca\"? Ao definir as opçÃĩes de bloqueio para \"Nunca\" armazena a chave de encriptaÃ§ÃŖo do seu cofre no seu dispositivo. Se utilizar esta opÃ§ÃŖo deve assegurar-se de que mantÊm o seu dispositivo devidamente protegido." }, "vault": { - "message": "Cofre" + "message": "Cofre", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Iniciar sessÃŖo com a palavra-passe mestra" @@ -3048,7 +3183,7 @@ "message": "SerÃĄ notificado quando o pedido for aprovado" }, "needAnotherOption": { - "message": "O início de sessÃŖo com o dispositivo deve ser ativado nas definiçÃĩes da aplicaÃ§ÃŖo Bitwarden. Precisa de outra opÃ§ÃŖo?" + "message": "O início de sessÃŖo com o dispositivo deve ser ativado nas definiçÃĩes da app Bitwarden. Precisa de outra opÃ§ÃŖo?" }, "viewAllLogInOptions": { "message": "Ver todas as opçÃĩes de início de sessÃŖo" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias de domínio" }, - "importData": { - "message": "Importar dados", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Erro de importaÃ§ÃŖo" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Ficheiro guardado no dispositivo. Faça a gestÃŖo a partir das transferÃĒncias do seu dispositivo." }, + "importantNotice": { + "message": "Aviso importante" + }, + "setupTwoStepLogin": { + "message": "Definir a verificaÃ§ÃŖo de dois passos" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "O Bitwarden enviarÃĄ um cÃŗdigo para o e-mail da sua conta para verificar as credenciais de novos dispositivos a partir de fevereiro de 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Pode configurar a verificaÃ§ÃŖo de dois passos como forma alternativa de proteger a sua conta ou alterar o seu e-mail para um a que possa aceder." + }, + "remindMeLater": { + "message": "Lembrar-me mais tarde" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Tem um acesso fiÃĄvel ao seu e-mail, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "NÃŖo, nÃŖo tenho" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Sim, consigo aceder de forma fiÃĄvel ao meu e-mail" + }, + "turnOnTwoStepLogin": { + "message": "Ativar a verificaÃ§ÃŖo de dois passos" + }, + "changeAcctEmail": { + "message": "Alterar o e-mail da conta" + }, + "passkeyLogin": { + "message": "Iniciar sessÃŖo com a chave de acesso?" + }, + "savePasskeyQuestion": { + "message": "Guardar a chave de acesso?" + }, + "saveNewPasskey": { + "message": "Guardar como nova credencial" + }, + "savePasskeyNewLogin": { + "message": "Guarde a chave de acesso como uma nova credencial" + }, + "noMatchingLoginsForSite": { + "message": "Sem credenciais correspondentes para este site" + }, + "overwritePasskey": { + "message": "Substituir chave de acesso?" + }, + "unableToSavePasskey": { + "message": "NÃŖo Ê possível guardar a chave de acesso" + }, + "alreadyContainsPasskey": { + "message": "Este item jÃĄ contÊm uma chave de acesso. Tem a certeza de que pretende substituir a chave de acesso atual?" + }, + "passkeyAlreadyExists": { + "message": "JÃĄ existe uma chave de acesso para esta aplicaÃ§ÃŖo." + }, + "applicationDoesNotSupportDuplicates": { + "message": "Esta aplicaÃ§ÃŖo nÃŖo suporta duplicados." + }, + "closeThisWindow": { + "message": "Fechar esta janela" + }, "allowScreenshots": { "message": "Permitir a captura de ecrÃŖ" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Esta credencial estÃĄ em risco e nÃŖo tem um site. Adicione um site e altere a palavra-passe para uma segurança mais forte." }, + "vulnerablePassword": { + "message": "Palavra-passe vulnerÃĄvel." + }, + "changeNow": { + "message": "Alterar agora" + }, "missingWebsite": { "message": "Site em falta" }, @@ -3906,10 +4112,16 @@ "message": "Envie informaçÃĩes sensíveis com segurança", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "NÃŖo foram apresentados resultados de pesquisa" + }, "sendsBodyNoItems": { "message": "Partilhe ficheiros e dados de forma segura com qualquer pessoa, em qualquer plataforma. As suas informaçÃĩes permanecerÃŖo encriptadas ponto a ponto, limitando a exposiÃ§ÃŖo.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Limpe os filtros ou tente outro termo de pesquisa" + }, "generatorNudgeTitle": { "message": "Criar rapidamente palavras-passe" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Introduzir atalho" }, - "editAutotypeShortcutDescription": { - "message": "Inclua um ou dois dos seguintes modificadores: Ctrl, Alt, Win, ou Shift, e uma letra." + "editAutotypeKeyboardModifiersDescription": { + "message": "Inclua um ou dois dos seguintes modificadores: Ctrl, Alt, Win e uma letra." }, "invalidShortcut": { "message": "Atalho invÃĄlido" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Desarquivar" }, + "archived": { + "message": "Arquivado" + }, "itemsInArchive": { "message": "Itens no arquivo" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Arquivar item" }, - "archiveItemConfirmDesc": { - "message": "Os itens arquivados sÃŖo excluídos dos resultados gerais da pesquisa e das sugestÃĩes de preenchimento automÃĄtico. Tem a certeza de que pretende arquivar este item?" + "archiveItemDialogContent": { + "message": "Depois de arquivado, este item serÃĄ excluído dos resultados de pesquisa e das sugestÃĩes de preenchimento automÃĄtico." + }, + "unArchiveAndSave": { + "message": "Desarquivar e guardar" + }, + "restartPremium": { + "message": "Reiniciar o Premium" + }, + "premiumSubscriptionEnded": { + "message": "A sua subscriÃ§ÃŖo Premium terminou" + }, + "premiumSubscriptionEndedDesc": { + "message": "Para recuperar o acesso ao seu arquivo, reinicie a sua subscriÃ§ÃŖo Premium. Se editar os detalhes de um item arquivado antes de reiniciar, ele serÃĄ movido de volta para o seu cofre." + }, + "itemRestored": { + "message": "O item foi restaurado" }, "zipPostalCodeLabel": { "message": "CÃŗdigo postal" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "E muito mais!" }, - "planDescPremium": { - "message": "Segurança total online" + "advancedOnlineSecurity": { + "message": "Segurança online avançada" }, "upgradeToPremium": { "message": "Atualizar para o Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "A sua organizaÃ§ÃŖo jÃĄ nÃŖo utiliza palavras-passe mestras para iniciar sessÃŖo no Bitwarden. Para continuar, verifique a organizaÃ§ÃŖo e o domínio." + }, + "continueWithLogIn": { + "message": "Continuar com o início de sessÃŖo" + }, + "doNotContinue": { + "message": "NÃŖo continuar" + }, + "domain": { + "message": "Domínio" + }, + "keyConnectorDomainTooltip": { + "message": "Este domínio armazenarÃĄ as chaves de encriptaÃ§ÃŖo da sua conta, portanto certifique-se de que confia nele. Se nÃŖo tiver a certeza, verifique com o seu administrador." + }, + "verifyYourOrganization": { + "message": "Verifique a sua organizaÃ§ÃŖo para iniciar sessÃŖo" + }, + "organizationVerified": { + "message": "OrganizaÃ§ÃŖo verificada" + }, + "domainVerified": { + "message": "Domínio verificado" + }, + "leaveOrganizationContent": { + "message": "Se nÃŖo verificar a sua organizaÃ§ÃŖo, o seu acesso à organizaÃ§ÃŖo serÃĄ revogado." + }, + "leaveNow": { + "message": "Sair agora" + }, + "verifyYourDomainToLogin": { + "message": "Verifique o seu domínio para iniciar sessÃŖo" + }, + "verifyYourDomainDescription": { + "message": "Para continuar com o início de sessÃŖo, verifique este domínio." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Para continuar com o início de sessÃŖo, verifique a organizaÃ§ÃŖo e o domínio." + }, "sessionTimeoutSettingsAction": { "message": "AÃ§ÃŖo de tempo limite" }, "sessionTimeoutHeader": { "message": "Tempo limite da sessÃŖo" + }, + "resizeSideNavigation": { + "message": "Redimensionar navegaÃ§ÃŖo lateral" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Esta configuraÃ§ÃŖo Ê gerida pela sua organizaÃ§ÃŖo." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "A sua organizaÃ§ÃŖo definiu o tempo limite mÃĄximo da sessÃŖo para $HOURS$ hora(s) e $MINUTES$ minuto(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "A sua organizaÃ§ÃŖo definiu o tempo limite de sessÃŖo predefinido para Ao bloquear o sistema." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "A sua organizaÃ§ÃŖo definiu o tempo limite predefinido da sessÃŖo para Ao reiniciar a app." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "O tempo limite mÃĄximo nÃŖo pode ser superior a $HOURS$ hora(s) e $MINUTES$ minuto(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Ao reiniciar" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Configure um mÊtodo de desbloqueio para alterar a sua aÃ§ÃŖo de tempo limite" + }, + "upgrade": { + "message": "Atualizar" + }, + "leaveConfirmationDialogTitle": { + "message": "Tem a certeza de que pretende sair?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Ao recusar, os seus itens pessoais permanecerÃŖo na sua conta, mas perderÃĄ o acesso aos itens partilhados e às funcionalidades da organizaÃ§ÃŖo." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Entre em contacto com o seu administrador para recuperar o acesso." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Sair de $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Como posso gerir o meu cofre?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transferir itens para $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ exige que todos os itens sejam propriedade da organizaÃ§ÃŖo por motivos de segurança e conformidade. Clique em Aceitar para transferir a propriedade dos seus itens.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Aceitar transferÃĒncia" + }, + "declineAndLeave": { + "message": "Recusar e sair" + }, + "whyAmISeeingThis": { + "message": "Porque Ê que estou a ver isto?" } } diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index a72ce3547e9..c3f84b77a45 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "URI nou" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "A survenit o eroare neașteptată." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Informații despre articol" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Aflați mai multe" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Funcție indisponibilă" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Urmăriți-ne" }, - "syncVault": { - "message": "Sincronizare seif" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Schimbare parolă principală" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB spațiu de stocare criptat pentru atașamente de fișiere." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export de seif" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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 fișier" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Articol șters permanent" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Articol restabilit" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Toate Send-urile", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Sigur doriți să ștergeți acest Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copiați link-ul Send-ului ÃŽn clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Parola principală ÃŽnlăturată" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Sunteți sigur că doriți să folosiți opțiunea „Niciodată”? Setarea opțiunilor de blocare la „Niciodată” stochează cheia de criptare a seifului pe dispozitivul dumneavoastră. Dacă utilizați această opțiune, trebuie să vă asigurați că vă păstrați dispozitivul protejat corespunzător." }, "vault": { - "message": "Seif" + "message": "Seif", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Autentificați-vă cu parola principală" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index 914bb603630..71e0d030702 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "ĐĐžĐ˛Ņ‹Đš", + "description": "for adding new items" + }, "newUri": { "message": "ĐĐžĐ˛Ņ‹Đš URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Đ”ĐžĐąĐ°Đ˛Đ¸Ņ‚ŅŒ вĐģĐžĐļĐĩĐŊиĐĩ" }, + "itemsTransferred": { + "message": "Đ­ĐģĐĩĐŧĐĩĐŊ҂ҋ ĐŋĐĩŅ€ĐĩдаĐŊŅ‹" + }, + "fixEncryption": { + "message": "Đ˜ŅĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊиĐĩ" + }, + "fixEncryptionTooltip": { + "message": "Đ­Ņ‚ĐžŅ‚ Ņ„Đ°ĐšĐģ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚ ŅƒŅŅ‚Đ°Ņ€ĐĩĐ˛ŅˆĐ¸Đš ĐŧĐĩŅ‚ĐžĐ´ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ." + }, + "attachmentUpdated": { + "message": "ВĐģĐžĐļĐĩĐŊиĐĩ ОйĐŊОвĐģĐĩĐŊĐž" + }, "maxFileSizeSansPunctuation": { "message": "МаĐēŅĐ¸ĐŧаĐģҌĐŊŅ‹Đš Ņ€Đ°ĐˇĐŧĐĩŅ€ Ņ„Đ°ĐšĐģа 500 МБ" }, @@ -775,7 +855,7 @@ "message": "Đ˜ŅĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ŅŒ ĐĩдиĐŊŅ‹Đš Đ˛Ņ…ĐžĐ´" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ҂ҀĐĩĐąŅƒĐĩŅ‚ ĐĩдиĐŊĐžĐŗĐž Đ˛Ņ…ĐžĐ´Đ°." }, "submit": { "message": "ĐžŅ‚ĐŋŅ€Đ°Đ˛Đ¸Ņ‚ŅŒ" @@ -908,6 +988,12 @@ "unexpectedError": { "message": "ĐŸŅ€ĐžĐ¸ĐˇĐžŅˆĐģа ĐŊĐĩĐŋŅ€ĐĩдвидĐĩĐŊĐŊĐ°Ņ ĐžŅˆĐ¸ĐąĐēа." }, + "unexpectedErrorShort": { + "message": "НĐĩĐžĐļидаĐŊĐŊĐ°Ņ ĐžŅˆĐ¸ĐąĐēа" + }, + "closeThisBitwardenWindow": { + "message": "ЗаĐēŅ€ĐžĐšŅ‚Đĩ ŅŅ‚Đž ĐžĐēĐŊĐž Bitwarden и ĐŋĐžĐ˛Ņ‚ĐžŅ€Đ¸Ņ‚Đĩ ĐŋĐžĐŋҋ҂Đē҃." + }, "itemInformation": { "message": "ИĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ Ой ŅĐģĐĩĐŧĐĩĐŊŅ‚Đĩ" }, @@ -1051,7 +1137,7 @@ "message": "ĐĸаКĐŧ-Đ°ŅƒŅ‚ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸" }, "authenticationSessionTimedOut": { - "message": "ĐĄĐĩаĐŊҁ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸ СавĐĩŅ€ŅˆĐ¸ĐģŅŅ ĐŋĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊи. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋĐĩŅ€ĐĩСаĐŋŅƒŅŅ‚Đ¸Ņ‚Đĩ ĐŋŅ€ĐžŅ†Đĩҁҁ Đ°Đ˛Ņ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Đ¸Đ¸." + "message": "ĐĄĐĩŅŅĐ¸Ņ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸ СавĐĩŅ€ŅˆĐ¸ĐģĐ°ŅŅŒ ĐŋĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊи. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋĐĩŅ€ĐĩСаĐŋŅƒŅŅ‚Đ¸Ņ‚Đĩ ĐŋŅ€ĐžŅ†Đĩҁҁ Đ°Đ˛Ņ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Đ¸Đ¸." }, "selfHostBaseUrl": { "message": "URL ŅĐžĐąŅŅ‚Đ˛ĐĩĐŊĐŊĐžĐŗĐž ҁĐĩŅ€Đ˛ĐĩŅ€Đ°", @@ -1093,6 +1179,24 @@ "learnMore": { "message": "ĐŸĐžĐ´Ņ€ĐžĐąĐŊĐĩĐĩ" }, + "migrationsFailed": { + "message": "ĐŸŅ€ĐžĐ¸ĐˇĐžŅˆĐģа ĐžŅˆĐ¸ĐąĐēа ĐŋŅ€Đ¸ ОйĐŊОвĐģĐĩĐŊии ĐŊĐ°ŅŅ‚Ņ€ĐžĐĩĐē ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ." + }, + "updateEncryptionSettingsTitle": { + "message": "ОбĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ" + }, + "updateEncryptionSettingsDesc": { + "message": "ĐĐžĐ˛Ņ‹Đĩ Ņ€ĐĩĐēĐžĐŧĐĩĐŊĐ´ŅƒĐĩĐŧŅ‹Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ ĐŋĐžĐ˛Ņ‹ŅŅŅ‚ ĐąĐĩСОĐŋĐ°ŅĐŊĐžŅŅ‚ŅŒ Đ˛Đ°ŅˆĐĩĐŗĐž аĐēĐēĐ°ŅƒĐŊŅ‚Đ°. ВвĐĩĐ´Đ¸Ņ‚Đĩ ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ, Ņ‡Ņ‚ĐžĐąŅ‹ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ҁĐĩĐšŅ‡Đ°Ņ." + }, + "confirmIdentityToContinue": { + "message": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ Đ˛Đ°ŅˆŅƒ ĐģĐ¸Ņ‡ĐŊĐžŅŅ‚ŅŒ, Ņ‡Ņ‚ĐžĐąŅ‹ ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ" + }, + "enterYourMasterPassword": { + "message": "ВвĐĩĐ´Đ¸Ņ‚Đĩ Đ˛Đ°Ņˆ ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ" + }, + "updateSettings": { + "message": "ОбĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи" + }, "featureUnavailable": { "message": "Đ¤ŅƒĐŊĐēŅ†Đ¸Ņ ĐŊĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊа" }, @@ -1103,7 +1207,7 @@ "message": "Đ’Ņ‹ Đ˛Ņ‹ŅˆĐģи иС ŅĐ˛ĐžĐĩĐŗĐž аĐēĐēĐ°ŅƒĐŊŅ‚Đ°." }, "loginExpired": { - "message": "Đ˜ŅŅ‚ĐĩĐē ŅŅ€ĐžĐē Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ Đ˛Đ°ŅˆĐĩĐŗĐž ҁĐĩаĐŊŅĐ°." + "message": "Đ˜ŅŅ‚ĐĩĐē ŅŅ€ĐžĐē Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ Đ˛Đ°ŅˆĐĩĐš ҁĐĩŅŅĐ¸Đ¸." }, "restartRegistration": { "message": "ПĐĩŅ€ĐĩСаĐŋŅƒŅŅ‚Đ¸Ņ‚ŅŒ Ņ€ĐĩĐŗĐ¸ŅŅ‚Ņ€Đ°Ņ†Đ¸ŅŽ" @@ -1162,8 +1266,8 @@ "followUs": { "message": "ПодĐŋĐ¸ŅŅ‹Đ˛Đ°ĐšŅ‚ĐĩҁҌ ĐŊа ĐŊĐ°Ņ" }, - "syncVault": { - "message": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đĩ" + "syncNow": { + "message": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ" }, "changeMasterPass": { "message": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 ГБ ĐˇĐ°ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐŊĐžĐŗĐž Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° Đ´ĐģŅ вĐģĐžĐļĐĩĐŊĐŊҋ҅ Ņ„Đ°ĐšĐģОв." }, + "premiumSignUpStorageV2": { + "message": "Đ—Đ°ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐŊĐžĐŗĐž Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° Đ´ĐģŅ вĐģĐžĐļĐĩĐŊĐŊҋ҅ Ņ„Đ°ĐšĐģОв: $SIZE$", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "ĐŸŅ€ĐžĐŋŅ€Đ¸ĐĩŅ‚Đ°Ņ€ĐŊŅ‹Đĩ Đ˛Đ°Ņ€Đ¸Đ°ĐŊ҂ҋ Đ´Đ˛ŅƒŅ…ŅŅ‚Đ°ĐŋĐŊОК Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸Đ¸, Ņ‚Đ°ĐēиĐĩ ĐēаĐē YubiKey иĐģи Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Đ­ĐēҁĐŋĐžŅ€Ņ‚ иС" }, - "exportVault": { - "message": "Đ­ĐēҁĐŋĐžŅ€Ņ‚ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°" + "exportNoun": { + "message": "Đ­ĐēҁĐŋĐžŅ€Ņ‚", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Đ­ĐēҁĐŋĐžŅ€Ņ‚Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ", + "description": "The verb 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": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚ Ņ„Đ°ĐšĐģа" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Đ­ĐģĐĩĐŧĐĩĐŊŅ‚ ŅƒĐ´Đ°ĐģĐĩĐŊ ĐŊĐ°Đ˛ŅĐĩĐŗĐ´Đ°" }, + "archivedItemRestored": { + "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊŅ‹Đš ŅĐģĐĩĐŧĐĩĐŊŅ‚ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ" + }, "restoredItem": { "message": "Đ­ĐģĐĩĐŧĐĩĐŊŅ‚ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "КоĐŊŅ‚Đ°ĐēŅ‚ĐŊĐ°Ņ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Đ’ŅĐĩ Sendâ€™Ņ‹", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ŅƒĐ´Đ°ĐģĐ¸Ņ‚ŅŒ ŅŅ‚Ņƒ Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "ĐĄĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ҁҁҋĐģĐē҃ ĐŊа Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "ĐĄĐēĐžĐŋĐ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ҁҁҋĐģĐē҃ ĐŊа Send в ĐąŅƒŅ„ĐĩŅ€ ОйĐŧĐĩĐŊа", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2431,10 +2568,10 @@ "message": "ОбĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ" }, "updateMasterPasswordWarning": { - "message": "ĐœĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ ĐŊĐĩдавĐŊĐž ĐąŅ‹Đģ иСĐŧĐĩĐŊĐĩĐŊ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ĐžĐŧ Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸. Đ§Ņ‚ĐžĐąŅ‹ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đē Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Ņƒ, Đ˛Ņ‹ Đ´ĐžĐģĐļĐŊŅ‹ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐĩĐŗĐž ҁĐĩĐšŅ‡Đ°Ņ. В Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Đĩ Ņ‚ĐĩĐēŅƒŅ‰Đ¸Đš ҁĐĩаĐŊҁ ĐąŅƒĐ´ĐĩŅ‚ СавĐĩŅ€ŅˆĐĩĐŊ, ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒĐĩŅ‚ŅŅ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊŅ‹Đš Đ˛Ņ…ĐžĐ´. ĐĄĐĩаĐŊҁҋ ĐŊа Đ´Ņ€ŅƒĐŗĐ¸Ņ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Ņ… ĐŧĐžĐŗŅƒŅ‚ ĐžŅŅ‚Đ°Đ˛Đ°Ņ‚ŅŒŅŅ аĐēŅ‚Đ¸Đ˛ĐŊŅ‹Đŧи в Ņ‚Đĩ҇ĐĩĐŊиĐĩ ОдĐŊĐžĐŗĐž Ņ‡Đ°ŅĐ°." + "message": "ĐœĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ ĐŊĐĩдавĐŊĐž ĐąŅ‹Đģ иСĐŧĐĩĐŊĐĩĐŊ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ĐžĐŧ Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸. Đ§Ņ‚ĐžĐąŅ‹ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đē Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Ņƒ, Đ˛Ņ‹ Đ´ĐžĐģĐļĐŊŅ‹ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ĐĩĐŗĐž ҁĐĩĐšŅ‡Đ°Ņ. В Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Đĩ Ņ‚ĐĩĐēŅƒŅ‰Đ°Ņ ҁĐĩŅŅĐ¸Ņ ĐąŅƒĐ´ĐĩŅ‚ СавĐĩŅ€ŅˆĐĩĐŊа, ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒĐĩŅ‚ŅŅ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊŅ‹Đš Đ˛Ņ…ĐžĐ´. ĐĄĐĩŅŅĐ¸Đ¸ ĐŊа Đ´Ņ€ŅƒĐŗĐ¸Ņ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Ņ… ĐŧĐžĐŗŅƒŅ‚ ĐžŅŅ‚Đ°Đ˛Đ°Ņ‚ŅŒŅŅ аĐēŅ‚Đ¸Đ˛ĐŊŅ‹Đŧи в Ņ‚Đĩ҇ĐĩĐŊиĐĩ ОдĐŊĐžĐŗĐž Ņ‡Đ°ŅĐ°." }, "updateWeakMasterPasswordWarning": { - "message": "Đ’Đ°Ņˆ ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ ĐŊĐĩ ŅĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛ŅƒĐĩŅ‚ ҂ҀĐĩйОваĐŊĐ¸ŅĐŧ ĐŋĐžĐģĐ¸Ņ‚Đ¸Đēи Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸. ДĐģŅ Đ´ĐžŅŅ‚ŅƒĐŋа Đē Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Ņƒ Đ˛Ņ‹ Đ´ĐžĐģĐļĐŊŅ‹ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ŅĐ˛ĐžĐš ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ ĐŋŅ€ŅĐŧĐž ҁĐĩĐšŅ‡Đ°Ņ. ĐŸŅ€Đ¸ ŅŅ‚ĐžĐŧ Ņ‚ĐĩĐēŅƒŅ‰Đ¸Đš ҁĐĩаĐŊҁ ĐąŅƒĐ´ĐĩŅ‚ СавĐĩŅ€ŅˆĐĩĐŊ и ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒĐĩŅ‚ŅŅ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐ°Ņ Đ°Đ˛Ņ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Đ¸Ņ. ĐĄĐĩаĐŊҁҋ ĐŊа Đ´Ņ€ŅƒĐŗĐ¸Ņ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Ņ… ĐŧĐžĐŗŅƒŅ‚ ĐžŅŅ‚Đ°Đ˛Đ°Ņ‚ŅŒŅŅ аĐēŅ‚Đ¸Đ˛ĐŊŅ‹Đŧи в Ņ‚Đĩ҇ĐĩĐŊиĐĩ Ņ‡Đ°ŅĐ°." + "message": "Đ’Đ°Ņˆ ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ ĐŊĐĩ ŅĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛ŅƒĐĩŅ‚ ҂ҀĐĩйОваĐŊĐ¸ŅĐŧ ĐŋĐžĐģĐ¸Ņ‚Đ¸Đēи Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸. ДĐģŅ Đ´ĐžŅŅ‚ŅƒĐŋа Đē Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Ņƒ Đ˛Ņ‹ Đ´ĐžĐģĐļĐŊŅ‹ ОйĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ŅĐ˛ĐžĐš ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ ĐŋŅ€ŅĐŧĐž ҁĐĩĐšŅ‡Đ°Ņ. ĐŸŅ€Đ¸ ŅŅ‚ĐžĐŧ Ņ‚ĐĩĐēŅƒŅ‰Đ°Ņ ҁĐĩŅŅĐ¸Ņ ĐąŅƒĐ´ĐĩŅ‚ СавĐĩŅ€ŅˆĐĩĐŊа и ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒĐĩŅ‚ŅŅ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐ°Ņ Đ°Đ˛Ņ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Đ¸Ņ. ĐĄĐĩŅŅĐ¸Đ¸ ĐŊа Đ´Ņ€ŅƒĐŗĐ¸Ņ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Ņ… ĐŧĐžĐŗŅƒŅ‚ ĐžŅŅ‚Đ°Đ˛Đ°Ņ‚ŅŒŅŅ аĐēŅ‚Đ¸Đ˛ĐŊŅ‹Đŧи в Ņ‚Đĩ҇ĐĩĐŊиĐĩ Ņ‡Đ°ŅĐ°." }, "changePasswordWarning": { "message": "ĐŸĐžŅĐģĐĩ ҁĐŧĐĩĐŊŅ‹ ĐŋĐ°Ņ€ĐžĐģŅ ĐŋĐžŅ‚Ņ€ĐĩĐąŅƒĐĩŅ‚ŅŅ Đ°Đ˛Ņ‚ĐžŅ€Đ¸ĐˇĐžĐ˛Đ°Ņ‚ŅŒŅŅ ҁ ĐŊĐžĐ˛Ņ‹Đŧ ĐŋĐ°Ņ€ĐžĐģĐĩĐŧ. АĐēŅ‚Đ¸Đ˛ĐŊŅ‹Đĩ ҁĐĩŅŅĐ¸Đ¸ ĐŊа Đ´Ņ€ŅƒĐŗĐ¸Ņ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°Ņ… ĐąŅƒĐ´ŅƒŅ‚ СавĐĩŅ€ŅˆĐĩĐŊŅ‹ в Ņ‚Đĩ҇ĐĩĐŊиĐĩ ОдĐŊĐžĐŗĐž Ņ‡Đ°ŅĐ°." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "ĐœĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ ŅƒĐ´Đ°ĐģĐĩĐŊ." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "ĐœĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģҌ йОĐģҌ҈Đĩ ĐŊĐĩ ҂ҀĐĩĐąŅƒĐĩŅ‚ŅŅ Đ´ĐģŅ ҇ĐģĐĩĐŊОв ҁĐģĐĩĐ´ŅƒŅŽŅ‰ĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ҃ĐēаСаĐŊĐŊŅ‹Đš ĐŊиĐļĐĩ Đ´ĐžĐŧĐĩĐŊ ҃ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸." - }, "organizationName": { "message": "НазваĐŊиĐĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸" }, @@ -2643,7 +2777,7 @@ "message": "ОĐŋŅ†Đ¸Đ¸" }, "sessionTimeout": { - "message": "Đ’Ņ€ĐĩĐŧŅ Đ˛Đ°ŅˆĐĩĐŗĐž ҁĐĩаĐŊŅĐ° Đ¸ŅŅ‚ĐĩĐēĐģĐž. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, вĐĩŅ€ĐŊĐ¸Ņ‚ĐĩҁҌ и ĐŋĐžĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ Đ˛ĐžĐšŅ‚Đ¸ ҁĐŊОва." + "message": "Đ’Ņ€ĐĩĐŧŅ Đ˛Đ°ŅˆĐĩĐš ҁĐĩŅŅĐ¸Đ¸ Đ¸ŅŅ‚ĐĩĐēĐģĐž. ПоĐļаĐģŅƒĐšŅŅ‚Đ°, вĐĩŅ€ĐŊĐ¸Ņ‚ĐĩҁҌ и ĐŋĐžĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ Đ˛ĐžĐšŅ‚Đ¸ ҁĐŊОва." }, "exportingPersonalVaultTitle": { "message": "Đ­ĐēҁĐŋĐžŅ€Ņ‚ ĐģĐ¸Ņ‡ĐŊĐžĐŗĐž Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°" @@ -2991,7 +3125,8 @@ "message": "Đ’Ņ‹ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ‚ĐĩĐģҌĐŊĐž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐžŅ‚ĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐąĐģĐžĐēĐ¸Ņ€ĐžĐ˛Đē҃ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ°? В ŅŅ‚ĐžĐŧ ҁĐģŅƒŅ‡Đ°Đĩ ĐēĐģŅŽŅ‡ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ Đ˛Đ°ŅˆĐĩĐŗĐž Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đ° ĐąŅƒĐ´ĐĩŅ‚ ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊ ĐŊа Đ˛Đ°ŅˆĐĩĐŧ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ. ĐžŅ‚ĐēĐģŅŽŅ‡Đ°Ņ ĐąĐģĐžĐēĐ¸Ņ€ĐžĐ˛Đē҃, Đ˛Ņ‹ Đ´ĐžĐģĐļĐŊŅ‹ ŅƒĐąĐĩĐ´Đ¸Ņ‚ŅŒŅŅ, Ņ‡Ņ‚Đž Đ˛Đ°ŅˆĐĩ ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đž ĐŊадĐĩĐļĐŊĐž ĐˇĐ°Ņ‰Đ¸Ņ‰ĐĩĐŊĐž." }, "vault": { - "message": "ĐĨŅ€Đ°ĐŊиĐģĐ¸Ņ‰Đĩ" + "message": "ĐĨŅ€Đ°ĐŊиĐģĐ¸Ņ‰Đĩ", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Đ’ĐžĐšŅ‚Đ¸ ҁ ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģĐĩĐŧ" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "ĐŸŅĐĩвдОĐŊиĐŧ Đ´ĐžĐŧĐĩĐŊа" }, - "importData": { - "message": "ИĐŧĐŋĐžŅ€Ņ‚ даĐŊĐŊҋ҅", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "ĐžŅˆĐ¸ĐąĐēа иĐŧĐŋĐžŅ€Ņ‚Đ°" }, @@ -3854,11 +3985,80 @@ "fileSavedToDevice": { "message": "ФаКĐģ ŅĐžŅ…Ņ€Đ°ĐŊĐĩĐŊ ĐŊа ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đĩ. ĐŖĐŋŅ€Đ°Đ˛ĐģŅĐšŅ‚Đĩ иĐŧ иС ĐˇĐ°ĐŗŅ€ŅƒĐˇĐžĐē ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛Đ°." }, + "importantNotice": { + "message": "ВаĐļĐŊĐžĐĩ ŅƒĐ˛ĐĩĐ´ĐžĐŧĐģĐĩĐŊиĐĩ" + }, + "setupTwoStepLogin": { + "message": "ĐĐ°ŅŅ‚Ņ€ĐžĐ¸Ņ‚ŅŒ Đ´Đ˛ŅƒŅ…ŅŅ‚Đ°ĐŋĐŊŅƒŅŽ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸ŅŽ" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "ĐĐ°Ņ‡Đ¸ĐŊĐ°Ņ ҁ Ņ„ĐĩĐ˛Ņ€Đ°ĐģŅ 2025 ĐŗĐžĐ´Đ° Bitwarden ĐąŅƒĐ´ĐĩŅ‚ ĐžŅ‚ĐŋŅ€Đ°Đ˛ĐģŅŅ‚ŅŒ ĐēОд ĐŊа ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊŅƒŅŽ ĐŋĐžŅ‡Ņ‚Ņƒ Đ˛Đ°ŅˆĐĩĐŗĐž аĐēĐēĐ°ŅƒĐŊŅ‚Đ° Đ´ĐģŅ ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€ĐļĐ´ĐĩĐŊĐ¸Ņ Đ°Đ˛Ņ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Đ¸Đ¸ ҁ ĐŊĐžĐ˛Ņ‹Ņ… ŅƒŅŅ‚Ņ€ĐžĐšŅŅ‚Đ˛." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "В ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đĩ аĐģŅŒŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊĐžĐŗĐž ҁĐŋĐžŅĐžĐąĐ° ĐˇĐ°Ņ‰Đ¸Ņ‚Ņ‹ ŅƒŅ‡ĐĩŅ‚ĐŊОК СаĐŋĐ¸ŅĐ¸ Đ˛Ņ‹ ĐŧĐžĐļĐĩŅ‚Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐ¸Ņ‚ŅŒ Đ´Đ˛ŅƒŅ…ŅŅ‚Đ°ĐŋĐŊŅƒŅŽ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸ŅŽ иĐģи ҁĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊŅƒŅŽ ĐŋĐžŅ‡Ņ‚Ņƒ ĐŊа Ņ‚Ņƒ, Đē ĐēĐžŅ‚ĐžŅ€ĐžĐš Đ˛Ņ‹ ĐŧĐžĐļĐĩŅ‚Đĩ ĐŋĐžĐģŅƒŅ‡Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ." + }, + "remindMeLater": { + "message": "НаĐŋĐžĐŧĐŊĐ¸Ņ‚ŅŒ ĐŋОСĐļĐĩ" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Đ•ŅŅ‚ŅŒ Đģи ҃ Đ˛Đ°Ņ ĐŊадĐĩĐļĐŊŅ‹Đš Đ´ĐžŅŅ‚ŅƒĐŋ Đē ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Đĩ $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "НĐĩŅ‚, ĐŊĐĩ СĐŊĐ°ŅŽ" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Да, Ņ иĐŧĐĩŅŽ ĐŊадĐĩĐļĐŊŅ‹Đš Đ´ĐžŅŅ‚ŅƒĐŋ Đē ŅĐ˛ĐžĐĩĐš ŅĐģĐĩĐēŅ‚Ņ€ĐžĐŊĐŊОК ĐŋĐžŅ‡Ņ‚Đĩ" + }, + "turnOnTwoStepLogin": { + "message": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ Đ´Đ˛ŅƒŅ…ŅŅ‚Đ°ĐŋĐŊŅƒŅŽ Đ°ŅƒŅ‚ĐĩĐŊŅ‚Đ¸Ņ„Đ¸ĐēĐ°Ņ†Đ¸ŅŽ" + }, + "changeAcctEmail": { + "message": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ email аĐēĐēĐ°ŅƒĐŊŅ‚Đ°" + }, + "passkeyLogin": { + "message": "Đ’ĐžĐšŅ‚Đ¸ ҁ passkey?" + }, + "savePasskeyQuestion": { + "message": "ĐĄĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ passkey?" + }, + "saveNewPasskey": { + "message": "ĐĄĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ ĐēаĐē ĐŊĐžĐ˛Ņ‹Đš ĐģĐžĐŗĐ¸ĐŊ" + }, + "savePasskeyNewLogin": { + "message": "ĐĄĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ passkey ĐēаĐē ĐŊĐžĐ˛Ņ‹Đš ĐģĐžĐŗĐ¸ĐŊ" + }, + "noMatchingLoginsForSite": { + "message": "НĐĩŅ‚ ĐŋĐžĐ´Ņ…ĐžĐ´ŅŅ‰Đ¸Ņ… ĐģĐžĐŗĐ¸ĐŊОв Đ´ĐģŅ ŅŅ‚ĐžĐŗĐž ŅĐ°ĐšŅ‚Đ°" + }, + "overwritePasskey": { + "message": "ПĐĩŅ€ĐĩСаĐŋĐ¸ŅĐ°Ņ‚ŅŒ passkey?" + }, + "unableToSavePasskey": { + "message": "НĐĩ ŅƒĐ´Đ°ĐģĐžŅŅŒ ŅĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ passkey" + }, + "alreadyContainsPasskey": { + "message": "Đ­Ņ‚ĐžŅ‚ ŅĐģĐĩĐŧĐĩĐŊŅ‚ ҃ĐļĐĩ ŅĐžĐ´ĐĩŅ€ĐļĐ¸Ņ‚ passkey. Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŋĐĩŅ€ĐĩСаĐŋĐ¸ŅĐ°Ņ‚ŅŒ Ņ‚ĐĩĐēŅƒŅ‰Đ¸Đš passkey?" + }, + "passkeyAlreadyExists": { + "message": "ДĐģŅ даĐŊĐŊĐžĐŗĐž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ ҃ĐļĐĩ ŅŅƒŅ‰ĐĩŅŅ‚Đ˛ŅƒĐĩŅ‚ passkey." + }, + "applicationDoesNotSupportDuplicates": { + "message": "Đ­Ņ‚Đž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ĐŊĐĩ ĐŋОддĐĩŅ€ĐļиваĐĩŅ‚ Đ´ŅƒĐąĐģиĐēĐ°Ņ‚Ņ‹." + }, + "closeThisWindow": { + "message": "ЗаĐēŅ€Ņ‹Ņ‚ŅŒ ŅŅ‚Đž ĐžĐēĐŊĐž" + }, "allowScreenshots": { "message": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ ĐˇĐ°Ņ…Đ˛Đ°Ņ‚ ŅĐēŅ€Đ°ĐŊа" }, "allowScreenshotsDesc": { - "message": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸ŅŽ Bitwarden ĐˇĐ°Ņ…Đ˛Đ°Ņ‚ ŅĐēŅ€Đ°ĐŊа Đ´ĐģŅ ҁĐēŅ€Đ¸ĐŊŅˆĐžŅ‚ĐžĐ˛ и ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ° в ҁĐĩаĐŊŅĐ°Ņ… ŅƒĐ´Đ°ĐģĐĩĐŊĐŊĐžĐŗĐž Ņ€Đ°ĐąĐžŅ‡ĐĩĐŗĐž ŅŅ‚ĐžĐģа. ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€Đ° СаĐŋŅ€ĐĩŅ‚Đ¸Ņ‚ Đ´ĐžŅŅ‚ŅƒĐŋ ĐŊа ĐŊĐĩĐēĐžŅ‚ĐžŅ€Ņ‹Ņ… вĐŊĐĩ҈ĐŊĐ¸Ņ… Đ´Đ¸ŅĐŋĐģĐĩŅŅ…." + "message": "Đ Đ°ĐˇŅ€ĐĩŅˆĐ¸Ņ‚ŅŒ ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸ŅŽ Bitwarden ĐˇĐ°Ņ…Đ˛Đ°Ņ‚ ŅĐēŅ€Đ°ĐŊа Đ´ĐģŅ ҁĐēŅ€Đ¸ĐŊŅˆĐžŅ‚ĐžĐ˛ и ĐŋŅ€ĐžŅĐŧĐžŅ‚Ņ€Đ° в ҁĐĩŅŅĐ¸ŅŅ… ŅƒĐ´Đ°ĐģĐĩĐŊĐŊĐžĐŗĐž Ņ€Đ°ĐąĐžŅ‡ĐĩĐŗĐž ŅŅ‚ĐžĐģа. ĐžŅ‚ĐēĐģŅŽŅ‡ĐĩĐŊиĐĩ ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€Đ° СаĐŋŅ€ĐĩŅ‚Đ¸Ņ‚ Đ´ĐžŅŅ‚ŅƒĐŋ ĐŊа ĐŊĐĩĐēĐžŅ‚ĐžŅ€Ņ‹Ņ… вĐŊĐĩ҈ĐŊĐ¸Ņ… Đ´Đ¸ŅĐŋĐģĐĩŅŅ…." }, "confirmWindowStillVisibleTitle": { "message": "ОĐēĐŊĐž ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€ĐļĐ´ĐĩĐŊĐ¸Ņ ĐžŅŅ‚Đ°ĐĩŅ‚ŅŅ видиĐŧŅ‹Đŧ" @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Đ­Ņ‚ĐžŅ‚ ĐģĐžĐŗĐ¸ĐŊ ĐŋОдвĐĩŅ€ĐļĐĩĐŊ Ņ€Đ¸ŅĐē҃ и ҃ ĐŊĐĩĐŗĐž ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒĐĩŅ‚ вĐĩĐą-ŅĐ°ĐšŅ‚. Đ”ĐžĐąĐ°Đ˛ŅŒŅ‚Đĩ вĐĩĐą-ŅĐ°ĐšŅ‚ и ҁĐŧĐĩĐŊĐ¸Ņ‚Đĩ ĐŋĐ°Ņ€ĐžĐģҌ Đ´ĐģŅ йОĐģҌ҈ĐĩĐš ĐąĐĩСОĐŋĐ°ŅĐŊĐžŅŅ‚Đ¸." }, + "vulnerablePassword": { + "message": "ĐŖŅĐˇĐ˛Đ¸ĐŧŅ‹Đš ĐŋĐ°Ņ€ĐžĐģҌ." + }, + "changeNow": { + "message": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ ҁĐĩĐšŅ‡Đ°Ņ" + }, "missingWebsite": { "message": "ĐžŅ‚ŅŅƒŅ‚ŅŅ‚Đ˛ŅƒĐĩŅ‚ ŅĐ°ĐšŅ‚" }, @@ -3906,10 +4112,16 @@ "message": "БĐĩСОĐŋĐ°ŅĐŊĐ°Ņ ĐžŅ‚ĐŋŅ€Đ°Đ˛Đēа ĐēĐžĐŊŅ„Đ¸Đ´ĐĩĐŊŅ†Đ¸Đ°ĐģҌĐŊОК иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Đ¸", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "ĐŸĐžĐ¸ŅĐē ĐŊĐĩ даĐģ Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚ĐžĐ˛" + }, "sendsBodyNoItems": { "message": "БĐĩСОĐŋĐ°ŅĐŊĐž ОйĐŧĐĩĐŊĐ¸Đ˛Đ°ĐšŅ‚ĐĩҁҌ Ņ„Đ°ĐšĐģаĐŧи и даĐŊĐŊŅ‹Đŧи ҁ ĐēĐĩĐŧ ŅƒĐŗĐžĐ´ĐŊĐž ĐŊа ĐģŅŽĐąĐžĐš ĐŋĐģĐ°Ņ‚Ņ„ĐžŅ€ĐŧĐĩ. Đ’Đ°ŅˆĐ° иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ ĐŊадĐĩĐļĐŊĐž ŅˆĐ¸Ņ„Ņ€ŅƒĐĩŅ‚ŅŅ и Đ´ĐžŅŅ‚ŅƒĐŋ Đē ĐŊĐĩĐš ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩĐŊ.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "ĐžŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đĩ Ņ„Đ¸ĐģŅŒŅ‚Ņ€Ņ‹ иĐģи ĐŋĐžĐŋŅ€ĐžĐąŅƒĐšŅ‚Đĩ Đ´Ņ€ŅƒĐŗĐžĐš ĐŋĐžĐ¸ŅĐēĐžĐ˛Ņ‹Đš СаĐŋŅ€ĐžŅ" + }, "generatorNudgeTitle": { "message": "Đ‘Ņ‹ŅŅ‚Ņ€ĐžĐĩ ŅĐžĐˇĐ´Đ°ĐŊиĐĩ ĐŋĐ°Ņ€ĐžĐģĐĩĐš" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "ВвĐĩĐ´Đ¸Ņ‚Đĩ ŅĐžŅ‡ĐĩŅ‚Đ°ĐŊиĐĩ ĐēĐģĐ°Đ˛Đ¸Ņˆ" }, - "editAutotypeShortcutDescription": { - "message": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ ОдиĐŊ иĐģи два иС ҁĐģĐĩĐ´ŅƒŅŽŅ‰Đ¸Ņ… ĐŧĐžĐ´Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžŅ€ĐžĐ˛: Ctrl, Alt, Win иĐģи Shift и ĐąŅƒĐēĐ˛Ņƒ." + "editAutotypeKeyboardModifiersDescription": { + "message": "ВĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ ОдиĐŊ иĐģи два иС ҁĐģĐĩĐ´ŅƒŅŽŅ‰Đ¸Ņ… ĐŧĐžĐ´Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžŅ€ĐžĐ˛: Ctrl, Alt, Win и ĐąŅƒĐēĐ˛Ņƒ." }, "invalidShortcut": { "message": "НĐĩĐ´ĐžĐŋŅƒŅŅ‚Đ¸ĐŧĐžĐĩ ŅĐžŅ‡ĐĩŅ‚Đ°ĐŊиĐĩ ĐēĐģĐ°Đ˛Đ¸Ņˆ" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Đ Đ°ĐˇĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ" }, + "archived": { + "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊ" + }, "itemsInArchive": { "message": "Đ­ĐģĐĩĐŧĐĩĐŊ҂ҋ в Đ°Ņ€Ņ…Đ¸Đ˛Đĩ" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ŅĐģĐĩĐŧĐĩĐŊŅ‚" }, - "archiveItemConfirmDesc": { - "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊŅ‹Đĩ ŅĐģĐĩĐŧĐĩĐŊ҂ҋ Đ¸ŅĐēĐģŅŽŅ‡ĐĩĐŊŅ‹ иС ĐžĐąŅ‰Đ¸Ņ… Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚ĐžĐ˛ ĐŋĐžĐ¸ŅĐēа и ĐŋŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊиК Đ°Đ˛Ņ‚ĐžĐˇĐ°ĐŋĐžĐģĐŊĐĩĐŊĐ¸Ņ. Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ ŅŅ‚ĐžŅ‚ ŅĐģĐĩĐŧĐĩĐŊŅ‚?" + "archiveItemDialogContent": { + "message": "ĐŸĐžŅĐģĐĩ Đ°Ņ€Ņ…Đ¸Đ˛Đ°Ņ†Đ¸Đ¸ ŅŅ‚ĐžŅ‚ ŅĐģĐĩĐŧĐĩĐŊŅ‚ ĐąŅƒĐ´ĐĩŅ‚ Đ¸ŅĐēĐģŅŽŅ‡ĐĩĐŊ иС Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚ĐžĐ˛ ĐŋĐžĐ¸ŅĐēа и ĐŋŅ€ĐĩĐ´ĐģĐžĐļĐĩĐŊиК ĐŋĐž Đ°Đ˛Ņ‚ĐžĐˇĐ°ĐŋĐžĐģĐŊĐĩĐŊĐ¸ŅŽ." + }, + "unArchiveAndSave": { + "message": "Đ Đ°ĐˇĐ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°Ņ‚ŅŒ и ŅĐžŅ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒ" + }, + "restartPremium": { + "message": "ПĐĩŅ€ĐĩĐŋОдĐēĐģŅŽŅ‡Đ¸Ņ‚ŅŒ ĐŸŅ€ĐĩĐŧĐ¸ŅƒĐŧ" + }, + "premiumSubscriptionEnded": { + "message": "Đ’Đ°ŅˆĐ° ĐŋОдĐŋĐ¸ŅĐēа ĐŸŅ€ĐĩĐŧĐ¸ŅƒĐŧ СаĐēĐžĐŊŅ‡Đ¸ĐģĐ°ŅŅŒ" + }, + "premiumSubscriptionEndedDesc": { + "message": "Đ§Ņ‚ĐžĐąŅ‹ Đ˛ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ´ĐžŅŅ‚ŅƒĐŋ Đē ŅĐ˛ĐžĐĩĐŧ҃ Đ°Ņ€Ņ…Đ¸Đ˛Ņƒ, ĐŋОдĐēĐģŅŽŅ‡Đ¸Ņ‚Đĩ ĐŋОдĐŋĐ¸ŅĐē҃ ĐŸŅ€ĐĩĐŧĐ¸ŅƒĐŧ ĐŋĐžĐ˛Ņ‚ĐžŅ€ĐŊĐž. Đ•ŅĐģи Đ˛Ņ‹ иСĐŧĐĩĐŊĐ¸Ņ‚Đĩ ŅĐ˛ĐĩĐ´ĐĩĐŊĐ¸Ņ Ой Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€ĐžĐ˛Đ°ĐŊĐŊĐžĐŧ ŅĐģĐĩĐŧĐĩĐŊŅ‚Đĩ ĐŋĐĩŅ€ĐĩĐ´ ĐŋĐĩŅ€ĐĩĐŋОдĐēĐģŅŽŅ‡ĐĩĐŊиĐĩĐŧ, ĐžĐŊ ĐąŅƒĐ´ĐĩŅ‚ ĐŋĐĩŅ€ĐĩĐŧĐĩ҉ĐĩĐŊ ĐžĐąŅ€Đ°Ņ‚ĐŊĐž в Đ˛Đ°ŅˆĐĩ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰Đĩ." + }, + "itemRestored": { + "message": "Đ­ĐģĐĩĐŧĐĩĐŊŅ‚ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ" }, "zipPostalCodeLabel": { "message": "ĐŸĐžŅ‡Ņ‚ĐžĐ˛Ņ‹Đš иĐŊĐ´ĐĩĐēҁ" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "И ĐŧĐŊĐžĐŗĐžĐĩ Đ´Ņ€ŅƒĐŗĐžĐĩ!" }, - "planDescPremium": { - "message": "ПоĐģĐŊĐ°Ņ ĐžĐŊĐģаКĐŊ-ĐˇĐ°Ņ‰Đ¸Ņ‰ĐĩĐŊĐŊĐžŅŅ‚ŅŒ" + "advancedOnlineSecurity": { + "message": "Đ Đ°ŅŅˆĐ¸Ņ€ĐĩĐŊĐŊĐ°Ņ ĐžĐŊĐģаКĐŊ-ĐąĐĩСОĐŋĐ°ŅĐŊĐžŅŅ‚ŅŒ" }, "upgradeToPremium": { "message": "ОбĐŊĐžĐ˛Đ¸Ņ‚ŅŒ Đ´Đž ĐŸŅ€ĐĩĐŧĐ¸ŅƒĐŧ" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ йОĐģҌ҈Đĩ ĐŊĐĩ Đ¸ŅĐŋĐžĐģŅŒĐˇŅƒĐĩŅ‚ ĐŧĐ°ŅŅ‚ĐĩŅ€-ĐŋĐ°Ņ€ĐžĐģи Đ´ĐģŅ Đ˛Ņ…ĐžĐ´Đ° в Bitwarden. Đ§Ņ‚ĐžĐąŅ‹ ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ, ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŽ и Đ´ĐžĐŧĐĩĐŊ." + }, + "continueWithLogIn": { + "message": "ĐŸŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ ҁ ĐģĐžĐŗĐ¸ĐŊĐžĐŧ" + }, + "doNotContinue": { + "message": "НĐĩ ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ°Ņ‚ŅŒ" + }, + "domain": { + "message": "ДоĐŧĐĩĐŊ" + }, + "keyConnectorDomainTooltip": { + "message": "В ŅŅ‚ĐžĐŧ Đ´ĐžĐŧĐĩĐŊĐĩ ĐąŅƒĐ´ŅƒŅ‚ Ņ…Ņ€Đ°ĐŊĐ¸Ņ‚ŅŒŅŅ ĐēĐģŅŽŅ‡Đ¸ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐ¸Ņ Đ˛Đ°ŅˆĐĩĐŗĐž аĐēĐēĐ°ŅƒĐŊŅ‚Đ°, ĐŋĐžŅŅ‚ĐžĐŧ҃ ŅƒĐąĐĩĐ´Đ¸Ņ‚ĐĩҁҌ, Ņ‡Ņ‚Đž Đ˛Ņ‹ ĐĩĐŧ҃ дОвĐĩŅ€ŅĐĩŅ‚Đĩ. Đ•ŅĐģи Đ˛Ņ‹ ĐŊĐĩ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, ĐžĐąŅ€Đ°Ņ‚Đ¸Ņ‚ĐĩҁҌ Đē ŅĐ˛ĐžĐĩĐŧ҃ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Ņƒ." + }, + "verifyYourOrganization": { + "message": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ŅĐ˛ĐžŅŽ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŽ Đ´ĐģŅ Đ˛Ņ…ĐžĐ´Đ°" + }, + "organizationVerified": { + "message": "ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€ĐļĐ´ĐĩĐŊа" + }, + "domainVerified": { + "message": "ДоĐŧĐĩĐŊ вĐĩŅ€Đ¸Ņ„Đ¸Ņ†Đ¸Ņ€ĐžĐ˛Đ°ĐŊ" + }, + "leaveOrganizationContent": { + "message": "Đ•ŅĐģи Đ˛Ņ‹ ĐŊĐĩ ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ŅĐ˛ĐžŅŽ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŽ, Đ˛Đ°Ņˆ Đ´ĐžŅŅ‚ŅƒĐŋ Đē ĐŊĐĩĐš ĐąŅƒĐ´ĐĩŅ‚ аĐŊĐŊ҃ĐģĐ¸Ņ€ĐžĐ˛Đ°ĐŊ." + }, + "leaveNow": { + "message": "ПоĐēиĐŊŅƒŅ‚ŅŒ" + }, + "verifyYourDomainToLogin": { + "message": "ĐŸĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ŅĐ˛ĐžĐš Đ´ĐžĐŧĐĩĐŊ Đ´ĐģŅ Đ˛Ņ…ĐžĐ´Đ°" + }, + "verifyYourDomainDescription": { + "message": "Đ§Ņ‚ĐžĐąŅ‹ ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ ҁ ĐģĐžĐŗĐ¸ĐŊĐžĐŧ, ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ŅŅ‚ĐžŅ‚ Đ´ĐžĐŧĐĩĐŊ." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Đ§Ņ‚ĐžĐąŅ‹ ĐŋŅ€ĐžĐ´ĐžĐģĐļĐ¸Ņ‚ŅŒ ҁ ĐģĐžĐŗĐ¸ĐŊĐžĐŧ, ĐŋĐžĐ´Ņ‚Đ˛ĐĩŅ€Đ´Đ¸Ņ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ŅŽ и Đ´ĐžĐŧĐĩĐŊ." + }, "sessionTimeoutSettingsAction": { "message": "ĐĸаКĐŧ-Đ°ŅƒŅ‚ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ" }, "sessionTimeoutHeader": { - "message": "ĐĸаКĐŧ-Đ°ŅƒŅ‚ ҁĐĩаĐŊŅĐ°" + "message": "ĐĸаКĐŧ-Đ°ŅƒŅ‚ ҁĐĩŅŅĐ¸Đ¸" + }, + "resizeSideNavigation": { + "message": "ИСĐŧĐĩĐŊĐ¸Ņ‚ŅŒ Ņ€Đ°ĐˇĐŧĐĩŅ€ йОĐēОвОК ĐŊĐ°Đ˛Đ¸ĐŗĐ°Ņ†Đ¸Đ¸" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Đ­Ņ‚Đ° ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēа ҃ĐŋŅ€Đ°Đ˛ĐģŅĐĩŅ‚ŅŅ Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ĐĩĐš." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "В Đ˛Đ°ŅˆĐĩĐš ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸ ĐŧаĐēŅĐ¸ĐŧаĐģҌĐŊŅ‹Đš Ņ‚Đ°ĐšĐŧ-Đ°ŅƒŅ‚ ҁĐĩŅŅĐ¸Đ¸ ŅƒŅŅ‚Đ°ĐŊОвĐģĐĩĐŊ Ņ€Đ°Đ˛ĐŊŅ‹Đŧ $HOURS$ Ņ‡Đ°Ņ. и $MINUTES$ ĐŧиĐŊ.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ŅƒŅŅ‚Đ°ĐŊОвиĐģа Ņ‚Đ°ĐšĐŧ-Đ°ŅƒŅ‚ ҁĐĩŅŅĐ¸Đ¸ ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ ĐŊа ĐŸŅ€Đ¸ ĐąĐģĐžĐēĐ¸Ņ€ĐžĐ˛ĐēĐĩ ŅĐ¸ŅŅ‚ĐĩĐŧŅ‹." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ ŅƒŅŅ‚Đ°ĐŊОвиĐģа Ņ‚Đ°ĐšĐŧ-Đ°ŅƒŅ‚ ҁĐĩŅŅĐ¸Đ¸ ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ ĐŊа ĐŸŅ€Đ¸ ĐŋĐĩŅ€ĐĩСаĐŋ҃ҁĐēĐĩ." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "МаĐēŅĐ¸ĐŧаĐģҌĐŊŅ‹Đš Ņ‚Đ°ĐšĐŧ-Đ°ŅƒŅ‚ ĐŊĐĩ ĐŧĐžĐļĐĩŅ‚ ĐŋŅ€ĐĩĐ˛Ņ‹ŅˆĐ°Ņ‚ŅŒ $HOURS$ Ņ‡Đ°Ņ. и $MINUTES$ ĐŧиĐŊ.", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "ĐŸŅ€Đ¸ ĐŋĐĩŅ€ĐĩСаĐŋ҃ҁĐēĐĩ" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "ĐŖŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚Đĩ ҁĐŋĐžŅĐžĐą Ņ€Đ°ĐˇĐąĐģĐžĐēĐ¸Ņ€ĐžĐ˛Đēи Đ´ĐģŅ иСĐŧĐĩĐŊĐĩĐŊĐ¸Ņ Đ´ĐĩĐšŅŅ‚Đ˛Đ¸Ņ ĐŋŅ€Đ¸ Đ¸ŅŅ‚Đĩ҇ĐĩĐŊии Ņ‚Đ°ĐšĐŧ-Đ°ŅƒŅ‚Đ°" + }, + "upgrade": { + "message": "ПĐĩŅ€ĐĩĐšŅ‚Đ¸" + }, + "leaveConfirmationDialogTitle": { + "message": "Đ’Ņ‹ ŅƒĐ˛ĐĩŅ€ĐĩĐŊŅ‹, Ņ‡Ņ‚Đž Ņ…ĐžŅ‚Đ¸Ņ‚Đĩ ĐŋĐžĐēиĐŊŅƒŅ‚ŅŒ?" + }, + "leaveConfirmationDialogContentOne": { + "message": "В ҁĐģŅƒŅ‡Đ°Đĩ ĐžŅ‚ĐēаСа Đ˛Đ°ŅˆĐ¸ ĐģĐ¸Ņ‡ĐŊŅ‹Đĩ даĐŊĐŊŅ‹Đĩ ĐžŅŅ‚Đ°ĐŊŅƒŅ‚ŅŅ в Đ˛Đ°ŅˆĐĩĐŧ аĐēĐēĐ°ŅƒĐŊŅ‚Đĩ, ĐŊĐž Đ˛Ņ‹ ĐŋĐžŅ‚ĐĩŅ€ŅĐĩŅ‚Đĩ Đ´ĐžŅŅ‚ŅƒĐŋ Đē ĐžĐąŅ‰Đ¸Đŧ ŅĐģĐĩĐŧĐĩĐŊŅ‚Đ°Đŧ и вОСĐŧĐžĐļĐŊĐžŅŅ‚ŅĐŧ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸." + }, + "leaveConfirmationDialogContentTwo": { + "message": "ĐĄĐ˛ŅĐļĐ¸Ņ‚ĐĩҁҌ ҁ Đ˛Đ°ŅˆĐ¸Đŧ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ĐžĐŧ Đ´ĐģŅ Đ˛ĐžŅŅŅ‚Đ°ĐŊОвĐģĐĩĐŊĐ¸Ņ Đ´ĐžŅŅ‚ŅƒĐŋа." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "ПоĐēиĐŊŅƒŅ‚ŅŒ $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "КаĐē Ņ ĐŧĐžĐŗŅƒ ҃ĐŋŅ€Đ°Đ˛ĐģŅŅ‚ŅŒ ŅĐ˛ĐžĐ¸Đŧ Ņ…Ņ€Đ°ĐŊиĐģĐ¸Ņ‰ĐĩĐŧ?" + }, + "transferItemsToOrganizationTitle": { + "message": "ПĐĩŅ€ĐĩĐŊĐĩŅŅ‚Đ¸ ŅĐģĐĩĐŧĐĩĐŊ҂ҋ в $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ ҂ҀĐĩĐąŅƒĐĩŅ‚, Ņ‡Ņ‚ĐžĐąŅ‹ Đ˛ŅĐĩ ŅĐģĐĩĐŧĐĩĐŊ҂ҋ ĐŋŅ€Đ¸ĐŊадĐģĐĩĐļаĐģи ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Đ¸ Đ´ĐģŅ ОйĐĩҁĐŋĐĩ҇ĐĩĐŊĐ¸Ņ ĐąĐĩСОĐŋĐ°ŅĐŊĐžŅŅ‚Đ¸ и ŅĐžĐžŅ‚Đ˛ĐĩŅ‚ŅŅ‚Đ˛Đ¸Ņ ҂ҀĐĩйОваĐŊĐ¸ŅĐŧ. НаĐļĐŧĐ¸Ņ‚Đĩ ĐŸŅ€Đ¸ĐŊŅŅ‚ŅŒ, Ņ‡Ņ‚ĐžĐąŅ‹ ĐŋĐĩŅ€ĐĩĐ´Đ°Ņ‚ŅŒ ŅĐžĐąŅŅ‚Đ˛ĐĩĐŊĐŊĐžŅŅ‚ŅŒ ĐŊа Đ˛Đ°ŅˆĐ¸ ŅĐģĐĩĐŧĐĩĐŊ҂ҋ.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "ĐŸŅ€Đ¸ĐŊŅŅ‚ŅŒ ĐŋĐĩŅ€ĐĩĐ´Đ°Ņ‡Ņƒ" + }, + "declineAndLeave": { + "message": "ĐžŅ‚ĐēĐģĐžĐŊĐ¸Ņ‚ŅŒ и ĐŋĐžĐēиĐŊŅƒŅ‚ŅŒ" + }, + "whyAmISeeingThis": { + "message": "ĐŸĐžŅ‡ĐĩĐŧ҃ Ņ ŅŅ‚Đž виĐļ҃?" } } diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index a83b2cbf536..3dcfe31c354 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "New URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Item information" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Feature unavailable" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow us" }, - "syncVault": { - "message": "Sync vault" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Change master password" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index 0b14b961bbb..9bfb31061b3 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "Send bude natrvalo odstrÃĄnenÃŊ v tento deň.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "SÃēbor, ktorÃŊ chcete zdieÄžaÅĨ" + }, + "hideTextByDefault": { + "message": "V predvolenom nastavení skryÅĨ text" + }, + "hideYourEmail": { + "message": "SkryÅĨ moju e-mailovÃē adresu pri zobrazení." + }, + "limitSendViews": { + "message": "ObmedziÅĨ zobrazenia" + }, + "limitSendViewsCount": { + "message": "ZostÃĄvajÃēce zobrazenia: $ACCESSCOUNT$", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "Po dosiahnutí limitu si tento Send nemôŞe nikto zobraziÅĨ.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "SÃēkromnÃĄ poznÃĄmka" + }, + "sendDetails": { + "message": "Podrobnosti o Sende", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Pridajte voliteÄžnÊ heslo pre príjemcov na prístup k tomuto Sendu.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text, ktorÃŊ chcete zdieÄžaÅĨ" + }, + "newItemHeaderTextSend": { + "message": "NovÃŊ textovÃŊ Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "NovÃŊ sÃēborovÃŊ Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "UpraviÅĨ textovÃŊ Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "UpraviÅĨ sÃēborovÃŊ Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Naozaj chcete natrvalo odstrÃĄniÅĨ tento Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "NovÊ", + "description": "for adding new items" + }, "newUri": { "message": "NovÊ URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "PriloÅžiÅĨ prílohu" }, + "itemsTransferred": { + "message": "PoloÅžky boli prenesenÊ" + }, + "fixEncryption": { + "message": "OpraviÅĨ ÅĄifrovanie" + }, + "fixEncryptionTooltip": { + "message": "Tento sÃēbor pouŞíva zastaranÃē metÃŗdu ÅĄifrovania." + }, + "attachmentUpdated": { + "message": "Príloha bola aktualizovanÃĄ" + }, "maxFileSizeSansPunctuation": { "message": "MaximÃĄlna veÄžkosÅĨ sÃēboru je 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Vyskytla sa neočakÃĄvanÃĄ chyba." }, + "unexpectedErrorShort": { + "message": "NeočakÃĄvanÃĄÂ chyba" + }, + "closeThisBitwardenWindow": { + "message": "Zatvorte toto okno Bitwardenu a skÃēste to znova." + }, "itemInformation": { "message": "InformÃĄcie o poloÅžke" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "ZistiÅĨ viac" }, + "migrationsFailed": { + "message": "Pri aktualizÃĄcii nastavení ÅĄifrovania doÅĄlo k chybe." + }, + "updateEncryptionSettingsTitle": { + "message": "Aktualizujte nastavenie ÅĄifrovania" + }, + "updateEncryptionSettingsDesc": { + "message": "NovÊ odporÃēčanÊ nastavenia ÅĄifrovania zlepÅĄia bezpečnosÅĨ vÃĄÅĄho Ãēčtu. Ak ich chcete aktualizovaÅĨ teraz, zadajte hlavnÊ heslo." + }, + "confirmIdentityToContinue": { + "message": "Ak chcete pokračovaÅĨ, potvrďte svoju identitu" + }, + "enterYourMasterPassword": { + "message": "Zadajte hlavnÊ heslo" + }, + "updateSettings": { + "message": "AktualizovaÅĨ nastavenia" + }, "featureUnavailable": { "message": "Funkcia nie je k dispozícii" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Sledujte nÃĄs" }, - "syncVault": { - "message": "SynchronizovaÅĨ trezor" + "syncNow": { + "message": "SynchronizovaÅĨ teraz" }, "changeMasterPass": { "message": "ZmeniÅĨ hlavnÊ heslo" @@ -1301,7 +1405,7 @@ "message": "Keď je systÊm v reÅžime spÃĄnku" }, "onLocked": { - "message": "Keď je systÊm uzamknutÃŊ" + "message": "Pri uzamknutí systÊmu" }, "onRestart": { "message": "Pri reÅĄtarte" @@ -1378,7 +1482,7 @@ "message": "ZobraziÅĨ Bitwarden v Docku aj keď je minimalizovanÃŊ na panel Ãēloh." }, "confirmTrayTitle": { - "message": "PotvrdiÅĨ vypnutie systÊmovej liÅĄty" + "message": "PotvrdiÅĨ skrÃŊvanie systÊmovej liÅĄty" }, "confirmTrayDesc": { "message": "Vypnutím tohto nastavenia vypnete aj ostatnÊ nastavenia sÃēvisiace so systÊmovou liÅĄtou." @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB ÅĄifrovanÊho ÃēloÅžiska." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ ÅĄifrovanÊho ÃēloÅžiska na prílohy.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "ProprietÃĄrne moÅžnosti dvojstupňovÊho prihlÃĄsenia ako napríklad YubiKey a Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "ExportovaÅĨ z" }, - "exportVault": { - "message": "Export trezoru" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "ExportovaÅĨ", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "PoloÅžka bola natrvalo odstrÃĄnenÃĄ" }, + "archivedItemRestored": { + "message": "ArchivovanÃĄ poloÅžka bola obnovenÃĄ" + }, "restoredItem": { "message": "PoloÅžka bola obnovenÃĄ" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "KontaktnÊ informÃĄcie" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "VÅĄetky Sendy", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Naozaj chcete odstrÃĄniÅĨ tento Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "KopírovaÅĨ odkaz na Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "KopírovaÅĨ odkaz na Send do schrÃĄnky", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "HlavnÊ heslo bolo odstrÃĄnenÊ." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "HlavnÊ heslo sa uÅž nevyÅžaduje pre členov tejto organizÃĄcie. NiÅžÅĄie uvedenÃē domÊnu potvrďte u sprÃĄvcu organizÃĄcie." - }, "organizationName": { "message": "NÃĄzov organizÃĄcie" }, @@ -2790,10 +2924,10 @@ "message": "PouÅžiÅĨ moÅžnosti subadresovania svojho poskytovateÄža e-mailu." }, "catchallEmail": { - "message": "E-mail Catch-all" + "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Ã´ÅĄ." }, "useThisEmail": { "message": "PouÅžiÅĨ tento e-mail" @@ -2991,7 +3125,8 @@ "message": "Ste si istí, Åže chcete pouÅžiÅĨ moÅžnosÅĨ \"Nikdy\"? TÃĄto predvoÄžba ukladÃĄ ÅĄifrovací kÄžÃēč od trezora priamo na zariadení. Ak pouÅžijete tÃēto moÅžnosÅĨ, mali by ste svoje zariadenie nÃĄleÅžite zabezpečiÅĨ." }, "vault": { - "message": "Trezor" + "message": "Trezor", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "PrihlÃĄsenie pomocou hlavnÊho hesla" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domÊna" }, - "importData": { - "message": "Import Ãēdajov", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Chyba importu" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "SÃēbor sa uloÅžil do zariadenia. Spravujte stiahnutÊ sÃēbory zo zariadenia." }, + "importantNotice": { + "message": "DôleÅžitÊ upozornenie" + }, + "setupTwoStepLogin": { + "message": "NastaviÅĨ dvojstupňovÊ prihlÃĄsenie" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden vÃĄm od februÃĄra 2025 poÅĄle na e-mail vÃĄÅĄho Ãēčtu kÃŗd na overenie prihlÃĄsenia z novÃŊch zariadení." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Ako alternatívny spôsob ochrany svojho Ãēčtu môŞete nastaviÅĨ dvojstupňovÊ prihlÃĄsenie alebo zmeniÅĨ e-mail na takÃŊ, ku ktorÊmu mÃĄte prístup." + }, + "remindMeLater": { + "message": "PripomenÃēÅĨ neskôr" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "MÃĄte zaručenÃŊ prístup k e-mailu $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nie, nemÃĄm" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Áno, mÃĄm zaručenÃŊ prístup k e-mailu" + }, + "turnOnTwoStepLogin": { + "message": "ZapnÃēÅĨ dvojstupňovÊ prihlÃĄsenie" + }, + "changeAcctEmail": { + "message": "ZmeniÅĨ e-mail Ãēčtu" + }, + "passkeyLogin": { + "message": "PrihlÃĄsiÅĨ sa s prístupovÃŊm kÄžÃēčom?" + }, + "savePasskeyQuestion": { + "message": "UloÅžiÅĨ prístupovÃŊ kÄžÃēč?" + }, + "saveNewPasskey": { + "message": "UloÅžiÅĨ ako novÊ prihlasovacie Ãēdaje" + }, + "savePasskeyNewLogin": { + "message": "UloÅžiÅĨ prístupovÃŊ kÄžÃēč ako novÊ prihlÃĄsenie" + }, + "noMatchingLoginsForSite": { + "message": "Pre tÃēto strÃĄnku sa nenaÅĄli prihlasovacie Ãēdaje" + }, + "overwritePasskey": { + "message": "PrepísaÅĨ prístupovÃŊ kÄžÃēč?" + }, + "unableToSavePasskey": { + "message": "PrístupovÃŊ kÄžÃēč sa nepodarilo uloÅžiÅĨ" + }, + "alreadyContainsPasskey": { + "message": "TÃĄto poloÅžka uÅž obsahuje prístupovÃŊ kÄžÃēč. Naozaj chcete prepísaÅĨ aktuÃĄlny prístupovÃŊ kÄžÃēč?" + }, + "passkeyAlreadyExists": { + "message": "Pre tÃēto aplikÃĄciu uÅž existuje prístupovÃŊ kÄžÃēč." + }, + "applicationDoesNotSupportDuplicates": { + "message": "TÃĄto aplikÃĄcia nepodporuje duplikÃĄty." + }, + "closeThisWindow": { + "message": "ZatvoriÅĨ toto okno" + }, "allowScreenshots": { "message": "PovoliÅĨ snímanie obrazovky" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Toto prihlÃĄsenie je v ohrození a chÃŊba mu webovÃĄ strÃĄnka. Pridajte webovÃē strÃĄnku a zmeňte heslo na silnejÅĄie zabezpečenie." }, + "vulnerablePassword": { + "message": "ZraniteÄžnÊ heslo." + }, + "changeNow": { + "message": "ZmeniÅĨ teraz" + }, "missingWebsite": { "message": "ChÃŊbajÃēca webovÃĄ strÃĄnka" }, @@ -3906,10 +4112,16 @@ "message": "Send, citlivÊ informÃĄcie bezpečne", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "NenaÅĄli sa Åžiadne vÃŊsledky vyhÄžadÃĄvania" + }, "sendsBodyNoItems": { "message": "Bezpečne zdieÄžajte sÃēbory a Ãēdaje s kÃŊmkoÄžvek a na akejkoÄžvek platforme. VaÅĄe informÃĄcie zostanÃē end-to-end zaÅĄifrovanÊ a zÃĄroveň sa obmedzí ich odhalenie.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "VymaÅžte filtre alebo zmeňte vyhÄžadÃĄvanÃŊ vÃŊraz" + }, "generatorNudgeTitle": { "message": "RÃŊchle vytvÃĄranie hesiel" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Zadajte klÃĄvesovÃē skratku" }, - "editAutotypeShortcutDescription": { - "message": "PouÅžite jeden alebo dva z nasledujÃēcich modifikÃĄtorov: Ctrl, Alt, Win, alebo Shift a písmeno." + "editAutotypeKeyboardModifiersDescription": { + "message": "PouÅžite jeden alebo dva z nasledujÃēcich modifikÃĄtorov: Ctrl, Alt, Win a písmeno." }, "invalidShortcut": { "message": "NeplatnÃĄ klÃĄvesovÃĄ skratka" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "ZruÅĄiÅĨ archivÃĄciu" }, + "archived": { + "message": "ArchivovanÊ" + }, "itemsInArchive": { "message": "PoloÅžky v archíve" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "ArchivovaÅĨ poloÅžku" }, - "archiveItemConfirmDesc": { - "message": "ArchivovanÊ poloÅžky sÃē vylÃēčenÊ zo vÅĄeobecnÊho vyhÄžadÃĄvania a z nÃĄvrhov automatickÊho vypÄēňania. Naozaj chcete archivovaÅĨ tÃēto poloÅžku?" + "archiveItemDialogContent": { + "message": "Po archivÃĄcii bude tÃĄto poloÅžka vylÃēčenÃĄ z vÃŊsledkov vyhÄžadÃĄvania a nÃĄvrhov automatickÊho vypÄēňania." + }, + "unArchiveAndSave": { + "message": "ZruÅĄiÅĨ archivÃĄciu a uloÅžiÅĨ" + }, + "restartPremium": { + "message": "ReÅĄtartovaÅĨ PrÊmium" + }, + "premiumSubscriptionEnded": { + "message": "VaÅĄe predplatnÊ PrÊmium skončilo" + }, + "premiumSubscriptionEndedDesc": { + "message": "Ak chcete obnoviÅĨ prístup k svojmu archívu, reÅĄtartujte predplatnÊ PrÊmium. Ak pred reÅĄtartom upravíte podrobnosti archivovanej poloÅžky, bude presunutÃĄ späÅĨ do trezoru." + }, + "itemRestored": { + "message": "PoloÅžka bola obnovenÃĄ" }, "zipPostalCodeLabel": { "message": "PSČ" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "A eÅĄte viac!" }, - "planDescPremium": { - "message": "ÚplnÊ online zabezpečenie" + "advancedOnlineSecurity": { + "message": "PokročilÃĄ online ochrana" }, "upgradeToPremium": { "message": "UpgradovaÅĨ na PrÊmium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "VaÅĄa organizÃĄcia uÅž nepouŞíva hlavnÊ heslÃĄ na prihlÃĄsenie do Bitwardenu. Ak chcete pokračovaÅĨ, overte organizÃĄciu a domÊnu." + }, + "continueWithLogIn": { + "message": "Pokračujte prihlÃĄsením" + }, + "doNotContinue": { + "message": "NepokračovaÅĨ" + }, + "domain": { + "message": "DomÊna" + }, + "keyConnectorDomainTooltip": { + "message": "TÃĄto domÊna bude ukladaÅĨ ÅĄifrovacie kÄžÃēče vÃĄÅĄho Ãēčtu, takÅže sa uistite, Åže jej dôverujete. Ak si nie ste istí, overte si to u sprÃĄvcu." + }, + "verifyYourOrganization": { + "message": "Na prihlÃĄsenie overte organizÃĄciu" + }, + "organizationVerified": { + "message": "OrganizÃĄcia je overenÃĄ" + }, + "domainVerified": { + "message": "DomÊna je overenÃĄ" + }, + "leaveOrganizationContent": { + "message": "Ak organizÃĄciu neoveríte, vÃĄÅĄ prístup k nej bude zruÅĄenÃŊ." + }, + "leaveNow": { + "message": "OpustiÅĨ teraz" + }, + "verifyYourDomainToLogin": { + "message": "Na prihlÃĄsenie overte domÊnu" + }, + "verifyYourDomainDescription": { + "message": "Na pokračovanie prihlÃĄsením, overte tÃēto domÊnu." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Na pokračovanie prihlÃĄsením, overte organizÃĄciu a domÊnu." + }, "sessionTimeoutSettingsAction": { "message": "Akcia pri vyprÅĄaní časovÊho limitu" }, "sessionTimeoutHeader": { "message": "ČasovÃŊ limit relÃĄcie" + }, + "resizeSideNavigation": { + "message": "ZmeniÅĨ veÄžkosÅĨ bočnej navigÃĄcie" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Toto nastavenie spravuje vaÅĄa organizÃĄcia." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "VaÅĄa organizÃĄcia nastavila maximÃĄlny časovÃŊ limit relÃĄcie na $HOURS$ hod. a $MINUTES$ min.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "VaÅĄa organizÃĄcia nastavila predvolenÃŊ časovÃŊ limit relÃĄcie na Pri uzamknutí systÊmu." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "VaÅĄa organizÃĄcia nastavila predvolenÃŊ časovÃŊ limit relÃĄcie na Pri reÅĄtarte prehliadača." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "MaximÃĄlny časovÃŊ limit nesmie prekročiÅĨ $HOURS$ hod. a $MINUTES$ min.", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Pri reÅĄtarte" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Nastavte metÃŗdu odomknutia, aby ste zmenili akciu pri vyprÅĄaní časovÊho limitu" + }, + "upgrade": { + "message": "UpgradovaÅĨ" + }, + "leaveConfirmationDialogTitle": { + "message": "Naozaj chcete odísÅĨ?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Ak odmietnete, vaÅĄe osobnÊ poloÅžky zostanÃē vo vaÅĄom Ãēčte, ale stratíte prístup k zdieÄžanÃŊm poloÅžkÃĄm a funkciÃĄm organizÃĄcie." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Ak chcete obnoviÅĨ prístup, obrÃĄÅĨte sa na sprÃĄvcu." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "OpustiÅĨ $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Ako môŞem spravovaÅĨ svoj trezor?" + }, + "transferItemsToOrganizationTitle": { + "message": "Prenos poloÅžiek do $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ vyÅžaduje, aby vÅĄetky poloÅžky boli vo vlastníctve organizÃĄcie z dôvodu bezpečnosti a dodrÅžiavania predpisov. Ak chcete previesÅĨ vlastníctvo poloÅžiek, kliknite na tlačidlo PrijaÅĨ.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "PrijaÅĨ prenos" + }, + "declineAndLeave": { + "message": "ZamietnuÅĨ a odísÅĨ" + }, + "whyAmISeeingThis": { + "message": "Prečo to vidím?" } } diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index 353c6858afa..fb12bca7dfe 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Nov URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Informacije o elementu" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Več o tem" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Funkcija ni na voljo" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Sledite nam" }, - "syncVault": { - "message": "Sinhroniziraj trezor" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Spremeni glavno geslo" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Izvoz trezorja" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Vsi Sendsi", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Glavno geslo je bilo odstranjeno." }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index 1bc4a0ed016..612ba9bc6ae 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Нови ĐŖĐ Đ›" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Đ”ĐžĐ´Đ°Ņ˜ ĐŋŅ€Đ¸ĐģĐžĐŗ" }, + "itemsTransferred": { + "message": "ĐŸŅ€ĐĩĐŊĐĩŅ‚Đĩ ŅŅ‚Đ°Đ˛ĐēĐĩ" + }, + "fixEncryption": { + "message": "ПоĐŋŅ€Đ°Đ˛Đ¸ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ŅšĐĩ" + }, + "fixEncryptionTooltip": { + "message": "Ова Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēа ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ĐˇĐ°ŅŅ‚Đ°Ņ€ĐĩĐģи ĐŧĐĩŅ‚ĐžĐ´ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ŅšĐ°." + }, + "attachmentUpdated": { + "message": "ĐŸŅ€Đ¸ĐģĐžĐŗ ҘĐĩ аĐļŅƒŅ€Đ¸Ņ€Đ°ĐŊ" + }, "maxFileSizeSansPunctuation": { "message": "МаĐēŅĐ¸ĐŧаĐģĐŊа вĐĩĐģĐ¸Ņ‡Đ¸ĐŊа ҘĐĩ 500МБ" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Đ”ĐžŅˆĐģĐž ҘĐĩ Đ´Đž ĐŊĐĩĐžŅ‡ĐĩĐēиваĐŊĐĩ ĐŗŅ€Đĩ҈ĐēĐĩ." }, + "unexpectedErrorShort": { + "message": "НĐĩĐžŅ‡ĐĩĐēиваĐŊа ĐŗŅ€Đĩ҈Đēа" + }, + "closeThisBitwardenWindow": { + "message": "Đ—Đ°Ņ‚Đ˛ĐžŅ€Đ¸Ņ‚Đĩ ĐžĐ˛Đ°Ņ˜ Bitwarden ĐŋŅ€ĐžĐˇĐžŅ€ и ĐŋĐžĐēŅƒŅˆĐ°Ņ˜Ņ‚Đĩ ĐŋĐžĐŊОвО." + }, "itemInformation": { "message": "ИĐŊŅ„Đž Đž ŅŅ‚Đ°Đ˛Ņ†Đ¸" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "ХаСĐŊĐ°Ņ˜ Đ˛Đ¸ŅˆĐĩ" }, + "migrationsFailed": { + "message": "Đ”ĐžŅˆĐģĐž ҘĐĩ Đ´Đž ĐŗŅ€Đĩ҈ĐēĐĩ ĐŋŅ€Đ¸ аĐļŅƒŅ€Đ¸Ņ€Đ°ŅšŅƒ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ŅšĐ°." + }, + "updateEncryptionSettingsTitle": { + "message": "АĐļŅƒŅ€Đ¸Ņ€Đ°Ņ˜ ŅĐ˛ĐžŅ˜Đĩ ĐŋĐžŅŅ‚Đ°Đ˛ĐēĐĩ Са ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ŅšĐĩ" + }, + "updateEncryptionSettingsDesc": { + "message": "Нова ĐŋŅ€ĐĩĐŋĐžŅ€ŅƒŅ‡ĐĩĐŊа ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ° ŅˆĐ¸Ņ„Ņ€Đ¸Ņ€Đ°ŅšĐ° ĐŋĐžĐąĐžŅ™ŅˆĐ°Ņ›Đĩ Đ˛Đ°ŅˆŅƒ ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚ ĐŊаĐģĐžĐŗĐ°. ĐŖĐŊĐĩŅĐ¸Ņ‚Đĩ ŅĐ˛ĐžŅ˜Ņƒ ĐŗĐģавĐŊ҃ ĐģОСиĐŊĐē҃ Са аĐļŅƒŅ€Đ¸Ņ€Đ°ŅšĐĩ." + }, + "confirmIdentityToContinue": { + "message": "Да ĐąĐ¸ŅŅ‚Đĩ ĐŊĐ°ŅŅ‚Đ°Đ˛Đ¸Đģи ĐŋĐžŅ‚Đ˛Ņ€Đ´Đ¸Ņ‚Đĩ Đ˛Đ°Ņˆ идĐĩĐŊŅ‚Đ¸Ņ‚ĐĩŅ‚" + }, + "enterYourMasterPassword": { + "message": "ĐŖĐŊĐĩŅ‚Đ¸ Đ˛Đ°ŅˆŅƒ ĐŗĐģавĐŊ҃ ĐģОСиĐŊĐē҃" + }, + "updateSettings": { + "message": "АĐļŅƒŅ€Đ¸Ņ€Đ°Ņ˜ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐ°" + }, "featureUnavailable": { "message": "Đ¤ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ° ҘĐĩ ĐŊĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊа" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "ĐŸŅ€Đ°Ņ‚Đ¸Ņ‚Đĩ ĐŊĐ°Ņ" }, - "syncVault": { - "message": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇŅƒŅ˜ ҁĐĩŅ„" + "syncNow": { + "message": "ХиĐŊŅ…Ņ€ĐžĐŊĐ¸ĐˇŅƒŅ˜ ŅĐ°Đ´Đ°" }, "changeMasterPass": { "message": "ĐŸŅ€ĐžĐŧĐĩĐŊи ĐŗĐģавĐŊ҃ ĐģОСиĐŊĐē҃" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1ГБ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐž ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚Đĩ Са ĐŋŅ€Đ¸ĐģĐžĐŗĐĩ." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐž ҁĐēĐģĐ°Đ´Đ¸ŅˆŅ‚Đĩ Са ĐŋŅ€Đ¸ĐģĐžĐŗĐĩ.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "ĐŸŅ€Đ¸ĐžŅ€Đ¸Ņ‚Đ°Ņ€ĐŊĐĩ ĐžĐŋŅ†Đ¸Ņ˜Đĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đĩ ҃ два ĐēĐžŅ€Đ°Đēа ĐēаО ŅˆŅ‚Đž ҁ҃ YubiKey и Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "ИСвОС Од" }, - "exportVault": { - "message": "ИСвОС ҁĐĩŅ„Đ°" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "ĐĸŅ€Đ°Ņ˜ĐŊĐž Đ¸ĐˇĐąŅ€Đ¸ŅĐ°ĐŊа ŅŅ‚Đ°Đ˛Đēа" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "ĐĄŅ‚Đ°Đ˛Đēа Đ˛Ņ€Đ°Ņ›ĐĩĐŊа" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "КоĐŊŅ‚Đ°ĐēŅ‚ ĐŋĐžĐ´Đ°Ņ†Đ¸" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Хва ҁĐģĐ°ŅšĐ°", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "ĐĄĐ¸ĐŗŅƒŅ€ĐŊĐž ĐžĐąŅ€Đ¸ŅĐ°Ņ‚Đ¸ ОвО ҁĐģĐ°ŅšĐĩ?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "КоĐŋĐ¸Ņ€Đ°Ņ˜ вĐĩĐˇŅƒ ҁĐģĐ°ŅšĐ° ҃ ĐŋŅ€Đ¸Đ˛Ņ€ĐĩĐŧĐĩĐŊ҃ ĐŧĐĩĐŧĐžŅ€Đ¸Ņ˜Ņƒ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "ГĐģавĐŊа ĐģОСиĐŊĐēа ҃ĐēĐģĐžŅšĐĩĐŊа" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "ГĐģавĐŊа ĐģОСиĐŊĐēа Đ˛Đ¸ŅˆĐĩ ĐŊĐ¸Ņ˜Đĩ ĐŋĐžŅ‚Ņ€ĐĩĐąĐŊа Са ҇ĐģаĐŊОвĐĩ ҁĐģĐĩĐ´ĐĩŅ›Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đĩ. МоĐģиĐŧĐž ĐŋĐžŅ‚Đ˛Ņ€Đ´Đ¸Ņ‚Đĩ Đ´ĐžĐŧĐĩĐŊ ŅĐ° адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ĐžĐŧ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đĩ." - }, "organizationName": { "message": "Назив ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đĩ" }, @@ -2991,7 +3125,8 @@ "message": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ ĐžĐŋŅ†Đ¸Ņ˜Ņƒ „НиĐēад“? АĐēĐž ĐŋĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚Đĩ ĐžĐŋŅ†Đ¸Ņ˜Đĩ СаĐēŅ™ŅƒŅ‡Đ°Đ˛Đ°ŅšĐ° ĐŊа „НиĐēада“, ĐŊа Đ˛Đ°ŅˆĐĩĐŧ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Ņƒ ҁĐĩ Ņ‡ŅƒĐ˛Đ° ĐēŅ™ŅƒŅ‡ Са ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ŅšĐĩ ҁĐĩŅ„Đ°. АĐēĐž ĐēĐžŅ€Đ¸ŅŅ‚Đ¸Ņ‚Đĩ ĐžĐ˛Ņƒ ĐžĐŋŅ†Đ¸Ņ˜Ņƒ, ĐžŅĐ¸ĐŗŅƒŅ€Đ°Ņ˜Ņ‚Đĩ да ҘĐĩ ŅƒŅ€ĐĩŅ’Đ°Ņ˜ ĐŋŅ€Đ°Đ˛Đ¸ĐģĐŊĐž ĐˇĐ°ŅˆŅ‚Đ¸Ņ›ĐĩĐŊ." }, "vault": { - "message": "ĐĄĐĩŅ„" + "message": "ĐĄĐĩŅ„", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Ņ‚Đĩ ҁĐĩ ŅĐ° ĐŗĐģавĐŊĐžĐŧ ĐģОСиĐŊĐēĐžĐŧ" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "ДоĐŧĐĩĐŊ аĐģĐ¸Ņ˜Đ°ŅĐ°" }, - "importData": { - "message": "ĐŖĐ˛ĐĩСи ĐŋĐžĐ´Đ°Ņ‚ĐēĐĩ", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Đ“Ņ€Đĩ҈Đēа ĐŋŅ€Đ¸ ŅƒĐ˛ĐžĐˇŅƒ" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Đ”Đ°Ņ‚ĐžŅ‚ĐĩĐēа ҘĐĩ ŅĐ°Ņ‡ŅƒĐ˛Đ°ĐŊа ĐŊа ŅƒŅ€ĐĩŅ’Đ°Ņ˜Ņƒ. ĐŖĐŋŅ€Đ°Đ˛Ņ™Đ°Ņ˜Ņ‚Đĩ ĐŋŅ€ĐĩŅƒĐˇĐ¸ĐŧĐ°ŅšĐ¸Đŧа ŅĐ° ŅĐ˛ĐžĐŗ ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ°." }, + "importantNotice": { + "message": "ВаĐļĐŊĐž ОйавĐĩŅˆŅ‚ĐĩҚĐĩ" + }, + "setupTwoStepLogin": { + "message": "ĐŸĐžŅŅ‚Đ°Đ˛Đ¸Ņ‚Đ¸ двО-ҁ҂ĐĩĐŋĐĩĐŊҁĐē҃ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden Ņ›Đĩ ĐŋĐžŅĐģĐ°Ņ‚Đ¸ ĐēÃ´Đ´ ĐŊа иĐŧĐĩҘĐģ Đ˛Đ°ŅˆĐĩĐŗ ĐŊаĐģĐžĐŗĐ° Са вĐĩŅ€Đ¸Ņ„Đ¸ĐēĐžĐ˛Đ°ŅšĐĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™Đ¸Đ˛Đ°ŅšĐ° ŅĐ° ĐŊĐžĐ˛Đ¸Ņ… ŅƒŅ€ĐĩŅ’Đ°Ņ˜Đ° ĐŋĐžŅ‡ĐĩĐ˛ŅˆĐ¸ Од Ņ„ĐĩĐąŅ€ŅƒĐ°Ņ€Đ° 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "МоĐļĐĩŅ‚Đĩ да ĐŋОдĐĩŅĐ¸Ņ‚Đĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ ҃ два ĐēĐžŅ€Đ°Đēа ĐēаО аĐģŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊи ĐŊĐ°Ņ‡Đ¸ĐŊ да ĐˇĐ°ŅˆŅ‚Đ¸Ņ‚Đ¸Ņ‚Đĩ ŅĐ˛ĐžŅ˜ ĐŊаĐģĐžĐŗ иĐģи да ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ ŅĐ˛ĐžŅ˜ иĐŧĐĩҘĐģ ҃ ҘĐĩдаĐŊ ĐēĐžŅ˜Đ¸ ĐŧĐžĐļĐĩŅ‚Đĩ да ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋĐ¸Ņ‚Đĩ." + }, + "remindMeLater": { + "message": "ĐŸĐžĐ´ŅĐĩŅ‚Đ¸ ĐŧĐĩ ĐēĐ°ŅĐŊĐ¸Ņ˜Đĩ" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Да Đģи иĐŧĐ°Ņ‚Đĩ ĐŋĐžŅƒĐˇĐ´Đ°ĐŊ ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋ ŅĐ˛ĐžŅ˜Đ¸Đŧ иĐŧĐĩҘĐģĐžĐŧ, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "НĐĩ, ĐŊĐĩĐŊаĐŧ" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Да, ĐŧĐžĐŗŅƒ ĐŋĐžŅƒĐˇĐ´Đ°ĐŊĐž да ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋиĐŧ ОвиĐŧ иĐŧĐĩҘĐģĐžĐŧ" + }, + "turnOnTwoStepLogin": { + "message": "ĐŖĐŋаĐģĐ¸Ņ‚Đ¸ двО-ҁ҂ĐĩĐŋĐĩĐŊҁĐē҃ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ" + }, + "changeAcctEmail": { + "message": "ĐŸŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đ¸ иĐŧĐĩҘĐģ ĐŊаĐģĐžĐŗĐ°" + }, + "passkeyLogin": { + "message": "ĐŸŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Ņ‚Đĩ ҁĐĩ ŅĐ° ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋĐŊиĐŧ ĐēŅ™ŅƒŅ‡ĐĩĐŧ?" + }, + "savePasskeyQuestion": { + "message": "ĐĄĐ°Ņ‡ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋĐŊи ĐēŅ™ŅƒŅ‡?" + }, + "saveNewPasskey": { + "message": "ĐĄĐ°Ņ‡ŅƒĐ˛Đ°Ņ‚Đ¸ ĐēаО ĐŊĐžĐ˛Ņƒ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ" + }, + "savePasskeyNewLogin": { + "message": "ĐĄĐ°Ņ‡ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋĐŊи ĐēŅ™ŅƒŅ‡ ĐēаО ĐŊĐžĐ˛Ņƒ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ" + }, + "noMatchingLoginsForSite": { + "message": "НĐĩĐŧа ĐžĐ´ĐŗĐžĐ˛Đ°Ņ€Đ°Ņ˜ŅƒŅ›Đ¸Ņ… ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đ° Са ĐžĐ˛Đ°Ņ˜ ŅĐ°Ņ˜Ņ‚" + }, + "overwritePasskey": { + "message": "ЗаĐŧĐĩĐŊĐ¸Ņ‚Đ¸ ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋĐŊи ĐēŅ™ŅƒŅ‡?" + }, + "unableToSavePasskey": { + "message": "ĐĐ¸Ņ˜Đĩ ĐŧĐžĐŗŅƒŅ›Đĩ ŅĐ°Ņ‡ŅƒĐ˛Đ°Ņ‚Đ¸ ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋĐŊи ĐēŅ™ŅƒŅ‡" + }, + "alreadyContainsPasskey": { + "message": "Ова ŅŅ‚Đ°Đ˛Đēа вĐĩŅ› ŅĐ°Đ´Ņ€Đļи ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋĐŊи ĐēŅ™ŅƒŅ‡. Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да СаĐŧĐĩĐŊĐ¸Ņ‚Đĩ ҂ҀĐĩĐŊŅƒŅ‚ĐŊи ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋĐŊи ĐēŅ™ŅƒŅ‡?" + }, + "passkeyAlreadyExists": { + "message": "За ĐžĐ˛Ņƒ аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Ņƒ вĐĩŅ› ĐŋĐžŅŅ‚ĐžŅ˜Đ¸ ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋĐŊи ĐēŅ™ŅƒŅ‡." + }, + "applicationDoesNotSupportDuplicates": { + "message": "Ова аĐŋĐģиĐēĐ°Ņ†Đ¸Ņ˜Đ° ĐŊĐĩ ĐŋĐžĐ´Ņ€Đļава Đ´ŅƒĐŋĐģиĐēĐ°Ņ‚Đĩ." + }, + "closeThisWindow": { + "message": "Đ—Đ°Ņ‚Đ˛ĐžŅ€Đ¸ ĐžĐ˛Đ°Ņ˜ ĐŋŅ€ĐžĐˇĐžŅ€" + }, "allowScreenshots": { "message": "ДозвоĐģи ҁĐŊиĐŧĐ°ŅšĐĩ ĐĩĐēŅ€Đ°ĐŊа" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Ова ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đ° ҘĐĩ Ņ€Đ¸ĐˇĐ¸Ņ‡ĐŊа и ĐŊĐĩĐ´ĐžŅŅ‚Đ°Ņ˜Đĩ вĐĩĐą ĐģĐžĐēĐ°Ņ†Đ¸Ņ˜Đ°. Đ”ĐžĐ´Đ°Ņ˜Ņ‚Đĩ вĐĩĐą ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Ņƒ и ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ ĐģОСиĐŊĐē҃ Са Ņ˜Đ°Ņ‡Ņƒ ŅĐ¸ĐŗŅƒŅ€ĐŊĐžŅŅ‚." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "НĐĩĐ´ĐžŅŅ‚Đ°Ņ˜Đĩ вĐĩĐą ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ°" }, @@ -3906,10 +4112,16 @@ "message": "Đ¨Đ°Ņ™Đ¸Ņ‚Đĩ йСйĐĩĐ´ĐŊĐž ĐžŅĐĩŅ‚Ņ™Đ¸Đ˛Đĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đĩ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "ДĐĩĐģĐ¸Ņ‚Đĩ Đ´Đ°Ņ‚ĐžŅ‚ĐĩĐēĐĩ и ĐŋĐžĐ´Đ°Ņ‚ĐēĐĩ ĐąĐĩСйĐĩĐ´ĐŊĐž ŅĐ° йиĐģĐž ĐēиĐŧ, ĐŊа йиĐģĐž ĐēĐžŅ˜ĐžŅ˜ ĐŋĐģĐ°Ņ‚Ņ„ĐžŅ€Đŧи. Đ’Đ°ŅˆĐĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ˜Đĩ Ņ›Đĩ ĐžŅŅ‚Đ°Ņ‚Đ¸ ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐĩ Од ĐŋĐžŅ‡ĐĩŅ‚Đēа-Đ´Đž-ĐēŅ€Đ°Ņ˜Đ° ŅƒĐˇ ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩҚĐĩ иСĐģĐžĐļĐĩĐŊĐžŅŅ‚Đ¸.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Đ‘Ņ€ĐˇĐž ĐēŅ€ĐĩĐ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ĐģОСиĐŊĐēĐĩ" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "ĐŖĐŊĐĩŅ‚Đ¸ ĐŋŅ€ĐĩŅ‡Đ¸Ņ†Ņƒ" }, - "editAutotypeShortcutDescription": { - "message": "ĐŖĐēŅ™ŅƒŅ‡Đ¸Ņ‚Đĩ ҘĐĩдаĐŊ иĐģи два ҁĐģĐĩĐ´ĐĩŅ›Đ° ĐŧĐžĐ´Đ¸Ņ„Đ¸ĐēĐ°Ņ‚ĐžŅ€Đ°: Ctrl, Alt, Win, иĐģи Shift, и ҁĐģОвО." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "НĐĩваĐļĐĩŅ›Đ° ĐŋŅ€ĐĩŅ‡Đ¸Ņ†Đ°" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Đ’Ņ€Đ°Ņ‚Đ¸ иС Đ°Ņ€Ņ…Đ¸Đ˛Đĩ" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "ĐĄŅ‚Đ°Đ˛ĐēĐĩ ҃ Đ°Ņ€Ņ…Đ¸Đ˛Đ¸" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°Ņ˜ ŅŅ‚Đ°Đ˛Đē҃" }, - "archiveItemConfirmDesc": { - "message": "ĐŅ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ŅŅ‚Đ°Đ˛ĐēĐĩ ҁ҃ Đ¸ŅĐēŅ™ŅƒŅ‡ĐĩĐŊĐĩ иС ĐžĐŋŅˆŅ‚Đ¸Ņ… Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ° ĐŋŅ€ĐĩŅ‚Ņ€Đ°ĐŗĐĩ и ĐŋŅ€ĐĩĐ´ĐģĐžĐŗĐ° Са Đ°ŅƒŅ‚Đž ĐŋĐžĐŋŅƒŅšĐ°Đ˛Đ°ŅšĐĩ. ЈĐĩҁ҂Đĩ Đģи ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да Đ°Ņ€Ņ…Đ¸Đ˛Đ¸Ņ€Đ°Ņ‚Đĩ ĐžĐ˛Ņƒ ŅŅ‚Đ°Đ˛Đē҃?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP/ĐŸĐžŅˆŅ‚Đ°ĐŊҁĐēи ĐąŅ€ĐžŅ˜" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "И Ņ˜ĐžŅˆ Đ˛Đ¸ŅˆĐĩ!" }, - "planDescPremium": { - "message": "ĐŸĐžŅ‚Đŋ҃ĐŊа ĐžĐŊĐģĐ°Ņ˜ĐŊ ĐąĐĩСйĐĩĐ´ĐŊĐžŅŅ‚" + "advancedOnlineSecurity": { + "message": "НаĐŋŅ€ĐĩĐ´ĐŊа ĐžĐŊĐģĐ°Ņ˜ĐŊ ĐąĐĩСйĐĩĐ´ĐŊĐžŅŅ‚" }, "upgradeToPremium": { "message": "ĐĐ°Đ´ĐžĐŗŅ€Đ°Đ´Đ¸Ņ‚Đĩ ĐŊа Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ° Đ˛Đ¸ŅˆĐĩ ĐŊĐĩ ĐēĐžŅ€Đ¸ŅŅ‚Đ¸ ĐŗĐģавĐŊĐĩ ĐģОСиĐŊĐēĐĩ Са ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņƒ ĐŊа Bitwarden. Да ĐąĐ¸ŅŅ‚Đĩ ĐŊĐ°ŅŅ‚Đ°Đ˛Đ¸Đģи, вĐĩŅ€Đ¸Ņ„Đ¸ĐēŅƒŅ˜Ņ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Ņƒ и Đ´ĐžĐŧĐĩĐŊ." + }, + "continueWithLogIn": { + "message": "ĐĐ°ŅŅ‚Đ°Đ˛Đ¸Ņ‚Đ¸ ŅĐ° ĐŋŅ€Đ¸Ņ˜Đ°Đ˛ĐžĐŧ" + }, + "doNotContinue": { + "message": "НĐĩ ĐŊĐ°ŅŅ‚Đ°Đ˛Đ¸" + }, + "domain": { + "message": "ДоĐŧĐĩĐŊ" + }, + "keyConnectorDomainTooltip": { + "message": "ĐžĐ˛Đ°Ņ˜ Đ´ĐžĐŧĐĩĐŊ Ņ›Đĩ Ņ‡ŅƒĐ˛Đ°Ņ‚Đ¸ ĐēŅ™ŅƒŅ‡ĐĩвĐĩ Са ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ŅšĐĩ Đ˛Đ°ŅˆĐĩĐŗ ĐŊаĐģĐžĐŗĐ°, Đŋа ҁĐĩ ŅƒĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ да Đŧ҃ вĐĩŅ€ŅƒŅ˜ĐĩŅ‚Đĩ. АĐēĐž ĐŊĐ¸ŅŅ‚Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи, ĐŋŅ€ĐžĐ˛ĐĩŅ€Đ¸Ņ‚Đĩ ĐēОд ŅĐ˛ĐžĐŗ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ°." + }, + "verifyYourOrganization": { + "message": "ВĐĩŅ€Đ¸Ņ„Đ¸ĐēŅƒŅ˜Ņ‚Đĩ ŅĐ˛ĐžŅ˜Ņƒ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Ņƒ да ĐąĐ¸ŅŅ‚Đĩ ҁĐĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Đģи" + }, + "organizationVerified": { + "message": "ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ° вĐĩŅ€Đ¸Ņ„Đ¸ĐēОваĐŊа" + }, + "domainVerified": { + "message": "ДоĐŧĐĩĐŊ вĐĩŅ€Đ¸Ņ„Đ¸ĐēОваĐŊ" + }, + "leaveOrganizationContent": { + "message": "АĐēĐž ĐŊĐĩ вĐĩŅ€Đ¸Ņ„Đ¸Đē҃ҘĐĩŅ‚Đĩ ŅĐ˛ĐžŅ˜Ņƒ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Ņƒ, Đ˛Đ°Ņˆ ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ¸ Ņ›Đĩ ĐąĐ¸Ņ‚Đ¸ ĐžĐŋОСваĐŊ." + }, + "leaveNow": { + "message": "НаĐŋŅƒŅŅ‚Đ¸ ŅĐ°Đ´Đ°" + }, + "verifyYourDomainToLogin": { + "message": "ВĐĩŅ€Đ¸Ņ„Đ¸ĐēŅƒŅ˜Ņ‚Đĩ Đ´ĐžĐŧĐĩĐŊ да ĐąĐ¸ŅŅ‚Đĩ ҁĐĩ ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Đ¸Đģи" + }, + "verifyYourDomainDescription": { + "message": "Да ĐąĐ¸ŅŅ‚Đĩ ĐŊĐ°ŅŅ‚Đ°Đ˛Đ¸Đģи ŅĐ° ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™Đ¸Đ˛Đ°ŅšĐĩĐŧ, вĐĩŅ€Đ¸Ņ„Đ¸ĐēŅƒŅ˜Ņ‚Đĩ ĐžĐ˛Đ°Ņ˜ Đ´ĐžĐŧĐĩĐŊ." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Да ĐąĐ¸ŅŅ‚Đĩ ĐŊĐ°ŅŅ‚Đ°Đ˛Đ¸Đģи ŅĐ° ĐŋŅ€Đ¸Ņ˜Đ°Đ˛Ņ™Đ¸Đ˛Đ°ŅšĐĩĐŧ, вĐĩŅ€Đ¸Ņ„Đ¸ĐēŅƒŅ˜Ņ‚Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Ņƒ и Đ´ĐžĐŧĐĩĐŊ." + }, "sessionTimeoutSettingsAction": { - "message": "Timeout action" + "message": "АĐēŅ†Đ¸Ņ˜Đ° Ņ‚Đ°Ņ˜ĐŧĐ°ŅƒŅ‚Đ°" }, "sessionTimeoutHeader": { - "message": "Session timeout" + "message": "Đ˜ŅŅ‚ĐĩĐē ҁĐĩŅĐ¸Ņ˜Đĩ" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "ОвиĐŧ ĐŋОдĐĩŅˆĐ°Đ˛Đ°ŅšĐĩĐŧ ҃ĐŋŅ€Đ°Đ˛Ņ™Đ° Đ˛Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ°." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ° ҘĐĩ ĐŋОдĐĩŅĐ¸Đģа ĐŧаĐēŅĐ¸ĐŧаĐģĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐēĐž ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩҚĐĩ ҁĐĩŅĐ¸Ņ˜Đĩ ĐŊа $HOURS$ ŅĐ°Ņ‚Đ¸ и $MINUTES$ ĐŧиĐŊŅƒŅ‚Đ°.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ° ҘĐĩ ĐŋĐžŅŅ‚Đ°Đ˛Đ¸Đģа ĐŋĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐēĐž ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩҚĐĩ ҁĐĩŅĐ¸Ņ˜Đĩ ĐŊа БĐģĐžĐēĐ¸Ņ€Đ°ŅšĐĩ ŅĐ¸ŅŅ‚ĐĩĐŧа." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Đ’Đ°ŅˆĐ° ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đ° ҘĐĩ ĐŋĐžŅŅ‚Đ°Đ˛Đ¸Đģа ĐŋĐžĐ´Ņ€Đ°ĐˇŅƒĐŧĐĩваĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐēĐž ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩҚĐĩ ҁĐĩŅĐ¸Ņ˜Đĩ ĐŊа ĐŸŅ€Đ¸ Ņ€ĐĩŅŅ‚Đ°Ņ€Ņ‚ĐžĐ˛Đ°ŅšŅƒ." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "МаĐēŅĐ¸ĐŧаĐģĐŊĐž Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐēĐž ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩҚĐĩ ĐŊĐĩ ĐŧĐžĐļĐĩ да ĐŋŅ€ĐĩŅ’Đĩ $HOURS$ ŅĐ°Ņ‚(а) и $MINUTES$ ĐŧиĐŊŅƒŅ‚(а)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "На ĐŋĐžĐŊОвĐŊĐž ĐŋĐžĐēŅ€ĐĩŅ‚Đ°ŅšĐĩ" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "ПодĐĩŅĐ¸Ņ‚Đĩ ĐŧĐĩŅ‚ĐžĐ´ ĐžŅ‚ĐēŅ™ŅƒŅ‡Đ°Đ˛Đ°ŅšĐ° да ĐąĐ¸ŅŅ‚Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊиĐģи Ņ€Đ°Đ´ŅšŅƒ Đ˛Ņ€ĐĩĐŧĐĩĐŊҁĐēĐžĐŗ ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩŅšĐ°" + }, + "upgrade": { + "message": "ĐĐ°Đ´ĐžĐŗŅ€Đ°Đ´Đ¸" + }, + "leaveConfirmationDialogTitle": { + "message": "Да Đģи ҁ҂Đĩ ŅĐ¸ĐŗŅƒŅ€ĐŊи да ĐļĐĩĐģĐ¸Ņ‚Đĩ да ĐŊаĐŋŅƒŅŅ‚Đ¸Ņ‚Đĩ?" + }, + "leaveConfirmationDialogContentOne": { + "message": "АĐēĐž ĐžĐ´ĐąĐ¸Ņ˜ĐĩŅ‚Đĩ, Đ˛Đ°ŅˆĐ¸ ĐģĐ¸Ņ‡ĐŊи ĐŋŅ€ĐĩĐ´ĐŧĐĩŅ‚Đ¸ Ņ›Đĩ ĐžŅŅ‚Đ°Ņ‚Đ¸ ĐŊа Đ˛Đ°ŅˆĐĩĐŧ ĐŊаĐģĐžĐŗŅƒ, аĐģи Ņ›ĐĩŅ‚Đĩ Đ¸ĐˇĐŗŅƒĐąĐ¸Ņ‚Đ¸ ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋ Đ´ĐĩŅ™ĐĩĐŊиĐŧ ŅŅ‚Đ°Đ˛ĐēаĐŧа и ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸ĐžĐŊиĐŧ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ˜Đ°Đŧа." + }, + "leaveConfirmationDialogContentTwo": { + "message": "КоĐŊŅ‚Đ°ĐēŅ‚Đ¸Ņ€Đ°Ņ˜Ņ‚Đĩ ŅĐ˛ĐžĐŗ адĐŧиĐŊĐ¸ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€Đ° да ĐąĐ¸ŅŅ‚Đĩ ĐŋĐžĐŊОвО дОйиĐģи ĐŋŅ€Đ¸ŅŅ‚ŅƒĐŋ." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "НаĐŋŅƒŅŅ‚Đ¸Ņ‚Đ¸ $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "КаĐēĐž да ҃ĐŋŅ€Đ°Đ˛Ņ™Đ°Đŧ ŅĐ˛ĐžŅ˜Đ¸Đŧ ҁĐĩŅ„ĐžĐŧ?" + }, + "transferItemsToOrganizationTitle": { + "message": "ĐŸŅ€ĐĩĐŧĐĩŅŅ‚Đ¸ ŅŅ‚Đ°Đ˛ĐēĐĩ ҃ $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ ĐˇĐ°Ņ…Ņ‚Đĩва да ŅĐ˛Đĩ ŅŅ‚Đ°Đ˛ĐēĐĩ ĐąŅƒĐ´Ņƒ ҃ вĐģĐ°ŅĐŊĐ¸ŅˆŅ‚Đ˛Ņƒ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ°Ņ†Đ¸Ņ˜Đĩ Ņ€Đ°Đ´Đ¸ ĐąĐĩСйĐĩĐ´ĐŊĐžŅŅ‚Đ¸ и ҃ҁĐēĐģĐ°Ņ’ĐĩĐŊĐžŅŅ‚Đ¸. КĐģиĐēĐŊĐ¸Ņ‚Đĩ ĐŊа ĐŋŅ€Đ¸Ņ…Đ˛Đ°Ņ‚Đ¸ да ĐąĐ¸ŅŅ‚Đĩ ĐŋŅ€ĐĩĐŊĐĩĐģи вĐģĐ°ŅĐŊĐ¸ŅˆŅ‚Đ˛Đž ĐŊад ŅĐ˛ĐžŅ˜Đ¸Đŧ ŅŅ‚Đ°Đ˛ĐēаĐŧа.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "ĐŸŅ€Đ¸Ņ…Đ˛Đ°Ņ‚Đ¸ Ņ‚Ņ€Đ°ĐŊҁ҄ĐĩŅ€" + }, + "declineAndLeave": { + "message": "ĐžĐ´ĐąĐ¸Ņ˜ и ĐŊаĐŋŅƒŅŅ‚Đ¸" + }, + "whyAmISeeingThis": { + "message": "Đ—Đ°ŅˆŅ‚Đž Đ˛Đ¸Đ´Đ¸Ņ‚Đĩ ОвО?" } } diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index 93d56419ae3..9bba277c3d0 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "Denna Send kommer att raderas permanent pÃĨ detta datum.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "Fil att dela" + }, + "hideTextByDefault": { + "message": "DÃļlj text som standard" + }, + "hideYourEmail": { + "message": "DÃļlj din e-postadress frÃĨn tittarna." + }, + "limitSendViews": { + "message": "Begränsa antalet visningar" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ visningar kvar", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "Ingen kan se denna Send efter att gränsen har nÃĨtts.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Privat anteckning" + }, + "sendDetails": { + "message": "Send-detaljer", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Lägg till ett valfritt lÃļsenord fÃļr att mottagarna ska fÃĨ ÃĨtkomst till denna Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text att dela" + }, + "newItemHeaderTextSend": { + "message": "Ny Send fÃļr text", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "Ny Send fÃļr fil", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Redigera Send fÃļr text", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Redigera Send fÃļr fil", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Är du säker pÃĨ att du vill ta bort denna Send permanent?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "Ny", + "description": "for adding new items" + }, "newUri": { "message": "Ny URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Lägg till bilaga" }, + "itemsTransferred": { + "message": "Objekt ÃļverfÃļrda" + }, + "fixEncryption": { + "message": "Fixa kryptering" + }, + "fixEncryptionTooltip": { + "message": "Denna fil använder en fÃļrÃĨldrad krypteringsmetod." + }, + "attachmentUpdated": { + "message": "Bilaga uppdaterad" + }, "maxFileSizeSansPunctuation": { "message": "Maximal filstorlek är 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Ett oväntat fel har inträffat." }, + "unexpectedErrorShort": { + "message": "Oväntat fel" + }, + "closeThisBitwardenWindow": { + "message": "Stäng detta Bitwarden-fÃļnster och fÃļrsÃļk igen." + }, "itemInformation": { "message": "Objektinformation" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Läs mer" }, + "migrationsFailed": { + "message": "Ett fel inträffade när krypteringsinställningarna skulle uppdateras." + }, + "updateEncryptionSettingsTitle": { + "message": "Uppdatera dina krypteringsinställningar" + }, + "updateEncryptionSettingsDesc": { + "message": "De nya rekommenderade krypteringsinställningarna kommer att fÃļrbättra säkerheten fÃļr ditt konto. Ange ditt huvudlÃļsenord fÃļr att uppdatera nu." + }, + "confirmIdentityToContinue": { + "message": "Bekräfta din identitet fÃļr att fortsätta" + }, + "enterYourMasterPassword": { + "message": "Ange ditt huvudlÃļsenord" + }, + "updateSettings": { + "message": "Uppdatera inställningar" + }, "featureUnavailable": { "message": "Funktion ej tillgänglig" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "FÃļlj oss" }, - "syncVault": { - "message": "Synkronisera valv" + "syncNow": { + "message": "Synkronisera nu" }, "changeMasterPass": { "message": "Ändra huvudlÃļsenord" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB krypterad lagring." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ krypterad lagring fÃļr filbilagor.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Premium-alternativ fÃļr tvÃĨstegsverifiering, sÃĨsom YubiKey och Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Exportera frÃĨn" }, - "exportVault": { - "message": "Exportera valv" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Exportera", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Raderade objekt permanent" }, + "archivedItemRestored": { + "message": "Arkiverat objekt ÃĨterställt" + }, "restoredItem": { "message": "Återställde objekt" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Kontaktinformation" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "Alla Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Är du säker pÃĨ att du vill radera denna Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Kopiera Send-länk", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Kopiera Send-länk till urklipp", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "HuvudlÃļsenord togs bort" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Ett huvudlÃļsenord krävs inte längre fÃļr medlemmar i fÃļljande organisation. Vänligen bekräfta domänen nedan med din organisationsadministratÃļr." - }, "organizationName": { "message": "Organisationsnamn" }, @@ -2991,7 +3125,8 @@ "message": "Är du säker pÃĨ att du vill använda alternativet ”Aldrig”? Att ställa in lÃĨsnings-alternativet till ”Aldrig” lagrar valvets krypteringsnyckel pÃĨ datorn. Om du använder det här alternativet bÃļr du se till att du hÃĨller datorn ordentligt skyddad." }, "vault": { - "message": "Valv" + "message": "Valv", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Logga in med huvudlÃļsenord" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Aliasdomän" }, - "importData": { - "message": "Importera data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Fel vid import" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Fil sparad till enhet. Hantera nedladdningar frÃĨn din enhet." }, + "importantNotice": { + "message": "Viktigt meddelande" + }, + "setupTwoStepLogin": { + "message": "Ställ in tvÃĨstegsverifiering" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden kommer att skicka en kod till din e-postadress fÃļr ditt konto fÃļr att verifiera inloggningar frÃĨn nya enheter med start i februari 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "Du kan ställa in tvÃĨstegsverifiering som ett alternativt sätt att skydda ditt konto eller ändra din e-post till en som du kan komma ÃĨt." + }, + "remindMeLater": { + "message": "PÃĨminn mig senare" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Har du tillfÃļrlitlig ÃĨtkomst till din e-post, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Nej, det har jag inte" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Ja, jag har tillfÃļrlitlig ÃĨtkomst till min e-post" + }, + "turnOnTwoStepLogin": { + "message": "Aktivera tvÃĨstegsverifiering" + }, + "changeAcctEmail": { + "message": "Byt e-postadress fÃļr konto" + }, + "passkeyLogin": { + "message": "Logga in med inloggningsnyckel?" + }, + "savePasskeyQuestion": { + "message": "Spara inloggningsnyckel?" + }, + "saveNewPasskey": { + "message": "Spara som ny inloggning" + }, + "savePasskeyNewLogin": { + "message": "Spara inloggningsnyckel som ny inloggning" + }, + "noMatchingLoginsForSite": { + "message": "Inga matchande inloggningar fÃļr denna webbplats" + }, + "overwritePasskey": { + "message": "Skriv Ãļver inloggningsnyckel?" + }, + "unableToSavePasskey": { + "message": "Kunde inte spara inloggningsnyckel" + }, + "alreadyContainsPasskey": { + "message": "Detta objekt innehÃĨller redan en inloggningsnyckel. Är du säker pÃĨ att du vill skriva Ãļver den aktuella inloggningsnyckeln?" + }, + "passkeyAlreadyExists": { + "message": "En inloggningsnyckel finns redan fÃļr detta program." + }, + "applicationDoesNotSupportDuplicates": { + "message": "Denna applikation har inte stÃļd fÃļr dubbletter." + }, + "closeThisWindow": { + "message": "Stäng detta fÃļnster" + }, "allowScreenshots": { "message": "TillÃĨt skärmdump" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Denna inloggning är utsatt fÃļr risk och saknar en webbplats. Lägg till en webbplats och ändra lÃļsenordet fÃļr Ãļkad säkerhet." }, + "vulnerablePassword": { + "message": "SÃĨrbart lÃļsenord." + }, + "changeNow": { + "message": "Ändra nu" + }, "missingWebsite": { "message": "Saknar webbplats" }, @@ -3906,10 +4112,16 @@ "message": "Skicka känslig information pÃĨ ett säkert sätt", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "Inga sÃļkresultat returnerades" + }, "sendsBodyNoItems": { "message": "Dela filer och data pÃĨ ett säkert sätt med vem som helst, pÃĨ vilken plattform som helst. Din information kommer att fÃļrbli krypterad frÃĨn bÃļrjan till slut samtidigt som exponeringen begränsas.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "TÃļm filtren eller fÃļrsÃļk med en annan sÃļkterm" + }, "generatorNudgeTitle": { "message": "Skapa lÃļsenord snabbt" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Inmatningsgenväg" }, - "editAutotypeShortcutDescription": { - "message": "Inkludera en eller tvÃĨ av fÃļljande modifierare: Ctrl, Alt, Win, eller Skift och en bokstav." + "editAutotypeKeyboardModifiersDescription": { + "message": "Inkludera en eller tvÃĨ av fÃļljande modifierare: Ctrl, Alt, Win och en bokstav." }, "invalidShortcut": { "message": "Ogiltig genväg" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Avarkivera" }, + "archived": { + "message": "Arkiverade" + }, "itemsInArchive": { "message": "Objekt i arkivet" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Arkivera objekt" }, - "archiveItemConfirmDesc": { - "message": "Arkiverade objekt är uteslutna frÃĨn allmänna sÃļkresultat och fÃļrslag fÃļr autofyll. Är du säker pÃĨ att du vill arkivera detta objekt?" + "archiveItemDialogContent": { + "message": "När du har arkiverat kommer detta objekt att uteslutas frÃĨn sÃļkresultat och fÃļrslag till autofyll." + }, + "unArchiveAndSave": { + "message": "Avarkivera och spara" + }, + "restartPremium": { + "message": "Starta om Premium" + }, + "premiumSubscriptionEnded": { + "message": "Ditt Premium-abonnemang avslutades" + }, + "premiumSubscriptionEndedDesc": { + "message": "FÃļr att ÃĨterfÃĨ ÃĨtkomst till ditt arkiv, starta om Premium-abonnemanget. Om du redigerar detaljer fÃļr ett arkiverat objekt innan du startar om kommer det att flyttas tillbaka till ditt valv." + }, + "itemRestored": { + "message": "Objektet har ÃĨterställts" }, "zipPostalCodeLabel": { "message": "Postnummer" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "och mer!" }, - "planDescPremium": { - "message": "Komplett säkerhet online" + "advancedOnlineSecurity": { + "message": "Avancerad säkerhet online" }, "upgradeToPremium": { "message": "Uppgradera till Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Din organisation använder inte längre huvudlÃļsenord fÃļr att logga in pÃĨ Bitwarden. FÃļr att fortsätta, verifiera organisationen och domänen." + }, + "continueWithLogIn": { + "message": "Fortsätt med inloggning" + }, + "doNotContinue": { + "message": "Fortsätt inte" + }, + "domain": { + "message": "Domän" + }, + "keyConnectorDomainTooltip": { + "message": "Denna domän kommer att lagra dina krypteringsnycklar, sÃĨ se till att du litar pÃĨ den. Om du inte är säker, kontrollera med din administratÃļr." + }, + "verifyYourOrganization": { + "message": "Verifiera din organisation fÃļr att logga in" + }, + "organizationVerified": { + "message": "Organisation verifierad" + }, + "domainVerified": { + "message": "Domän verifierad" + }, + "leaveOrganizationContent": { + "message": "Om du inte verifierar din organisation kommer din ÃĨtkomst till organisationen att ÃĨterkallas." + }, + "leaveNow": { + "message": "Lämna nu" + }, + "verifyYourDomainToLogin": { + "message": "Verifiera din domän fÃļr att logga in" + }, + "verifyYourDomainDescription": { + "message": "FÃļr att fortsätta med inloggning, verifiera denna domän." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "FÃļr att fortsätta logga in, verifiera organisationen och domänen." + }, "sessionTimeoutSettingsAction": { "message": "TidsgränsÃĨtgärd" }, "sessionTimeoutHeader": { "message": "Sessionstidsgräns" + }, + "resizeSideNavigation": { + "message": "Ändra storlek pÃĨ sidnavigering" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Den här inställningen hanteras av din organisation." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Din organisation har ställt in maximal sessionstidsgräns till $HOURS$ timmar och $MINUTES$ minut(er).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Din organisation har ställt in tidsgräns fÃļr standardsession till Vid systemlÃĨsning." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Din organisation har ställt in tidsgräns fÃļr standardsession till Vid omstart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximal tidsgräns fÃĨr inte Ãļverstiga $HOURS$ timmar och $MINUTES$ minut(er)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Vid omstart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Ställ in en upplÃĨsningsmetod fÃļr att ändra din tidsgränsÃĨtgärd" + }, + "upgrade": { + "message": "Uppgradera" + }, + "leaveConfirmationDialogTitle": { + "message": "Är du säker pÃĨ att du vill lämna?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Genom att avbÃļja kommer dina personliga objekt att stanna pÃĨ ditt konto, men du kommer att fÃļrlora ÃĨtkomst till delade objekt och organisationsfunktioner." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Kontakta administratÃļren fÃļr att ÃĨterfÃĨ ÃĨtkomst." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Lämna $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Hur hanterar jag mitt valv?" + }, + "transferItemsToOrganizationTitle": { + "message": "ÖverfÃļr objekt till $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ kräver att alla objekt ägs av organisationen fÃļr säkerhet och efterlevnad. Klicka pÃĨ godkänn fÃļr att ÃļverfÃļra ägarskapet fÃļr dina objekt.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Godkänn ÃļverfÃļring" + }, + "declineAndLeave": { + "message": "AvbÃļj och lämna" + }, + "whyAmISeeingThis": { + "message": "VarfÃļr ser jag det här?" } } diff --git a/apps/desktop/src/locales/ta/messages.json b/apps/desktop/src/locales/ta/messages.json index 2f9d12917d6..60f3a54e8db 100644 --- a/apps/desktop/src/locales/ta/messages.json +++ b/apps/desktop/src/locales/ta/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "āŽĒā¯āŽ¤āŽŋāŽ¯ URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯ˆāŽšā¯ āŽšā¯‡āŽ°ā¯" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "āŽ…āŽ¤āŽŋāŽ•āŽĒāŽŸā¯āŽš āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽ…āŽŗāŽĩ❁ 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "āŽ’āŽ°ā¯ āŽŽāŽ¤āŽŋāŽ°ā¯āŽĒāŽžāŽ°āŽžāŽ¤ āŽĒāŽŋāŽ´ā¯ˆ āŽāŽąā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "āŽ‰āŽ°ā¯āŽĒā¯āŽĒāŽŸāŽŋ āŽ¤āŽ•āŽĩāŽ˛ā¯" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "āŽŽā¯‡āŽ˛ā¯āŽŽā¯ āŽ…āŽąāŽŋāŽ¯āŽĩā¯āŽŽā¯" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "āŽĩāŽšāŽ¤āŽŋ āŽ•āŽŋāŽŸā¯ˆāŽ•ā¯āŽ•āŽĩāŽŋāŽ˛ā¯āŽ˛ā¯ˆ" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "āŽŽāŽ™ā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽŋāŽŠā¯āŽ¤ā¯ŠāŽŸāŽ°āŽĩā¯āŽŽā¯" }, - "syncVault": { - "message": "āŽĒā¯†āŽŸā¯āŽŸāŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽ’āŽ¤ā¯āŽ¤āŽŋāŽšā¯ˆāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "āŽŽā¯āŽ¤āŽŠā¯āŽŽā¯ˆ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯ˆ āŽŽāŽžāŽąā¯āŽąāŽĩā¯āŽŽā¯" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ 1 GB āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸ āŽšā¯‡āŽŽāŽŋāŽĒā¯āŽĒāŽ•āŽŽā¯." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "YubiKey āŽŽāŽąā¯āŽąā¯āŽŽā¯ Duo āŽĒā¯‹āŽŠā¯āŽą āŽĒāŽŋāŽ°āŽ¤ā¯āŽ¯ā¯‡āŽ• āŽ‡āŽ°āŽŖā¯āŽŸā¯-āŽĒāŽŸāŽŋ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽĩ❁ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "āŽ‡āŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽāŽąā¯āŽąā¯āŽŽāŽ¤āŽŋ" }, - "exportVault": { - "message": "āŽĒā¯†āŽŸā¯āŽŸāŽ•āŽ¤ā¯āŽ¤ā¯ˆ āŽāŽąā¯āŽąā¯āŽŽāŽ¤āŽŋ āŽšā¯†āŽ¯ā¯" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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": "āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽĩāŽŸāŽŋāŽĩāŽŽā¯" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "āŽ‰āŽ°ā¯āŽĒā¯āŽĒāŽŸāŽŋ āŽ¨āŽŋāŽ°āŽ¨ā¯āŽ¤āŽ°āŽŽāŽžāŽ• āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "āŽ‰āŽ°ā¯āŽĒā¯āŽĒāŽŸāŽŋ āŽŽā¯€āŽŸā¯āŽŸā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "āŽ¤ā¯ŠāŽŸāŽ°ā¯āŽĒā¯āŽ¤ā¯ āŽ¤āŽ•āŽĩāŽ˛ā¯" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "āŽ…āŽŠā¯ˆāŽ¤ā¯āŽ¤ā¯ āŽ…āŽŠā¯āŽĒā¯āŽĒā¯āŽ¤āŽ˛ā¯āŽ•āŽŗā¯āŽŽā¯", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "āŽ‡āŽ¨ā¯āŽ¤ Send-āŽ āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽ¨ā¯€āŽ•ā¯āŽ• āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "āŽ•āŽŋāŽŗāŽŋāŽĒā¯āŽĒā¯‹āŽ°ā¯āŽŸā¯āŽ•ā¯āŽ•ā¯ Send āŽ‡āŽŖā¯ˆāŽĒā¯āŽĒ❈ āŽ¨āŽ•āŽ˛ā¯†āŽŸā¯āŽ•ā¯āŽ•āŽĩā¯āŽŽā¯", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "āŽŽā¯āŽ¤āŽŠā¯āŽŽā¯ˆ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽ¨ā¯€āŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "āŽĒāŽŋāŽŠā¯āŽĩāŽ°ā¯āŽŽā¯ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒāŽŋāŽŠā¯ āŽ‰āŽąā¯āŽĒā¯āŽĒāŽŋāŽŠāŽ°ā¯āŽ•āŽŗā¯āŽ•ā¯āŽ•ā¯ āŽ’āŽ°ā¯ āŽŽā¯āŽ¤āŽŠā¯āŽŽā¯ˆ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯ āŽ‡āŽŠāŽŋ āŽ¤ā¯‡āŽĩā¯ˆāŽ¯āŽŋāŽ˛ā¯āŽ˛ā¯ˆ. āŽ•ā¯€āŽ´ā¯‡ āŽ‰āŽŗā¯āŽŗ āŽŸā¯ŠāŽŽā¯ˆāŽŠā¯ˆ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¨āŽŋāŽąā¯āŽĩāŽŠ āŽ¨āŽŋāŽ°ā¯āŽĩāŽžāŽ•āŽŋāŽ¯ā¯āŽŸāŽŠā¯ āŽ‰āŽąā¯āŽ¤āŽŋāŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽĩā¯āŽŽā¯." - }, "organizationName": { "message": "āŽ¨āŽŋāŽąā¯āŽĩāŽŠāŽ¤ā¯āŽ¤āŽŋāŽŠā¯ āŽĒā¯†āŽ¯āŽ°ā¯" }, @@ -2991,7 +3125,8 @@ "message": "\"āŽ’āŽ°ā¯āŽĒā¯‹āŽ¤ā¯āŽŽā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ\" āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ āŽĩāŽŋāŽ°ā¯āŽŽā¯āŽĒā¯āŽ•āŽŋāŽąā¯€āŽ°ā¯āŽ•āŽŗāŽž? āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĒā¯‚āŽŸā¯āŽŸā¯ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ™ā¯āŽ•āŽŗā¯ˆ \"āŽ’āŽ°ā¯āŽĒā¯‹āŽ¤ā¯āŽŽā¯ āŽ‡āŽ˛ā¯āŽ˛ā¯ˆ\" āŽŽāŽŠ āŽ…āŽŽā¯ˆāŽĒā¯āŽĒāŽ¤ā¯ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽĩāŽžāŽ˛ā¯āŽŸā¯āŽŸāŽŋāŽŠā¯ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ• āŽšāŽžāŽĩāŽŋāŽ¯ā¯ˆ āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽšāŽžāŽ¤āŽŠāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽŋāŽąāŽ¤ā¯. āŽ‡āŽ¨ā¯āŽ¤ āŽĩāŽŋāŽ°ā¯āŽĒā¯āŽĒāŽ¤ā¯āŽ¤ā¯ˆāŽĒā¯ āŽĒāŽ¯āŽŠā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤āŽŋāŽŠāŽžāŽ˛ā¯, āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽšāŽžāŽ¤āŽŠāŽ¤ā¯āŽ¤ā¯ˆ āŽ¨ā¯€āŽ™ā¯āŽ•āŽŗā¯ āŽšāŽ°āŽŋāŽ¯āŽžāŽ•āŽĒā¯ āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽĒā¯āŽĒāŽ¤ā¯ˆ āŽ‰āŽąā¯āŽ¤āŽŋ āŽšā¯†āŽ¯ā¯āŽ¯ āŽĩā¯‡āŽŖā¯āŽŸā¯āŽŽā¯." }, "vault": { - "message": "āŽĩāŽžāŽ˛ā¯āŽŸā¯" + "message": "āŽĩāŽžāŽ˛ā¯āŽŸā¯", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "āŽŽā¯āŽ¤āŽŠā¯āŽŽā¯ˆ āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽ˛ā¯āŽ˛ā¯āŽŸāŽŠā¯ āŽ‰āŽŗā¯āŽ¨ā¯āŽ´ā¯ˆāŽ•" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "āŽĒā¯āŽŠā¯ˆāŽĒā¯āŽĒā¯†āŽ¯āŽ°ā¯ āŽŸā¯ŠāŽŽā¯ˆāŽŠā¯" }, - "importData": { - "message": "āŽ¤āŽ°āŽĩ❈ āŽ‡āŽąāŽ•ā¯āŽ•ā¯āŽŽāŽ¤āŽŋ āŽšā¯†āŽ¯ā¯", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "āŽ‡āŽąāŽ•ā¯āŽ•ā¯āŽŽāŽ¤āŽŋ āŽĒāŽŋāŽ´ā¯ˆ" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "āŽ•ā¯‹āŽĒā¯āŽĒ❁ āŽšāŽžāŽ¤āŽŠāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯ āŽšā¯‡āŽŽāŽŋāŽ•ā¯āŽ•āŽĒā¯āŽĒāŽŸā¯āŽŸāŽ¤ā¯. āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽšāŽžāŽ¤āŽŠ āŽĒāŽ¤āŽŋāŽĩāŽŋāŽąāŽ•ā¯āŽ•āŽ™ā¯āŽ•āŽŗāŽŋāŽ˛āŽŋāŽ°ā¯āŽ¨ā¯āŽ¤ā¯ āŽ¨āŽŋāŽ°ā¯āŽĩāŽ•āŽŋ." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "āŽ¤āŽŋāŽ°ā¯ˆ āŽĒāŽŋāŽŸāŽŋāŽĒā¯āŽĒ❈ āŽ…āŽŠā¯āŽŽāŽ¤āŽŋ" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "āŽ‰āŽŖāŽ°ā¯āŽ¤āŽŋāŽąāŽŠā¯ āŽ¤āŽ•āŽĩāŽ˛ā¯āŽ•āŽŗā¯ˆāŽĒā¯ āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽĒā¯āŽĒāŽžāŽ• āŽ…āŽŠā¯āŽĒā¯āŽĒāŽĩā¯āŽŽā¯", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "āŽŽāŽ¨ā¯āŽ¤āŽĩā¯ŠāŽ°ā¯ āŽ¤āŽŗāŽ¤ā¯āŽ¤āŽŋāŽ˛ā¯āŽŽā¯, āŽ¯āŽžāŽ°ā¯āŽŸāŽŠā¯āŽŽā¯ āŽ•ā¯‹āŽĒā¯āŽĒā¯āŽ•āŽŗā¯ˆāŽ¯ā¯āŽŽā¯ āŽ¤āŽ°āŽĩā¯ˆāŽ¯ā¯āŽŽā¯ āŽĒāŽžāŽ¤ā¯āŽ•āŽžāŽĒā¯āŽĒāŽžāŽ•āŽĒā¯ āŽĒāŽ•āŽŋāŽ°āŽĩā¯āŽŽā¯. āŽ‰āŽ™ā¯āŽ•āŽŗā¯ āŽ¤āŽ•āŽĩāŽ˛ā¯ āŽĩā¯†āŽŗāŽŋāŽĒā¯āŽĒāŽžāŽŸā¯āŽŸā¯ˆāŽ•ā¯ āŽ•āŽŸā¯āŽŸā¯āŽĒā¯āŽĒāŽŸā¯āŽ¤ā¯āŽ¤ā¯āŽŽā¯āŽĒā¯‹āŽ¤ā¯, āŽ…āŽ¤ā¯ āŽŽā¯āŽ´ā¯āŽŽā¯ˆāŽ¯āŽžāŽŠ āŽ•ā¯āŽąāŽŋāŽ¯āŽžāŽ•ā¯āŽ•āŽŽāŽžāŽ• āŽ‡āŽ°ā¯āŽ•ā¯āŽ•ā¯āŽŽā¯.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "āŽ•āŽŸāŽĩā¯āŽšā¯āŽšā¯ŠāŽąā¯āŽ•āŽŗā¯ˆ āŽĩāŽŋāŽ°ā¯ˆāŽĩāŽžāŽ• āŽ‰āŽ°ā¯āŽĩāŽžāŽ•ā¯āŽ•āŽĩā¯āŽŽā¯" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index d607bb8d097..c4a52a24d6f 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "New URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "An unexpected error has occurred." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "Item information" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Learn more" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Feature unavailable" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow us" }, - "syncVault": { - "message": "Sync vault" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "Change master password" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index d794ace629c..844fe92c955 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "URL āšƒā¸Ģā¸Ąāšˆ" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Add attachment" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "Maximum file size is 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "ā¸‚āš‰ā¸­ā¸œā¸´ā¸”ā¸žā¸Ĩ⏞⏔⏗ā¸ĩāšˆāš„ā¸Ąāšˆā¸„ā¸˛ā¸”ā¸„ā¸´ā¸”āš„ā¸”āš‰āš€ā¸ā¸´ā¸”ā¸‚ā¸ļāš‰ā¸™." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "ā¸‚āš‰ā¸­ā¸Ąā¸šā¸Ĩ⏪⏞ā¸ĸ⏁⏞⏪" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "āš€ā¸Ŗā¸ĩā¸ĸā¸™ā¸Ŗā¸šāš‰āš€ā¸žā¸´āšˆā¸Ąāš€ā¸•ā¸´ā¸Ą" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Feature Unavailable" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Follow Us" }, - "syncVault": { - "message": "Sync Vault" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "āš€ā¸›ā¸Ĩā¸ĩāšˆā¸ĸ⏙⏪ā¸Ģā¸ąā¸Ēā¸œāšˆā¸˛ā¸™ā¸Ģā¸Ĩā¸ąā¸" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 GB of encrypted file storage." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "Export from" }, - "exportVault": { - "message": "Export Vault" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "Item permanently deleted" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "Item restored" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "ā¸Ēāšˆā¸‡ā¸—ā¸ąāš‰ā¸‡ā¸Ģā¸Ąā¸”", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Are you sure you want to delete this Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Copy Send link to clipboard", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Master password removed" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." - }, "organizationName": { "message": "Organization name" }, @@ -2991,7 +3125,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias domain" }, - "importData": { - "message": "Import data", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Import error" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "File saved to device. Manage from your device downloads." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "Allow screen capture" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "Missing website" }, @@ -3906,10 +4112,16 @@ "message": "Send sensitive information safely", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "Quickly create passwords" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Type shortcut" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Invalid shortcut" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Unarchive" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "Items in archive" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Archive item" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ZIP / Postal code" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "And more!" }, - "planDescPremium": { - "message": "Complete online security" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "Upgrade to Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index ac67b177cbf..0ff95da162e 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "Bu Send belirtilen tarihte kalÄącÄą olacak silinecek.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "PaylaÅŸÄąlacak dosya" + }, + "hideTextByDefault": { + "message": "Metni varsayÄąlan olarak gizle" + }, + "hideYourEmail": { + "message": "E-posta adresimi Send'i gÃļrÃŧntÃŧleyenlerden gizle." + }, + "limitSendViews": { + "message": "GÃļsterimi sÄąnÄąrla" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ gÃļsterim kaldÄą", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "Bu sÄąnÄąra ulaÅŸÄąldÄąktan sonra bu Send'i kimse gÃļremez.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Özel not" + }, + "sendDetails": { + "message": "Send ayrÄąntÄąlarÄą", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "AlÄącÄąlarÄąn bu Send'e erişmesi için isterseniz parola ekleyebilirsiniz.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "PaylaÅŸÄąlacak metin" + }, + "newItemHeaderTextSend": { + "message": "Yeni Send metni", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "Yeni Send dosyasÄą", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Send metnini dÃŧzenle", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Send dosyasÄąnÄą dÃŧzenle", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Bu Send'i kalÄącÄą olarak silmek istediğinizden emin misiniz?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "Yeni", + "description": "for adding new items" + }, "newUri": { "message": "Yeni URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Dosya ekle" }, + "itemsTransferred": { + "message": "KayÄątlar aktarÄąldÄą" + }, + "fixEncryption": { + "message": "Şifrelemeyi dÃŧzelt" + }, + "fixEncryptionTooltip": { + "message": "Bu dosya eski bir şifreleme yÃļntemi kullanÄąyor." + }, + "attachmentUpdated": { + "message": "Ek gÃŧncellendi" + }, "maxFileSizeSansPunctuation": { "message": "Maksimum dosya boyutu 500 MB'dir" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "Beklenmedik bir hata oluştu." }, + "unexpectedErrorShort": { + "message": "Beklenmeyen hata" + }, + "closeThisBitwardenWindow": { + "message": "Bu Bitwarden penceresini kapatÄąp yeniden deneyin." + }, "itemInformation": { "message": "KayÄąt bilgileri" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "Daha fazla bilgi al" }, + "migrationsFailed": { + "message": "Şifreleme ayarlarÄą gÃŧncellenirken bir hata oluştu." + }, + "updateEncryptionSettingsTitle": { + "message": "Şifreleme ayarlarÄąnÄązÄą gÃŧncelleyin" + }, + "updateEncryptionSettingsDesc": { + "message": "Önerilen yeni şifreleme ayarlarÄą hesap gÃŧvenliğinizi artÄąracaktÄąr. Şimdi gÃŧncellemek için ana parolanÄązÄą girin." + }, + "confirmIdentityToContinue": { + "message": "Devam etmek için kimliğinizi doğrulayÄąn" + }, + "enterYourMasterPassword": { + "message": "Ana parolanÄązÄą girin" + }, + "updateSettings": { + "message": "AyarlarÄą gÃŧncelle" + }, "featureUnavailable": { "message": "Özellik kullanÄąlamÄąyor" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Bizi takip edin" }, - "syncVault": { - "message": "KasayÄą eşitle" + "syncNow": { + "message": "Şimdi eşitle" }, "changeMasterPass": { "message": "Ana parolayÄą değiştir" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "Dosya ekleri için 1 GB şifrelenmiş depolama." }, + "premiumSignUpStorageV2": { + "message": "Dosya ekleri için $SIZE$ şifrelenmiş depolama.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "YubiKey ve Duo gibi marka bazlÄą iki aşamalÄą giriş seçenekleri." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "DÄąÅŸa aktarÄąlacak konum" }, - "exportVault": { - "message": "KasayÄą dÄąÅŸa aktar" + "exportNoun": { + "message": "DÄąÅŸa aktar", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "DÄąÅŸa aktar", + "description": "The verb 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" @@ -1761,7 +1887,7 @@ "message": "Hesap kÄąsÄątlÄą" }, "restrictCardTypeImport": { - "message": "Kart Ãļge tÃŧrleri içe aktarÄąlamÄąyor" + "message": "Kart kayÄąt tÃŧrleri içe aktarÄąlamÄąyor" }, "restrictCardTypeImportDesc": { "message": "1 veya daha fazla kuruluş tarafÄąndan belirlenen bir ilke, kasalarÄąnÄąza kart aktarmanÄązÄą engelliyor." @@ -1841,7 +1967,7 @@ "message": "Kilidi Windows Hello ile aç" }, "unlockWithPolkit": { - "message": "Sistem kimlik doğrulamasÄą ile kilit açma" + "message": "Kilidi sistem kimlik doğrulamasÄąyla aç" }, "windowsHelloConsentMessage": { "message": "Bitwarden için doğrulayÄąn." @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "KayÄąt kalÄącÄą olarak silindi" }, + "archivedItemRestored": { + "message": "Arşivlenmiş kayÄąt geri getirildi" + }, "restoredItem": { "message": "KayÄąt geri yÃŧklendi" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "İletişim bilgileri" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "TÃŧm Send'ler", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Bu Send'i silmek istediğinizden emin misiniz?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Send bağlantÄąsÄąnÄą kopyala", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Send linkini panoya kopyala", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "Ana parola kaldÄąrÄąldÄą" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Aşağıdaki organizasyonun Ãŧyeleri için artÄąk ana parola gerekmemektedir. LÃŧtfen alan adÄąnÄą organizasyon yÃļneticinizle doğrulayÄąn." - }, "organizationName": { "message": "Kuruluş adÄą" }, @@ -2679,7 +2813,7 @@ } }, "exportingOrganizationVaultFromPasswordManagerWithDataOwnershipDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported.", + "message": "YalnÄązca $ORGANIZATION$ ile ilişkili kuruluş kasasÄą dÄąÅŸa aktarÄąlacaktÄąr.", "placeholders": { "organization": { "content": "$1", @@ -2688,7 +2822,7 @@ } }, "exportingOrganizationVaultFromAdminConsoleWithDataOwnershipDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. My items collections will not be included.", + "message": "YalnÄązca $ORGANIZATION$ ile ilişkilendirilmiş kuruluş kasasÄą dÄąÅŸa aktarÄąlacaktÄąr. KayÄątlarÄąm koleksiyonlarÄą dahil edilmeyecek.", "placeholders": { "organization": { "content": "$1", @@ -2991,7 +3125,8 @@ "message": "\"Asla\" seçeneğini kullanmak istediğinizden emin misiniz? Kilit seçeneklerinizi \"Asla\" olarak ayarlarsanÄąz kasanÄązÄąn şifreleme anahtarÄą cihazÄąnÄązda saklanacaktÄąr. Bu seçeneği kullanÄąrsanÄąz cihazÄąnÄązÄą çok iyi korumalÄąsÄąnÄąz." }, "vault": { - "message": "Kasa" + "message": "Kasa", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Ana parola ile giriş yap" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "Alias alan adÄą" }, - "importData": { - "message": "Verileri içe aktar", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "İçe aktarma hatasÄą" }, @@ -3819,22 +3950,22 @@ "message": "UyarÄą: AracÄą YÃļnlendirme" }, "agentForwardingWarningText": { - "message": "Bu istek, oturum açtığınÄąz uzak bir cihazdan gelir" + "message": "Bu istek, giriş yaptığınÄąz uzak bir cihazdan geliyor" }, "sshkeyApprovalMessageInfix": { - "message": "erişim istiyor" + "message": "buraya erişmek istiyor:" }, "sshkeyApprovalMessageSuffix": { - "message": "amacÄąyla" + "message": "amaç:" }, "sshActionLogin": { - "message": "bir sunucuya kimlik doğrulamak" + "message": "sunucuda kimlik doğrulamak" }, "sshActionSign": { - "message": "bir iletiyi imzala" + "message": "ileti imzalamak" }, "sshActionGitSign": { - "message": "bir git commit'i imzala" + "message": "git commit'i imzalamak" }, "unknownApplication": { "message": "Bir uygulama" @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Dosya cihaza kaydedildi. CihazÄąnÄązÄąn indirilenler klasÃļrÃŧnden yÃļnetebilirsiniz." }, + "importantNotice": { + "message": "Önemli uyarÄą" + }, + "setupTwoStepLogin": { + "message": "İki adÄąmlÄą girişi ayarlayÄąn" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Şubat 2025 itibarÄąyla Bitwarden, yeni cihazlardan yeni girişleri doğrulamanÄąz için e-posta adresinize bir kod gÃļnderecektir." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "HesabÄąnÄązÄą korumanÄąn alternatif bir yolu olarak iki adÄąmlÄą girişi etkinleştirebilirsiniz. Aksi halde e-posta adresinizin doğru olduğundan emin olmalÄąsÄąnÄąz." + }, + "remindMeLater": { + "message": "Daha sonra hatÄąrlat" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "$EMAIL$ adresinize sağlÄąklÄą bir şekilde erişebiliyor musunuz?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "HayÄąr, erişemiyorum" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Evet, e-postalarÄąma sağlÄąklÄą bir şekilde erişebiliyorum" + }, + "turnOnTwoStepLogin": { + "message": "İki adÄąmlÄą girişi etkinleştir" + }, + "changeAcctEmail": { + "message": "Hesap e-postasÄąnÄą değiştir" + }, + "passkeyLogin": { + "message": "Geçiş anahtarÄą ile giriş yapÄąlsÄąn mÄą?" + }, + "savePasskeyQuestion": { + "message": "Geçiş anahtarÄą kaydedilsin mi?" + }, + "saveNewPasskey": { + "message": "Yeni hesap olarak kaydet" + }, + "savePasskeyNewLogin": { + "message": "Geçiş anahtarÄąnÄą yeni hesap olarak kaydet" + }, + "noMatchingLoginsForSite": { + "message": "Bu siteyle eşleşen hesap bulunamadÄą" + }, + "overwritePasskey": { + "message": "Geçiş anahtarÄąnÄąn Ãŧzerine yazÄąlsÄąn mÄą?" + }, + "unableToSavePasskey": { + "message": "Geçiş anahtarÄą kaydedilemedi" + }, + "alreadyContainsPasskey": { + "message": "Bu kayÄąt zaten bir geçiş anahtarÄą içeriyor. Mevcut geçiş anahtarÄąnÄąn Ãŧzerine yazmak istediğinizden emin misiniz?" + }, + "passkeyAlreadyExists": { + "message": "Bu uygulama için bir geçiş anahtarÄą zaten mevcut." + }, + "applicationDoesNotSupportDuplicates": { + "message": "Bu uygulama yinelenen kayÄątlarÄą desteklemiyor." + }, + "closeThisWindow": { + "message": "Bu pencereyi kapat" + }, "allowScreenshots": { "message": "Ekran kaydÄąna izin ver" }, @@ -3861,7 +4061,7 @@ "message": "Bitwarden masaÃŧstÃŧ uygulamasÄąnÄąn ekran gÃļrÃŧntÃŧlerinde yakalanmasÄąna ve uzak masaÃŧstÃŧ oturumlarÄąnda gÃļrÃŧntÃŧlenmesine izin verin. Bunun devre dÄąÅŸÄą bÄąrakÄąlmasÄą bazÄą harici ekranlarda erişimi engelleyecektir." }, "confirmWindowStillVisibleTitle": { - "message": "Pencerenin hala gÃļrÃŧnÃŧr durumda olduğunu onayla" + "message": "Pencerenin hÃĸlÃĸ gÃļrÃŧnÃŧr durumda olduğunu onayla" }, "confirmWindowStillVisibleContent": { "message": "LÃŧtfen pencerenin hala gÃļrÃŧnÃŧr olduğunu onaylayÄąn." @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Bu hesap risk altÄąnda ve web sitesi eksik. Bir web sitesi ekleyin ve gÃŧvenliğinizi artÄąrmak için parolayÄą değiştirin." }, + "vulnerablePassword": { + "message": "GÃŧvensiz parola." + }, + "changeNow": { + "message": "Şimdi değiştir" + }, "missingWebsite": { "message": "Web sitesi eksik" }, @@ -3906,10 +4112,16 @@ "message": "Hassas bilgileri gÃŧvenle paylaÅŸÄąn", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "Hiçbir sonuç bulunamadÄą" + }, "sendsBodyNoItems": { "message": "DosyalarÄą ve verileri istediğiniz kişilerle, istediğiniz platformda paylaÅŸÄąn. Bilgileriniz başkalarÄąnÄąn eline geçmemesi için uçtan şifrelenecektir.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Filtreleri temizleyin veya başka bir arama yapmayÄą deneyin" + }, "generatorNudgeTitle": { "message": "HÄązlÄąca parola oluşturun" }, @@ -3980,7 +4192,7 @@ "message": "Bu ayar hakkÄąnda" }, "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, hangi simgenin veya parola değiştirme URL'sinin kullanÄąlacağınÄą belirlemek için kaydedilmiş hesap URI'larÄąnÄą kullanacaktÄąr; bu da deneyiminizi iyileştirmeye yardÄąmcÄą olur. Bu hizmeti kullandığınÄązda herhangi bir bilgi toplanmaz veya kaydedilmez." }, "assignToCollections": { "message": "Koleksiyonlara ata" @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "KÄąsayolu yazÄąn" }, - "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Geçersiz kÄąsayol" @@ -4148,7 +4360,7 @@ "message": "Onayla" }, "enableAutotypeShortcutPreview": { - "message": "Enable autotype shortcut (Feature Preview)" + "message": "Otomatik yazma kÄąsayolunu etkinleştir (Özellik Önizlemesi)" }, "enableAutotypeShortcutDescription": { "message": "Verilerin yanlÄąÅŸ yere doldurulmasÄąnÄą Ãļnlemek için kÄąsayolu kullanmadan Ãļnce doğru alanda olduğunuzdan emin olun." @@ -4167,14 +4379,17 @@ "unArchive": { "message": "Arşivden Ã§Äąkar" }, + "archived": { + "message": "Arşivlendi" + }, "itemsInArchive": { - "message": "Items in archive" + "message": "Arşivdeki kayÄątlar" }, "noItemsInArchive": { - "message": "No items in archive" + "message": "Arşivde kayÄąt yok" }, "noItemsInArchiveDesc": { - "message": "Archived items will appear here and will be excluded from general search results and autofill suggestions." + "message": "Arşivlenmiş kayÄątlar burada gÃļrÃŧnecek ve genel arama sonuçlarÄą ile otomatik doldurma Ãļnerilerinden hariç tutulacaktÄąr." }, "itemWasSentToArchive": { "message": "KayÄąt arşive gÃļnderildi" @@ -4183,10 +4398,25 @@ "message": "KayÄąt arşivden Ã§ÄąkarÄąldÄą" }, "archiveItem": { - "message": "Archive item" + "message": "KaydÄą arşivle" }, - "archiveItemConfirmDesc": { - "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Arşivden Ã§Äąkar ve kaydet" + }, + "restartPremium": { + "message": "Premium’u yeniden başlat" + }, + "premiumSubscriptionEnded": { + "message": "Premium aboneliğiniz sona erdi" + }, + "premiumSubscriptionEndedDesc": { + "message": "Arşivinize yeniden erişebilmek için Premium aboneliğinizi yeniden başlatÄąn. Yeniden başlatmadan Ãļnce arşivlenmiş bir kaydÄąn ayrÄąntÄąlarÄąnÄą dÃŧzenlerseniz kayÄąt tekrar kasanÄąza taÅŸÄąnÄąr." + }, + "itemRestored": { + "message": "KayÄąt geri yÃŧklendi" }, "zipPostalCodeLabel": { "message": "ZIP / posta kodu" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "Ve daha fazlasÄą!" }, - "planDescPremium": { - "message": "Eksiksiz çevrimiçi gÃŧvenlik" + "advancedOnlineSecurity": { + "message": "Gelişmiş çevrimiçi gÃŧvenlik" }, "upgradeToPremium": { "message": "Premium'a yÃŧkselt" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Zaman aÅŸÄąmÄą eylemi" }, "sessionTimeoutHeader": { "message": "Oturum zaman aÅŸÄąmÄą" + }, + "resizeSideNavigation": { + "message": "Kenar menÃŧsÃŧnÃŧ yeniden boyutlandÄąr" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Bu ayar kuruluşunuz tarafÄąndan yÃļnetiliyor." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Kuruluşunuz maksimum kasa zaman aÅŸÄąmÄąnÄą $HOURS$ saat $MINUTES$ dakika olarak belirlemiş.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Kuruluşunuz varsayÄąlan oturum zaman aÅŸÄąmÄąnÄą “Sistem kilitlenince” olarak ayarlamÄąÅŸ." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Kuruluşunuz varsayÄąlan oturum zaman aÅŸÄąmÄąnÄą “Yeniden başlatÄąlÄąnca” olarak ayarlamÄąÅŸ." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maksimum zaman aÅŸÄąmÄą en fazla $HOURS$ saat $MINUTES$ dakika olabilir", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Yeniden başlatÄąlÄąnca" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Zaman aÅŸÄąmÄą eyleminizi değiştirmek için kilit açma yÃļnteminizi ayarlayÄąn" + }, + "upgrade": { + "message": "YÃŧkselt" + }, + "leaveConfirmationDialogTitle": { + "message": "AyrÄąlmak istediğinizden emin misiniz?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Reddetmeniz halinde kişisel kayÄątlarÄąnÄąz hesabÄąnÄązda kalmaya devam edecek, ancak paylaÅŸÄąlan kayÄątlara ve kuruluş Ãļzelliklerine erişiminizi kaybedeceksiniz." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Erişiminizi yeniden kazanmak için yÃļneticinizle iletişime geçin." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "$ORGANIZATION$ kuruluşundan ayrÄąl", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "KasamÄą nasÄąl yÃļnetebilirim?" + }, + "transferItemsToOrganizationTitle": { + "message": "KayÄątlarÄą $ORGANIZATION$ kuruluşuna aktar", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$, gÃŧvenlik ve mevzuata uyum amacÄąyla tÃŧm kayÄątlarÄąn kuruluşa ait olmasÄąnÄą zorunlu kÄąlÄąyor. KayÄątlarÄąnÄązÄąn sahipliğini devretmek için \"Kabul et\"e tÄąklayÄąn.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "AktarÄąmÄą kabul et" + }, + "declineAndLeave": { + "message": "Reddet ve ayrÄąl" + }, + "whyAmISeeingThis": { + "message": "Bunu neden gÃļrÃŧyorum?" } } diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index 7ed0710ca74..7e068b33d02 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "The Send will be permanently deleted on this date.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "File to share" + }, + "hideTextByDefault": { + "message": "Hide text by default" + }, + "hideYourEmail": { + "message": "Hide your email address from viewers." + }, + "limitSendViews": { + "message": "Limit views" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ views left", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "No one can view this Send after the limit is reached.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Private note" + }, + "sendDetails": { + "message": "Send details", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "Add an optional password for recipients to access this Send.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Text to share" + }, + "newItemHeaderTextSend": { + "message": "New Text Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "New File Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Edit Text Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Edit File Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "Are you sure you want to permanently delete this Send?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "New", + "description": "for adding new items" + }, "newUri": { "message": "Новий URI" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "Đ”ĐžĐ´Đ°Ņ‚Đ¸ вĐēĐģадĐĩĐŊĐŊŅ" }, + "itemsTransferred": { + "message": "Items transferred" + }, + "fixEncryption": { + "message": "Fix encryption" + }, + "fixEncryptionTooltip": { + "message": "This file is using an outdated encryption method." + }, + "attachmentUpdated": { + "message": "Attachment updated" + }, "maxFileSizeSansPunctuation": { "message": "МаĐēŅĐ¸ĐŧаĐģҌĐŊиК Ņ€ĐžĐˇĐŧŅ–Ņ€ Ņ„Đ°ĐšĐģ҃ – 500 МБ" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "ĐĄŅ‚Đ°ĐģĐ°ŅŅ ĐŊĐĩĐžŅ‡Ņ–ĐēŅƒĐ˛Đ°ĐŊа ĐŋĐžĐŧиĐģĐēа." }, + "unexpectedErrorShort": { + "message": "Unexpected error" + }, + "closeThisBitwardenWindow": { + "message": "Close this Bitwarden window and try again." + }, "itemInformation": { "message": "ІĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ ĐŋŅ€Đž СаĐŋĐ¸Ņ" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "ДоĐēĐģадĐŊŅ–ŅˆĐĩ" }, + "migrationsFailed": { + "message": "An error occurred updating the encryption settings." + }, + "updateEncryptionSettingsTitle": { + "message": "Update your encryption settings" + }, + "updateEncryptionSettingsDesc": { + "message": "The new recommended encryption settings will improve your account security. Enter your master password to update now." + }, + "confirmIdentityToContinue": { + "message": "Confirm your identity to continue" + }, + "enterYourMasterPassword": { + "message": "Enter your master password" + }, + "updateSettings": { + "message": "Update settings" + }, "featureUnavailable": { "message": "Đ¤ŅƒĐŊĐēŅ†Ņ–Ņ ĐŊĐĩĐ´ĐžŅŅ‚ŅƒĐŋĐŊа" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "ĐĄŅ‚ĐĩĐļŅ‚Đĩ Са ĐŊаĐŧи" }, - "syncVault": { - "message": "ХиĐŊŅ…Ņ€ĐžĐŊŅ–ĐˇŅƒĐ˛Đ°Ņ‚Đ¸ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đĩ" + "syncNow": { + "message": "Sync now" }, "changeMasterPass": { "message": "ЗĐŧŅ–ĐŊĐ¸Ņ‚Đ¸ ĐŗĐžĐģОвĐŊиК ĐŋĐ°Ņ€ĐžĐģҌ" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1 ГБ ĐˇĐ°ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊĐžĐŗĐž ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ° Đ´ĐģŅ Ņ„Đ°ĐšĐģŅ–Đ˛." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Đ”ĐžĐ´Đ°Ņ‚ĐēĐžĐ˛Ņ– ĐŧĐžĐļĐģĐ¸Đ˛ĐžŅŅ‚Ņ– двОĐĩŅ‚Đ°ĐŋĐŊĐžŅ— Đ°Đ˛Ņ‚ĐžŅ€Đ¸ĐˇĐ°Ņ†Ņ–Ņ—, ŅĐē-ĐžŅ‚ YubiKey Ņ‚Đ° Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "ЕĐēҁĐŋĐžŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ С" }, - "exportVault": { - "message": "ЕĐēҁĐŋĐžŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đĩ" + "exportNoun": { + "message": "Export", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "Export", + "description": "The verb 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": "Đ¤ĐžŅ€ĐŧĐ°Ņ‚ Ņ„Đ°ĐšĐģ҃" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "ЗаĐŋĐ¸Ņ ĐžŅŅ‚Đ°Ņ‚ĐžŅ‡ĐŊĐž видаĐģĐĩĐŊĐž" }, + "archivedItemRestored": { + "message": "Archived item restored" + }, "restoredItem": { "message": "ЗаĐŋĐ¸Ņ Đ˛Ņ–Đ´ĐŊОвĐģĐĩĐŊĐž" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "КоĐŊŅ‚Đ°ĐēŅ‚ĐŊа Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "ĐŖŅŅ– Đ˛Ņ–Đ´ĐŋŅ€Đ°Đ˛ĐģĐĩĐŊĐŊŅ", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "Ви Đ´Ņ–ĐšŅĐŊĐž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ видаĐģĐ¸Ņ‚Đ¸ ҆Đĩ Đ˛Ņ–Đ´ĐŋŅ€Đ°Đ˛ĐģĐĩĐŊĐŊŅ?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Copy Send link", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "КоĐŋŅ–ŅŽĐ˛Đ°Ņ‚Đ¸ ĐŋĐžŅĐ¸ĐģаĐŊĐŊŅ ĐŊа Đ˛Ņ–Đ´ĐŋŅ€Đ°Đ˛ĐģĐĩĐŊĐŊŅ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "ГоĐģОвĐŊиК ĐŋĐ°Ņ€ĐžĐģҌ виĐģŅƒŅ‡ĐĩĐŊĐž" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "ГоĐģОвĐŊиК ĐŋĐ°Ņ€ĐžĐģҌ ĐąŅ–ĐģҌ҈Đĩ ĐŊĐĩ Ņ” ОйОв'ŅĐˇĐēОвиĐŧ Đ´ĐģŅ ŅƒŅ‡Đ°ŅĐŊиĐēŅ–Đ˛ СаСĐŊĐ°Ņ‡ĐĩĐŊĐžŅ— ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ—. ĐŸŅ–Đ´Ņ‚Đ˛ĐĩŅ€Đ´ŅŒŅ‚Đĩ вĐēаСаĐŊиК ĐŊиĐļ҇Đĩ Đ´ĐžĐŧĐĩĐŊ С адĐŧŅ–ĐŊŅ–ŅŅ‚Ņ€Đ°Ņ‚ĐžŅ€ĐžĐŧ Đ˛Đ°ŅˆĐžŅ— ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ—." - }, "organizationName": { "message": "Назва ĐžŅ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Ņ†Ņ–Ņ—" }, @@ -2991,7 +3125,8 @@ "message": "Ви вĐŋĐĩвĐŊĐĩĐŊŅ–, Ņ‰Đž ĐŊŅ–ĐēĐžĐģи ĐŊĐĩ Ņ…ĐžŅ‡ĐĩŅ‚Đĩ ĐąĐģĐžĐēŅƒĐ˛Đ°Ņ‚Đ¸? Đ’ŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Đ˛ŅˆĐ¸ ĐŋĐ°Ņ€Đ°ĐŧĐĩ҂Ҁ ĐąĐģĐžĐēŅƒĐ˛Đ°ĐŊĐŊŅ \"ĐŅ–ĐēĐžĐģи\", ĐēĐģŅŽŅ‡ ŅˆĐ¸Ņ„Ņ€ŅƒĐ˛Đ°ĐŊĐŊŅ ŅŅ…ĐžĐ˛Đ¸Ņ‰Đ° СйĐĩŅ€Ņ–ĐŗĐ°Ņ‚Đ¸ĐŧĐĩŅ‚ŅŒŅŅ ĐŊа Đ˛Đ°ŅˆĐžĐŧ҃ ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—. ĐšĐžŅ€Đ¸ŅŅ‚ŅƒŅŽŅ‡Đ¸ŅŅŒ Ņ†Đ¸Đŧ ĐŋĐ°Ņ€Đ°ĐŧĐĩŅ‚Ņ€ĐžĐŧ, ви ĐŧĐ°Ņ”Ņ‚Đĩ ĐąŅƒŅ‚Đ¸ вĐŋĐĩвĐŊĐĩĐŊŅ– в Ņ‚ĐžĐŧ҃, Ņ‰Đž Đ˛Đ°Ņˆ ĐŋŅ€Đ¸ŅŅ‚Ņ€Ņ–Đš ĐŊĐ°Đ´Ņ–ĐšĐŊĐž ĐˇĐ°Ņ…Đ¸Ņ‰ĐĩĐŊиК." }, "vault": { - "message": "ĐĄŅ…ĐžĐ˛Đ¸Ņ‰Đĩ" + "message": "ĐĄŅ…ĐžĐ˛Đ¸Ņ‰Đĩ", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "ĐŖĐ˛Ņ–ĐšŅ‚Đ¸ С ĐŗĐžĐģОвĐŊиĐŧ ĐŋĐ°Ņ€ĐžĐģĐĩĐŧ" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "ĐŸŅĐĩвдОĐŊŅ–Đŧ Đ´ĐžĐŧĐĩĐŊ҃" }, - "importData": { - "message": "ІĐŧĐŋĐžŅ€Ņ‚ŅƒĐ˛Đ°Ņ‚Đ¸ даĐŊŅ–", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "ПоĐŧиĐģĐēа Ņ–ĐŧĐŋĐžŅ€Ņ‚Ņƒ" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "ФаКĐģ СйĐĩŅ€ĐĩĐļĐĩĐŊĐž ĐŊа ĐŋŅ€Đ¸ŅŅ‚Ņ€ĐžŅ—. Ви ĐŧĐžĐļĐĩŅ‚Đĩ ĐšĐžĐŗĐž СĐŊĐ°ĐšŅ‚Đ¸ ҃ Ņ‚Đĩ҆Җ СаваĐŊŅ‚Đ°ĐļĐĩĐŊҌ." }, + "importantNotice": { + "message": "Important notice" + }, + "setupTwoStepLogin": { + "message": "Set up two-step login" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden will send a code to your account email to verify logins from new devices starting in February 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "You can set up two-step login as an alternative way to protect your account or change your email to one you can access." + }, + "remindMeLater": { + "message": "Remind me later" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "Do you have reliable access to your email, $EMAIL$?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "No, I do not" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "Yes, I can reliably access my email" + }, + "turnOnTwoStepLogin": { + "message": "Turn on two-step login" + }, + "changeAcctEmail": { + "message": "Change account email" + }, + "passkeyLogin": { + "message": "Log in with passkey?" + }, + "savePasskeyQuestion": { + "message": "Save passkey?" + }, + "saveNewPasskey": { + "message": "Save as new login" + }, + "savePasskeyNewLogin": { + "message": "Save passkey as new login" + }, + "noMatchingLoginsForSite": { + "message": "No matching logins for this site" + }, + "overwritePasskey": { + "message": "Overwrite passkey?" + }, + "unableToSavePasskey": { + "message": "Unable to save passkey" + }, + "alreadyContainsPasskey": { + "message": "This item already contains a passkey. Are you sure you want to overwrite the current passkey?" + }, + "passkeyAlreadyExists": { + "message": "A passkey already exists for this application." + }, + "applicationDoesNotSupportDuplicates": { + "message": "This application does not support duplicates." + }, + "closeThisWindow": { + "message": "Close this window" + }, "allowScreenshots": { "message": "ДозвоĐģĐ¸Ņ‚Đ¸ ĐˇĐ°Ņ…ĐžĐŋĐģĐĩĐŊĐŊŅ ĐĩĐēŅ€Đ°ĐŊа" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "ĐĻĐĩĐš СаĐŋĐ¸Ņ Ņ€Đ¸ĐˇĐ¸ĐēОваĐŊиК, Ņ– ĐŊĐĩ ĐŧĐ°Ņ” Đ°Đ´Ņ€ĐĩŅĐ¸ вĐĩĐąŅĐ°ĐšŅ‚Ņƒ. Đ”ĐžĐ´Đ°ĐšŅ‚Đĩ Đ°Đ´Ņ€Đĩҁ҃ вĐĩĐąŅĐ°ĐšŅ‚Ņƒ Ņ– СĐŧŅ–ĐŊŅ–Ņ‚ŅŒ ĐŋĐ°Ņ€ĐžĐģҌ Đ´ĐģŅ Đ˛Đ´ĐžŅĐēĐžĐŊаĐģĐĩĐŊĐŊŅ ĐąĐĩСĐŋĐĩĐēи." }, + "vulnerablePassword": { + "message": "Vulnerable password." + }, + "changeNow": { + "message": "Change now" + }, "missingWebsite": { "message": "НĐĩĐŧĐ°Ņ” вĐĩĐąŅĐ°ĐšŅ‚Ņƒ" }, @@ -3906,10 +4112,16 @@ "message": "БĐĩСĐŋĐĩ҇ĐŊĐž ĐŊĐ°Đ´ŅĐ¸ĐģĐ°ĐšŅ‚Đĩ ĐēĐžĐŊŅ„Ņ–Đ´ĐĩĐŊŅ†Ņ–ĐšĐŊ҃ Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–ŅŽ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "No search results returned" + }, "sendsBodyNoItems": { "message": "БĐĩСĐŋĐĩ҇ĐŊĐž Đ´Ņ–ĐģŅ–Ņ‚ŅŒŅŅ Ņ„Đ°ĐšĐģаĐŧи Đš даĐŊиĐŧи С ĐēиĐŧ ĐˇĐ°Đ˛ĐŗĐžĐ´ĐŊĐž, ĐŊа ĐąŅƒĐ´ŅŒ-ŅĐēŅ–Đš ĐŋĐģĐ°Ņ‚Ņ„ĐžŅ€ĐŧŅ–. Đ’Đ°ŅˆĐ° Ņ–ĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Ņ–Ņ ĐŊĐ°ŅĐēŅ€Ņ–ĐˇĐŊĐž ĐˇĐ°ŅˆĐ¸Ņ„Ņ€ĐžĐ˛Đ°ĐŊа Ņ‚Đ° ĐŧĐ°Ņ” ОйĐŧĐĩĐļĐĩĐŊиК Đ´ĐžŅŅ‚ŅƒĐŋ.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "Clear filters or try another search term" + }, "generatorNudgeTitle": { "message": "ШвидĐēĐž ŅŅ‚Đ˛ĐžŅ€ŅŽĐšŅ‚Đĩ ĐŋĐ°Ņ€ĐžĐģŅ–" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "ВвĐĩĐ´Ņ–Ņ‚ŅŒ ĐēĐžĐŧĐąŅ–ĐŊĐ°Ņ†Ņ–ŅŽ ĐēĐģĐ°Đ˛Ņ–Ņˆ" }, - "editAutotypeShortcutDescription": { - "message": "ВиĐēĐžŅ€Đ¸ŅŅ‚Đ°ĐšŅ‚Đĩ ОдиĐŊ айО два Ņ‚Đ°ĐēĐ¸Ņ… ĐŧĐžĐ´Đ¸Ņ„Ņ–ĐēĐ°Ņ†Ņ–Đš: Ctrl, Alt, Win, Shift, Ņ– ĐģŅ–Ņ‚ĐĩŅ€Ņƒ." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "НĐĩĐ´Ņ–ĐšŅĐŊа ĐēĐžĐŧĐąŅ–ĐŊĐ°Ņ†Ņ–Ņ ĐēĐģĐ°Đ˛Ņ–Ņˆ" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Đ’Đ¸Đ´ĐžĐąŅƒŅ‚Đ¸" }, + "archived": { + "message": "Archived" + }, "itemsInArchive": { "message": "ЗаĐŋĐ¸ŅĐ¸ в Đ°Ņ€Ņ…Ņ–Đ˛Ņ–" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "ĐŅ€Ņ…Ņ–Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸ СаĐŋĐ¸Ņ" }, - "archiveItemConfirmDesc": { - "message": "ĐŅ€Ņ…Ņ–Đ˛ĐžĐ˛Đ°ĐŊŅ– СаĐŋĐ¸ŅĐ¸ виĐēĐģŅŽŅ‡Đ°ŅŽŅ‚ŅŒŅŅ С Ņ€ĐĩĐˇŅƒĐģŅŒŅ‚Đ°Ņ‚Ņ–Đ˛ ĐˇĐ˛Đ¸Ņ‡Đ°ĐšĐŊĐžĐŗĐž ĐŋĐžŅˆŅƒĐē҃ Ņ‚Đ° ĐŋŅ€ĐžĐŋĐžĐˇĐ¸Ņ†Ņ–Đš Đ°Đ˛Ņ‚ĐžĐˇĐ°ĐŋОвĐŊĐĩĐŊĐŊŅ. Ви Đ´Ņ–ĐšŅĐŊĐž Ņ…ĐžŅ‡ĐĩŅ‚Đĩ Đ°Ņ€Ņ…Ņ–Đ˛ŅƒĐ˛Đ°Ņ‚Đ¸ ҆ĐĩĐš СаĐŋĐ¸Ņ?" + "archiveItemDialogContent": { + "message": "Once archived, this item will be excluded from search results and autofill suggestions." + }, + "unArchiveAndSave": { + "message": "Unarchive and save" + }, + "restartPremium": { + "message": "Restart Premium" + }, + "premiumSubscriptionEnded": { + "message": "Your Premium subscription ended" + }, + "premiumSubscriptionEndedDesc": { + "message": "To regain access to your archive, restart your Premium subscription. If you edit details for an archived item before restarting, it’ll be moved back into your vault." + }, + "itemRestored": { + "message": "Item has been restored" }, "zipPostalCodeLabel": { "message": "ĐŸĐžŅˆŅ‚ĐžĐ˛Đ¸Đš Ņ–ĐŊĐ´ĐĩĐēҁ" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "ІĐŊŅˆŅ– ĐŧĐžĐļĐģĐ¸Đ˛ĐžŅŅ‚Ņ–!" }, - "planDescPremium": { - "message": "ПовĐŊа ĐžĐŊĐģаКĐŊ-ĐąĐĩСĐŋĐĩĐēа" + "advancedOnlineSecurity": { + "message": "Advanced online security" }, "upgradeToPremium": { "message": "ПоĐēŅ€Đ°Ņ‰Đ¸Ņ‚Đ¸ Đ´Đž Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Your organization is no longer using master passwords to log into Bitwarden. To continue, verify the organization and domain." + }, + "continueWithLogIn": { + "message": "Continue with log in" + }, + "doNotContinue": { + "message": "Do not continue" + }, + "domain": { + "message": "Domain" + }, + "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." + }, + "verifyYourOrganization": { + "message": "Verify your organization to log in" + }, + "organizationVerified": { + "message": "Organization verified" + }, + "domainVerified": { + "message": "Domain verified" + }, + "leaveOrganizationContent": { + "message": "If you don't verify your organization, your access to the organization will be revoked." + }, + "leaveNow": { + "message": "Leave now" + }, + "verifyYourDomainToLogin": { + "message": "Verify your domain to log in" + }, + "verifyYourDomainDescription": { + "message": "To continue with log in, verify this domain." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "To continue with log in, verify the organization and domain." + }, "sessionTimeoutSettingsAction": { "message": "Timeout action" }, "sessionTimeoutHeader": { "message": "Session timeout" + }, + "resizeSideNavigation": { + "message": "Resize side navigation" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "This setting is managed by your organization." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Your organization has set the maximum session timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Your organization has set the default session timeout to On system lock." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Your organization has set the default session timeout to On restart." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Maximum timeout cannot exceed $HOURS$ hour(s) and $MINUTES$ minute(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "On restart" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Set an unlock method to change your timeout action" + }, + "upgrade": { + "message": "Upgrade" + }, + "leaveConfirmationDialogTitle": { + "message": "Are you sure you want to leave?" + }, + "leaveConfirmationDialogContentOne": { + "message": "By declining, your personal items will stay in your account, but you'll lose access to shared items and organization features." + }, + "leaveConfirmationDialogContentTwo": { + "message": "Contact your admin to regain access." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Leave $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "How do I manage my vault?" + }, + "transferItemsToOrganizationTitle": { + "message": "Transfer items to $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "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.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "Accept transfer" + }, + "declineAndLeave": { + "message": "Decline and leave" + }, + "whyAmISeeingThis": { + "message": "Why am I seeing this?" } } diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index 8bf88aba458..fcad01eb5b1 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -70,7 +70,7 @@ } }, "noEditPermissions": { - "message": "You don't have permission to edit this item" + "message": "BáēĄn không cÃŗ quyáģn cháģ‰nh sáģ­a máģĨc này" }, "welcomeBack": { "message": "Chào máģĢng báēĄn tráģŸ láēĄi" @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "Send sáēŊ đưáģŖc xÃŗa vÄŠnh viáģ…n vào ngày này.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "Táģ‡p đáģƒ chia sáēģ" + }, + "hideTextByDefault": { + "message": "áē¨n văn báēŖn theo máēˇc đáģ‹nh" + }, + "hideYourEmail": { + "message": "áē¨n đáģ‹a cháģ‰ email cáģ§a báēĄn váģ›i ngưáģi xem." + }, + "limitSendViews": { + "message": "Giáģ›i háēĄn sáģ‘ lưáģŖt xem" + }, + "limitSendViewsCount": { + "message": "$ACCESSCOUNT$ lưáģŖt xem cÃ˛n láēĄi", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "Không ai cÃŗ tháģƒ xem Send này sau khi đáēĄt đáēŋn giáģ›i háēĄn.", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "Ghi chÃē riÃĒng tư" + }, + "sendDetails": { + "message": "Chi tiáēŋt Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "ThÃĒm máē­t kháēŠu tÚy cháģn cho ngưáģi nháē­n đáģƒ cÃŗ tháģƒ truy cáē­p vào Send này.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "Náģ™i dung chia sáēģ" + }, + "newItemHeaderTextSend": { + "message": "Send văn báēŖn máģ›i", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "Send táē­p tin máģ›i", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "Sáģ­a Send văn báēŖn", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "Sáģ­a Send táē­p tin", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n xÃŗa vÄŠnh viáģ…n Send này không?", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "Máģ›i", + "description": "for adding new items" + }, "newUri": { "message": "Đưáģng dáēĢn máģ›i" }, @@ -708,6 +776,18 @@ "addAttachment": { "message": "ThÃĒm táģ‡p đính kèm" }, + "itemsTransferred": { + "message": "CÃĄc máģĨc Ä‘ÃŖ chuyáģƒn" + }, + "fixEncryption": { + "message": "Sáģ­a mÃŖ hÃŗa" + }, + "fixEncryptionTooltip": { + "message": "Táģ‡p này đang sáģ­ dáģĨng phÆ°ÆĄng phÃĄp mÃŖ hÃŗa láģ—i tháģi." + }, + "attachmentUpdated": { + "message": "Táģ‡p đính kèm Ä‘ÃŖ đưáģŖc cáē­p nháē­t" + }, "maxFileSizeSansPunctuation": { "message": "Kích thưáģ›c táģ‘i đa cáģ§a táē­p tin là 500MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "ÄÃŖ xáēŖy ra láģ—i không mong muáģ‘n." }, + "unexpectedErrorShort": { + "message": "Láģ—i báēĨt thưáģng" + }, + "closeThisBitwardenWindow": { + "message": "ÄÃŗng cáģ­a sáģ• Bitwarden này ráģ“i tháģ­ láēĄi." + }, "itemInformation": { "message": "Thông tin máģĨc" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "TÃŦm hiáģƒu thÃĒm" }, + "migrationsFailed": { + "message": "ÄÃŖ xáēŖy ra láģ—i khi cáē­p nháē­t cài đáēˇt mÃŖ hÃŗa." + }, + "updateEncryptionSettingsTitle": { + "message": "Cáē­p nháē­t cài đáēˇt mÃŖ hÃŗa cáģ§a báēĄn" + }, + "updateEncryptionSettingsDesc": { + "message": "Cài đáēˇt mÃŖ hÃŗa đưáģŖc khuyáēŋn ngháģ‹ sáēŊ cáēŖi thiáģ‡n báēŖo máē­t cho tài khoáēŖn cáģ§a báēĄn. Nháē­p máē­t kháēŠu chính đáģƒ cáē­p nháē­t ngay." + }, + "confirmIdentityToContinue": { + "message": "XÃĄc minh danh tính đáģƒ tiáēŋp táģĨc" + }, + "enterYourMasterPassword": { + "message": "Nháē­p máē­t kháēŠu chính cáģ§a báēĄn" + }, + "updateSettings": { + "message": "Cáē­p nháē­t cài đáēˇt" + }, "featureUnavailable": { "message": "Tính năng không cÃŗ sáēĩn" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "Theo dÃĩi chÃēng tôi" }, - "syncVault": { - "message": "Đáģ“ng báģ™ kho" + "syncNow": { + "message": "Đáģ“ng báģ™ ngay" }, "changeMasterPass": { "message": "Thay đáģ•i máē­t kháēŠu chính" @@ -1295,7 +1399,7 @@ "message": "4 giáģ" }, "onIdle": { - "message": "Khi háģ‡ tháģ‘ng không hoáēĄt đáģ™ng (ráēŖnh ráģ—i)" + "message": "Khi háģ‡ tháģ‘ng nhàn ráģ—i" }, "onSleep": { "message": "Khi háģ‡ tháģ‘ng ngáģ§" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "1GB báģ™ nháģ› lưu tráģ¯ Ä‘Æ°áģŖc mÃŖ hÃŗa cho cÃĄc táģ‡p đính kèm." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ báģ™ nháģ› lưu tráģ¯ Ä‘Æ°áģŖc mÃŖ hÃŗa cho cÃĄc táģ‡p đính kèm.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "CÃĄc tÚy cháģn xÃĄc minh hai bưáģ›c như YubiKey và Duo." }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "XuáēĨt táģĢ" }, - "exportVault": { - "message": "XuáēĨt kho" + "exportNoun": { + "message": "XuáēĨt", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "XuáēĨt", + "description": "The verb form of the word Export" + }, + "importNoun": { + "message": "Nháē­p", + "description": "The noun form of the word Import" + }, + "importVerb": { + "message": "Nháē­p", + "description": "The verb form of the word Import" }, "fileFormat": { "message": "Đáģ‹nh dáēĄng táē­p tin" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "ÄÃŖ xÃŗa vÄŠnh viáģ…n máģĨc" }, + "archivedItemRestored": { + "message": "MáģĨc lưu tráģ¯ Ä‘ÃŖ đưáģŖc khôi pháģĨc" + }, "restoredItem": { "message": "MáģĨc Ä‘ÃŖ đưáģŖc khôi pháģĨc" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "Thông tin liÃĒn háģ‡" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "TáēĨt cáēŖ Send", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n xÃŗa Send này?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "Sao chÊp liÃĒn káēŋt Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "Sao chÊp liÃĒn káēŋt Send vào báēŖng nháģ› táēĄm", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "ÄÃŖ xÃŗa máē­t kháēŠu chính" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Máē­t kháēŠu chính không cÃ˛n đưáģŖc yÃĒu cáē§u đáģ‘i váģ›i cÃĄc thành viÃĒn cáģ§a táģ• cháģŠc sau đÃĸy. Vui lÃ˛ng xÃĄc nháē­n tÃĒn miáģn bÃĒn dưáģ›i váģ›i quáēŖn tráģ‹ viÃĒn cáģ§a táģ• cháģŠc." - }, "organizationName": { "message": "TÃĒn táģ• cháģŠc" }, @@ -2991,7 +3125,8 @@ "message": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n cháģn \"Không bao giáģ\" không? Láģąa cháģn này sáēŊ lưu khÃŗa mÃŖ hÃŗa kho cáģ§a báēĄn tráģąc tiáēŋp trÃĒn thiáēŋt báģ‹. HÃŖy nháģ› báēŖo váģ‡ thiáēŋt báģ‹ cáģ§a báēĄn tháē­t cáēŠn tháē­n náēŋu báēĄn cháģn tÚy cháģn này." }, "vault": { - "message": "Kho lưu tráģ¯" + "message": "Kho lưu tráģ¯", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Đăng nháē­p báēąng máē­t kháēŠu chính" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "TÃĒn miáģn thay tháēŋ" }, - "importData": { - "message": "Nháē­p dáģ¯ liáģ‡u", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "Láģ—i khi nháē­p" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "Táģ‡p Ä‘ÃŖ đưáģŖc lưu vào thiáēŋt báģ‹. QuáēŖn lÃŊ táģĢ pháē§n TáēŖi váģ trÃĒn thiáēŋt báģ‹ cáģ§a báēĄn." }, + "importantNotice": { + "message": "Thông bÃĄo quan tráģng" + }, + "setupTwoStepLogin": { + "message": "Thiáēŋt láē­p đăng nháē­p hai bưáģ›c" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "Bitwarden sáēŊ gáģ­i mÃŖ đáēŋn email tài khoáēŖn cáģ§a báēĄn đáģƒ xÃĄc minh thông tin đăng nháē­p táģĢ thiáēŋt báģ‹ máģ›i báē¯t đáē§u táģĢ thÃĄng 2 năm 2025." + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "BáēĄn cÃŗ tháģƒ thiáēŋt láē­p đăng nháē­p hai bưáģ›c như máģ™t cÃĄch thay tháēŋ đáģƒ báēŖo váģ‡ tài khoáēŖn cáģ§a mÃŦnh hoáēˇc thay đáģ•i email thành email mà báēĄn cÃŗ tháģƒ truy cáē­p." + }, + "remindMeLater": { + "message": "Nháē¯c tôi sau" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "BáēĄn cÃŗ tháģƒ truy cáē­p vào email $EMAIL$ không?", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "Không, tôi không cÃŗ" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "CÃŗ, tôi cÃŗ quyáģn truy cáē­p email này" + }, + "turnOnTwoStepLogin": { + "message": "Báē­t đăng nháē­p hai bưáģ›c" + }, + "changeAcctEmail": { + "message": "Đáģ•i email tài khoáēŖn" + }, + "passkeyLogin": { + "message": "Đăng nháē­p báēąng mÃŖ khÃŗa?" + }, + "savePasskeyQuestion": { + "message": "Lưu mÃŖ khÃŗa?" + }, + "saveNewPasskey": { + "message": "Lưu như đăng nháē­p máģ›i" + }, + "savePasskeyNewLogin": { + "message": "Lưu mÃŖ khoÃĄ như đăng nháē­p máģ›i" + }, + "noMatchingLoginsForSite": { + "message": "Không cÃŗ đăng nháē­p kháģ›p váģ›i trang web này" + }, + "overwritePasskey": { + "message": "Ghi đè mÃŖ khoÃĄ?" + }, + "unableToSavePasskey": { + "message": "Không tháģƒ lưu mÃŖ khÃŗa" + }, + "alreadyContainsPasskey": { + "message": "MáģĨc này Ä‘ÃŖ cháģŠa mÃŖ khÃŗa. BáēĄn cÃŗ cháē¯c muáģ‘n ghi đè mÃŖ khÃŗa hiáģ‡n táēĄi không?" + }, + "passkeyAlreadyExists": { + "message": "áģ¨ng dáģĨng này Ä‘ÃŖ cÃŗ mÃŖ khoÃĄ." + }, + "applicationDoesNotSupportDuplicates": { + "message": "áģ¨ng dáģĨng này không háģ— tráģŖ cÃĄc máģĨc trÚng láēˇp." + }, + "closeThisWindow": { + "message": "ÄÃŗng cáģ­a sáģ• này" + }, "allowScreenshots": { "message": "Cho phÊp cháģĨp màn hÃŦnh" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "Thông tin đăng nháē­p này cÃŗ ráģ§i ro và thiáēŋu máģ™t trang web. HÃŖy thÃĒm trang web và đáģ•i máē­t kháēŠu đáģƒ tăng cưáģng báēŖo máē­t." }, + "vulnerablePassword": { + "message": "Máē­t kháēŠu dáģ… báģ‹ táēĨn công." + }, + "changeNow": { + "message": "Thay đáģ•i ngay" + }, "missingWebsite": { "message": "Thiáēŋu trang web" }, @@ -3906,10 +4112,16 @@ "message": "Gáģ­i thông tin nháēĄy cáēŖm máģ™t cÃĄch an toàn", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "Không cÃŗ káēŋt quáēŖ tÃŦm kiáēŋm nào đưáģŖc tráēŖ váģ" + }, "sendsBodyNoItems": { "message": "Chia sáēģ táģ‡p tin và dáģ¯ liáģ‡u máģ™t cÃĄch an toàn váģ›i báēĨt káģŗ ai, trÃĒn báēĨt káģŗ náģn táēŖng nào. Thông tin cáģ§a báēĄn sáēŊ đưáģŖc mÃŖ hÃŗa đáē§u cuáģ‘i đáģƒ háēĄn cháēŋ ráģ§i ro báģ‹ láģ™.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "XÃŗa báģ™ láģc hoáēˇc tháģ­ cáģĨm táģĢ tÃŦm kiáēŋm khÃĄc" + }, "generatorNudgeTitle": { "message": "TáēĄo máē­t kháēŠu nhanh chÃŗng" }, @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "Phím táē¯t nháē­p liáģ‡u" }, - "editAutotypeShortcutDescription": { - "message": "Bao gáģ“m máģ™t hoáēˇc hai trong sáģ‘ cÃĄc phím báģ• tráģŖ sau: Ctrl, Alt, Win hoáēˇc Shift, và máģ™t cháģ¯ cÃĄi." + "editAutotypeKeyboardModifiersDescription": { + "message": "Bao gáģ“m máģ™t hoáēˇc hai phím báģ• tráģŖ sau: Ctrl, Alt, Win và máģ™t cháģ¯ cÃĄi." }, "invalidShortcut": { "message": "Phím táē¯t không háģŖp láģ‡" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "Háģ§y lưu tráģ¯" }, + "archived": { + "message": "ÄÃŖ lưu tráģ¯" + }, "itemsInArchive": { "message": "CÃĄc máģĨc trong kho lưu tráģ¯" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "Lưu tráģ¯ máģĨc" }, - "archiveItemConfirmDesc": { - "message": "CÃĄc máģĨc Ä‘ÃŖ lưu tráģ¯ sáēŊ báģ‹ loáēĄi kháģi káēŋt quáēŖ tÃŦm kiáēŋm chung và gáģŖi ÃŊ táģą Ä‘áģ™ng điáģn. BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n lưu tráģ¯ máģĨc này không?" + "archiveItemDialogContent": { + "message": "Khi Ä‘ÃŖ lưu tráģ¯, máģĨc này sáēŊ báģ‹ loáēĄi kháģi káēŋt quáēŖ tÃŦm kiáēŋm và gáģŖi ÃŊ táģą Ä‘áģ™ng điáģn." + }, + "unArchiveAndSave": { + "message": "Báģ lưu tráģ¯ và lưu" + }, + "restartPremium": { + "message": "KháģŸi đáģ™ng láēĄi gÃŗi Cao cáēĨp" + }, + "premiumSubscriptionEnded": { + "message": "GÃŗi đăng kÃŊ Cao cáēĨp cáģ§a báēĄn Ä‘ÃŖ káēŋt thÃēc" + }, + "premiumSubscriptionEndedDesc": { + "message": "Đáģƒ láēĨy láēĄi quyáģn truy cáē­p vào lưu tráģ¯ cáģ§a báēĄn, hÃŖy kháģŸi đáģ™ng láēĄi gÃŗi đăng kÃŊ Cao cáēĨp. Náēŋu báēĄn cháģ‰nh sáģ­a chi tiáēŋt cho máģ™t máģĨc Ä‘ÃŖ lưu tráģ¯ trưáģ›c khi kháģŸi đáģ™ng láēĄi, máģĨc Ä‘Ãŗ sáēŊ đưáģŖc chuyáģƒn tráģŸ láēĄi kho cáģ§a báēĄn." + }, + "itemRestored": { + "message": "MáģĨc Ä‘ÃŖ đưáģŖc khôi pháģĨc" }, "zipPostalCodeLabel": { "message": "MÃŖ ZIP / Bưu điáģ‡n" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "Và nhiáģu hÆĄn náģ¯a!" }, - "planDescPremium": { - "message": "BáēŖo máē­t tráģąc tuyáēŋn toàn diáģ‡n" + "advancedOnlineSecurity": { + "message": "BáēŖo máē­t tráģąc tuyáēŋn nÃĸng cao" }, "upgradeToPremium": { "message": "NÃĸng cáēĨp lÃĒn gÃŗi Cao cáēĨp" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "Táģ• cháģŠc cáģ§a báēĄn không cÃ˛n sáģ­ dáģĨng máē­t kháēŠu chính đáģƒ Ä‘Äƒng nháē­p vào Bitwarden. Đáģƒ tiáēŋp táģĨc, hÃŖy xÃĄc minh táģ• cháģŠc và tÃĒn miáģn." + }, + "continueWithLogIn": { + "message": "Tiáēŋp táģĨc đăng nháē­p" + }, + "doNotContinue": { + "message": "Không tiáēŋp táģĨc" + }, + "domain": { + "message": "TÃĒn miáģn" + }, + "keyConnectorDomainTooltip": { + "message": "TÃĒn miáģn này sáēŊ lưu tráģ¯ cÃĄc khÃŗa mÃŖ hÃŗa tài khoáēŖn cáģ§a báēĄn, vÃŦ váē­y hÃŖy đáēŖm báēŖo báēĄn tin tưáģŸng nÃŗ. Náēŋu báēĄn không cháē¯c cháē¯n, hÃŖy kiáģƒm tra váģ›i quáēŖn tráģ‹ viÃĒn cáģ§a báēĄn." + }, + "verifyYourOrganization": { + "message": "XÃĄc minh táģ• cháģŠc cáģ§a báēĄn đáģƒ Ä‘Äƒng nháē­p" + }, + "organizationVerified": { + "message": "Táģ• cháģŠc Ä‘ÃŖ đưáģŖc xÃĄc minh" + }, + "domainVerified": { + "message": "TÃĒn miáģn Ä‘ÃŖ đưáģŖc xÃĄc minh" + }, + "leaveOrganizationContent": { + "message": "Náēŋu báēĄn không xÃĄc minh táģ• cháģŠc cáģ§a mÃŦnh, quyáģn truy cáē­p vào táģ• cháģŠc sáēŊ báģ‹ thu háģ“i." + }, + "leaveNow": { + "message": "Ráģi kháģi ngay" + }, + "verifyYourDomainToLogin": { + "message": "XÃĄc minh tÃĒn miáģn cáģ§a báēĄn đáģƒ Ä‘Äƒng nháē­p" + }, + "verifyYourDomainDescription": { + "message": "Đáģƒ tiáēŋp táģĨc đăng nháē­p, hÃŖy xÃĄc minh tÃĒn miáģn này." + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "Đáģƒ tiáēŋp táģĨc đăng nháē­p, hÃŖy xÃĄc minh táģ• cháģŠc và tÃĒn miáģn." + }, "sessionTimeoutSettingsAction": { - "message": "Timeout action" + "message": "Hành đáģ™ng sau khi Ä‘Ãŗng kho" }, "sessionTimeoutHeader": { - "message": "Session timeout" + "message": "Tháģi gian háēŋt phiÃĒn" + }, + "resizeSideNavigation": { + "message": "Thay đáģ•i kích thưáģ›c thanh bÃĒn" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "Cài đáēˇt này do táģ• cháģŠc cáģ§a báēĄn quáēŖn lÃŊ." + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "Táģ• cháģŠc cáģ§a báēĄn Ä‘ÃŖ đáēˇt tháģi gian cháģ phiÃĒn táģ‘i đa là $HOURS$ giáģ và $MINUTES$ phÃēt.", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "Táģ• cháģŠc cáģ§a báēĄn Ä‘ÃŖ đáēˇt tháģi gian cháģ phiÃĒn máēˇc đáģ‹nh là Máģ—i khi khÃŗa mÃĄy." + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "Táģ• cháģŠc cáģ§a báēĄn Ä‘ÃŖ đáēˇt tháģi gian cháģ phiÃĒn máēˇc đáģ‹nh là Khi kháģŸi đáģ™ng láēĄi." + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "Tháģi gian cháģ táģ‘i đa không tháģƒ vưáģŖt quÃĄ $HOURS$ giáģ và $MINUTES$ phÃēt", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "Khi kháģŸi đáģ™ng láēĄi mÃĄy" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "Đáēˇt phÆ°ÆĄng tháģŠc máģŸ khÃŗa đáģƒ thay đáģ•i hành đáģ™ng khi háēŋt tháģi gian cháģ" + }, + "upgrade": { + "message": "NÃĸng cáēĨp" + }, + "leaveConfirmationDialogTitle": { + "message": "BáēĄn cÃŗ cháē¯c cháē¯n muáģ‘n ráģi đi không?" + }, + "leaveConfirmationDialogContentOne": { + "message": "Báēąng viáģ‡c táģĢ cháģ‘i, cÃĄc máģĨc cÃĄ nhÃĸn sáēŊ váēĢn náēąm trong tài khoáēŖn cáģ§a báēĄn, nhưng báēĄn sáēŊ máēĨt quyáģn truy cáē­p vào cÃĄc máģĨc đưáģŖc chia sáēģ và tính năng táģ• cháģŠc." + }, + "leaveConfirmationDialogContentTwo": { + "message": "LiÃĒn háģ‡ quáēŖn tráģ‹ viÃĒn cáģ§a báēĄn đáģƒ láēĨy láēĄi quyáģn truy cáē­p." + }, + "leaveConfirmationDialogConfirmButton": { + "message": "Ráģi kháģi $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "Tôi quáēŖn lÃŊ kho cáģ§a mÃŦnh như tháēŋ nào?" + }, + "transferItemsToOrganizationTitle": { + "message": "Chuyáģƒn cÃĄc máģĨc đáēŋn $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ yÃĒu cáē§u táēĨt cáēŖ cÃĄc máģĨc pháēŖi thuáģ™c sáģŸ háģ¯u cáģ§a táģ• cháģŠc đáģƒ Ä‘áēŖm báēŖo an ninh và tuÃĸn tháģ§. NháēĨp cháēĨp nháē­n đáģƒ chuyáģƒn quyáģn sáģŸ háģ¯u cÃĄc máģĨc cáģ§a báēĄn.", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "CháēĨp nháē­n chuyáģƒn" + }, + "declineAndLeave": { + "message": "TáģĢ cháģ‘i và ráģi đi" + }, + "whyAmISeeingThis": { + "message": "TáēĄi sao tôi tháēĨy điáģu này?" } } diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index 5e2b7f7ff7c..1e7f860a65f 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "æ­¤ Send 将在此æ—Ĩ期后čĸĢæ°¸äš…删除。", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "čĻåˆ†äēĢįš„æ–‡äģļ" + }, + "hideTextByDefault": { + "message": "éģ˜čŽ¤éšč—æ–‡æœŦ" + }, + "hideYourEmail": { + "message": "寚æŸĨįœ‹č€…éšč—æ‚¨įš„į”ĩ子邎äģļ地址。" + }, + "limitSendViews": { + "message": "限åˆļæŸĨįœ‹æŦĄæ•°" + }, + "limitSendViewsCount": { + "message": "削äŊ™ $ACCESSCOUNT$ æŦĄæŸĨįœ‹æŦĄæ•°", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "čžžåˆ°é™éĸåŽīŧŒäģģäŊ•äēēæ— æŗ•æŸĨįœ‹æ­¤ Send。", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "į§å¯†å¤‡æŗ¨" + }, + "sendDetails": { + "message": "Send č¯Ļįģ†äŋĄæ¯", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "æˇģ加一ä¸ĒᔍäēŽæŽĨæ”ļ者čŽŋé—Žæ­¤ Send įš„å¯é€‰å¯†į ã€‚", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "čĻåˆ†äēĢįš„æ–‡æœŦ" + }, + "newItemHeaderTextSend": { + "message": "新åĸžæ–‡æœŦ Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "新åĸžæ–‡äģļ Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "įŧ–čž‘æ–‡æœŦ Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "įŧ–čž‘æ–‡äģļ Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "įĄŽåŽščĻæ°¸äš…åˆ é™¤æ­¤ Send 吗īŧŸ", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "新åĸž", + "description": "for adding new items" + }, "newUri": { "message": "新åĸž URI" }, @@ -287,7 +355,7 @@ "description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'" }, "contactCSToAvoidDataLossPart2": { - "message": "äģĨéŋ免éĸå¤–įš„æ•°æŽä¸ĸå¤ąã€‚", + "message": "äģĨéŋ免čŋ›ä¸€æ­Ĩįš„æ•°æŽä¸ĸå¤ąã€‚", "description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'" }, "january": { @@ -708,6 +776,18 @@ "addAttachment": { "message": "æˇģ加附äģļ" }, + "itemsTransferred": { + "message": "éĄšį›Žåˇ˛čŊŦį§ģ" + }, + "fixEncryption": { + "message": "äŋŽå¤åР坆" + }, + "fixEncryptionTooltip": { + "message": "此文äģļæ­Ŗåœ¨äŊŋᔍčŋ‡æ—ļįš„åŠ å¯†æ–šåŧã€‚" + }, + "attachmentUpdated": { + "message": "附äģļåˇ˛æ›´æ–°" + }, "maxFileSizeSansPunctuation": { "message": "文äģᅵ€å¤§ä¸ē 500 MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "å‘į”Ÿæ„å¤–é”™č¯¯ã€‚" }, + "unexpectedErrorShort": { + "message": "æ„å¤–é”™č¯¯" + }, + "closeThisBitwardenWindow": { + "message": "å…ŗé—­æ­¤ Bitwarden įĒ—åŖīŧŒį„ļåŽé‡č¯•ã€‚" + }, "itemInformation": { "message": "éĄšį›ŽäŋĄæ¯" }, @@ -927,7 +1013,7 @@ "message": "énj蝁᠁" }, "confirmIdentity": { - "message": "įĄŽčŽ¤åŽįģ§įģ­ã€‚" + "message": "įĄŽčŽ¤æ‚¨įš„čēĢäģŊäģĨįģ§įģ­ã€‚" }, "verificationCodeRequired": { "message": "åŋ…éĄģåĄĢ写éĒŒč¯į ã€‚" @@ -1093,6 +1179,24 @@ "learnMore": { "message": "čŋ›ä¸€æ­Ĩäē†č§Ŗ" }, + "migrationsFailed": { + "message": "æ›´æ–°åŠ å¯†čŽžįŊŽæ—ļå‘į”Ÿé”™č¯¯ã€‚" + }, + "updateEncryptionSettingsTitle": { + "message": "æ›´æ–°æ‚¨įš„åŠ å¯†čŽžįŊŽ" + }, + "updateEncryptionSettingsDesc": { + "message": "æ–°æŽ¨čįš„åŠ å¯†čŽžįŊŽå°†æéĢ˜æ‚¨įš„č´ĻæˆˇåŽ‰å…¨æ€§ã€‚čž“å…Ĩæ‚¨įš„ä¸ģ坆᠁äģĨįĢ‹åŗæ›´æ–°ã€‚" + }, + "confirmIdentityToContinue": { + "message": "įĄŽčŽ¤æ‚¨įš„čēĢäģŊäģĨįģ§įģ­" + }, + "enterYourMasterPassword": { + "message": "输å…Ĩæ‚¨įš„ä¸ģ坆᠁" + }, + "updateSettings": { + "message": "æ›´æ–°čŽžįŊŽ" + }, "featureUnavailable": { "message": "功čƒŊä¸å¯į”¨" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "å…ŗæŗ¨æˆ‘äģŦ" }, - "syncVault": { - "message": "同æ­Ĩ坆᠁åē“" + "syncNow": { + "message": "įĢ‹åŗåŒæ­Ĩ" }, "changeMasterPass": { "message": "äŋŽæ”šä¸ģ坆᠁" @@ -1241,7 +1345,7 @@ } }, "twoStepLoginConfirmation": { - "message": "两æ­Ĩį™ģåŊ•čĻæą‚æ‚¨äģŽå…ļäģ–čŽžå¤‡īŧˆäž‹åĻ‚åŽ‰å…¨å¯†é’Ĩ、éĒŒč¯å™¨ Appã€įŸ­äŋĄã€į”ĩč¯æˆ–č€…į”ĩ子邎äģļīŧ‰æĨéĒŒč¯æ‚¨įš„į™ģåŊ•īŧŒčŋ™čƒŊäŊŋæ‚¨įš„č´Ļæˆˇæ›´åŠ åŽ‰å…¨ã€‚ä¸¤æ­Ĩį™ģåŊ•需čρ圍 bitwarden.com įŊ‘éĄĩį‰ˆå¯†į åē“中莞įŊŽã€‚įŽ°åœ¨čŽŋé—Žæ­¤įŊ‘į̙吗īŧŸ" + "message": "两æ­Ĩį™ģåŊ•čĻæą‚æ‚¨äģŽå…ļäģ–čŽžå¤‡īŧˆäž‹åĻ‚åŽ‰å…¨å¯†é’Ĩ、éĒŒč¯å™¨ Appã€įŸ­äŋĄã€į”ĩč¯æˆ–č€…į”ĩ子邎äģļīŧ‰æĨéĒŒč¯æ‚¨įš„į™ģåŊ•īŧŒčŋ™čƒŊäŊŋæ‚¨įš„č´Ļæˆˇæ›´åŠ åŽ‰å…¨ã€‚ä¸¤æ­Ĩį™ģåŊ•需čρ圍 bitwarden.com įŊ‘éĄĩį‰ˆå¯†į åē“中莞įŊŽã€‚įŽ°åœ¨čρčŽŋé—Žæ­¤įŊ‘į̙吗īŧŸ" }, "twoStepLogin": { "message": "两æ­Ĩį™ģåŊ•" @@ -1387,7 +1491,7 @@ "message": "蝭荀" }, "languageDesc": { - "message": "更攚åē”ᔍፋåēæ‰€äŊŋį”¨įš„č¯­č¨€ã€‚é‡å¯åŽį”Ÿæ•ˆã€‚" + "message": "更攚åē”ᔍፋåēæ‰€äŊŋį”¨įš„č¯­č¨€ã€‚éœ€čĻé‡å¯ã€‚" }, "theme": { "message": "ä¸ģéĸ˜" @@ -1435,7 +1539,7 @@ "message": "æœ‰å¯į”¨įš„æ›´æ–°" }, "updateAvailableDesc": { - "message": "å‘įŽ°æ›´æ–°ã€‚æ˜¯åĻįĢ‹åŗä¸‹čŊŊīŧŸ" + "message": "å‘įŽ°æ›´æ–°ã€‚čρįĢ‹åŗä¸‹čŊŊ吗īŧŸ" }, "restart": { "message": "重启" @@ -1476,7 +1580,7 @@ "message": "įŽĄį†äŧšå‘˜čĩ„æ ŧ" }, "premiumManageAlert": { - "message": "您可äģĨ在 bitwarden.com įŊ‘éĄĩį‰ˆå¯†į åē“įŽĄį†æ‚¨įš„äŧšå‘˜čĩ„æ ŧã€‚įŽ°åœ¨čρčŽŋ闎吗īŧŸ" + "message": "您可äģĨ在 bitwarden.com įŊ‘éĄĩį‰ˆå¯†į åē“įŽĄį†æ‚¨įš„äŧšå‘˜čĩ„æ ŧã€‚įŽ°åœ¨čρčŽŋé—Žæ­¤įŊ‘į̙吗īŧŸ" }, "premiumRefresh": { "message": "åˆˇæ–°äŧšå‘˜čĩ„æ ŧ" @@ -1488,7 +1592,16 @@ "message": "æŗ¨å†Œé̘įē§äŧšå‘˜å°†čŽˇåž—īŧš" }, "premiumSignUpStorage": { - "message": "1 GB 文äģļ附äģļ加密存储。" + "message": "1 GB 文äģļ附äģļ加密存储įŠē间。" + }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ 文äģļ附äģļ加密存储įŠē间。", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } }, "premiumSignUpTwoStepOptions": { "message": "ä¸“æœ‰įš„ä¸¤æ­Ĩį™ģåŊ•选饚īŧŒåĻ‚ YubiKey 和 Duo。" @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "å¯ŧå‡ēč‡Ē" }, - "exportVault": { - "message": "å¯ŧå‡ē坆᠁åē“" + "exportNoun": { + "message": "å¯ŧå‡ē", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "å¯ŧå‡ē", + "description": "The verb 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": "文äģļæ ŧåŧ" @@ -1835,7 +1961,7 @@ "message": "æ— æ•ˆįš„ PIN į ã€‚" }, "tooManyInvalidPinEntryAttemptsLoggingOut": { - "message": "æ— æ•ˆįš„ PIN 输å…Ĩå°č¯•æŦĄæ•°čŋ‡å¤šīŧŒæ­Ŗåœ¨æŗ¨é”€ã€‚" + "message": "æ— æ•ˆįš„ PIN 输å…Ĩå°č¯•æŦĄæ•°čŋ‡å¤šã€‚æ­Ŗåœ¨æŗ¨é”€ã€‚" }, "unlockWithWindowsHello": { "message": "äŊŋᔍ Windows Hello 觪锁" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "éĄšį›Žåˇ˛æ°¸äš…åˆ é™¤" }, + "archivedItemRestored": { + "message": "åŊ’æĄŖéĄšį›Žåˇ˛æĸ复" + }, "restoredItem": { "message": "éĄšį›Žåˇ˛æĸ复" }, @@ -2148,7 +2277,7 @@ "message": "吝ᔍæĩč§ˆå™¨é›†æˆæ—ļå‡ē错" }, "browserIntegrationErrorDesc": { - "message": "吝ᔍæĩč§ˆå™¨é›†æˆæ—ļå‡ē错。" + "message": "吝ᔍæĩč§ˆå™¨é›†æˆæ—ļå‘į”Ÿé”™č¯¯ã€‚" }, "browserIntegrationWindowsStoreDesc": { "message": "垈遗憞īŧŒMicrosoft Store į‰ˆæœŦį›Žå‰ä¸æ”¯æŒæĩč§ˆå™¨é›†æˆã€‚" @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "联įŗģäŋĄæ¯" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "æ‰€æœ‰įš„ Send", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "įĄŽåŽščĻåˆ é™¤æ­¤ Send 吗īŧŸ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "复åˆļ Send 链æŽĨ", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "复åˆļ Send 链æŽĨ到å‰Ēč´´æŋ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2431,10 +2568,10 @@ "message": "更新ä¸ģ坆᠁" }, "updateMasterPasswordWarning": { - "message": "æ‚¨įš„ä¸ģå¯†į æœ€čŋ‘čĸĢæ‚¨įģ„įģ‡įš„įŽĄį†å‘˜æ›´æ”ščŋ‡ã€‚čρčŽŋé—Žå¯†į åē“īŧŒæ‚¨åŋ…éĄģįĢ‹åŗæ›´æ–°åŽƒã€‚įģ§įģ­æ“äŊœå°†äŊŋ您退å‡ēåŊ“前äŧšč¯īŧŒåšļčĻæą‚æ‚¨é‡æ–°į™ģåŊ•。å…ļäģ–čŽžå¤‡ä¸Šįš„æ´ģ动äŧšč¯å¯čƒŊäŧšįģ§įģ­äŋæŒæ´ģ动įŠļ态é•ŋčžžä¸€å°æ—ļ。" + "message": "æ‚¨įš„ä¸ģå¯†į æœ€čŋ‘čĸĢæ‚¨įģ„įģ‡įš„įŽĄį†å‘˜æ›´æ”ščŋ‡ã€‚čρčŽŋé—Žå¯†į åē“īŧŒæ‚¨åŋ…éĄģįĢ‹åŗæ›´æ–°åŽƒã€‚įģ§įģ­æ“äŊœå°†äŊŋæ‚¨æŗ¨é”€åŊ“前äŧšč¯īŧŒåšļčĻæą‚æ‚¨é‡æ–°į™ģåŊ•。å…ļäģ–čŽžå¤‡ä¸Šįš„æ´ģ动äŧšč¯å¯čƒŊäŧšįģ§įģ­äŋæŒæ´ģ动įŠļ态é•ŋčžžä¸€å°æ—ļ。" }, "updateWeakMasterPasswordWarning": { - "message": "æ‚¨įš„ä¸ģå¯†į ä¸įŦĻåˆæŸä¸€éĄšæˆ–å¤šéĄšįģ„įģ‡į­–į•ĨčĻæą‚ã€‚čρčŽŋé—Žå¯†į åē“īŧŒåŋ…éĄģįĢ‹åŗæ›´æ–°æ‚¨įš„ä¸ģå¯†į ã€‚įģ§įģ­æ“äŊœå°†äŊŋ您退å‡ēåŊ“前äŧšč¯īŧŒåšļčĻæą‚æ‚¨é‡æ–°į™ģåŊ•。å…ļäģ–čŽžå¤‡ä¸Šįš„æ´ģ动äŧšč¯å¯čƒŊäŧšįģ§įģ­äŋæŒæ´ģ动įŠļ态é•ŋčžžä¸€å°æ—ļ。" + "message": "æ‚¨įš„ä¸ģå¯†į ä¸įŦĻåˆæŸä¸€éĄšæˆ–å¤šéĄšįģ„įģ‡į­–į•ĨčĻæą‚ã€‚čρčŽŋé—Žå¯†į åē“īŧŒåŋ…éĄģįĢ‹åŗæ›´æ–°æ‚¨įš„ä¸ģå¯†į ã€‚įģ§įģ­æ“äŊœå°†äŊŋæ‚¨æŗ¨é”€åŊ“前äŧšč¯īŧŒåšļčĻæą‚æ‚¨é‡æ–°į™ģåŊ•。å…ļäģ–čŽžå¤‡ä¸Šįš„æ´ģ动äŧšč¯å¯čƒŊäŧšįģ§įģ­äŋæŒæ´ģ动įŠļ态é•ŋčžžä¸€å°æ—ļ。" }, "changePasswordWarning": { "message": "æ›´æ”šå¯†į åŽīŧŒæ‚¨éœ€čρäŊŋį”¨æ–°å¯†į į™ģåŊ•。在å…ļäģ–čŽžå¤‡ä¸Šįš„æ´ģ动äŧšč¯å°†åœ¨ä¸€å°æ—ļå†…æŗ¨é”€ã€‚" @@ -2504,7 +2641,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "æ‚¨įš„įģ„įģ‡į­–į•Ĩæ­Ŗåœ¨åŊąå“æ‚¨įš„坆᠁åē“čļ…æ—ļã€‚æœ€å¤§å…čŽ¸įš„å¯†į åē“čļ…æ—ļä¸ē $HOURS$ 小æ—ļ $MINUTES$ åˆ†é’Ÿã€‚æ‚¨įš„å¯†į åē“čļ…æ—ļ动äŊœčĸĢ莞įŊŽä¸ē $ACTION$。", + "message": "æ‚¨įš„įģ„įģ‡į­–į•Ĩæ­Ŗåœ¨åŊąå“æ‚¨įš„坆᠁åē“čļ…æ—ļã€‚æœ€å¤§å…čŽ¸įš„å¯†į åē“čļ…æ—ļä¸ē $HOURS$ 小æ—ļ $MINUTES$ åˆ†é’Ÿã€‚æ‚¨įš„å¯†į åē“čļ…æ—ļ动äŊœčĸĢ莞įŊŽä¸ē「$ACTION$」。", "placeholders": { "hours": { "content": "$1", @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "ä¸ģ坆᠁厞į§ģ除" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "äģĨ下įģ„įģ‡įš„æˆå‘˜ä¸å†éœ€čρä¸ģå¯†į ã€‚č¯ˇä¸Žæ‚¨įš„įģ„įģ‡įŽĄį†å‘˜įĄŽčŽ¤ä¸‹éĸįš„åŸŸåã€‚" - }, "organizationName": { "message": "įģ„įģ‡åį§°" }, @@ -2991,7 +3125,8 @@ "message": "įĄŽåŽščρäŊŋį”¨ã€ŒäģŽä¸ã€é€‰éĄšå—īŧŸå°†é”åŽšé€‰éĄščŽžįŊŽä¸ē「äģŽä¸ã€äŧšå°†å¯†į åē“įš„åŠ å¯†å¯†é’Ĩå­˜å‚¨åœ¨æ‚¨įš„čŽžå¤‡ä¸Šã€‚åĻ‚æžœäŊŋį”¨æ­¤é€‰éĄšīŧŒæ‚¨åŋ…éĄģįĄŽäŋæ‚¨įš„čŽžå¤‡åŽ‰å…¨ã€‚" }, "vault": { - "message": "坆᠁åē“" + "message": "坆᠁åē“", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "äŊŋᔍä¸ģ坆᠁į™ģåŊ•" @@ -3048,7 +3183,7 @@ "message": "č¯ˇæą‚čŽˇåž—æ‰šå‡†åŽīŧŒæ‚¨å°†æ”ļ到通įŸĨ" }, "needAnotherOption": { - "message": "åŋ…éĄģ在 Bitwarden App įš„čŽžįŊŽä¸­å¯į”¨čŽžå¤‡į™ģåŊ•。需čρå…ļäģ–į™ģåŊ•é€‰éĄšå—īŧŸ" + "message": "åŋ…éĄģ在 Bitwarden App įš„čŽžįŊŽä¸­čŽžįŊŽčŽžå¤‡į™ģåŊ•。需čρå…ļäģ–é€‰éĄšå—īŧŸ" }, "viewAllLogInOptions": { "message": "æŸĨįœ‹æ‰€æœ‰į™ģåŊ•选饚" @@ -3171,10 +3306,10 @@ "message": "æŖ€æŸĨæ‚¨įš„į”ĩå­é‚ŽįŽą" }, "followTheLinkInTheEmailSentTo": { - "message": "į‚šå‡ģ发送到į”ĩ子邎äģļä¸­įš„é“žæŽĨ" + "message": "į‚šå‡ģ发送到" }, "andContinueCreatingYourAccount": { - "message": "į„ļ后įģ§įģ­åˆ›åģēæ‚¨įš„č´Ļæˆˇã€‚" + "message": "įš„į”ĩ子邎äģļä¸­įš„é“žæŽĨīŧŒį„ļ后įģ§įģ­åˆ›åģēæ‚¨įš„č´Ļæˆˇã€‚" }, "noEmail": { "message": "æ˛Ąæœ‰æ”ļ到į”ĩ子邎äģļ吗īŧŸ" @@ -3416,7 +3551,7 @@ "message": "清除全部" }, "plusNMore": { - "message": "åĻ外 $QUANTITY$ ä¸Ē", + "message": "čŋ˜æœ‰ $QUANTITY$ ä¸Ē", "placeholders": { "quantity": { "content": "$1", @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "åˆĢ名域" }, - "importData": { - "message": "å¯ŧå…Ĩ数捎", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "å¯ŧå…Ĩå‡ē错" }, @@ -3729,7 +3860,7 @@ "description": "Button text to navigate back" }, "removeItem": { - "message": "删除 $NAME$", + "message": "į§ģ除 $NAME$", "description": "Remove a selected option, such as a folder or collection", "placeholders": { "name": { @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "文äģļ厞äŋå­˜åˆ°čŽžå¤‡ã€‚å¯äģĨåœ¨čŽžå¤‡ä¸‹čŊŊ中čŋ›čĄŒįŽĄį†ã€‚" }, + "importantNotice": { + "message": "重čĻé€šįŸĨ" + }, + "setupTwoStepLogin": { + "message": "莞įŊŽä¸¤æ­Ĩį™ģåŊ•" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "äģŽ 2025 åš´ 02 月čĩˇīŧŒBitwarden å°†å‘æ‚¨įš„č´Ļæˆˇį”ĩå­é‚ŽįŽąå‘é€énj蝁᠁īŧŒäģĨénj蝁æĨč‡Ēæ–°čŽžå¤‡įš„į™ģåŊ•。" + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "您可äģĨ莞įŊŽä¸¤æ­Ĩį™ģåŊ•äŊœä¸ēäŋæŠ¤č´Ļæˆˇįš„æ›ŋäģŖæ–šæŗ•īŧŒæˆ–å°†æ‚¨įš„į”ĩå­é‚ŽįŽąæ›´æ”šä¸ē您可äģĨčŽŋé—Žįš„į”ĩå­é‚ŽįŽąã€‚" + }, + "remindMeLater": { + "message": "į¨åŽæé†’æˆ‘" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "您可äģĨæ­Ŗå¸¸čŽŋé—Žæ‚¨įš„į”ĩå­é‚ŽįŽą $EMAIL$ 吗īŧŸ", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "不īŧŒæˆ‘不čƒŊ" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "æ˜¯įš„īŧŒæˆ‘可äģĨæ­Ŗå¸¸čŽŋé—Žæˆ‘įš„į”ĩå­é‚ŽįŽą" + }, + "turnOnTwoStepLogin": { + "message": "吝ᔍ䏤æ­Ĩį™ģåŊ•" + }, + "changeAcctEmail": { + "message": "更攚č´Ļæˆˇį”ĩå­é‚ŽįŽą" + }, + "passkeyLogin": { + "message": "äŊŋį”¨é€ščĄŒå¯†é’Ĩį™ģåŊ•吗īŧŸ" + }, + "savePasskeyQuestion": { + "message": "äŋå­˜é€ščĄŒå¯†é’Ĩ吗īŧŸ" + }, + "saveNewPasskey": { + "message": "äŋå­˜ä¸ēæ–°įš„į™ģåŊ•" + }, + "savePasskeyNewLogin": { + "message": "å°†é€ščĄŒå¯†é’Ĩäŋå­˜ä¸ēæ–°įš„į™ģåŊ•" + }, + "noMatchingLoginsForSite": { + "message": "æ­¤įĢ™į‚šæ˛Ąæœ‰åŒšé…įš„į™ģåŊ•" + }, + "overwritePasskey": { + "message": "čĻ†į›–é€ščĄŒå¯†é’Ĩ吗īŧŸ" + }, + "unableToSavePasskey": { + "message": "æ— æŗ•äŋå­˜é€ščĄŒå¯†é’Ĩ" + }, + "alreadyContainsPasskey": { + "message": "æ­¤éĄšį›Žåˇ˛åŒ…åĢ一ä¸Ēé€ščĄŒå¯†é’Ĩã€‚įĄŽåŽščρčφᛖåŊ“å‰įš„é€ščĄŒå¯†é’Ĩ吗īŧŸ" + }, + "passkeyAlreadyExists": { + "message": "æ­¤åē”ᔍፋåēåˇ˛å­˜åœ¨ä¸€ä¸Ēé€ščĄŒå¯†é’Ĩ。" + }, + "applicationDoesNotSupportDuplicates": { + "message": "æ­¤åē”ᔍፋåēä¸æ”¯æŒé‡å¤ã€‚" + }, + "closeThisWindow": { + "message": "å…ŗé—­æ­¤įĒ—åŖ" + }, "allowScreenshots": { "message": "å…čŽ¸åąåš•æˆĒ回" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "æ­¤į™ģåŊ•å­˜åœ¨éŖŽé™Šä¸”įŧē少įŊ‘įĢ™ã€‚č¯ˇæˇģ加įŊ‘įĢ™åšļæ›´æ”šå¯†į äģĨåĸžåŧē厉全性。" }, + "vulnerablePassword": { + "message": "易受æ”ģå‡ģįš„å¯†į ã€‚" + }, + "changeNow": { + "message": "įĢ‹åŗæ›´æ”š" + }, "missingWebsite": { "message": "įŧē少įŊ‘įĢ™" }, @@ -3906,10 +4112,16 @@ "message": "厉全地发送敏感äŋĄæ¯", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "æœĒčŋ”回搜į´ĸį쓿žœ" + }, "sendsBodyNoItems": { "message": "在äģģäŊ•åšŗå°ä¸ŠåŽ‰å…¨åœ°ä¸ŽäģģäŊ•äēēå…ąäēĢæ–‡äģļå’Œæ•°æŽã€‚æ‚¨įš„äŋĄæ¯å°†åœ¨é™åˆļæ›å…‰įš„åŒæ—ļäŋæŒį̝到įĢ¯åŠ å¯†ã€‚", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "æ¸…é™¤į­›é€‰æˆ–å°č¯•å…ļäģ–æœį´ĸč¯" + }, "generatorNudgeTitle": { "message": "åŋĢ速创åģē坆᠁" }, @@ -3980,7 +4192,7 @@ "message": "å…ŗäēŽæ­¤čŽžįŊŽ" }, "permitCipherDetailsDescription": { - "message": "Bitwarden 将äŊŋᔍ厞äŋå­˜įš„į™ģåŊ• URI æĨ蝆åˆĢåē”äŊŋᔍå“Ēä¸Ēå›žæ ‡æˆ–æ›´æ”šå¯†į įš„ URL æĨæ”šå–„æ‚¨įš„äŊ“éĒŒã€‚åŊ“您äŊŋį”¨æ­¤æœåŠĄæ—ļīŧŒä¸äŧšæ”ļ集或äŋå­˜äģģäŊ•äŋĄæ¯ã€‚" + "message": "Bitwarden 将äŊŋᔍ厞äŋå­˜įš„į™ģåŊ• URI æĨįĄŽåŽšåē”äŊŋį”¨įš„å›žæ ‡æˆ–æ›´æ”šå¯†į įš„ URLīŧŒäģĨæå‡æ‚¨įš„äŊŋᔍäŊ“éĒŒã€‚äŊŋį”¨æ­¤æœåŠĄæ—ļ不äŧšæ”ļ集或äŋå­˜äģģäŊ•äŋĄæ¯ã€‚" }, "assignToCollections": { "message": "分配到集合" @@ -4128,8 +4340,8 @@ "typeShortcut": { "message": "输å…ĨåŋĢæˇé”Ž" }, - "editAutotypeShortcutDescription": { - "message": "包åĢäģĨ下一ä¸Ē或两ä¸ĒäŋŽéĨ°įŦĻīŧšCtrl、Alt、Win 或 ShiftīŧŒå¤–加一ä¸Ē字母。" + "editAutotypeKeyboardModifiersDescription": { + "message": "包åĢäģĨ下äŋŽéĨ°é”Žä¸­įš„一ä¸Ē或两ä¸ĒīŧšCtrl、Alt、WinīŧŒäģĨ及一ä¸Ē字母锎。" }, "invalidShortcut": { "message": "æ— æ•ˆįš„åŋĢæˇé”Ž" @@ -4167,6 +4379,9 @@ "unArchive": { "message": "取æļˆåŊ’æĄŖ" }, + "archived": { + "message": "厞åŊ’æĄŖ" + }, "itemsInArchive": { "message": "åŊ’æĄŖä¸­įš„éĄšį›Ž" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "åŊ’æĄŖéĄšį›Ž" }, - "archiveItemConfirmDesc": { - "message": "厞åŊ’æĄŖįš„éĄšį›Žå°†čĸĢæŽ’é™¤åœ¨ä¸€čˆŦ搜į´ĸį쓿žœå’Œč‡Ē动åĄĢ充åģēčŽŽäš‹å¤–ã€‚įĄŽåŽščρåŊ’æĄŖæ­¤éĄšį›Žå—īŧŸ" + "archiveItemDialogContent": { + "message": "åŊ’æĄŖåŽīŧŒæ­¤éĄšį›Žå°†čĸĢæŽ’é™¤åœ¨ä¸€čˆŦ搜į´ĸį쓿žœå’Œč‡Ē动åĄĢ充åģēčŽŽäš‹å¤–ã€‚" + }, + "unArchiveAndSave": { + "message": "取æļˆåŊ’æĄŖåšļäŋå­˜" + }, + "restartPremium": { + "message": "重启é̘įē§į‰ˆ" + }, + "premiumSubscriptionEnded": { + "message": "æ‚¨įš„é̘įē§į‰ˆčŽĸé˜…åˇ˛į쓿Ÿ" + }, + "premiumSubscriptionEndedDesc": { + "message": "čĻé‡æ–°čŽˇå–åŊ’æĄŖå†…åŽšįš„čŽŋ闎权限īŧŒč¯ˇé‡å¯æ‚¨įš„é̘įē§į‰ˆčŽĸ阅。åĻ‚æžœæ‚¨åœ¨é‡å¯å‰įŧ–čž‘ä熿Ÿä¸Ē厞åŊ’æĄŖéĄšį›Žįš„č¯Ļįģ†äŋĄæ¯īŧŒåŽƒå°†čĸĢį§ģå›žæ‚¨įš„å¯†į åē“中。" + }, + "itemRestored": { + "message": "éĄšį›Žåˇ˛æĸ复" }, "zipPostalCodeLabel": { "message": "ZIP / 邮æ”ŋįŧ–᠁" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "äģĨ及更多īŧ" }, - "planDescPremium": { - "message": "全éĸįš„åœ¨įēŋåŽ‰å…¨é˜˛æŠ¤" + "advancedOnlineSecurity": { + "message": "é̘įē§åœ¨įēŋåŽ‰å…¨é˜˛æŠ¤" }, "upgradeToPremium": { "message": "升įē§ä¸ēé̘įē§į‰ˆ" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "æ‚¨įš„įģ„įģ‡åˇ˛ä¸å†äŊŋᔍä¸ģ坆᠁į™ģåŊ• Bitwarden。čρįģ§įģ­īŧŒč¯ˇénj蝁įģ„įģ‡å’ŒåŸŸåã€‚" + }, + "continueWithLogIn": { + "message": "įģ§įģ­į™ģåŊ•" + }, + "doNotContinue": { + "message": "不čρįģ§įģ­" + }, + "domain": { + "message": "域名" + }, + "keyConnectorDomainTooltip": { + "message": "æ­¤åŸŸåå°†å­˜å‚¨æ‚¨įš„č´ĻæˆˇåŠ å¯†å¯†é’ĨīŧŒæ‰€äģĨč¯ˇįĄŽäŋæ‚¨äŋĄäģģ厃。åĻ‚æžœæ‚¨ä¸įĄŽåŽšīŧŒč¯ˇä¸Žæ‚¨įš„įŽĄį†å‘˜č”įŗģ。" + }, + "verifyYourOrganization": { + "message": "éĒŒč¯æ‚¨įš„įģ„įģ‡äģĨį™ģåŊ•" + }, + "organizationVerified": { + "message": "įģ„įģ‡åˇ˛énj蝁" + }, + "domainVerified": { + "message": "åŸŸååˇ˛énj蝁" + }, + "leaveOrganizationContent": { + "message": "åĻ‚æžœä¸éĒŒč¯æ‚¨įš„įģ„įģ‡īŧŒæ‚¨å¯šįģ„įģ‡įš„čŽŋ闎权限将čĸĢæ’¤é”€ã€‚" + }, + "leaveNow": { + "message": "įĢ‹åŗé€€å‡ē" + }, + "verifyYourDomainToLogin": { + "message": "éĒŒč¯æ‚¨įš„åŸŸåäģĨį™ģåŊ•" + }, + "verifyYourDomainDescription": { + "message": "čρįģ§įģ­į™ģåŊ•īŧŒč¯ˇéĒŒč¯æ­¤åŸŸåã€‚" + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "čρįģ§įģ­į™ģåŊ•īŧŒč¯ˇénj蝁įģ„įģ‡å’ŒåŸŸåã€‚" + }, "sessionTimeoutSettingsAction": { "message": "čļ…æ—ļ动äŊœ" }, "sessionTimeoutHeader": { "message": "äŧšč¯čļ…æ—ļ" + }, + "resizeSideNavigation": { + "message": "č°ƒæ•´äž§čžšå¯ŧčˆĒ栏大小" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "æ­¤čŽžįŊŽį”ąæ‚¨įš„įģ„įģ‡įŽĄį†ã€‚" + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "æ‚¨įš„įģ„įģ‡åˇ˛å°†æœ€å¤§äŧšč¯čļ…æ—ļ莞įŊŽä¸ē $HOURS$ 小æ—ļ $MINUTES$ 分钟。", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "æ‚¨įš„įģ„įģ‡åˇ˛å°†éģ˜čޤäŧšč¯čļ…æ—ļ莞įŊŽä¸ē「įŗģįģŸé”åޚæ—ļ」。" + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "æ‚¨įš„įģ„įģ‡åˇ˛å°†éģ˜čޤäŧšč¯čļ…æ—ļ莞įŊŽä¸ē「重启æ—ļ」。" + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "最大čļ…æ—ļ不čƒŊčļ…čŋ‡ $HOURS$ 小æ—ļ $MINUTES$ 分钟", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "重启æ—ļ" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "莞įŊŽä¸€ä¸Ēč§Ŗé”æ–šåŧäģĨæ›´æ”šæ‚¨įš„čļ…æ—ļ动äŊœ" + }, + "upgrade": { + "message": "升įē§" + }, + "leaveConfirmationDialogTitle": { + "message": "įĄŽåŽščρ退å‡ē吗īŧŸ" + }, + "leaveConfirmationDialogContentOne": { + "message": "拒įģåŽīŧŒæ‚¨įš„ä¸ĒäēēéĄšį›Žå°†äŋį•™åœ¨æ‚¨įš„č´Ļæˆˇä¸­īŧŒäŊ†æ‚¨å°†å¤ąåŽģå¯šå…ąäēĢéĄšį›Žå’Œįģ„įģ‡åŠŸčƒŊįš„čŽŋ闎权限。" + }, + "leaveConfirmationDialogContentTwo": { + "message": "联įŗģæ‚¨įš„įŽĄį†å‘˜äģĨé‡æ–°čŽˇå–čŽŋ闎权限。" + }, + "leaveConfirmationDialogConfirmButton": { + "message": "退å‡ē $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "我č¯ĨåĻ‚äŊ•įŽĄį†æˆ‘įš„å¯†į åē“īŧŸ" + }, + "transferItemsToOrganizationTitle": { + "message": "čŊŦį§ģéĄšį›Žåˆ° $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "å‡ēäēŽåŽ‰å…¨å’Œåˆč§„č€ƒč™‘īŧŒ$ORGANIZATION$ čĻæą‚æ‰€æœ‰éĄšį›ŽåŊ’įģ„į쇿‰€æœ‰ã€‚į‚šå‡ģ「æŽĨ受」äģĨčŊŦį§ģæ‚¨įš„éĄšį›Žįš„æ‰€æœ‰æƒã€‚", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "æŽĨ受čŊŦį§ģ" + }, + "declineAndLeave": { + "message": "拒įģåšļ退å‡ē" + }, + "whyAmISeeingThis": { + "message": "ä¸ēäģ€äšˆæˆ‘äŧšįœ‹åˆ°čŋ™ä¸ĒīŧŸ" } } diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index 61fc00543ed..6ca01de8d71 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -100,6 +100,74 @@ } } }, + "deletionDateDescV2": { + "message": "æ­¤ Send å°‡åœ¨æŒ‡åŽšįš„æ—Ĩ期垌čĸĢæ°¸äš…åˆĒ除。", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "fileToShare": { + "message": "čĻåˆ†äēĢįš„æ–‡äģļ" + }, + "hideTextByDefault": { + "message": "éģ˜čĒéšąč—æ–‡å­—" + }, + "hideYourEmail": { + "message": "對æŸĨįœ‹č€…éšąč—æ‚¨įš„é›ģ子éƒĩäģļ地址。" + }, + "limitSendViews": { + "message": "限åˆļæŸĨįœ‹" + }, + "limitSendViewsCount": { + "message": "削餘 $ACCESSCOUNT$ æŦĄæŸĨįœ‹æŦĄæ•¸", + "description": "Displayed under the limit views field on Send", + "placeholders": { + "accessCount": { + "content": "$1", + "example": "2" + } + } + }, + "limitSendViewsHint": { + "message": "åœ¨é”åˆ°é™éĄåžŒīŧŒæ˛’有äēēčƒŊæŸĨįœ‹æ­¤ Send。", + "description": "Displayed under the limit views field on Send" + }, + "privateNote": { + "message": "ᧁäēē備č¨ģ" + }, + "sendDetails": { + "message": "Send čŠŗį´°čŗ‡č¨Š", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendPasswordDescV3": { + "message": "新åĸžä¸€å€‹į”¨æ–ŧæ”ļäģļäēē存取此 Send įš„å¯é¸å¯†įĸŧ。", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendTypeTextToShare": { + "message": "čĻåˆ†äēĢįš„æ–‡å­—" + }, + "newItemHeaderTextSend": { + "message": "新åĸžæ–‡å­— Send", + "description": "Header for new text send" + }, + "newItemHeaderFileSend": { + "message": "新åĸžæĒ”æĄˆ Send", + "description": "Header for new file send" + }, + "editItemHeaderTextSend": { + "message": "ᎍčŧ¯æ–‡å­— Send", + "description": "Header for edit text send" + }, + "editItemHeaderFileSend": { + "message": "ᎍčŧ¯æĒ”æĄˆ Send", + "description": "Header for edit file send" + }, + "deleteSendPermanentConfirmation": { + "message": "您įĸē厚čĻæ°¸äš…åˆĒ除此 Send 嗎īŧŸ", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "new": { + "message": "新åĸž", + "description": "for adding new items" + }, "newUri": { "message": "新åĸž URI" }, @@ -241,10 +309,10 @@ "message": "å•Ÿį”¨ SSH äģŖį†į¨‹åŧ" }, "enableSshAgentDesc": { - "message": "å•Ÿį”¨SSBitwardenHäģŖį†äģĨåžž Bitwarden 密įĸŧåēĢį°Ŋį™ŧSSHčĢ‹æą‚" + "message": "å•Ÿį”¨ SSH äģŖį†äģĨåžž Bitwarden 密įĸŧåēĢį›´æŽĨį°ŊįŊ˛ SSH čĢ‹æą‚ã€‚" }, "enableSshAgentHelp": { - "message": "SSHäģŖį†æ˜¯ä¸€å€‹é‡å°é–‹į™ŧč€…įš„æœå‹™īŧŒåރčƒŊ夠ᛴæŽĨåžž Bitwarden 密įĸŧåēĢį°Ŋį™ŧSSHčĢ‹æą‚ã€‚" + "message": "SSH äģŖį†æ˜¯ä¸€å€‹é‡å°é–‹į™ŧč€…įš„æœå‹™īŧŒåރčƒŊ夠ᛴæŽĨåžž Bitwarden 密įĸŧåēĢį°ŊįŊ˛ SSH čĢ‹æą‚ã€‚" }, "sshAgentPromptBehavior": { "message": "äŊŋᔍ SSH äģŖį†į¨‹åŧæ™‚čĻæą‚æŽˆæŦŠ" @@ -708,6 +776,18 @@ "addAttachment": { "message": "新åĸžé™„äģļ" }, + "itemsTransferred": { + "message": "é …į›Žåˇ˛čŊ‰į§ģ" + }, + "fixEncryption": { + "message": "äŋŽæ­ŖåР坆" + }, + "fixEncryptionTooltip": { + "message": "æ­¤æĒ”æĄˆäŊŋᔍäē†éŽæ™‚įš„åŠ å¯†æ–šåŧã€‚" + }, + "attachmentUpdated": { + "message": "附äģļåˇ˛æ›´æ–°" + }, "maxFileSizeSansPunctuation": { "message": "最大æĒ”æĄˆå¤§å°į‚ē 500MB" }, @@ -908,6 +988,12 @@ "unexpectedError": { "message": "į™ŧį”Ÿä熿œĒé æœŸįš„éŒ¯čĒ¤ã€‚" }, + "unexpectedErrorShort": { + "message": "æœĒé æœŸįš„éŒ¯čǤ" + }, + "closeThisBitwardenWindow": { + "message": "關閉此 Bitwarden čĻ–įĒ—åžŒå†čŠĻ一æŦĄã€‚" + }, "itemInformation": { "message": "é …į›Žčŗ‡č¨Š" }, @@ -1093,6 +1179,24 @@ "learnMore": { "message": "äē†č§Ŗæ›´å¤š" }, + "migrationsFailed": { + "message": "æ›´æ–°åŠ å¯†č¨­åŽšæ™‚į™ŧį”ŸéŒ¯čĒ¤ã€‚" + }, + "updateEncryptionSettingsTitle": { + "message": "æ›´æ–°æ‚¨įš„åŠ å¯†č¨­åŽš" + }, + "updateEncryptionSettingsDesc": { + "message": "æ–°įš„åģēč­°åŠ å¯†č¨­åŽšå°‡æå‡æ‚¨įš„å¸ŗæˆļ厉全性。čĢ‹čŧ¸å…Ĩä¸ģ密įĸŧäģĨįĢ‹åŗæ›´æ–°ã€‚" + }, + "confirmIdentityToContinue": { + "message": "čĢ‹å…ˆįĸēčĒčēĢ分垌再įšŧįēŒ" + }, + "enterYourMasterPassword": { + "message": "čŧ¸å…Ĩæ‚¨įš„ä¸ģ密įĸŧ" + }, + "updateSettings": { + "message": "æ›´æ–°č¨­åŽš" + }, "featureUnavailable": { "message": "功čƒŊä¸å¯į”¨" }, @@ -1162,8 +1266,8 @@ "followUs": { "message": "é—œæŗ¨æˆ‘å€‘" }, - "syncVault": { - "message": "同æ­Ĩ密įĸŧåēĢ" + "syncNow": { + "message": "įĢ‹åŗåŒæ­Ĩ" }, "changeMasterPass": { "message": "čŽŠæ›´ä¸ģ密įĸŧ" @@ -1490,6 +1594,15 @@ "premiumSignUpStorage": { "message": "ᔍæ–ŧæĒ”æĄˆé™„äģļįš„ 1 GB įš„åŠ å¯†æĒ”æĄˆå„˛å­˜įŠē間。" }, + "premiumSignUpStorageV2": { + "message": "ᔍæ–ŧæĒ”æĄˆé™„äģļįš„ $SIZE$ åŠ å¯†å„˛å­˜įŠē間。", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "å°ˆæœ‰įš„å…Šæ­Ĩ驟į™ģå…Ĩ選項īŧŒäž‹åĻ‚ YubiKey 和 Duo。" }, @@ -1730,8 +1843,21 @@ "exportFrom": { "message": "匯å‡ēč‡Ē" }, - "exportVault": { - "message": "匯å‡ē密įĸŧåēĢ" + "exportNoun": { + "message": "匯å‡ē", + "description": "The noun form of the word Export" + }, + "exportVerb": { + "message": "匯å‡ē", + "description": "The verb 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": "æĒ”æĄˆæ ŧåŧ" @@ -1966,6 +2092,9 @@ "permanentlyDeletedItem": { "message": "é …į›Žåˇ˛æ°¸äš…åˆĒ除" }, + "archivedItemRestored": { + "message": "åˇ˛é‚„åŽŸå°å­˜é …į›Ž" + }, "restoredItem": { "message": "é …į›Žåˇ˛é‚„åŽŸ" }, @@ -2228,6 +2357,10 @@ "contactInfo": { "message": "聝įĩĄčŗ‡č¨Š" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "所有 Send", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2349,6 +2482,10 @@ "message": "您įĸē厚čρåˆĒ除此 Send 嗎īŧŸ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "copySendLink": { + "message": "複čŖŊ Send 逪įĩ", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "copySendLinkToClipboard": { "message": "複čŖŊ Send 逪įĩåˆ°å‰Ēč˛ŧį°ŋ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2588,9 +2725,6 @@ "removedMasterPassword": { "message": "ä¸ģ密įĸŧ厞į§ģ除" }, - "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "äģĨ下įĩ„įš”įš„æˆå“Ąåˇ˛ä¸å†éœ€čρä¸ģ密įĸŧ。čĢ‹čˆ‡äŊ įš„įĩ„įš”įŽĄį†å“ĄįĸēčĒä¸‹æ–šįš„įļ˛åŸŸã€‚" - }, "organizationName": { "message": "æŠŸæ§‹åį¨ą" }, @@ -2991,7 +3125,8 @@ "message": "您įĸē厚čρäŊŋį”¨ã€Œæ°¸ä¸ã€é¸é …å—ŽīŧŸå°‡éŽ–åŽšé¸é …č¨­åŽšį‚ē「永不」會將密įĸŧåēĢįš„åŠ å¯†é‡‘é‘°å„˛å­˜åœ¨æ‚¨įš„čŖįŊŽä¸Šã€‚åĻ‚æžœäŊŋį”¨æ­¤é¸é …īŧŒæ‡‰įĸēäŋæ‚¨įš„čŖįŊŽæ˜¯åŽ‰å…¨įš„ã€‚" }, "vault": { - "message": "密įĸŧåēĢ" + "message": "密įĸŧåēĢ", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "äŊŋᔍä¸ģ密įĸŧį™ģå…Ĩ" @@ -3445,10 +3580,6 @@ "aliasDomain": { "message": "åˆĨ名įļ˛åŸŸ" }, - "importData": { - "message": "匯å…Ĩčŗ‡æ–™", - "description": "Used for the desktop menu item and the header of the import dialog" - }, "importError": { "message": "匯å…Ĩ時į™ŧį”ŸéŒ¯čǤ" }, @@ -3854,6 +3985,75 @@ "fileSavedToDevice": { "message": "æĒ”æĄˆåˇ˛å„˛å­˜åˆ°čŖįŊŽã€‚åœ¨æ‚¨įš„čŖįŊŽä¸ŠįŽĄį†ä¸‹čŧ‰æĒ”æĄˆã€‚" }, + "importantNotice": { + "message": "重čĻé€šįŸĨ" + }, + "setupTwoStepLogin": { + "message": "啟動兊階æŽĩį™ģå…Ĩ" + }, + "newDeviceVerificationNoticeContentPage1": { + "message": "åžž 2025 åš´ 2 月開始īŧŒBitwarden æœƒå‚ŗé€äģŖįĸŧåˆ°æ‚¨įš„å¸ŗč™Ÿé›ģ子éƒĩäģļä¸­äž†éŠ—č­‰æ–°čŖįŊŽįš„į™ģå…Ĩ。" + }, + "newDeviceVerificationNoticeContentPage2": { + "message": "您可äģĨ啟動兊階æŽĩčĒč­‰äž†äŋč­ˇæ‚¨įš„å¸ŗč™Ÿæˆ–æ›´æ”šæ‚¨å¯äģĨå­˜å–įš„é›ģ子éƒĩäģļäŊå€ã€‚" + }, + "remindMeLater": { + "message": "į¨åžŒå†æé†’æˆ‘" + }, + "newDeviceVerificationNoticePageOneFormContent": { + "message": "您可äģĨå­˜å–æ‚¨įš„é›ģ子éƒĩäģļäŊå€ $EMAIL$ 嗎īŧŸ", + "placeholders": { + "email": { + "content": "$1", + "example": "your_name@email.com" + } + } + }, + "newDeviceVerificationNoticePageOneEmailAccessNo": { + "message": "不īŧŒæˆ‘ä¸čĄŒ" + }, + "newDeviceVerificationNoticePageOneEmailAccessYes": { + "message": "是īŧŒæˆ‘可äģĨå­˜å–æˆ‘įš„é›ģ子éƒĩäģļäŊå€" + }, + "turnOnTwoStepLogin": { + "message": "啟動兊階æŽĩį™ģå…Ĩ" + }, + "changeAcctEmail": { + "message": "æ›´æ”šå¸ŗč™Ÿé›ģ子éƒĩäģļäŊå€" + }, + "passkeyLogin": { + "message": "äŊŋᔍ坆įĸŧ金鑰į™ģå…ĨīŧŸ" + }, + "savePasskeyQuestion": { + "message": "å„˛å­˜å¯†įĸŧ金鑰īŧŸ" + }, + "saveNewPasskey": { + "message": "å„˛å­˜į‚ēæ–°įš„į™ģå…Ĩčŗ‡č¨Š" + }, + "savePasskeyNewLogin": { + "message": "將密įĸŧé‡‘é‘°å„˛å­˜į‚ēæ–°įš„į™ģå…Ĩčŗ‡č¨Š" + }, + "noMatchingLoginsForSite": { + "message": "æœĒ扞到此įļ˛įĢ™įš„į™ģå…Ĩčŗ‡č¨Š" + }, + "overwritePasskey": { + "message": "čρčφå¯Ģ密įĸŧ金鑰嗎īŧŸ" + }, + "unableToSavePasskey": { + "message": "į„Ąæŗ•å„˛å­˜é€ščĄŒé‡‘é‘°" + }, + "alreadyContainsPasskey": { + "message": "čŠ˛é …į›Žåˇ˛åŒ…åĢ一個密įĸŧ金鑰。您įĸē厚čρčφå¯Ģį›Žå‰įš„å¯†įĸŧ金鑰嗎īŧŸ" + }, + "passkeyAlreadyExists": { + "message": "ᔍæ–ŧé€™å€‹æ‡‰į”¨į¨‹åŧįš„密įĸŧ金鑰厞įļ“存在。" + }, + "applicationDoesNotSupportDuplicates": { + "message": "æ­¤æ‡‰į”¨į¨‹åŧä¸æ”¯æ´é‡č¤‡é …į›Žã€‚" + }, + "closeThisWindow": { + "message": "關閉此čĻ–įĒ—" + }, "allowScreenshots": { "message": "å…č¨ąčžĸåš•æ“ˇå–" }, @@ -3878,6 +4078,12 @@ "changeAtRiskPasswordAndAddWebsite": { "message": "æ­¤į™ģå…Ĩčŗ‡č¨Šå­˜åœ¨éĸ¨éšĒīŧŒä¸”įŧē少įļ˛įĢ™ã€‚čĢ‹æ–°åĸžįļ˛įĢ™ä¸ĻčŽŠæ›´å¯†įĸŧäģĨ提升厉全性。" }, + "vulnerablePassword": { + "message": "æœ‰åŽ‰å…¨į–‘æ…Žįš„å¯†įĸŧ。" + }, + "changeNow": { + "message": "įĢ‹åŗčŽŠæ›´" + }, "missingWebsite": { "message": "įŧē少įļ˛įĢ™" }, @@ -3906,10 +4112,16 @@ "message": "åŽ‰å…¨å‚ŗé€æŠŸå¯†įš„čŗ‡č¨Š", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsTitleNoSearchResults": { + "message": "æ˛’æœ‰æœå°‹įĩæžœ" + }, "sendsBodyNoItems": { "message": "åŽ‰å…¨įš„å’ŒäģģäŊ•äēē及äģģäŊ•åšŗč‡ē分äēĢæĒ”æĄˆåŠčŗ‡æ–™ã€‚æ‚¨įš„čŗ‡æ–™æœƒå—åˆ°įĢ¯å°įĢ¯åŠ å¯†įš„äŋč­ˇã€‚", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendsBodyNoSearchResults": { + "message": "清除過æŋžå™¨æˆ–更換åĻ一個搜尋æĸäģļ" + }, "generatorNudgeTitle": { "message": "åŋĢ速åģēį̋坆įĸŧ" }, @@ -4128,7 +4340,7 @@ "typeShortcut": { "message": "čŧ¸å…ĨåŋĢæˇéĩ" }, - "editAutotypeShortcutDescription": { + "editAutotypeKeyboardModifiersDescription": { "message": "čĢ‹åŒ…åĢäģĨ下äŋŽéŖžéĩ䚋一或兊個īŧšCtrl、Alt、Win 或 ShiftīŧŒå†åŠ ä¸Šä¸€å€‹å­—æ¯ã€‚" }, "invalidShortcut": { @@ -4167,6 +4379,9 @@ "unArchive": { "message": "取æļˆå°å­˜" }, + "archived": { + "message": "厞封存" + }, "itemsInArchive": { "message": "å°å­˜ä¸­įš„é …į›Ž" }, @@ -4185,8 +4400,23 @@ "archiveItem": { "message": "å°å­˜é …į›Ž" }, - "archiveItemConfirmDesc": { - "message": "å°å­˜įš„é …į›Žå°‡ä¸æœƒå‡ēįžåœ¨ä¸€čˆŦ搜尋įĩæžœæˆ–č‡Ē動åĄĢå…Ĩåģēč­°ä¸­ã€‚įĸē厚čĻå°å­˜æ­¤é …į›Žå—ŽīŧŸ" + "archiveItemDialogContent": { + "message": "封存垌īŧŒæ­¤é …į›Žå°‡ä¸æœƒéĄ¯į¤ē在搜尋įĩæžœčˆ‡č‡Ē動åĄĢå…Ĩåģēč­°ä¸­ã€‚" + }, + "unArchiveAndSave": { + "message": "取æļˆå°å­˜ä¸Ļå„˛å­˜" + }, + "restartPremium": { + "message": "é‡æ–°å•Ÿį”¨é€˛éšŽį‰ˆ" + }, + "premiumSubscriptionEnded": { + "message": "æ‚¨įš„é€˛éšŽį‰ˆč¨‚é–ąåˇ˛åˆ°æœŸ" + }, + "premiumSubscriptionEndedDesc": { + "message": "č‹ĨčĻé‡æ–°å­˜å–æ‚¨įš„å°å­˜é …į›ŽīŧŒčĢ‹é‡æ–°å•Ÿį”¨é€˛éšŽį‰ˆč¨‚é–ąã€‚č‹Ĩæ‚¨åœ¨é‡æ–°å•Ÿį”¨å‰įˇ¨čŧ¯å°å­˜é …į›Žįš„čŠŗį´°čŗ‡æ–™īŧŒåŽƒå°‡æœƒčĸĢį§ģå›žæ‚¨įš„å¯†įĸŧåēĢ。" + }, + "itemRestored": { + "message": "åˇ˛é‚„åŽŸé …į›Ž" }, "zipPostalCodeLabel": { "message": "éƒĩᎍ / éƒĩæ”ŋäģŖįĸŧ" @@ -4212,16 +4442,150 @@ "andMoreFeatures": { "message": "äģĨ及å…ļäģ–功čƒŊ功čƒŊīŧ" }, - "planDescPremium": { - "message": "åŽŒæ•´įš„įˇšä¸ŠåŽ‰å…¨" + "advancedOnlineSecurity": { + "message": "é€˛éšŽįˇšä¸ŠåŽ‰å…¨é˜˛č­ˇ" }, "upgradeToPremium": { "message": "å‡į´šåˆ° Premium" }, + "removeMasterPasswordForOrgUserKeyConnector": { + "message": "æ‚¨įš„įĩ„įš”åˇ˛ä¸å†äŊŋᔍä¸ģ密įĸŧį™ģå…Ĩ Bitwarden。č‹ĨčρįšŧįēŒīŧŒčĢ‹éŠ—č­‰įĩ„įš”čˆ‡įļ˛åŸŸã€‚" + }, + "continueWithLogIn": { + "message": "įšŧįēŒį™ģå…Ĩ" + }, + "doNotContinue": { + "message": "不čρįšŧįēŒ" + }, + "domain": { + "message": "įļ˛åŸŸ" + }, + "keyConnectorDomainTooltip": { + "message": "æ­¤įļ˛åŸŸå°‡å„˛å­˜æ‚¨å¸ŗč™Ÿįš„加密金鑰īŧŒčĢ‹įĸēčĒæ‚¨äŋĄäģģ厃。č‹Ĩ不įĸē厚īŧŒčĢ‹æ´ŊčŠĸæ‚¨įš„įŽĄį†å“Ąã€‚" + }, + "verifyYourOrganization": { + "message": "éŠ—č­‰æ‚¨įš„įĩ„įš”äģĨį™ģå…Ĩ" + }, + "organizationVerified": { + "message": "įĩ„įš”åˇ˛éŠ—č­‰" + }, + "domainVerified": { + "message": "åˇ˛éŠ—č­‰įļ˛åŸŸ" + }, + "leaveOrganizationContent": { + "message": "č‹Ĩ您æœĒ驗證įĩ„įš”īŧŒå°‡æœƒčĸĢæ’¤éŠˇå°čОįĩ„įš”įš„å­˜å–æŦŠé™ã€‚" + }, + "leaveNow": { + "message": "įĢ‹åŗé›ĸ開" + }, + "verifyYourDomainToLogin": { + "message": "éŠ—č­‰æ‚¨įš„įļ˛åŸŸäģĨį™ģå…Ĩ" + }, + "verifyYourDomainDescription": { + "message": "č‹ĨčρįšŧįēŒį™ģå…ĨīŧŒčĢ‹éŠ—č­‰æ­¤įļ˛åŸŸã€‚" + }, + "confirmKeyConnectorOrganizationUserDescription": { + "message": "č‹ĨčρįšŧįēŒį™ģå…ĨīŧŒčĢ‹éŠ—č­‰įĩ„įš”čˆ‡įļ˛åŸŸã€‚" + }, "sessionTimeoutSettingsAction": { "message": "逞時垌動äŊœ" }, "sessionTimeoutHeader": { "message": "åˇĨäŊœéšŽæŽĩ逞時" + }, + "resizeSideNavigation": { + "message": "čĒŋ整側邊æŦ„大小" + }, + "sessionTimeoutSettingsManagedByOrganization": { + "message": "æ­¤č¨­åŽšį”ąæ‚¨įš„įĩ„įš”įŽĄį†ã€‚" + }, + "sessionTimeoutSettingsPolicySetMaximumTimeoutToHoursMinutes": { + "message": "æ‚¨įš„įĩ„įš”åˇ˛å°‡æœ€é•ˇåˇĨäŊœéšŽæŽĩé€žæ™‚č¨­į‚ē $HOURS$ å°æ™‚čˆ‡ $MINUTES$ 分鐘。", + "placeholders": { + "hours": { + "content": "$1", + "example": "8" + }, + "minutes": { + "content": "$2", + "example": "2" + } + } + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnLocked": { + "message": "æ‚¨įš„įĩ„įš”åˇ˛å°‡é č¨­åˇĨäŊœéšŽæŽĩé€žæ™‚č¨­åŽšį‚ē「在įŗģįĩąéŽ–åŽšæ™‚ã€ã€‚" + }, + "sessionTimeoutSettingsPolicySetDefaultTimeoutToOnRestart": { + "message": "æ‚¨įš„įĩ„įš”åˇ˛å°‡é č¨­åˇĨäŊœéšŽæŽĩé€žæ™‚č¨­åŽšį‚ē「在重新啟動時」。" + }, + "sessionTimeoutSettingsPolicyMaximumError": { + "message": "æœ€é•ˇé€žæ™‚æ™‚é–“ä¸å¯čļ…過 $HOURS$ 小時 $MINUTES$ 分鐘", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "sessionTimeoutOnRestart": { + "message": "重新啟動時" + }, + "sessionTimeoutSettingsSetUnlockMethodToChangeTimeoutAction": { + "message": "č¨­åŽšä¸€å€‹č§ŖéŽ–æ–šåŧäž†čŽŠæ›´æ‚¨įš„å¯†įĸŧåēĢ逞時動äŊœã€‚" + }, + "upgrade": { + "message": "å‡į´š" + }, + "leaveConfirmationDialogTitle": { + "message": "įĸē厚čρé›ĸ開嗎īŧŸ" + }, + "leaveConfirmationDialogContentOne": { + "message": "č‹Ĩ選擇拒įĩ•īŧŒæ‚¨įš„個äēēé …į›Žå°‡äŋį•™åœ¨å¸ŗč™Ÿä¸­īŧŒäŊ†æ‚¨å°‡å¤ąåŽģå°å…ąį”¨é …į›Žčˆ‡įĩ„įš”åŠŸčƒŊįš„å­˜å–æŦŠã€‚" + }, + "leaveConfirmationDialogContentTwo": { + "message": "č̋聝įĩĄæ‚¨įš„įŽĄį†å“ĄäģĨ重新取垗存取æŦŠé™ã€‚" + }, + "leaveConfirmationDialogConfirmButton": { + "message": "é›ĸ開 $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "howToManageMyVault": { + "message": "我čρåĻ‚äŊ•įŽĄį†æˆ‘įš„å¯†įĸŧåēĢīŧŸ" + }, + "transferItemsToOrganizationTitle": { + "message": "å°‡é …į›ŽčŊ‰į§ģ臺 $ORGANIZATION$", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "transferItemsToOrganizationContent": { + "message": "$ORGANIZATION$ į‚ēäē†åŽ‰å…¨æ€§čˆ‡åˆčĻæ€§īŧŒčĻæą‚æ‰€æœ‰é …į›Žįš†į”ąįĩ„į𔿓æœ‰ã€‚éģžæ“ŠæŽĨå—åŗå¯čŊ‰į§ģæ‚¨é …į›Žįš„æ“æœ‰æŦŠã€‚", + "placeholders": { + "organization": { + "content": "$1", + "example": "My Org Name" + } + } + }, + "acceptTransfer": { + "message": "同意čŊ‰į§ģ" + }, + "declineAndLeave": { + "message": "拒įĩ•ä¸Ļé›ĸ開" + }, + "whyAmISeeingThis": { + "message": "į‚ēäģ€éēŧæˆ‘æœƒįœ‹åˆ°æ­¤č¨Šæ¯īŧŸ" } } diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index fbb83a1bf56..63b288e9161 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -50,10 +50,10 @@ import { WindowMain } from "./main/window.main"; import { NativeAutofillMain } from "./platform/main/autofill/native-autofill.main"; import { ClipboardMain } from "./platform/main/clipboard.main"; import { DesktopCredentialStorageListener } from "./platform/main/desktop-credential-storage-listener"; +import { ElectronStorageService } from "./platform/main/electron-storage.service"; import { VersionMain } from "./platform/main/version.main"; import { DesktopSettingsService } from "./platform/services/desktop-settings.service"; import { ElectronLogMainService } from "./platform/services/electron-log.main.service"; -import { ElectronStorageService } from "./platform/services/electron-storage.service"; import { EphemeralValueStorageService } from "./platform/services/ephemeral-value-storage.main.service"; import { I18nMainService } from "./platform/services/i18n.main.service"; import { SSOLocalhostCallbackService } from "./platform/services/sso-localhost-callback.service"; @@ -311,17 +311,8 @@ export class Main { this.windowMain, ); - app - .whenReady() - .then(() => { - this.mainDesktopAutotypeService.init(); - }) - .catch((reason) => { - this.logService.error("Error initializing Autotype.", reason); - }); - app.on("will-quit", () => { - this.mainDesktopAutotypeService.disableAutotype(); + this.mainDesktopAutotypeService.dispose(); }); } diff --git a/apps/desktop/src/main/menu/menu.file.ts b/apps/desktop/src/main/menu/menu.file.ts index a8cdb347a77..d5e8cb468f9 100644 --- a/apps/desktop/src/main/menu/menu.file.ts +++ b/apps/desktop/src/main/menu/menu.file.ts @@ -146,8 +146,8 @@ export class FileMenu extends FirstMenu implements IMenubarMenu { private get syncVault(): MenuItemConstructorOptions { return { - id: "syncVault", - label: this.localize("syncVault"), + id: "syncNow", + label: this.localize("syncNow"), click: () => this.sendMessage("syncVault"), enabled: this.hasAuthenticatedAccounts, }; @@ -155,8 +155,8 @@ export class FileMenu extends FirstMenu implements IMenubarMenu { private get importVault(): MenuItemConstructorOptions { return { - id: "importVault", - label: this.localize("importData"), + id: "import", + label: this.localize("importNoun"), click: () => this.sendMessage("importVault"), enabled: !this._isLocked, }; @@ -164,8 +164,8 @@ export class FileMenu extends FirstMenu implements IMenubarMenu { private get exportVault(): MenuItemConstructorOptions { return { - id: "exportVault", - label: this.localize("exportVault"), + id: "export", + label: this.localize("exportNoun"), click: () => this.sendMessage("exportVault"), enabled: !this._isLocked, }; diff --git a/apps/desktop/src/main/native-messaging.main.ts b/apps/desktop/src/main/native-messaging.main.ts index ba5d8616752..a0c17a115e0 100644 --- a/apps/desktop/src/main/native-messaging.main.ts +++ b/apps/desktop/src/main/native-messaging.main.ts @@ -14,7 +14,7 @@ import { isDev } from "../utils"; import { WindowMain } from "./window.main"; export class NativeMessagingMain { - private ipcServer: ipc.IpcServer | null; + private ipcServer: ipc.NativeIpcServer | null; private connected: number[] = []; constructor( @@ -78,7 +78,7 @@ export class NativeMessagingMain { this.ipcServer.stop(); } - this.ipcServer = await ipc.IpcServer.listen("bw", (error, msg) => { + this.ipcServer = await ipc.NativeIpcServer.listen("bw", (error, msg) => { switch (msg.kind) { case ipc.IpcMessageType.Connected: { this.connected.push(msg.clientId); @@ -314,6 +314,7 @@ export class NativeMessagingMain { "Microsoft Edge Canary": `${this.homedir()}/Library/Application\ Support/Microsoft\ Edge\ Canary/`, Vivaldi: `${this.homedir()}/Library/Application\ Support/Vivaldi/`, Zen: `${this.homedir()}/Library/Application\ Support/Zen/`, + Helium: `${this.homedir()}/Library/Application\ Support/net.imput.helium/`, }; /* eslint-enable no-useless-escape */ } diff --git a/apps/desktop/src/main/tray.main.ts b/apps/desktop/src/main/tray.main.ts index b7ddefe6e1b..81df6497ca8 100644 --- a/apps/desktop/src/main/tray.main.ts +++ b/apps/desktop/src/main/tray.main.ts @@ -53,9 +53,14 @@ export class TrayMain { }, { visible: isDev(), - label: "Fake Popup", + label: "Fake Popup Select", click: () => this.fakePopup(), }, + { + visible: isDev(), + label: "Fake Popup Create", + click: () => this.fakePopupCreate(), + }, { type: "separator" }, { label: this.i18nService.t("exit"), @@ -218,4 +223,8 @@ export class TrayMain { private async fakePopup() { await this.messagingService.send("loadurl", { url: "/passkeys", modal: true }); } + + private async fakePopupCreate() { + await this.messagingService.send("loadurl", { url: "/create-passkey", modal: true }); + } } diff --git a/apps/desktop/src/main/window.main.ts b/apps/desktop/src/main/window.main.ts index 0e234126ea3..b2008d57bcd 100644 --- a/apps/desktop/src/main/window.main.ts +++ b/apps/desktop/src/main/window.main.ts @@ -4,7 +4,7 @@ import { once } from "node:events"; import * as path from "path"; import * as url from "url"; -import { app, BrowserWindow, ipcMain, nativeTheme, screen, session } from "electron"; +import { app, BrowserWindow, dialog, ipcMain, nativeTheme, screen, session } from "electron"; import { concatMap, firstValueFrom, pairwise } from "rxjs"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; @@ -100,10 +100,10 @@ export class WindowMain { applyMainWindowStyles(this.win, this.windowStates[mainWindowSizeKey]); // Because modal is used in front of another app, UX wise it makes sense to hide the main window when leaving modal mode. this.win.hide(); - } else if (!lastValue.isModalModeActive && newValue.isModalModeActive) { + } else if (newValue.isModalModeActive) { // Apply the popup modal styles this.logService.info("Applying popup modal styles", newValue.modalPosition); - applyPopupModalStyles(this.win, newValue.modalPosition); + applyPopupModalStyles(this.win, newValue.showTrafficButtons, newValue.modalPosition); this.win.show(); } }), @@ -122,6 +122,7 @@ export class WindowMain { if (!isMacAppStore()) { const gotTheLock = app.requestSingleInstanceLock(); if (!gotTheLock) { + dialog.showErrorBox("Error", "An instance of Bitwarden Desktop is already running."); app.quit(); return; } else { @@ -273,7 +274,7 @@ export class WindowMain { this.win = new BrowserWindow({ width: this.windowStates[mainWindowSizeKey].width, height: this.windowStates[mainWindowSizeKey].height, - minWidth: 680, + minWidth: 600, minHeight: 500, x: this.windowStates[mainWindowSizeKey].x, y: this.windowStates[mainWindowSizeKey].y, diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index 44fdb5c23b0..08cbdb913e6 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitwarden/desktop", - "version": "2025.12.0", + "version": "2026.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bitwarden/desktop", - "version": "2025.12.0", + "version": "2026.1.0", "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-napi": "file:../desktop_native/napi" diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index 4c396304f4a..859a18fefd0 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -2,7 +2,7 @@ "name": "@bitwarden/desktop", "productName": "Bitwarden", "description": "A secure and free password manager for all of your devices.", - "version": "2025.12.0", + "version": "2026.1.0", "author": "Bitwarden Inc. (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/apps/desktop/src/platform/main/autofill/native-autofill.main.ts b/apps/desktop/src/platform/main/autofill/native-autofill.main.ts index 71cfcab84ba..c0d860d74db 100644 --- a/apps/desktop/src/platform/main/autofill/native-autofill.main.ts +++ b/apps/desktop/src/platform/main/autofill/native-autofill.main.ts @@ -7,6 +7,11 @@ import { WindowMain } from "../../../main/window.main"; import { CommandDefinition } from "./command"; +type BufferedMessage = { + channel: string; + data: any; +}; + export type RunCommandParams = { namespace: C["namespace"]; command: C["name"]; @@ -16,13 +21,44 @@ export type RunCommandParams = { export type RunCommandResult = C["output"]; export class NativeAutofillMain { - private ipcServer: autofill.IpcServer | null; + private ipcServer?: autofill.AutofillIpcServer; + private messageBuffer: BufferedMessage[] = []; + private listenerReady = false; constructor( private logService: LogService, private windowMain: WindowMain, ) {} + /** + * Safely sends a message to the renderer, buffering it if the server isn't ready yet + */ + private safeSend(channel: string, data: any) { + if (this.listenerReady && this.windowMain.win?.webContents) { + this.windowMain.win.webContents.send(channel, data); + } else { + this.messageBuffer.push({ channel, data }); + } + } + + /** + * Flushes all buffered messages to the renderer + */ + private flushMessageBuffer() { + if (!this.windowMain.win?.webContents) { + this.logService.error("Cannot flush message buffer - window not available"); + return; + } + + this.logService.info(`Flushing ${this.messageBuffer.length} buffered messages`); + + for (const { channel, data } of this.messageBuffer) { + this.windowMain.win.webContents.send(channel, data); + } + + this.messageBuffer = []; + } + async init() { ipcMain.handle( "autofill.runCommand", @@ -34,16 +70,16 @@ export class NativeAutofillMain { }, ); - this.ipcServer = await autofill.IpcServer.listen( + this.ipcServer = await autofill.AutofillIpcServer.listen( "af", // RegistrationCallback (error, clientId, sequenceNumber, request) => { if (error) { this.logService.error("autofill.IpcServer.registration", error); - this.ipcServer.completeError(clientId, sequenceNumber, String(error)); + this.ipcServer?.completeError(clientId, sequenceNumber, String(error)); return; } - this.windowMain.win.webContents.send("autofill.passkeyRegistration", { + this.safeSend("autofill.passkeyRegistration", { clientId, sequenceNumber, request, @@ -53,10 +89,10 @@ export class NativeAutofillMain { (error, clientId, sequenceNumber, request) => { if (error) { this.logService.error("autofill.IpcServer.assertion", error); - this.ipcServer.completeError(clientId, sequenceNumber, String(error)); + this.ipcServer?.completeError(clientId, sequenceNumber, String(error)); return; } - this.windowMain.win.webContents.send("autofill.passkeyAssertion", { + this.safeSend("autofill.passkeyAssertion", { clientId, sequenceNumber, request, @@ -66,33 +102,54 @@ export class NativeAutofillMain { (error, clientId, sequenceNumber, request) => { if (error) { this.logService.error("autofill.IpcServer.assertion", error); - this.ipcServer.completeError(clientId, sequenceNumber, String(error)); + this.ipcServer?.completeError(clientId, sequenceNumber, String(error)); return; } - this.windowMain.win.webContents.send("autofill.passkeyAssertionWithoutUserInterface", { + this.safeSend("autofill.passkeyAssertionWithoutUserInterface", { clientId, sequenceNumber, request, }); }, + // NativeStatusCallback + (error, clientId, sequenceNumber, status) => { + if (error) { + this.logService.error("autofill.IpcServer.nativeStatus", error); + this.ipcServer?.completeError(clientId, sequenceNumber, String(error)); + return; + } + this.safeSend("autofill.nativeStatus", { + clientId, + sequenceNumber, + status, + }); + }, ); + ipcMain.on("autofill.listenerReady", () => { + this.listenerReady = true; + this.logService.info( + `Listener is ready, flushing ${this.messageBuffer.length} buffered messages`, + ); + this.flushMessageBuffer(); + }); + ipcMain.on("autofill.completePasskeyRegistration", (event, data) => { - this.logService.warning("autofill.completePasskeyRegistration", data); + this.logService.debug("autofill.completePasskeyRegistration", data); const { clientId, sequenceNumber, response } = data; - this.ipcServer.completeRegistration(clientId, sequenceNumber, response); + this.ipcServer?.completeRegistration(clientId, sequenceNumber, response); }); ipcMain.on("autofill.completePasskeyAssertion", (event, data) => { - this.logService.warning("autofill.completePasskeyAssertion", data); + this.logService.debug("autofill.completePasskeyAssertion", data); const { clientId, sequenceNumber, response } = data; - this.ipcServer.completeAssertion(clientId, sequenceNumber, response); + this.ipcServer?.completeAssertion(clientId, sequenceNumber, response); }); ipcMain.on("autofill.completeError", (event, data) => { - this.logService.warning("autofill.completeError", data); + this.logService.debug("autofill.completeError", data); const { clientId, sequenceNumber, error } = data; - this.ipcServer.completeError(clientId, sequenceNumber, String(error)); + this.ipcServer?.completeError(clientId, sequenceNumber, String(error)); }); } diff --git a/apps/desktop/src/platform/services/electron-storage.service.ts b/apps/desktop/src/platform/main/electron-storage.service.ts similarity index 93% rename from apps/desktop/src/platform/services/electron-storage.service.ts rename to apps/desktop/src/platform/main/electron-storage.service.ts index 34aa8837475..08cc5cfbd40 100644 --- a/apps/desktop/src/platform/services/electron-storage.service.ts +++ b/apps/desktop/src/platform/main/electron-storage.service.ts @@ -3,7 +3,7 @@ import * as fs from "fs"; import { ipcMain } from "electron"; -import ElectronStore from "electron-store"; +import ElectronStore, { Options as ElectronStoreOptions } from "electron-store"; import { Subject } from "rxjs"; import { @@ -35,7 +35,7 @@ export class ElectronStorageService implements AbstractStorageService { NodeUtils.mkdirpSync(dir, "700"); } const fileMode = isWindowsPortable() ? 0o666 : 0o600; - const storeConfig: ElectronStore.Options> = { + const storeConfig: ElectronStoreOptions> = { defaults: defaults, name: "data", configFileMode: fileMode, diff --git a/apps/desktop/src/platform/main/tsconfig.json b/apps/desktop/src/platform/main/tsconfig.json new file mode 100644 index 00000000000..8b49a11b574 --- /dev/null +++ b/apps/desktop/src/platform/main/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../../tsconfig.main", + "include": ["./**/*.ts"] +} diff --git a/apps/desktop/src/platform/models/domain/window-state.ts b/apps/desktop/src/platform/models/domain/window-state.ts index 0efc9a1efab..ab52531bb5d 100644 --- a/apps/desktop/src/platform/models/domain/window-state.ts +++ b/apps/desktop/src/platform/models/domain/window-state.ts @@ -14,5 +14,6 @@ export class WindowState { export class ModalModeState { isModalModeActive: boolean; + showTrafficButtons?: boolean; modalPosition?: { x: number; y: number }; // Modal position is often passed from the native UI } diff --git a/apps/desktop/src/platform/popup-modal-styles.ts b/apps/desktop/src/platform/popup-modal-styles.ts index 5c5619bd463..6ad00b44171 100644 --- a/apps/desktop/src/platform/popup-modal-styles.ts +++ b/apps/desktop/src/platform/popup-modal-styles.ts @@ -3,15 +3,19 @@ import { BrowserWindow } from "electron"; import { WindowState } from "./models/domain/window-state"; // change as needed, however limited by mainwindow minimum size -const popupWidth = 680; -const popupHeight = 500; +const popupWidth = 600; +const popupHeight = 600; type Position = { x: number; y: number }; -export function applyPopupModalStyles(window: BrowserWindow, position?: Position) { +export function applyPopupModalStyles( + window: BrowserWindow, + showTrafficButtons: boolean = true, + position?: Position, +) { window.unmaximize(); window.setSize(popupWidth, popupHeight); - window.setWindowButtonVisibility?.(false); + window.setWindowButtonVisibility?.(showTrafficButtons); window.setMenuBarVisibility?.(false); window.setResizable(false); window.setAlwaysOnTop(true); @@ -40,7 +44,7 @@ function positionWindow(window: BrowserWindow, position?: Position) { } export function applyMainWindowStyles(window: BrowserWindow, existingWindowState: WindowState) { - window.setMinimumSize(680, 500); + window.setMinimumSize(popupWidth, popupHeight); // need to guard against null/undefined values diff --git a/apps/desktop/src/platform/preload.ts b/apps/desktop/src/platform/preload.ts index 5af2fa571ec..5f643242a9c 100644 --- a/apps/desktop/src/platform/preload.ts +++ b/apps/desktop/src/platform/preload.ts @@ -17,6 +17,7 @@ import { isFlatpak, isMacAppStore, isSnapStore, + isWindowsPortable, isWindowsStore, } from "../utils"; @@ -108,8 +109,13 @@ const ephemeralStore = { }; const localhostCallbackService = { - openSsoPrompt: (codeChallenge: string, state: string, email: string): Promise => { - return ipcRenderer.invoke("openSsoPrompt", { codeChallenge, state, email }); + openSsoPrompt: ( + codeChallenge: string, + state: string, + email: string, + orgSsoIdentifier?: string, + ): Promise => { + return ipcRenderer.invoke("openSsoPrompt", { codeChallenge, state, email, orgSsoIdentifier }); }, }; @@ -128,6 +134,7 @@ export default { isDev: isDev(), isMacAppStore: isMacAppStore(), isWindowsStore: isWindowsStore(), + isWindowsPortable: isWindowsPortable(), isFlatpak: isFlatpak(), isSnapStore: isSnapStore(), isAppImage: isAppImage(), diff --git a/apps/desktop/src/platform/services/desktop-settings.service.ts b/apps/desktop/src/platform/services/desktop-settings.service.ts index c11f10646d7..d7c17433471 100644 --- a/apps/desktop/src/platform/services/desktop-settings.service.ts +++ b/apps/desktop/src/platform/services/desktop-settings.service.ts @@ -335,9 +335,14 @@ export class DesktopSettingsService { * Sets the modal mode of the application. Setting this changes the windows-size and other properties. * @param value `true` if the application is in modal mode, `false` if it is not. */ - async setModalMode(value: boolean, modalPosition?: { x: number; y: number }) { + async setModalMode( + value: boolean, + showTrafficButtons?: boolean, + modalPosition?: { x: number; y: number }, + ) { await this.modalModeState.update(() => ({ isModalModeActive: value, + showTrafficButtons, modalPosition, })); } diff --git a/apps/desktop/src/platform/services/electron-log.main.service.ts b/apps/desktop/src/platform/services/electron-log.main.service.ts index 947f4449271..e148f7a45c8 100644 --- a/apps/desktop/src/platform/services/electron-log.main.service.ts +++ b/apps/desktop/src/platform/services/electron-log.main.service.ts @@ -22,7 +22,7 @@ export class ElectronLogMainService extends BaseLogService { return; } - log.transports.file.level = "info"; + log.transports.file.level = isDev() ? "debug" : "info"; if (this.logDir != null) { log.transports.file.resolvePathFn = () => path.join(this.logDir, "app.log"); } diff --git a/apps/desktop/src/platform/services/sso-localhost-callback.service.ts b/apps/desktop/src/platform/services/sso-localhost-callback.service.ts index 75a84919b07..fdd9bc29237 100644 --- a/apps/desktop/src/platform/services/sso-localhost-callback.service.ts +++ b/apps/desktop/src/platform/services/sso-localhost-callback.service.ts @@ -25,20 +25,25 @@ export class SSOLocalhostCallbackService { private messagingService: MessageSender, private ssoUrlService: SsoUrlService, ) { - ipcMain.handle("openSsoPrompt", async (event, { codeChallenge, state, email }) => { - // Close any existing server before starting new one - if (this.currentServer) { - await this.closeCurrentServer(); - } + ipcMain.handle( + "openSsoPrompt", + async (event, { codeChallenge, state, email, orgSsoIdentifier }) => { + // Close any existing server before starting new one + if (this.currentServer) { + await this.closeCurrentServer(); + } - return this.openSsoPrompt(codeChallenge, state, email).then(({ ssoCode, recvState }) => { - this.messagingService.send("ssoCallback", { - code: ssoCode, - state: recvState, - redirectUri: this.ssoRedirectUri, - }); - }); - }); + return this.openSsoPrompt(codeChallenge, state, email, orgSsoIdentifier).then( + ({ ssoCode, recvState }) => { + this.messagingService.send("ssoCallback", { + code: ssoCode, + state: recvState, + redirectUri: this.ssoRedirectUri, + }); + }, + ); + }, + ); } private async closeCurrentServer(): Promise { @@ -58,6 +63,7 @@ export class SSOLocalhostCallbackService { codeChallenge: string, state: string, email: string, + orgSsoIdentifier?: string, ): Promise<{ ssoCode: string; recvState: string }> { const env = await firstValueFrom(this.environmentService.environment$); @@ -121,6 +127,7 @@ export class SSOLocalhostCallbackService { state, codeChallenge, email, + orgSsoIdentifier, ); // Set up error handler before attempting to listen diff --git a/apps/desktop/src/scss/migration.scss b/apps/desktop/src/scss/migration.scss new file mode 100644 index 00000000000..ba70d4fa009 --- /dev/null +++ b/apps/desktop/src/scss/migration.scss @@ -0,0 +1,29 @@ +/** + * Desktop UI Migration + * + * These are temporary styles during the desktop ui migration. + **/ + +/** + * This removes any padding applied by the bit-layout to content. + * This should be revisited once the table is migrated, and again once drawers are migrated. + **/ +bit-layout { + #main-content { + padding: 0 0 0 0; + } +} +/** + * Send list panel styling for send-v2 component + * Temporary during migration - width handled by tw-w-2/5 + **/ +.vault > .send-items-panel { + order: 2; + min-width: 200px; + border-right: 1px solid; + + @include themify($themes) { + background-color: themed("backgroundColor"); + border-right-color: themed("borderColor"); + } +} diff --git a/apps/desktop/src/scss/styles.scss b/apps/desktop/src/scss/styles.scss index c579e6acdc0..b4082afd38c 100644 --- a/apps/desktop/src/scss/styles.scss +++ b/apps/desktop/src/scss/styles.scss @@ -15,5 +15,6 @@ @import "left-nav.scss"; @import "loading.scss"; @import "plugins.scss"; +@import "migration.scss"; @import "../../../../libs/angular/src/scss/icons.scss"; @import "../../../../libs/components/src/multi-select/scss/bw.theme"; diff --git a/apps/desktop/src/services/biometric-message-handler.service.spec.ts b/apps/desktop/src/services/biometric-message-handler.service.spec.ts index 49d346bfa3a..3b343fcc0fb 100644 --- a/apps/desktop/src/services/biometric-message-handler.service.spec.ts +++ b/apps/desktop/src/services/biometric-message-handler.service.spec.ts @@ -2,7 +2,7 @@ import { NgZone } from "@angular/core"; import { mock, MockProxy } from "jest-mock-extended"; import { BehaviorSubject, filter, firstValueFrom, of, take, timeout, timer } from "rxjs"; -import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; @@ -10,7 +10,7 @@ import { EncryptService } from "@bitwarden/common/key-management/crypto/abstract import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { FakeAccountService } from "@bitwarden/common/spec"; +import { mockAccountInfoWith, FakeAccountService } from "@bitwarden/common/spec"; import { CsprngArray } from "@bitwarden/common/types/csprng"; import { UserId } from "@bitwarden/common/types/guid"; import { DialogService } from "@bitwarden/components"; @@ -23,17 +23,15 @@ import { BiometricMessageHandlerService } from "./biometric-message-handler.serv const SomeUser = "SomeUser" as UserId; const AnotherUser = "SomeOtherUser" as UserId; -const accounts: Record = { - [SomeUser]: { +const accounts = { + [SomeUser]: mockAccountInfoWith({ name: "some user", email: "some.user@example.com", - emailVerified: true, - }, - [AnotherUser]: { + }), + [AnotherUser]: mockAccountInfoWith({ name: "some other user", email: "some.other.user@example.com", - emailVerified: true, - }, + }), }; describe("BiometricMessageHandlerService", () => { diff --git a/apps/desktop/src/services/desktop-premium-upgrade-prompt.service.spec.ts b/apps/desktop/src/services/desktop-premium-upgrade-prompt.service.spec.ts index 1eee4cd54f6..3c36d648167 100644 --- a/apps/desktop/src/services/desktop-premium-upgrade-prompt.service.spec.ts +++ b/apps/desktop/src/services/desktop-premium-upgrade-prompt.service.spec.ts @@ -4,26 +4,24 @@ import { mock, MockProxy } from "jest-mock-extended"; import { PremiumUpgradeDialogComponent } from "@bitwarden/angular/billing/components"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { DialogService } from "@bitwarden/components"; +import { PremiumComponent } from "../billing/app/accounts/premium.component"; + import { DesktopPremiumUpgradePromptService } from "./desktop-premium-upgrade-prompt.service"; describe("DesktopPremiumUpgradePromptService", () => { let service: DesktopPremiumUpgradePromptService; - let messager: MockProxy; let configService: MockProxy; let dialogService: MockProxy; beforeEach(async () => { - messager = mock(); configService = mock(); dialogService = mock(); await TestBed.configureTestingModule({ providers: [ DesktopPremiumUpgradePromptService, - { provide: MessagingService, useValue: messager }, { provide: ConfigService, useValue: configService }, { provide: DialogService, useValue: dialogService }, ], @@ -52,10 +50,10 @@ describe("DesktopPremiumUpgradePromptService", () => { FeatureFlag.PM23713_PremiumBadgeOpensNewPremiumUpgradeDialog, ); expect(openSpy).toHaveBeenCalledWith(dialogService); - expect(messager.send).not.toHaveBeenCalled(); + expect(dialogService.open).not.toHaveBeenCalled(); }); - it("sends openPremium message when feature flag is disabled", async () => { + it("opens the PremiumComponent when feature flag is disabled", async () => { configService.getFeatureFlag.mockResolvedValue(false); await service.promptForPremium(); @@ -63,7 +61,7 @@ describe("DesktopPremiumUpgradePromptService", () => { expect(configService.getFeatureFlag).toHaveBeenCalledWith( FeatureFlag.PM23713_PremiumBadgeOpensNewPremiumUpgradeDialog, ); - expect(messager.send).toHaveBeenCalledWith("openPremium"); + expect(dialogService.open).toHaveBeenCalledWith(PremiumComponent); expect(openSpy).not.toHaveBeenCalled(); }); }); diff --git a/apps/desktop/src/services/desktop-premium-upgrade-prompt.service.ts b/apps/desktop/src/services/desktop-premium-upgrade-prompt.service.ts index 5004e5ed547..0161baba801 100644 --- a/apps/desktop/src/services/desktop-premium-upgrade-prompt.service.ts +++ b/apps/desktop/src/services/desktop-premium-upgrade-prompt.service.ts @@ -3,15 +3,15 @@ import { inject } from "@angular/core"; import { PremiumUpgradeDialogComponent } from "@bitwarden/angular/billing/components"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { DialogService } from "@bitwarden/components"; +import { PremiumComponent } from "../billing/app/accounts/premium.component"; + /** * This class handles the premium upgrade process for the desktop. */ export class DesktopPremiumUpgradePromptService implements PremiumUpgradePromptService { - private messagingService = inject(MessagingService); private configService = inject(ConfigService); private dialogService = inject(DialogService); @@ -23,7 +23,7 @@ export class DesktopPremiumUpgradePromptService implements PremiumUpgradePromptS if (showNewDialog) { PremiumUpgradeDialogComponent.open(this.dialogService); } else { - this.messagingService.send("openPremium"); + this.dialogService.open(PremiumComponent); } } } diff --git a/apps/desktop/src/services/encrypted-message-handler.service.ts b/apps/desktop/src/services/encrypted-message-handler.service.ts index 366a144c021..ccbc7c539d0 100644 --- a/apps/desktop/src/services/encrypted-message-handler.service.ts +++ b/apps/desktop/src/services/encrypted-message-handler.service.ts @@ -166,8 +166,7 @@ export class EncryptedMessageHandlerService { try { const activeUserId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); - const encrypted = await this.cipherService.encrypt(cipherView, activeUserId); - await this.cipherService.createWithServer(encrypted); + await this.cipherService.createWithServer(cipherView, activeUserId); // Notify other clients of new login await this.messagingService.send("addedCipher"); @@ -212,9 +211,8 @@ export class EncryptedMessageHandlerService { cipherView.login.password = credentialUpdatePayload.password; cipherView.login.username = credentialUpdatePayload.userName; cipherView.login.uris[0].uri = credentialUpdatePayload.uri; - const encrypted = await this.cipherService.encrypt(cipherView, activeUserId); - await this.cipherService.updateWithServer(encrypted); + await this.cipherService.updateWithServer(cipherView, activeUserId); // Notify other clients of update await this.messagingService.send("editedCipher"); diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html new file mode 100644 index 00000000000..2ee78adcfb0 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.html @@ -0,0 +1,24 @@ +@if (collection().children.length) { + + @for (childCollection of collection().children; track childCollection.node.id) { + + } + +} @else { + +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts new file mode 100644 index 00000000000..6a801e78ec2 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/collection-filter.component.ts @@ -0,0 +1,38 @@ +import { Component, input, computed } from "@angular/core"; + +import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { NavigationModule, A11yTitleDirective } from "@bitwarden/components"; +import { VaultFilter, CollectionFilter } from "@bitwarden/vault"; + +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection +@Component({ + selector: "app-collection-filter", + templateUrl: "collection-filter.component.html", + imports: [A11yTitleDirective, NavigationModule], +}) +export class CollectionFilterComponent { + protected readonly collection = input.required>(); + protected readonly activeFilter = input(); + + protected readonly displayName = computed(() => { + return this.collection().node.name; + }); + + protected readonly isActive = computed(() => { + return ( + this.collection().node.id === this.activeFilter()?.collectionId && + !!this.activeFilter()?.selectedCollectionNode + ); + }); + + protected applyFilter(event: Event) { + event.stopPropagation(); + + const filter = this.activeFilter(); + + if (filter) { + filter.selectedCollectionNode = this.collection(); + } + } +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html new file mode 100644 index 00000000000..f063167c48f --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.html @@ -0,0 +1,50 @@ +@if (folder().children.length) { + + @if (folder()?.node.id) { + + } + @for (childFolder of folder().children; track childFolder.node.id) { + + } + +} @else { + + @if (folder()?.node.id) { + + } + +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts new file mode 100644 index 00000000000..dd2d5b504c8 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/folder-filter.component.ts @@ -0,0 +1,43 @@ +import { Component, input, computed, output } from "@angular/core"; + +import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { IconButtonModule, NavigationModule, A11yTitleDirective } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; +import { VaultFilter, FolderFilter } from "@bitwarden/vault"; + +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection +@Component({ + selector: "app-folder-filter", + templateUrl: "folder-filter.component.html", + imports: [A11yTitleDirective, NavigationModule, IconButtonModule, I18nPipe], +}) +export class FolderFilterComponent { + protected readonly folder = input.required>(); + protected readonly activeFilter = input(); + protected onEditFolder = output(); + + protected readonly displayName = computed(() => { + return this.folder().node.name; + }); + + protected readonly isActive = computed(() => { + return ( + this.folder().node.id === this.activeFilter()?.folderId && + !!this.activeFilter()?.selectedFolderNode + ); + }); + + protected applyFilter(event: Event) { + event.stopPropagation(); + const filter = this.activeFilter(); + + if (filter) { + filter.selectedFolderNode = this.folder(); + } + } + + protected editFolder(folder: FolderFilter) { + this.onEditFolder.emit(folder); + } +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html new file mode 100644 index 00000000000..daf84c642d6 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.html @@ -0,0 +1,32 @@ +@if (show()) { + + @for (organization of organizations().children ?? []; track organization.node.id) { + + @if (!organization.node.enabled) { + + } + + } + +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts new file mode 100644 index 00000000000..520c29833e3 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/organization-filter.component.ts @@ -0,0 +1,82 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore +import { Component, computed, input, inject } from "@angular/core"; + +import { DisplayMode } from "@bitwarden/angular/vault/vault-filter/models/display-mode"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { ToastService, NavigationModule, A11yTitleDirective } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; +import { OrganizationFilter, VaultFilter, VaultFilterServiceAbstraction } from "@bitwarden/vault"; + +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection +@Component({ + selector: "app-organization-filter", + templateUrl: "organization-filter.component.html", + imports: [A11yTitleDirective, NavigationModule, I18nPipe], +}) +export class OrganizationFilterComponent { + private toastService: ToastService = inject(ToastService); + private i18nService: I18nService = inject(I18nService); + private vaultFilterService: VaultFilterServiceAbstraction = inject(VaultFilterServiceAbstraction); + + protected readonly hide = input(false); + protected readonly organizations = input.required>(); + protected readonly activeFilter = input(); + protected readonly activeOrganizationDataOwnership = input(false); + protected readonly activeSingleOrganizationPolicy = input(false); + + protected readonly show = computed(() => { + const hiddenDisplayModes: DisplayMode[] = [ + "singleOrganizationAndOrganizatonDataOwnershipPolicies", + ]; + return ( + !this.hide() && + this.organizations()?.children.length > 0 && + hiddenDisplayModes.indexOf(this.displayMode()) === -1 + ); + }); + + protected readonly displayMode = computed(() => { + let displayMode: DisplayMode = "organizationMember"; + if (this.organizations() == null || this.organizations().children.length < 1) { + displayMode = "noOrganizations"; + } else if (this.activeOrganizationDataOwnership() && !this.activeSingleOrganizationPolicy()) { + displayMode = "organizationDataOwnershipPolicy"; + } else if (!this.activeOrganizationDataOwnership() && this.activeSingleOrganizationPolicy()) { + displayMode = "singleOrganizationPolicy"; + } else if (this.activeOrganizationDataOwnership() && this.activeSingleOrganizationPolicy()) { + displayMode = "singleOrganizationAndOrganizatonDataOwnershipPolicies"; + } + + return displayMode; + }); + + protected applyFilter(event: Event, organization: TreeNode) { + event.stopPropagation(); + if (!organization.node.enabled) { + this.toastService.showToast({ + variant: "error", + message: this.i18nService.t("disabledOrganizationFilterError"), + }); + return; + } + + this.vaultFilterService.setOrganizationFilter(organization.node); + const filter = this.activeFilter(); + + if (filter) { + filter.selectedOrganizationNode = organization; + } + } + + protected applyAllVaultsFilter() { + this.vaultFilterService.clearOrganizationFilter(); + const filter = this.activeFilter(); + + if (filter) { + filter.selectedOrganizationNode = null; + } + } +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html new file mode 100644 index 00000000000..b6b22a3c68d --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.html @@ -0,0 +1,24 @@ +@if (!hideArchive()) { + + @if (!(canArchive$ | async)) { + + + + } + +} + diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts new file mode 100644 index 00000000000..f3e23cd04e5 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/status-filter.component.ts @@ -0,0 +1,80 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore +import { CommonModule } from "@angular/common"; +import { Component, viewChild, input, inject } from "@angular/core"; +import { combineLatest, firstValueFrom, map, switchMap } from "rxjs"; + +import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; +import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { NavigationModule, A11yTitleDirective } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; +import { VaultFilter, CipherStatus, CipherTypeFilter } from "@bitwarden/vault"; + +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection +@Component({ + selector: "app-status-filter", + templateUrl: "status-filter.component.html", + imports: [CommonModule, A11yTitleDirective, NavigationModule, PremiumBadgeComponent, I18nPipe], +}) +export class StatusFilterComponent { + private accountService: AccountService = inject(AccountService); + private cipherArchiveService: CipherArchiveService = inject(CipherArchiveService); + + protected readonly hideArchive = input(false); + protected readonly activeFilter = input.required(); + + private readonly premiumBadgeComponent = viewChild(PremiumBadgeComponent); + + protected readonly archiveFilter: CipherTypeFilter = { + id: "archive", + name: "archiveNoun", + type: "archive", + icon: "bwi-archive", + }; + protected readonly trashFilter: CipherTypeFilter = { + id: "trash", + name: "trash", + type: "trash", + icon: "bwi-trash", + }; + + protected applyFilter(filterType: CipherStatus) { + let filter: CipherTypeFilter | null = null; + if (filterType === "archive") { + filter = this.archiveFilter; + } else if (filterType === "trash") { + filter = this.trashFilter; + } + + if (filter) { + this.activeFilter().selectedCipherTypeNode = new TreeNode(filter, null); + } + } + + private userId$ = this.accountService.activeAccount$.pipe(getUserId); + protected canArchive$ = this.userId$.pipe( + switchMap((userId) => this.cipherArchiveService.userCanArchive$(userId)), + ); + + protected hasArchivedCiphers$ = this.userId$.pipe( + switchMap((userId) => + this.cipherArchiveService.archivedCiphers$(userId).pipe(map((ciphers) => ciphers.length > 0)), + ), + ); + + protected async handleArchiveFilter(event: Event) { + const [canArchive, hasArchivedCiphers] = await firstValueFrom( + combineLatest([this.canArchive$, this.hasArchivedCiphers$]), + ); + + if (canArchive || hasArchivedCiphers) { + this.applyFilter("archive"); + } else { + await this.premiumBadgeComponent()?.promptForPremium(event); + } + } +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html new file mode 100644 index 00000000000..c9807e62066 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.html @@ -0,0 +1,26 @@ + + @for (typeFilter of typeFilters$ | async; track typeFilter) { + + } + diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts new file mode 100644 index 00000000000..e5e7a7691e4 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/filters/type-filter.component.ts @@ -0,0 +1,57 @@ +import { CommonModule } from "@angular/common"; +import { Component, input, inject } from "@angular/core"; +import { map, shareReplay } from "rxjs"; + +import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; +import { NavigationModule, A11yTitleDirective } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; +import { VaultFilter, CipherTypeFilter } from "@bitwarden/vault"; + +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection +@Component({ + selector: "app-type-filter", + templateUrl: "type-filter.component.html", + imports: [CommonModule, A11yTitleDirective, NavigationModule, I18nPipe], +}) +export class TypeFilterComponent { + private restrictedItemTypesService: RestrictedItemTypesService = inject( + RestrictedItemTypesService, + ); + + protected readonly cipherTypes = input.required>(); + protected readonly activeFilter = input(); + + protected applyTypeFilter(event: Event, cipherType: TreeNode) { + event.stopPropagation(); + const filter = this.activeFilter(); + + if (filter) { + filter.selectedCipherTypeNode = cipherType; + } + } + + protected applyAllItemsFilter(event: Event) { + const filter = this.activeFilter(); + + if (filter) { + filter.selectedCipherTypeNode = this.cipherTypes(); + } + } + + protected typeFilters$ = this.restrictedItemTypesService.restricted$.pipe( + map((restrictedItemTypes) => + // Filter out restricted item types from the typeFilters array + this.cipherTypes().children.filter( + (type) => + !restrictedItemTypes.some( + (restrictedType) => + restrictedType.allowViewOrgIds.length === 0 && + restrictedType.cipherType === type.node.type, + ), + ), + ), + shareReplay({ bufferSize: 1, refCount: true }), + ); +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html new file mode 100644 index 00000000000..2110c545d9e --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.html @@ -0,0 +1,44 @@ +@if (!isLoaded) { +
      + +
      +} @else { + + + + + @if (showCollectionsFilter()) { + + @for (collection of collections()?.children ?? []; track collection.node.id) { + + } + + } + + @for (folder of folders()?.children ?? []; track folder.node.id) { + + } + + +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts new file mode 100644 index 00000000000..35f958e495d --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault-filter/vault-filter.component.ts @@ -0,0 +1,147 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore +import { CommonModule } from "@angular/common"; +import { Component, inject, OnInit, output, computed, signal } from "@angular/core"; +import { toSignal } from "@angular/core/rxjs-interop"; +import { firstValueFrom, Subject, takeUntil } from "rxjs"; + +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { UserId } from "@bitwarden/common/types/guid"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; +import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; +import { NavigationModule, DialogService, A11yTitleDirective } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; +import { + FolderFilter, + VaultFilter, + VaultFilterServiceAbstraction as VaultFilterService, + AddEditFolderDialogComponent, + RoutedVaultFilterBridgeService, +} from "@bitwarden/vault"; + +import { DesktopPremiumUpgradePromptService } from "../../../../services/desktop-premium-upgrade-prompt.service"; + +import { CollectionFilterComponent } from "./filters/collection-filter.component"; +import { FolderFilterComponent } from "./filters/folder-filter.component"; +import { OrganizationFilterComponent } from "./filters/organization-filter.component"; +import { StatusFilterComponent } from "./filters/status-filter.component"; +import { TypeFilterComponent } from "./filters/type-filter.component"; + +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection +@Component({ + selector: "app-vault-filter", + templateUrl: "vault-filter.component.html", + imports: [ + I18nPipe, + NavigationModule, + CommonModule, + OrganizationFilterComponent, + StatusFilterComponent, + TypeFilterComponent, + CollectionFilterComponent, + FolderFilterComponent, + A11yTitleDirective, + ], + providers: [ + { + provide: PremiumUpgradePromptService, + useClass: DesktopPremiumUpgradePromptService, + }, + ], +}) +export class VaultFilterComponent implements OnInit { + private routedVaultFilterBridgeService = inject(RoutedVaultFilterBridgeService); + private vaultFilterService: VaultFilterService = inject(VaultFilterService); + private accountService: AccountService = inject(AccountService); + private cipherArchiveService: CipherArchiveService = inject(CipherArchiveService); + private folderService: FolderService = inject(FolderService); + private policyService: PolicyService = inject(PolicyService); + private dialogService: DialogService = inject(DialogService); + private componentIsDestroyed$ = new Subject(); + + protected readonly activeFilter = signal(null); + protected onFilterChange = output(); + + private activeUserId: UserId; + protected isLoaded = false; + protected showArchiveVaultFilter = false; + protected activeOrganizationDataOwnershipPolicy: boolean; + protected activeSingleOrganizationPolicy: boolean; + protected readonly organizations = toSignal(this.vaultFilterService.organizationTree$); + protected readonly collections = toSignal(this.vaultFilterService.collectionTree$); + protected readonly folders = toSignal(this.vaultFilterService.folderTree$); + protected readonly cipherTypes = toSignal(this.vaultFilterService.cipherTypeTree$); + + protected readonly showCollectionsFilter = computed(() => { + return ( + this.organizations() != null && + !this.activeFilter()?.isMyVaultSelected && + !this.allOrganizationsDisabled() + ); + }); + + protected readonly allOrganizationsDisabled = computed(() => { + if (!this.organizations()) { + return false; + } + const orgs = this.organizations().children.filter((org) => org.node.id !== "MyVault"); + return orgs.length > 0 && orgs.every((org) => !org.node.enabled); + }); + + private async setActivePolicies() { + this.activeOrganizationDataOwnershipPolicy = await firstValueFrom( + this.policyService.policyAppliesToUser$( + PolicyType.OrganizationDataOwnership, + this.activeUserId, + ), + ); + this.activeSingleOrganizationPolicy = await firstValueFrom( + this.policyService.policyAppliesToUser$(PolicyType.SingleOrg, this.activeUserId), + ); + } + + async ngOnInit(): Promise { + this.activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + if (this.organizations() != null && this.organizations().children.length > 0) { + await this.setActivePolicies(); + } + + this.showArchiveVaultFilter = await firstValueFrom( + this.cipherArchiveService.hasArchiveFlagEnabled$, + ); + + this.routedVaultFilterBridgeService.activeFilter$ + .pipe(takeUntil(this.componentIsDestroyed$)) + .subscribe((filter) => { + this.activeFilter.set(filter); + }); + + this.isLoaded = true; + } + + protected async editFolder(folder: FolderFilter) { + if (!this.activeUserId) { + return; + } + const folderView = await firstValueFrom( + this.folderService.getDecrypted$(folder.id, this.activeUserId), + ); + + if (!folderView) { + return; + } + + AddEditFolderDialogComponent.open(this.dialogService, { + editFolderConfig: { + folder: { + ...folderView, + }, + }, + }); + } +} diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.html b/apps/desktop/src/vault/app/vault-v3/vault.component.html new file mode 100644 index 00000000000..a9a25f57994 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.html @@ -0,0 +1,70 @@ +
      + + +
      + +
      +
      +
      + + + + + + + +
      +
      +
      +
      + +
      + diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.ts b/apps/desktop/src/vault/app/vault-v3/vault.component.ts new file mode 100644 index 00000000000..9d5fad2fe4c --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.ts @@ -0,0 +1,1019 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore +import { CommonModule } from "@angular/common"; +import { ChangeDetectorRef, Component, NgZone, OnDestroy, OnInit, ViewChild } from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; +import { + firstValueFrom, + Subject, + takeUntil, + switchMap, + lastValueFrom, + Observable, + from, +} from "rxjs"; +import { filter, map, take } from "rxjs/operators"; + +import { CollectionService } from "@bitwarden/admin-console/common"; +import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; +import { VaultViewPasswordHistoryService } from "@bitwarden/angular/services/view-password-history.service"; +import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { CollectionView } from "@bitwarden/common/admin-console/models/collections"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +import { EventType } from "@bitwarden/common/enums"; +import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { getByIds } from "@bitwarden/common/platform/misc"; +import { SyncService } from "@bitwarden/common/platform/sync"; +import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; +import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service"; +import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions/view-password-history.service"; +import { CipherType, toCipherType } from "@bitwarden/common/vault/enums"; +import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { + CipherViewLike, + CipherViewLikeUtils, +} from "@bitwarden/common/vault/utils/cipher-view-like-utils"; +import { filterOutNullish } from "@bitwarden/common/vault/utils/observable-utilities"; +import { + BadgeModule, + ButtonModule, + DialogService, + ItemModule, + ToastService, + CopyClickListener, + COPY_CLICK_LISTENER, +} from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; +import { + AttachmentDialogResult, + AttachmentsV2Component, + ChangeLoginPasswordService, + CipherFormConfig, + CipherFormConfigService, + CipherFormGenerationService, + CipherFormMode, + CipherFormModule, + CipherViewComponent, + CollectionAssignmentResult, + DecryptionFailureDialogComponent, + DefaultChangeLoginPasswordService, + DefaultCipherFormConfigService, + PasswordRepromptService, + CipherFormComponent, + ArchiveCipherUtilitiesService, + VaultFilter, + VaultFilterServiceAbstraction as VaultFilterService, + RoutedVaultFilterBridgeService, + VaultItemsTransferService, + DefaultVaultItemsTransferService, +} from "@bitwarden/vault"; + +import { SearchBarService } from "../../../app/layout/search/search-bar.service"; +import { DesktopCredentialGenerationService } from "../../../services/desktop-cipher-form-generator.service"; +import { DesktopPremiumUpgradePromptService } from "../../../services/desktop-premium-upgrade-prompt.service"; +import { invokeMenu, RendererMenuItem } from "../../../utils"; +import { AssignCollectionsDesktopComponent } from "../vault/assign-collections"; +import { ItemFooterComponent } from "../vault/item-footer.component"; +import { VaultItemsV2Component } from "../vault/vault-items-v2.component"; + +const BroadcasterSubscriptionId = "VaultComponent"; + +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection +@Component({ + selector: "app-vault-v3", + templateUrl: "vault.component.html", + imports: [ + BadgeModule, + CommonModule, + CipherFormModule, + CipherViewComponent, + ItemFooterComponent, + I18nPipe, + ItemModule, + ButtonModule, + PremiumBadgeComponent, + VaultItemsV2Component, + ], + providers: [ + { + provide: CipherFormConfigService, + useClass: DefaultCipherFormConfigService, + }, + { + provide: ChangeLoginPasswordService, + useClass: DefaultChangeLoginPasswordService, + }, + { + provide: ViewPasswordHistoryService, + useClass: VaultViewPasswordHistoryService, + }, + { + provide: PremiumUpgradePromptService, + useClass: DesktopPremiumUpgradePromptService, + }, + { provide: CipherFormGenerationService, useClass: DesktopCredentialGenerationService }, + { + provide: COPY_CLICK_LISTENER, + useExisting: VaultComponent, + }, + { provide: VaultItemsTransferService, useClass: DefaultVaultItemsTransferService }, + ], +}) +export class VaultComponent implements OnInit, OnDestroy, CopyClickListener { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals + @ViewChild(VaultItemsV2Component, { static: true }) + vaultItemsComponent: VaultItemsV2Component | null = null; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals + @ViewChild(CipherFormComponent) + cipherFormComponent: CipherFormComponent | null = null; + + action: CipherFormMode | "view" | null = null; + cipherId: string | null = null; + favorites = false; + type: CipherType | null = null; + folderId: string | null | undefined = null; + collectionId: string | null = null; + organizationId: string | null = null; + myVaultOnly = false; + addType: CipherType | undefined = undefined; + addOrganizationId: string | null = null; + addCollectionIds: string[] | null = null; + showingModal = false; + deleted = false; + userHasPremiumAccess = false; + activeFilter: VaultFilter = new VaultFilter(); + activeUserId: UserId | null = null; + cipherRepromptId: string | null = null; + cipher: CipherView | null = new CipherView(); + collections: CollectionView[] | null = null; + config: CipherFormConfig | null = null; + + /** Tracks the disabled status of the edit cipher form */ + protected formDisabled: boolean = false; + + private organizations$: Observable = this.accountService.activeAccount$.pipe( + map((a) => a?.id), + filterOutNullish(), + switchMap((id) => this.organizationService.organizations$(id)), + ); + + protected canAccessAttachments$ = this.accountService.activeAccount$.pipe( + filter((account): account is Account => !!account), + switchMap((account) => + this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id), + ), + ); + + private componentIsDestroyed$ = new Subject(); + private allOrganizations: Organization[] = []; + private allCollections: CollectionView[] = []; + private filteredCollections: CollectionView[] = []; + + constructor( + private route: ActivatedRoute, + private router: Router, + private i18nService: I18nService, + private broadcasterService: BroadcasterService, + private changeDetectorRef: ChangeDetectorRef, + private ngZone: NgZone, + private syncService: SyncService, + private messagingService: MessagingService, + private platformUtilsService: PlatformUtilsService, + private eventCollectionService: EventCollectionService, + private totpService: TotpService, + private passwordRepromptService: PasswordRepromptService, + private searchBarService: SearchBarService, + private dialogService: DialogService, + private billingAccountProfileStateService: BillingAccountProfileStateService, + private toastService: ToastService, + private accountService: AccountService, + private cipherService: CipherService, + private formConfigService: CipherFormConfigService, + private premiumUpgradePromptService: PremiumUpgradePromptService, + private collectionService: CollectionService, + private organizationService: OrganizationService, + private folderService: FolderService, + private authRequestService: AuthRequestServiceAbstraction, + private cipherArchiveService: CipherArchiveService, + private policyService: PolicyService, + private archiveCipherUtilitiesService: ArchiveCipherUtilitiesService, + private routedVaultFilterBridgeService: RoutedVaultFilterBridgeService, + private vaultFilterService: VaultFilterService, + private vaultItemTransferService: VaultItemsTransferService, + ) {} + + async ngOnInit() { + this.accountService.activeAccount$ + .pipe( + filter((account): account is Account => !!account), + switchMap((account) => + this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id), + ), + takeUntil(this.componentIsDestroyed$), + ) + .subscribe((canAccessPremium: boolean) => { + this.userHasPremiumAccess = canAccessPremium; + }); + + // Subscribe to filter changes from router params via the bridge service + this.routedVaultFilterBridgeService.activeFilter$ + .pipe( + switchMap((vaultFilter: VaultFilter) => from(this.applyVaultFilter(vaultFilter))), + takeUntil(this.componentIsDestroyed$), + ) + .subscribe(); + + this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => { + this.ngZone + .run(async () => { + let detectChanges = true; + try { + switch (message.command) { + case "newLogin": + await this.addCipher(CipherType.Login).catch(() => {}); + break; + case "newCard": + await this.addCipher(CipherType.Card).catch(() => {}); + break; + case "newIdentity": + await this.addCipher(CipherType.Identity).catch(() => {}); + break; + case "newSecureNote": + await this.addCipher(CipherType.SecureNote).catch(() => {}); + break; + case "newSshKey": + await this.addCipher(CipherType.SshKey).catch(() => {}); + break; + case "focusSearch": + (document.querySelector("#search") as HTMLInputElement)?.select(); + detectChanges = false; + break; + case "syncCompleted": + if (this.vaultItemsComponent) { + await this.vaultItemsComponent.refresh().catch(() => {}); + } + if (this.activeUserId) { + void this.vaultItemTransferService.enforceOrganizationDataOwnership( + this.activeUserId, + ); + } + break; + case "modalShown": + this.showingModal = true; + break; + case "modalClosed": + this.showingModal = false; + break; + case "copyUsername": { + if (this.cipher?.login?.username) { + this.copyValue(this.cipher, this.cipher?.login?.username, "username", "Username"); + } + break; + } + case "copyPassword": { + if (this.cipher?.login?.password && this.cipher.viewPassword) { + this.copyValue(this.cipher, this.cipher.login.password, "password", "Password"); + await this.eventCollectionService + .collect(EventType.Cipher_ClientCopiedPassword, this.cipher.id) + .catch(() => {}); + } + break; + } + case "copyTotp": { + if ( + this.cipher?.login?.hasTotp && + (this.cipher.organizationUseTotp || this.userHasPremiumAccess) + ) { + const value = await firstValueFrom( + this.totpService.getCode$(this.cipher.login.totp), + ).catch((): any => null); + if (value) { + this.copyValue(this.cipher, value.code, "verificationCodeTotp", "TOTP"); + } + } + break; + } + default: + detectChanges = false; + break; + } + } catch { + // Ignore errors + } + if (detectChanges) { + this.changeDetectorRef.detectChanges(); + } + }) + .catch(() => {}); + }); + + if (!this.syncService.syncInProgress) { + await this.load().catch(() => {}); + } + + this.searchBarService.setEnabled(true); + this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault")); + + const authRequests = await firstValueFrom( + this.authRequestService.getLatestPendingAuthRequest$()!, + ); + if (authRequests != null) { + this.messagingService.send("openLoginApproval", { + notificationId: authRequests.id, + }); + } + + this.activeUserId = await firstValueFrom( + this.accountService.activeAccount$.pipe(getUserId), + ).catch((): any => null); + + if (this.activeUserId) { + this.cipherService + .failedToDecryptCiphers$(this.activeUserId) + .pipe( + map((ciphers) => ciphers?.filter((c) => !c.isDeleted) ?? []), + filter((ciphers) => ciphers.length > 0), + take(1), + takeUntil(this.componentIsDestroyed$), + ) + .subscribe((ciphers) => { + DecryptionFailureDialogComponent.open(this.dialogService, { + cipherIds: ciphers.map((c) => c.id as CipherId), + }); + }); + } + + this.organizations$.pipe(takeUntil(this.componentIsDestroyed$)).subscribe((orgs) => { + this.allOrganizations = orgs; + }); + + if (!this.activeUserId) { + throw new Error("No user found."); + } + + this.collectionService + .decryptedCollections$(this.activeUserId) + .pipe(takeUntil(this.componentIsDestroyed$)) + .subscribe((collections) => { + this.allCollections = collections; + }); + + this.vaultFilterService.filteredCollections$ + .pipe(takeUntil(this.componentIsDestroyed$)) + .subscribe((collections) => { + this.filteredCollections = collections; + }); + + void this.vaultItemTransferService.enforceOrganizationDataOwnership(this.activeUserId); + } + + ngOnDestroy() { + this.searchBarService.setEnabled(false); + this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); + this.componentIsDestroyed$.next(true); + this.componentIsDestroyed$.complete(); + } + + async load() { + const params = await firstValueFrom(this.route.queryParams).catch(); + const paramCipherAddType = toCipherType(params.addType); + if (params.cipherId) { + const cipherView = new CipherView(); + cipherView.id = params.cipherId; + if (params.action === "clone") { + await this.cloneCipher(cipherView).catch(() => {}); + } else if (params.action === "edit") { + await this.editCipher(cipherView).catch(() => {}); + } else { + await this.viewCipher(cipherView).catch(() => {}); + } + } else if (params.action === "add" && paramCipherAddType) { + this.addType = paramCipherAddType; + await this.addCipher(this.addType).catch(() => {}); + } + } + + /** + * Handler for Vault level CopyClickDirectives to send the minimizeOnCopy message + */ + onCopy() { + this.messagingService.send("minimizeOnCopy"); + } + + async viewCipher(c: CipherViewLike) { + if (CipherViewLikeUtils.decryptionFailure(c)) { + DecryptionFailureDialogComponent.open(this.dialogService, { + cipherIds: [c.id as CipherId], + }); + return; + } + const cipher = await this.cipherService.getFullCipherView(c); + if (await this.shouldReprompt(cipher, "view")) { + return; + } + this.cipherId = cipher.id; + this.cipher = cipher; + this.collections = + this.filteredCollections?.filter((c) => cipher.collectionIds.includes(c.id)) ?? null; + this.action = "view"; + + await this.go().catch(() => {}); + await this.eventCollectionService.collect( + EventType.Cipher_ClientViewed, + cipher.id, + false, + cipher.organizationId, + ); + } + + formStatusChanged(status: "disabled" | "enabled") { + this.formDisabled = status === "disabled"; + } + + async openAttachmentsDialog() { + if (!this.userHasPremiumAccess) { + return; + } + const dialogRef = AttachmentsV2Component.open(this.dialogService, { + cipherId: this.cipherId as CipherId, + }); + const result = await firstValueFrom(dialogRef.closed).catch((): any => null); + if ( + result?.action === AttachmentDialogResult.Removed || + result?.action === AttachmentDialogResult.Uploaded + ) { + await this.vaultItemsComponent?.refresh().catch(() => {}); + + if (this.cipherFormComponent == null) { + return; + } + + // The encrypted state of ciphers is updated when an attachment is added, + // but the cache is also cleared. Depending on timing, `cipherService.get` can return the + // old cipher. Retrieve the updated cipher from `cipherViews$`, + // which refreshes after the cached is cleared. + const updatedCipherView = await firstValueFrom( + this.cipherService.cipherViews$(this.activeUserId!).pipe( + filter((c) => !!c), + map((ciphers) => ciphers.find((c) => c.id === this.cipherId)), + ), + ); + + // `find` can return undefined but that shouldn't happen as + // this would mean that the cipher was deleted. + // To make TypeScript happy, exit early if it isn't found. + if (!updatedCipherView) { + return; + } + + this.cipherFormComponent.patchCipher((currentCipher) => { + currentCipher.attachments = updatedCipherView.attachments; + currentCipher.revisionDate = updatedCipherView.revisionDate; + + return currentCipher; + }); + } + } + + async viewCipherMenu(c: CipherViewLike) { + const cipher = await this.cipherService.getFullCipherView(c); + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + const userCanArchive = await firstValueFrom(this.cipherArchiveService.userCanArchive$(userId)); + const orgOwnershipPolicy = await firstValueFrom( + this.policyService.policyAppliesToUser$(PolicyType.OrganizationDataOwnership, userId), + ); + + const menu: RendererMenuItem[] = [ + { + label: this.i18nService.t("view"), + click: () => { + this.functionWithChangeDetection(() => { + this.viewCipher(cipher).catch(() => {}); + }); + }, + }, + ]; + + if (cipher.decryptionFailure) { + invokeMenu(menu); + } + + if (!cipher.isDeleted) { + menu.push({ + label: this.i18nService.t("edit"), + click: () => { + this.functionWithChangeDetection(() => { + this.editCipher(cipher).catch(() => {}); + }); + }, + }); + + const archivedWithOrgOwnership = cipher.isArchived && orgOwnershipPolicy; + const canCloneArchived = !cipher.isArchived || userCanArchive; + + if (!cipher.organizationId && !archivedWithOrgOwnership && canCloneArchived) { + menu.push({ + label: this.i18nService.t("clone"), + click: () => { + this.functionWithChangeDetection(() => { + this.cloneCipher(cipher).catch(() => {}); + }); + }, + }); + } + + const hasEditableCollections = this.allCollections.some((collection) => !collection.readOnly); + + if (cipher.canAssignToCollections && hasEditableCollections) { + menu.push({ + label: this.i18nService.t("assignToCollections"), + click: () => + this.functionWithChangeDetection(async () => { + await this.shareCipher(cipher); + }), + }); + } + } + + if (!cipher.organizationId && !cipher.isDeleted && !cipher.isArchived) { + menu.push({ + label: this.i18nService.t("archiveVerb"), + click: async () => { + if (!userCanArchive) { + await this.premiumUpgradePromptService.promptForPremium(); + return; + } + + await this.archiveCipherUtilitiesService.archiveCipher(cipher); + await this.refreshCurrentCipher(); + }, + }); + } + + if (cipher.isArchived) { + menu.push({ + label: this.i18nService.t("unArchive"), + click: async () => { + await this.archiveCipherUtilitiesService.unarchiveCipher(cipher); + await this.refreshCurrentCipher(); + }, + }); + } + + switch (cipher.type) { + case CipherType.Login: + if ( + cipher.login.canLaunch || + cipher.login.username != null || + cipher.login.password != null + ) { + menu.push({ type: "separator" }); + } + if (cipher.login.canLaunch) { + menu.push({ + label: this.i18nService.t("launch"), + click: () => this.platformUtilsService.launchUri(cipher.login.launchUri), + }); + } + if (cipher.login.username != null) { + menu.push({ + label: this.i18nService.t("copyUsername"), + click: () => this.copyValue(cipher, cipher.login.username, "username", "Username"), + }); + } + if (cipher.login.password != null && cipher.viewPassword) { + menu.push({ + label: this.i18nService.t("copyPassword"), + click: () => { + this.copyValue(cipher, cipher.login.password, "password", "Password"); + this.eventCollectionService + .collect(EventType.Cipher_ClientCopiedPassword, cipher.id) + .catch(() => {}); + }, + }); + } + if (cipher.login.hasTotp && (cipher.organizationUseTotp || this.userHasPremiumAccess)) { + menu.push({ + label: this.i18nService.t("copyVerificationCodeTotp"), + click: async () => { + const value = await firstValueFrom( + this.totpService.getCode$(cipher.login.totp), + ).catch((): any => null); + if (value) { + this.copyValue(cipher, value.code, "verificationCodeTotp", "TOTP"); + } + }, + }); + } + break; + case CipherType.Card: + if (cipher.card.number != null || cipher.card.code != null) { + menu.push({ type: "separator" }); + } + if (cipher.card.number != null) { + menu.push({ + label: this.i18nService.t("copyNumber"), + click: () => this.copyValue(cipher, cipher.card.number, "number", "Card Number"), + }); + } + if (cipher.card.code != null) { + menu.push({ + label: this.i18nService.t("copySecurityCode"), + click: () => { + this.copyValue(cipher, cipher.card.code, "securityCode", "Security Code"); + this.eventCollectionService + .collect(EventType.Cipher_ClientCopiedCardCode, cipher.id) + .catch(() => {}); + }, + }); + } + break; + default: + break; + } + invokeMenu(menu); + } + + async shouldReprompt(cipher: CipherView, action: "edit" | "clone" | "view"): Promise { + return !(await this.canNavigateAway(action, cipher)) || !(await this.passwordReprompt(cipher)); + } + + async buildFormConfig(action: CipherFormMode) { + this.config = await this.formConfigService + .buildConfig(action, this.cipherId as CipherId, this.addType) + .catch((): any => null); + } + + async editCipher(cipher: CipherView) { + if (await this.shouldReprompt(cipher, "edit")) { + return; + } + this.cipherId = cipher.id; + this.cipher = cipher; + await this.buildFormConfig("edit"); + if (!cipher.edit && this.config) { + this.config.mode = "partial-edit"; + } + this.action = "edit"; + await this.go().catch(() => {}); + } + + async cloneCipher(cipher: CipherView) { + if (await this.shouldReprompt(cipher, "clone")) { + return; + } + this.cipherId = cipher.id; + this.cipher = cipher; + await this.buildFormConfig("clone"); + this.action = "clone"; + await this.go().catch(() => {}); + } + + async shareCipher(cipher: CipherView) { + if (!cipher) { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("nothingSelected"), + }); + return; + } + + if (!(await this.passwordReprompt(cipher))) { + return; + } + + const availableCollections = this.getAvailableCollections(cipher); + + const dialog = AssignCollectionsDesktopComponent.open(this.dialogService, { + data: { + ciphers: [cipher], + organizationId: cipher.organizationId as OrganizationId, + availableCollections, + }, + }); + + const result = await lastValueFrom(dialog.closed); + if (result === CollectionAssignmentResult.Saved) { + const updatedCipher = await firstValueFrom( + // Fetch the updated cipher from the service + this.cipherService.cipherViews$(this.activeUserId as UserId).pipe( + filter((ciphers) => ciphers != null), + map((ciphers) => ciphers!.find((c) => c.id === cipher.id)), + filter((foundCipher) => foundCipher != null), + ), + ); + await this.savedCipher(updatedCipher); + } + } + + async addCipher(type: CipherType) { + if (this.action === "add") { + return; + } + this.addType = type || this.activeFilter.cipherType; + this.cipher = new CipherView(); + this.cipherId = null; + await this.buildFormConfig("add"); + this.action = "add"; + this.prefillCipherFromFilter(); + await this.go().catch(() => {}); + + if (type === CipherType.SshKey) { + this.toastService.showToast({ + variant: "success", + title: "", + message: this.i18nService.t("sshKeyGenerated"), + }); + } + } + + async savedCipher(cipher: CipherView) { + this.cipherId = null; + this.action = "view"; + await this.vaultItemsComponent?.refresh().catch(() => {}); + + if (!this.activeUserId) { + throw new Error("No userId provided."); + } + + this.collections = await firstValueFrom( + this.collectionService + .decryptedCollections$(this.activeUserId) + .pipe(getByIds(cipher.collectionIds)), + ); + + this.cipherId = cipher.id; + this.cipher = cipher; + await this.go().catch(() => {}); + await this.vaultItemsComponent?.refresh().catch(() => {}); + } + + async deleteCipher() { + this.cipherId = null; + this.cipher = null; + this.action = null; + await this.go().catch(() => {}); + await this.vaultItemsComponent?.refresh().catch(() => {}); + } + + async restoreCipher() { + this.cipherId = null; + this.action = null; + await this.go().catch(() => {}); + await this.vaultItemsComponent?.refresh().catch(() => {}); + } + + async cancelCipher(cipher: CipherView) { + this.cipherId = cipher.id; + this.cipher = cipher; + this.action = this.cipherId ? "view" : null; + await this.go().catch(() => {}); + } + + /** + * Wraps a filter function to handle CipherListView objects. + * CipherListView has a different type structure where type can be a string or object. + * This wrapper converts it to CipherView-compatible structure before filtering. + */ + private wrapFilterForCipherListView( + filterFn: (cipher: CipherView) => boolean, + ): (cipher: CipherViewLike) => boolean { + return (cipher: CipherViewLike) => { + // For CipherListView, create a proxy object with the correct type property + if (CipherViewLikeUtils.isCipherListView(cipher)) { + const proxyCipher = { + ...cipher, + type: CipherViewLikeUtils.getType(cipher), + // Normalize undefined organizationId to null for filter compatibility + organizationId: cipher.organizationId ?? null, + // Normalize empty string folderId to null for filter compatibility + folderId: cipher.folderId ? cipher.folderId : null, + // Explicitly include isDeleted and isArchived since they might be getters + isDeleted: CipherViewLikeUtils.isDeleted(cipher), + isArchived: CipherViewLikeUtils.isArchived(cipher), + }; + return filterFn(proxyCipher as any); + } + return filterFn(cipher); + }; + } + + async applyVaultFilter(vaultFilter: VaultFilter) { + this.searchBarService.setPlaceholderText( + this.i18nService.t(this.calculateSearchBarLocalizationString(vaultFilter)), + ); + this.activeFilter = vaultFilter; + + const originalFilterFn = this.activeFilter.buildFilter(); + const wrappedFilterFn = this.wrapFilterForCipherListView(originalFilterFn); + + await this.vaultItemsComponent?.reload( + wrappedFilterFn, + vaultFilter.isDeleted, + vaultFilter.isArchived, + ); + } + + private getAvailableCollections(cipher: CipherView): CollectionView[] { + const orgId = cipher.organizationId; + if (!orgId || orgId === "MyVault") { + return []; + } + + const organization = this.allOrganizations.find((o) => o.id === orgId); + return this.allCollections.filter((c) => c.organizationId === organization?.id && !c.readOnly); + } + + private calculateSearchBarLocalizationString(vaultFilter: VaultFilter): string { + if (vaultFilter.isFavorites) { + return "searchFavorites"; + } + if (vaultFilter.isDeleted) { + return "searchTrash"; + } + if (vaultFilter.cipherType != null) { + return "searchType"; + } + if (vaultFilter.folderId != null && vaultFilter.folderId !== "none") { + return "searchFolder"; + } + if (vaultFilter.collectionId != null) { + return "searchCollection"; + } + if (vaultFilter.organizationId != null) { + return "searchOrganization"; + } + if (vaultFilter.isMyVaultSelected) { + return "searchMyVault"; + } + return "searchVault"; + } + + async addFolder() { + this.messagingService.send("newFolder"); + } + + async editFolder(folderId: string) { + if (!this.activeUserId) { + return; + } + const folderView = await firstValueFrom( + this.folderService.getDecrypted$(folderId, this.activeUserId), + ); + + if (!folderView) { + return; + } + } + + /** Refresh the current cipher object */ + protected async refreshCurrentCipher() { + if (!this.cipher) { + return; + } + + this.cipher = await firstValueFrom( + this.cipherService.cipherViews$(this.activeUserId!).pipe( + filter((c) => !!c), + map((ciphers) => ciphers.find((c) => c.id === this.cipherId) ?? null), + ), + ); + } + + private dirtyInput(): boolean { + return ( + (this.action === "add" || this.action === "edit" || this.action === "clone") && + document.querySelectorAll("vault-cipher-form .ng-dirty").length > 0 + ); + } + + private async wantsToSaveChanges(): Promise { + const confirmed = await this.dialogService + .openSimpleDialog({ + title: { key: "unsavedChangesTitle" }, + content: { key: "unsavedChangesConfirmation" }, + type: "warning", + }) + .catch(() => false); + return !confirmed; + } + + private async go(queryParams: any = null) { + if (queryParams == null) { + queryParams = { + action: this.action, + cipherId: this.cipherId, + }; + } + this.router + .navigate([], { + relativeTo: this.route, + queryParams: queryParams, + queryParamsHandling: "merge", + replaceUrl: true, + }) + .catch(() => {}); + } + + private copyValue(cipher: CipherView, value: string, labelI18nKey: string, aType: string) { + this.functionWithChangeDetection(() => { + (async () => { + if ( + cipher.reprompt !== CipherRepromptType.None && + this.passwordRepromptService.protectedFields().includes(aType) && + !(await this.passwordReprompt(cipher)) + ) { + return; + } + this.platformUtilsService.copyToClipboard(value); + this.toastService.showToast({ + variant: "info", + title: undefined, + message: this.i18nService.t("valueCopied", this.i18nService.t(labelI18nKey)), + }); + this.messagingService.send("minimizeOnCopy"); + })().catch(() => {}); + }); + } + + private functionWithChangeDetection(func: () => void) { + this.ngZone.run(() => { + func(); + this.changeDetectorRef.detectChanges(); + }); + } + + private prefillCipherFromFilter() { + if (this.activeFilter.collectionId != null) { + const collections = this.filteredCollections?.filter( + (c) => c.id === this.activeFilter.collectionId, + ); + if (collections?.length > 0) { + this.addOrganizationId = collections[0].organizationId; + this.addCollectionIds = [this.activeFilter.collectionId]; + } + } else if (this.activeFilter.organizationId) { + this.addOrganizationId = this.activeFilter.organizationId; + } else { + // clear out organizationId when the user switches to a personal vault filter + this.addOrganizationId = null; + } + if (this.activeFilter.folderId && this.activeFilter.selectedFolderNode) { + this.folderId = this.activeFilter.folderId; + } + + if (this.config == null) { + return; + } + + this.config.initialValues = { + ...this.config.initialValues, + folderId: this.folderId, + organizationId: this.addOrganizationId as OrganizationId, + collectionIds: this.addCollectionIds as CollectionId[], + }; + } + + private async canNavigateAway(action: string, cipher?: CipherView) { + if (this.action === action && (!cipher || this.cipherId === cipher.id)) { + return false; + } else if (this.dirtyInput() && (await this.wantsToSaveChanges())) { + return false; + } + return true; + } + + private async passwordReprompt(cipher: CipherView) { + if (cipher.reprompt === CipherRepromptType.None) { + this.cipherRepromptId = null; + return true; + } + if (this.cipherRepromptId === cipher.id) { + return true; + } + const repromptResult = await this.passwordRepromptService.showPasswordPrompt(); + if (repromptResult) { + this.cipherRepromptId = cipher.id; + } + return repromptResult; + } +} diff --git a/apps/desktop/src/vault/app/vault/item-footer.component.html b/apps/desktop/src/vault/app/vault/item-footer.component.html index 859b2f1bdc5..0af73bf7d8a 100644 --- a/apps/desktop/src/vault/app/vault/item-footer.component.html +++ b/apps/desktop/src/vault/app/vault/item-footer.component.html @@ -7,9 +7,9 @@ [hidden]="action === 'view'" bitButton class="primary" - appA11yTitle="{{ 'save' | i18n }}" + appA11yTitle="{{ submitButtonText() }}" > - {{ "save" | i18n }} + {{ submitButtonText() }} - + @if (showCloneOption) { + + }
      diff --git a/apps/desktop/src/vault/app/vault/item-footer.component.ts b/apps/desktop/src/vault/app/vault/item-footer.component.ts index 0034bd9a43c..d601f46e430 100644 --- a/apps/desktop/src/vault/app/vault/item-footer.component.ts +++ b/apps/desktop/src/vault/app/vault/item-footer.component.ts @@ -8,6 +8,7 @@ import { ViewChild, OnChanges, SimpleChanges, + input, } from "@angular/core"; import { combineLatest, firstValueFrom, switchMap } from "rxjs"; @@ -67,11 +68,14 @@ export class ItemFooterComponent implements OnInit, OnChanges { // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("submitBtn", { static: false }) submitBtn: ButtonComponent | null = null; + readonly submitButtonText = input(this.i18nService.t("save")); + activeUserId: UserId | null = null; passwordReprompted: boolean = false; protected showArchiveButton = false; protected showUnarchiveButton = false; + protected userCanArchive = false; constructor( protected cipherService: CipherService, @@ -131,6 +135,16 @@ export class ItemFooterComponent implements OnInit, OnChanges { ); } + protected get showCloneOption() { + return ( + this.cipher.id && + !this.cipher?.organizationId && + !this.cipher.isDeleted && + this.action === "view" && + (!this.cipher.isArchived || this.userCanArchive) + ); + } + cancel() { this.onCancel.emit(this.cipher); } @@ -170,16 +184,23 @@ export class ItemFooterComponent implements OnInit, OnChanges { } async restore(): Promise { + let toastMessage; if (!this.cipher.isDeleted) { return false; } + if (this.cipher.isArchived) { + toastMessage = this.i18nService.t("archivedItemRestored"); + } else { + toastMessage = this.i18nService.t("restoredItem"); + } + try { const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); await this.restoreCipher(activeUserId); this.toastService.showToast({ variant: "success", - message: this.i18nService.t("restoredItem"), + message: toastMessage, }); this.onRestore.emit(this.cipher); } catch (e) { @@ -218,24 +239,29 @@ export class ItemFooterComponent implements OnInit, OnChanges { } private async checkArchiveState() { - const cipherCanBeArchived = !this.cipher.isDeleted && this.cipher.organizationId == null; + const cipherCanBeArchived = !this.cipher.isDeleted; const [userCanArchive, hasArchiveFlagEnabled] = await firstValueFrom( this.accountService.activeAccount$.pipe( getUserId, switchMap((id) => combineLatest([ this.cipherArchiveService.userCanArchive$(id), - this.cipherArchiveService.hasArchiveFlagEnabled$(), + this.cipherArchiveService.hasArchiveFlagEnabled$, ]), ), ), ); + this.userCanArchive = userCanArchive; + this.showArchiveButton = cipherCanBeArchived && userCanArchive && this.action === "view" && !this.cipher.isArchived; // A user should always be able to unarchive an archived item this.showUnarchiveButton = - hasArchiveFlagEnabled && this.action === "view" && this.cipher.isArchived; + hasArchiveFlagEnabled && + this.action === "view" && + this.cipher.isArchived && + !this.cipher.isDeleted; } } diff --git a/apps/desktop/src/vault/app/vault/vault-filter/filters/status-filter.component.html b/apps/desktop/src/vault/app/vault/vault-filter/filters/status-filter.component.html index 8b064778444..80beae8407d 100644 --- a/apps/desktop/src/vault/app/vault/vault-filter/filters/status-filter.component.html +++ b/apps/desktop/src/vault/app/vault/vault-filter/filters/status-filter.component.html @@ -30,14 +30,14 @@
    + @if (showPremiumCallout()) { +
    + + +
    + {{ "premiumSubscriptionEndedDesc" | i18n }} +
    + + {{ "restartPremium" | i18n }} + +
    +
    +
    + } +
    extends BaseVaultItemsComponent { + readonly showPremiumCallout = input(false); + readonly organizationId = input(undefined); + protected CipherViewLikeUtils = CipherViewLikeUtils; + constructor( searchService: SearchService, private readonly searchBarService: SearchBarService, @@ -37,6 +43,7 @@ export class VaultItemsV2Component extends BaseVaultIt accountService: AccountService, restrictedItemTypesService: RestrictedItemTypesService, configService: ConfigService, + private premiumUpgradePromptService: PremiumUpgradePromptService, ) { super(searchService, cipherService, accountService, restrictedItemTypesService, configService); @@ -47,6 +54,10 @@ export class VaultItemsV2Component extends BaseVaultIt }); } + async navigateToGetPremium() { + await this.premiumUpgradePromptService.promptForPremium(this.organizationId()); + } + trackByFn(index: number, c: C): string { return uuidAsString(c.id!); } diff --git a/apps/desktop/src/vault/app/vault/vault-v2.component.html b/apps/desktop/src/vault/app/vault/vault-v2.component.html index 2696dd0d452..61b7c0ee355 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.html +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.html @@ -6,66 +6,83 @@ (onCipherClicked)="viewCipher($event)" (onCipherRightClicked)="viewCipherMenu($event)" (onAddCipher)="addCipher($event)" + [showPremiumCallout]="showPremiumCallout$ | async" + [organizationId]="organizationId" > -
    - -
    -
    -
    - - - - - - - + + + + + } +
    -
    - - + } - + @if (showUserManagementControls()) { - - + } @else { - + } - + @if (showUserManagementControls()) { {{ u.type | userType }} - - + } @else { {{ u.type | userType }} - + } - + @if (u.twoFactorEnabled) { {{ "userUsingTwoStep" | i18n }} - + } @let resetPasswordPolicyEnabled = resetPasswordPolicyEnabled$ | async; - + @if (showEnrolledStatus(u, organization, resetPasswordPolicyEnabled)) { {{ "enrolledAccountRecovery" | i18n }} - + } - +
    +
    + +
    - + @if (showUserManagementControls()) { + @if (u.status === userStatusType.Invited) { + + } + @if (u.status === userStatusType.Accepted) { + + } + @if ( + u.status === userStatusType.Accepted || u.status === userStatusType.Invited + ) { + + } - - - + @if (organization.useGroups) { + + } - - - + @if (organization.useEvents && u.status === userStatusType.Confirmed) { + + } + } - + @if (allowResetPassword(u, organization, resetPasswordPolicyEnabled)) { + + } - - - - - - + @if (showUserManagementControls()) { + @if (u.status === userStatusType.Revoked) { + + } + @if (u.status !== userStatusType.Revoked) { + + } + @if (!u.managedByOrganization) { + + } @else { + + } + } -
    - + } + } } diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.spec.ts b/apps/web/src/app/admin-console/organizations/members/members.component.spec.ts new file mode 100644 index 00000000000..246c3d8a1c0 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/members.component.spec.ts @@ -0,0 +1,696 @@ +import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { ActivatedRoute } from "@angular/router"; +import { mock, MockProxy } from "jest-mock-extended"; +import { BehaviorSubject, of } from "rxjs"; + +import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { OrganizationManagementPreferencesService } from "@bitwarden/common/admin-console/abstractions/organization-management-preferences/organization-management-preferences.service"; +import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { + OrganizationUserStatusType, + OrganizationUserType, +} from "@bitwarden/common/admin-console/enums"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { OrganizationMetadataServiceAbstraction } from "@bitwarden/common/billing/abstractions/organization-metadata.service.abstraction"; +import { OrganizationBillingMetadataResponse } from "@bitwarden/common/billing/models/response/organization-billing-metadata.response"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; +import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { DialogService, ToastService } from "@bitwarden/components"; +import { newGuid } from "@bitwarden/guid"; +import { KeyService } from "@bitwarden/key-management"; +import { BillingConstraintService } from "@bitwarden/web-vault/app/billing/members/billing-constraint/billing-constraint.service"; +import { OrganizationWarningsService } from "@bitwarden/web-vault/app/billing/organizations/warnings/services"; + +import { OrganizationUserView } from "../core/views/organization-user.view"; + +import { AccountRecoveryDialogResultType } from "./components/account-recovery/account-recovery-dialog.component"; +import { MemberDialogResult } from "./components/member-dialog"; +import { vNextMembersComponent } from "./members.component"; +import { + MemberDialogManagerService, + MemberExportService, + OrganizationMembersService, +} from "./services"; +import { DeleteManagedMemberWarningService } from "./services/delete-managed-member/delete-managed-member-warning.service"; +import { + MemberActionsService, + MemberActionResult, +} from "./services/member-actions/member-actions.service"; + +describe("vNextMembersComponent", () => { + let component: vNextMembersComponent; + let fixture: ComponentFixture; + + let mockApiService: MockProxy; + let mockI18nService: MockProxy; + let mockOrganizationManagementPreferencesService: MockProxy; + let mockKeyService: MockProxy; + let mockValidationService: MockProxy; + let mockLogService: MockProxy; + let mockUserNamePipe: MockProxy; + let mockDialogService: MockProxy; + let mockToastService: MockProxy; + let mockActivatedRoute: ActivatedRoute; + let mockDeleteManagedMemberWarningService: MockProxy; + let mockOrganizationWarningsService: MockProxy; + let mockMemberActionsService: MockProxy; + let mockMemberDialogManager: MockProxy; + let mockBillingConstraint: MockProxy; + let mockMemberService: MockProxy; + let mockOrganizationService: MockProxy; + let mockAccountService: FakeAccountService; + let mockPolicyService: MockProxy; + let mockPolicyApiService: MockProxy; + let mockOrganizationMetadataService: MockProxy; + let mockConfigService: MockProxy; + let mockEnvironmentService: MockProxy; + let mockMemberExportService: MockProxy; + let mockFileDownloadService: MockProxy; + + let routeParamsSubject: BehaviorSubject; + let queryParamsSubject: BehaviorSubject; + + const mockUserId = newGuid() as UserId; + const mockOrgId = newGuid() as OrganizationId; + const mockOrg = { + id: mockOrgId, + name: "Test Organization", + enabled: true, + canManageUsers: true, + useSecretsManager: true, + useResetPassword: true, + isProviderUser: false, + } as Organization; + + const mockUser = { + id: newGuid(), + userId: newGuid(), + type: OrganizationUserType.User, + status: OrganizationUserStatusType.Confirmed, + email: "test@example.com", + name: "Test User", + resetPasswordEnrolled: false, + accessSecretsManager: false, + managedByOrganization: false, + twoFactorEnabled: false, + usesKeyConnector: false, + hasMasterPassword: true, + } as OrganizationUserView; + + const mockBillingMetadata = { + isSubscriptionUnpaid: false, + } as Partial; + + beforeEach(async () => { + routeParamsSubject = new BehaviorSubject({ organizationId: mockOrgId }); + queryParamsSubject = new BehaviorSubject({}); + + mockActivatedRoute = { + params: routeParamsSubject.asObservable(), + queryParams: queryParamsSubject.asObservable(), + } as any; + + mockApiService = mock(); + mockI18nService = mock(); + mockI18nService.t.mockImplementation((key: string) => key); + + mockOrganizationManagementPreferencesService = mock(); + mockOrganizationManagementPreferencesService.autoConfirmFingerPrints = { + state$: of(false), + } as any; + + mockKeyService = mock(); + mockValidationService = mock(); + mockLogService = mock(); + mockUserNamePipe = mock(); + mockUserNamePipe.transform.mockReturnValue("Test User"); + + mockDialogService = mock(); + mockToastService = mock(); + mockDeleteManagedMemberWarningService = mock(); + mockOrganizationWarningsService = mock(); + mockMemberActionsService = mock(); + mockMemberDialogManager = mock(); + mockBillingConstraint = mock(); + + mockMemberService = mock(); + mockMemberService.loadUsers.mockResolvedValue([mockUser]); + + mockOrganizationService = mock(); + mockOrganizationService.organizations$.mockReturnValue(of([mockOrg])); + + mockAccountService = mockAccountServiceWith(mockUserId); + + mockPolicyService = mock(); + + mockPolicyApiService = mock(); + mockOrganizationMetadataService = mock(); + mockOrganizationMetadataService.getOrganizationMetadata$.mockReturnValue( + of(mockBillingMetadata), + ); + + mockConfigService = mock(); + mockConfigService.getFeatureFlag$.mockReturnValue(of(false)); + + mockEnvironmentService = mock(); + mockEnvironmentService.environment$ = of({ + isCloud: () => false, + } as any); + + mockMemberExportService = mock(); + mockFileDownloadService = mock(); + + await TestBed.configureTestingModule({ + declarations: [vNextMembersComponent], + providers: [ + { provide: ApiService, useValue: mockApiService }, + { provide: I18nService, useValue: mockI18nService }, + { + provide: OrganizationManagementPreferencesService, + useValue: mockOrganizationManagementPreferencesService, + }, + { provide: KeyService, useValue: mockKeyService }, + { provide: ValidationService, useValue: mockValidationService }, + { provide: LogService, useValue: mockLogService }, + { provide: UserNamePipe, useValue: mockUserNamePipe }, + { provide: DialogService, useValue: mockDialogService }, + { provide: ToastService, useValue: mockToastService }, + { provide: ActivatedRoute, useValue: mockActivatedRoute }, + { + provide: DeleteManagedMemberWarningService, + useValue: mockDeleteManagedMemberWarningService, + }, + { provide: OrganizationWarningsService, useValue: mockOrganizationWarningsService }, + { provide: MemberActionsService, useValue: mockMemberActionsService }, + { provide: MemberDialogManagerService, useValue: mockMemberDialogManager }, + { provide: BillingConstraintService, useValue: mockBillingConstraint }, + { provide: OrganizationMembersService, useValue: mockMemberService }, + { provide: OrganizationService, useValue: mockOrganizationService }, + { provide: AccountService, useValue: mockAccountService }, + { provide: PolicyService, useValue: mockPolicyService }, + { provide: PolicyApiServiceAbstraction, useValue: mockPolicyApiService }, + { + provide: OrganizationMetadataServiceAbstraction, + useValue: mockOrganizationMetadataService, + }, + { provide: ConfigService, useValue: mockConfigService }, + { provide: EnvironmentService, useValue: mockEnvironmentService }, + { provide: MemberExportService, useValue: mockMemberExportService }, + { provide: FileDownloadService, useValue: mockFileDownloadService }, + ], + schemas: [NO_ERRORS_SCHEMA], + }) + .overrideComponent(vNextMembersComponent, { + remove: { imports: [] }, + add: { template: "
    " }, + }) + .compileComponents(); + + fixture = TestBed.createComponent(vNextMembersComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + afterEach(() => { + if (fixture) { + fixture.destroy(); + } + jest.restoreAllMocks(); + }); + + describe("load", () => { + it("should load users and set data source", async () => { + const users = [mockUser]; + mockMemberService.loadUsers.mockResolvedValue(users); + + await component.load(mockOrg); + + expect(mockMemberService.loadUsers).toHaveBeenCalledWith(mockOrg); + expect(component["dataSource"]().data).toEqual(users); + expect(component["firstLoaded"]()).toBe(true); + }); + + it("should handle empty response", async () => { + mockMemberService.loadUsers.mockResolvedValue([]); + + await component.load(mockOrg); + + expect(component["dataSource"]().data).toEqual([]); + }); + }); + + describe("remove", () => { + it("should remove user when confirmed", async () => { + mockMemberDialogManager.openRemoveUserConfirmationDialog.mockResolvedValue(true); + mockMemberActionsService.removeUser.mockResolvedValue({ success: true }); + + const removeSpy = jest.spyOn(component["dataSource"](), "removeUser"); + + await component.remove(mockUser, mockOrg); + + expect(mockMemberDialogManager.openRemoveUserConfirmationDialog).toHaveBeenCalledWith( + mockUser, + ); + expect(mockMemberActionsService.removeUser).toHaveBeenCalledWith(mockOrg, mockUser.id); + expect(removeSpy).toHaveBeenCalledWith(mockUser); + expect(mockToastService.showToast).toHaveBeenCalled(); + }); + + it("should not remove user when not confirmed", async () => { + mockMemberDialogManager.openRemoveUserConfirmationDialog.mockResolvedValue(false); + + const result = await component.remove(mockUser, mockOrg); + + expect(result).toBe(false); + expect(mockMemberActionsService.removeUser).not.toHaveBeenCalled(); + }); + + it("should handle errors via handleMemberActionResult", async () => { + mockMemberDialogManager.openRemoveUserConfirmationDialog.mockResolvedValue(true); + mockMemberActionsService.removeUser.mockResolvedValue({ + success: false, + error: "Remove failed", + }); + + await component.remove(mockUser, mockOrg); + + expect(mockToastService.showToast).toHaveBeenCalledWith({ + variant: "error", + message: "Remove failed", + }); + expect(mockLogService.error).toHaveBeenCalledWith("Remove failed"); + }); + }); + + describe("reinvite", () => { + it("should reinvite user successfully", async () => { + mockMemberActionsService.reinviteUser.mockResolvedValue({ success: true }); + + await component.reinvite(mockUser, mockOrg); + + expect(mockMemberActionsService.reinviteUser).toHaveBeenCalledWith(mockOrg, mockUser.id); + expect(mockToastService.showToast).toHaveBeenCalled(); + }); + + it("should handle errors via handleMemberActionResult", async () => { + mockMemberActionsService.reinviteUser.mockResolvedValue({ + success: false, + error: "Reinvite failed", + }); + + await component.reinvite(mockUser, mockOrg); + + expect(mockToastService.showToast).toHaveBeenCalledWith({ + variant: "error", + message: "Reinvite failed", + }); + expect(mockLogService.error).toHaveBeenCalledWith("Reinvite failed"); + }); + }); + + describe("confirm", () => { + it("should confirm user with auto-confirm enabled", async () => { + mockOrganizationManagementPreferencesService.autoConfirmFingerPrints.state$ = of(true); + mockMemberActionsService.confirmUser.mockResolvedValue({ success: true }); + + // Mock getPublicKeyForConfirm to return a public key + const mockPublicKey = new Uint8Array([1, 2, 3, 4]); + mockMemberActionsService.getPublicKeyForConfirm.mockResolvedValue(mockPublicKey); + + const replaceSpy = jest.spyOn(component["dataSource"](), "replaceUser"); + + await component.confirm(mockUser, mockOrg); + + expect(mockMemberActionsService.getPublicKeyForConfirm).toHaveBeenCalledWith(mockUser); + expect(mockMemberActionsService.confirmUser).toHaveBeenCalledWith( + mockUser, + mockPublicKey, + mockOrg, + ); + expect(replaceSpy).toHaveBeenCalled(); + expect(mockToastService.showToast).toHaveBeenCalled(); + }); + + it("should handle null user", async () => { + mockOrganizationManagementPreferencesService.autoConfirmFingerPrints.state$ = of(true); + + // Mock getPublicKeyForConfirm to return null + mockMemberActionsService.getPublicKeyForConfirm.mockResolvedValue(null); + + await component.confirm(mockUser, mockOrg); + + expect(mockMemberActionsService.getPublicKeyForConfirm).toHaveBeenCalled(); + expect(mockMemberActionsService.confirmUser).not.toHaveBeenCalled(); + expect(mockLogService.warning).toHaveBeenCalledWith("Public key not found"); + }); + + it("should handle API errors gracefully", async () => { + // Mock getPublicKeyForConfirm to return null + mockMemberActionsService.getPublicKeyForConfirm.mockResolvedValue(null); + + await component.confirm(mockUser, mockOrg); + + expect(mockMemberActionsService.getPublicKeyForConfirm).toHaveBeenCalled(); + expect(mockLogService.warning).toHaveBeenCalledWith("Public key not found"); + }); + }); + + describe("revoke", () => { + it("should revoke user when confirmed", async () => { + mockMemberDialogManager.openRevokeUserConfirmationDialog.mockResolvedValue(true); + mockMemberActionsService.revokeUser.mockResolvedValue({ success: true }); + mockMemberService.loadUsers.mockResolvedValue([mockUser]); + + await component.revoke(mockUser, mockOrg); + + expect(mockMemberDialogManager.openRevokeUserConfirmationDialog).toHaveBeenCalledWith( + mockUser, + ); + expect(mockMemberActionsService.revokeUser).toHaveBeenCalledWith(mockOrg, mockUser.id); + expect(mockToastService.showToast).toHaveBeenCalled(); + }); + + it("should not revoke user when not confirmed", async () => { + mockMemberDialogManager.openRevokeUserConfirmationDialog.mockResolvedValue(false); + + const result = await component.revoke(mockUser, mockOrg); + + expect(result).toBe(false); + expect(mockMemberActionsService.revokeUser).not.toHaveBeenCalled(); + }); + }); + + describe("restore", () => { + it("should restore user successfully", async () => { + mockMemberActionsService.restoreUser.mockResolvedValue({ success: true }); + mockMemberService.loadUsers.mockResolvedValue([mockUser]); + + await component.restore(mockUser, mockOrg); + + expect(mockMemberActionsService.restoreUser).toHaveBeenCalledWith(mockOrg, mockUser.id); + expect(mockToastService.showToast).toHaveBeenCalled(); + expect(mockMemberService.loadUsers).toHaveBeenCalledWith(mockOrg); + }); + + it("should handle errors via handleMemberActionResult", async () => { + mockMemberActionsService.restoreUser.mockResolvedValue({ + success: false, + error: "Restore failed", + }); + + await component.restore(mockUser, mockOrg); + + expect(mockToastService.showToast).toHaveBeenCalledWith({ + variant: "error", + message: "Restore failed", + }); + expect(mockLogService.error).toHaveBeenCalledWith("Restore failed"); + }); + }); + + describe("invite", () => { + it("should open invite dialog when seat limit not reached", async () => { + mockBillingConstraint.seatLimitReached.mockResolvedValue(false); + mockMemberDialogManager.openInviteDialog.mockResolvedValue(MemberDialogResult.Saved); + + await component.invite(mockOrg); + + expect(mockBillingConstraint.checkSeatLimit).toHaveBeenCalledWith( + mockOrg, + mockBillingMetadata, + ); + expect(mockMemberDialogManager.openInviteDialog).toHaveBeenCalledWith( + mockOrg, + mockBillingMetadata, + expect.any(Array), + ); + }); + + it("should reload organization and refresh metadata cache after successful invite", async () => { + mockBillingConstraint.seatLimitReached.mockResolvedValue(false); + mockMemberDialogManager.openInviteDialog.mockResolvedValue(MemberDialogResult.Saved); + mockMemberService.loadUsers.mockResolvedValue([mockUser]); + + await component.invite(mockOrg); + + expect(mockMemberService.loadUsers).toHaveBeenCalledWith(mockOrg); + expect(mockOrganizationMetadataService.refreshMetadataCache).toHaveBeenCalled(); + }); + + it("should not open dialog when seat limit reached", async () => { + mockBillingConstraint.seatLimitReached.mockResolvedValue(true); + + await component.invite(mockOrg); + + expect(mockMemberDialogManager.openInviteDialog).not.toHaveBeenCalled(); + }); + }); + + describe("bulkRemove", () => { + it("should open bulk remove dialog and reload", async () => { + const users = [mockUser]; + jest.spyOn(component["dataSource"](), "getCheckedUsersWithLimit").mockReturnValue(users); + mockMemberService.loadUsers.mockResolvedValue([mockUser]); + + await component.bulkRemove(mockOrg); + + expect(mockMemberDialogManager.openBulkRemoveDialog).toHaveBeenCalledWith(mockOrg, users); + expect(mockOrganizationMetadataService.refreshMetadataCache).toHaveBeenCalled(); + expect(mockMemberService.loadUsers).toHaveBeenCalledWith(mockOrg); + }); + }); + + describe("bulkDelete", () => { + it("should open bulk delete dialog and reload", async () => { + const users = [mockUser]; + jest.spyOn(component["dataSource"](), "getCheckedUsersWithLimit").mockReturnValue(users); + mockMemberService.loadUsers.mockResolvedValue([mockUser]); + + await component.bulkDelete(mockOrg); + + expect(mockMemberDialogManager.openBulkDeleteDialog).toHaveBeenCalledWith(mockOrg, users); + expect(mockMemberService.loadUsers).toHaveBeenCalledWith(mockOrg); + }); + }); + + describe("bulkRevokeOrRestore", () => { + it.each([ + { isRevoking: true, action: "revoke" }, + { isRevoking: false, action: "restore" }, + ])( + "should open bulk $action dialog and reload when isRevoking is $isRevoking", + async ({ isRevoking }) => { + const users = [mockUser]; + jest.spyOn(component["dataSource"](), "getCheckedUsersWithLimit").mockReturnValue(users); + mockMemberService.loadUsers.mockResolvedValue([mockUser]); + + await component.bulkRevokeOrRestore(isRevoking, mockOrg); + + expect(mockMemberDialogManager.openBulkRestoreRevokeDialog).toHaveBeenCalledWith( + mockOrg, + users, + isRevoking, + ); + expect(mockMemberService.loadUsers).toHaveBeenCalledWith(mockOrg); + }, + ); + }); + + describe("bulkReinvite", () => { + it("should reinvite invited users", async () => { + const invitedUser = { + ...mockUser, + status: OrganizationUserStatusType.Invited, + }; + jest.spyOn(component["dataSource"](), "isIncreasedBulkLimitEnabled").mockReturnValue(false); + jest.spyOn(component["dataSource"](), "getCheckedUsers").mockReturnValue([invitedUser]); + mockMemberActionsService.bulkReinvite.mockResolvedValue({ successful: true }); + + await component.bulkReinvite(mockOrg); + + expect(mockMemberActionsService.bulkReinvite).toHaveBeenCalledWith(mockOrg, [invitedUser.id]); + expect(mockMemberDialogManager.openBulkStatusDialog).toHaveBeenCalled(); + }); + + it("should show error when no invited users selected", async () => { + const confirmedUser = { + ...mockUser, + status: OrganizationUserStatusType.Confirmed, + }; + jest.spyOn(component["dataSource"](), "isIncreasedBulkLimitEnabled").mockReturnValue(false); + jest.spyOn(component["dataSource"](), "getCheckedUsers").mockReturnValue([confirmedUser]); + + await component.bulkReinvite(mockOrg); + + expect(mockToastService.showToast).toHaveBeenCalledWith({ + variant: "error", + title: "errorOccurred", + message: "noSelectedUsersApplicable", + }); + expect(mockMemberActionsService.bulkReinvite).not.toHaveBeenCalled(); + }); + + it("should handle errors", async () => { + const invitedUser = { + ...mockUser, + status: OrganizationUserStatusType.Invited, + }; + jest.spyOn(component["dataSource"](), "isIncreasedBulkLimitEnabled").mockReturnValue(false); + jest.spyOn(component["dataSource"](), "getCheckedUsers").mockReturnValue([invitedUser]); + const error = new Error("Bulk reinvite failed"); + mockMemberActionsService.bulkReinvite.mockResolvedValue({ successful: false, failed: error }); + + await component.bulkReinvite(mockOrg); + + expect(mockValidationService.showError).toHaveBeenCalledWith(error); + }); + }); + + describe("bulkConfirm", () => { + it("should open bulk confirm dialog and reload", async () => { + const users = [mockUser]; + jest.spyOn(component["dataSource"](), "getCheckedUsersWithLimit").mockReturnValue(users); + mockMemberService.loadUsers.mockResolvedValue([mockUser]); + + await component.bulkConfirm(mockOrg); + + expect(mockMemberDialogManager.openBulkConfirmDialog).toHaveBeenCalledWith(mockOrg, users); + expect(mockMemberService.loadUsers).toHaveBeenCalledWith(mockOrg); + }); + }); + + describe("bulkEnableSM", () => { + it("should open bulk enable SM dialog and reload", async () => { + const users = [mockUser]; + jest.spyOn(component["dataSource"](), "getCheckedUsersWithLimit").mockReturnValue(users); + jest.spyOn(component["dataSource"](), "uncheckAllUsers"); + mockMemberService.loadUsers.mockResolvedValue([mockUser]); + + await component.bulkEnableSM(mockOrg); + + expect(mockMemberDialogManager.openBulkEnableSecretsManagerDialog).toHaveBeenCalledWith( + mockOrg, + users, + ); + expect(component["dataSource"]().uncheckAllUsers).toHaveBeenCalled(); + expect(mockMemberService.loadUsers).toHaveBeenCalledWith(mockOrg); + }); + }); + + describe("resetPassword", () => { + it("should open account recovery dialog", async () => { + mockMemberDialogManager.openAccountRecoveryDialog.mockResolvedValue( + AccountRecoveryDialogResultType.Ok, + ); + mockMemberService.loadUsers.mockResolvedValue([mockUser]); + + await component.resetPassword(mockUser, mockOrg); + + expect(mockMemberDialogManager.openAccountRecoveryDialog).toHaveBeenCalledWith( + mockUser, + mockOrg, + ); + expect(mockMemberService.loadUsers).toHaveBeenCalledWith(mockOrg); + }); + }); + + describe("deleteUser", () => { + it("should delete user when confirmed", async () => { + mockMemberDialogManager.openDeleteUserConfirmationDialog.mockResolvedValue(true); + mockMemberActionsService.deleteUser.mockResolvedValue({ success: true }); + const removeSpy = jest.spyOn(component["dataSource"](), "removeUser"); + + await component.deleteUser(mockUser, mockOrg); + + expect(mockMemberDialogManager.openDeleteUserConfirmationDialog).toHaveBeenCalledWith( + mockUser, + mockOrg, + ); + expect(mockMemberActionsService.deleteUser).toHaveBeenCalledWith(mockOrg, mockUser.id); + expect(removeSpy).toHaveBeenCalledWith(mockUser); + expect(mockToastService.showToast).toHaveBeenCalled(); + }); + + it("should not delete user when not confirmed", async () => { + mockMemberDialogManager.openDeleteUserConfirmationDialog.mockResolvedValue(false); + + const result = await component.deleteUser(mockUser, mockOrg); + + expect(result).toBe(false); + expect(mockMemberActionsService.deleteUser).not.toHaveBeenCalled(); + }); + + it("should handle errors via handleMemberActionResult", async () => { + mockMemberDialogManager.openDeleteUserConfirmationDialog.mockResolvedValue(true); + mockMemberActionsService.deleteUser.mockResolvedValue({ + success: false, + error: "Delete failed", + }); + + await component.deleteUser(mockUser, mockOrg); + + expect(mockToastService.showToast).toHaveBeenCalledWith({ + variant: "error", + message: "Delete failed", + }); + expect(mockLogService.error).toHaveBeenCalledWith("Delete failed"); + }); + }); + + describe("handleMemberActionResult", () => { + it("should show success toast when result is successful", async () => { + const result: MemberActionResult = { success: true }; + + await component.handleMemberActionResult(result, "testSuccessKey", mockUser); + + expect(mockToastService.showToast).toHaveBeenCalledWith({ + variant: "success", + message: "testSuccessKey", + }); + }); + + it("should execute side effect when provided and successful", async () => { + const result: MemberActionResult = { success: true }; + const sideEffect = jest.fn(); + + await component.handleMemberActionResult(result, "testSuccessKey", mockUser, sideEffect); + + expect(sideEffect).toHaveBeenCalled(); + }); + + it("should show error toast when result is not successful", async () => { + const result: MemberActionResult = { success: false, error: "Error message" }; + const sideEffect = jest.fn(); + + await component.handleMemberActionResult(result, "testSuccessKey", mockUser, sideEffect); + + expect(mockToastService.showToast).toHaveBeenCalledWith({ + variant: "error", + message: "Error message", + }); + expect(mockLogService.error).toHaveBeenCalledWith("Error message"); + expect(sideEffect).not.toHaveBeenCalled(); + }); + + it("should propagate error when side effect throws", async () => { + const result: MemberActionResult = { success: true }; + const error = new Error("Side effect failed"); + const sideEffect = jest.fn().mockRejectedValue(error); + + await expect( + component.handleMemberActionResult(result, "testSuccessKey", mockUser, sideEffect), + ).rejects.toThrow("Side effect failed"); + }); + }); +}); diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.ts b/apps/web/src/app/admin-console/organizations/members/members.component.ts index 59c4c4898ea..9d367657d55 100644 --- a/apps/web/src/app/admin-console/organizations/members/members.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/members.component.ts @@ -1,9 +1,12 @@ -import { Component, computed, Signal } from "@angular/core"; +import { Component, computed, inject, signal, Signal, WritableSignal } from "@angular/core"; import { takeUntilDestroyed, toSignal } from "@angular/core/rxjs-interop"; +import { FormControl } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; import { + BehaviorSubject, combineLatest, concatMap, + debounceTime, filter, firstValueFrom, from, @@ -15,11 +18,8 @@ import { take, } from "rxjs"; -import { OrganizationUserUserDetailsResponse } from "@bitwarden/admin-console/common"; import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { OrganizationManagementPreferencesService } from "@bitwarden/common/admin-console/abstractions/organization-management-preferences/organization-management-preferences.service"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { @@ -33,31 +33,46 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { OrganizationMetadataServiceAbstraction } from "@bitwarden/common/billing/abstractions/organization-metadata.service.abstraction"; import { OrganizationBillingMetadataResponse } from "@bitwarden/common/billing/models/response/organization-billing-metadata.response"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { getById } from "@bitwarden/common/platform/misc"; import { DialogService, ToastService } from "@bitwarden/components"; -import { KeyService } from "@bitwarden/key-management"; import { UserId } from "@bitwarden/user-core"; import { BillingConstraintService } from "@bitwarden/web-vault/app/billing/members/billing-constraint/billing-constraint.service"; import { OrganizationWarningsService } from "@bitwarden/web-vault/app/billing/organizations/warnings/services"; -import { BaseMembersComponent } from "../../common/base-members.component"; -import { PeopleTableDataSource } from "../../common/people-table-data-source"; +import { + CloudBulkReinviteLimit, + MaxCheckedCount, + MembersTableDataSource, + peopleFilter, + showConfirmBanner, +} from "../../common/people-table-data-source"; import { OrganizationUserView } from "../core/views/organization-user.view"; import { AccountRecoveryDialogResultType } from "./components/account-recovery/account-recovery-dialog.component"; import { MemberDialogResult, MemberDialogTab } from "./components/member-dialog"; -import { MemberDialogManagerService, OrganizationMembersService } from "./services"; +import { + MemberDialogManagerService, + MemberExportService, + OrganizationMembersService, +} from "./services"; import { DeleteManagedMemberWarningService } from "./services/delete-managed-member/delete-managed-member-warning.service"; import { MemberActionsService, MemberActionResult, } from "./services/member-actions/member-actions.service"; -class MembersTableDataSource extends PeopleTableDataSource { - protected statusType = OrganizationUserStatusType; +interface BulkMemberFlags { + showBulkRestoreUsers: boolean; + showBulkRevokeUsers: boolean; + showBulkRemoveUsers: boolean; + showBulkDeleteUsers: boolean; + showBulkConfirmUsers: boolean; + showBulkReinviteUsers: boolean; } // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush @@ -66,65 +81,76 @@ class MembersTableDataSource extends PeopleTableDataSource templateUrl: "members.component.html", standalone: false, }) -export class MembersComponent extends BaseMembersComponent { - userType = OrganizationUserType; - userStatusType = OrganizationUserStatusType; - memberTab = MemberDialogTab; - protected dataSource = new MembersTableDataSource(); - - readonly organization: Signal; - status: OrganizationUserStatusType | undefined; +export class vNextMembersComponent { + protected i18nService = inject(I18nService); + protected validationService = inject(ValidationService); + protected logService = inject(LogService); + protected userNamePipe = inject(UserNamePipe); + protected dialogService = inject(DialogService); + protected toastService = inject(ToastService); + private route = inject(ActivatedRoute); + protected deleteManagedMemberWarningService = inject(DeleteManagedMemberWarningService); + private organizationWarningsService = inject(OrganizationWarningsService); + private memberActionsService = inject(MemberActionsService); + private memberDialogManager = inject(MemberDialogManagerService); + protected billingConstraint = inject(BillingConstraintService); + protected memberService = inject(OrganizationMembersService); + private organizationService = inject(OrganizationService); + private accountService = inject(AccountService); + private policyService = inject(PolicyService); + private policyApiService = inject(PolicyApiServiceAbstraction); + private organizationMetadataService = inject(OrganizationMetadataServiceAbstraction); + private configService = inject(ConfigService); + private environmentService = inject(EnvironmentService); + private memberExportService = inject(MemberExportService); private userId$: Observable = this.accountService.activeAccount$.pipe(getUserId); - resetPasswordPolicyEnabled$: Observable; + protected userType = OrganizationUserType; + protected userStatusType = OrganizationUserStatusType; + protected memberTab = MemberDialogTab; + + protected searchControl = new FormControl("", { nonNullable: true }); + protected statusToggle = new BehaviorSubject(undefined); + + protected readonly dataSource: Signal = signal( + new MembersTableDataSource(this.configService, this.environmentService), + ); + protected readonly organization: Signal; + protected readonly firstLoaded: WritableSignal = signal(false); + + protected bulkMenuOptions$ = this.dataSource() + .usersUpdated() + .pipe(map((members) => this.bulkMenuOptions(members))); + + protected showConfirmBanner$ = this.dataSource() + .usersUpdated() + .pipe(map(() => showConfirmBanner(this.dataSource()))); + + protected isProcessing = this.memberActionsService.isProcessing; protected readonly canUseSecretsManager: Signal = computed( () => this.organization()?.useSecretsManager ?? false, ); + protected readonly showUserManagementControls: Signal = computed( () => this.organization()?.canManageUsers ?? false, ); + protected billingMetadata$: Observable; + protected resetPasswordPolicyEnabled$: Observable; + // Fixed sizes used for cdkVirtualScroll protected rowHeight = 66; protected rowHeightClass = `tw-h-[66px]`; - constructor( - apiService: ApiService, - i18nService: I18nService, - organizationManagementPreferencesService: OrganizationManagementPreferencesService, - keyService: KeyService, - validationService: ValidationService, - logService: LogService, - userNamePipe: UserNamePipe, - dialogService: DialogService, - toastService: ToastService, - private route: ActivatedRoute, - protected deleteManagedMemberWarningService: DeleteManagedMemberWarningService, - private organizationWarningsService: OrganizationWarningsService, - private memberActionsService: MemberActionsService, - private memberDialogManager: MemberDialogManagerService, - protected billingConstraint: BillingConstraintService, - protected memberService: OrganizationMembersService, - private organizationService: OrganizationService, - private accountService: AccountService, - private policyService: PolicyService, - private policyApiService: PolicyApiServiceAbstraction, - private organizationMetadataService: OrganizationMetadataServiceAbstraction, - ) { - super( - apiService, - i18nService, - keyService, - validationService, - logService, - userNamePipe, - dialogService, - organizationManagementPreferencesService, - toastService, - ); + constructor() { + combineLatest([this.searchControl.valueChanges.pipe(debounceTime(200)), this.statusToggle]) + .pipe(takeUntilDestroyed()) + .subscribe( + ([searchText, status]) => (this.dataSource().filter = peopleFilter(searchText, status)), + ); const organization$ = this.route.params.pipe( concatMap((params) => @@ -167,7 +193,7 @@ export class MembersComponent extends BaseMembersComponent this.searchControl.setValue(qParams.search); if (qParams.viewEvents != null) { - const user = this.dataSource.data.filter((u) => u.id === qParams.viewEvents); + const user = this.dataSource().data.filter((u) => u.id === qParams.viewEvents); if (user.length > 0 && user[0].status === OrganizationUserStatusType.Confirmed) { this.openEventsDialog(user[0], organization!); } @@ -201,80 +227,62 @@ export class MembersComponent extends BaseMembersComponent this.billingMetadata$.pipe(take(1), takeUntilDestroyed()).subscribe(); } - override async load(organization: Organization) { - await super.load(organization); + async load(organization: Organization) { + const response = await this.memberService.loadUsers(organization); + this.dataSource().data = response; + this.firstLoaded.set(true); } - async getUsers(organization: Organization): Promise { - return await this.memberService.loadUsers(organization); - } - - async removeUser(id: string, organization: Organization): Promise { - return await this.memberActionsService.removeUser(organization, id); - } - - async revokeUser(id: string, organization: Organization): Promise { - return await this.memberActionsService.revokeUser(organization, id); - } - - async restoreUser(id: string, organization: Organization): Promise { - return await this.memberActionsService.restoreUser(organization, id); - } - - async reinviteUser(id: string, organization: Organization): Promise { - return await this.memberActionsService.reinviteUser(organization, id); - } - - async confirmUser( - user: OrganizationUserView, - publicKey: Uint8Array, - organization: Organization, - ): Promise { - return await this.memberActionsService.confirmUser(user, publicKey, organization); - } - - async revoke(user: OrganizationUserView, organization: Organization) { - const confirmed = await this.revokeUserConfirmationDialog(user); + async remove(user: OrganizationUserView, organization: Organization) { + const confirmed = await this.memberDialogManager.openRemoveUserConfirmationDialog(user); if (!confirmed) { return false; } - this.actionPromise = this.revokeUser(user.id, organization); - try { - const result = await this.actionPromise; - if (result.success) { - this.toastService.showToast({ - variant: "success", - message: this.i18nService.t("revokedUserId", this.userNamePipe.transform(user)), - }); - await this.load(organization); - } else { - throw new Error(result.error); - } - } catch (e) { - this.validationService.showError(e); + const result = await this.memberActionsService.removeUser(organization, user.id); + const sideEffect = () => this.dataSource().removeUser(user); + await this.handleMemberActionResult(result, "removedUserId", user, sideEffect); + } + + async reinvite(user: OrganizationUserView, organization: Organization) { + const result = await this.memberActionsService.reinviteUser(organization, user.id); + await this.handleMemberActionResult(result, "hasBeenReinvited", user); + } + + async confirm(user: OrganizationUserView, organization: Organization) { + const confirmUserSideEffect = () => { + user.status = this.userStatusType.Confirmed; + this.dataSource().replaceUser(user); + }; + + const publicKeyResult = await this.memberActionsService.getPublicKeyForConfirm(user); + + if (publicKeyResult == null) { + this.logService.warning("Public key not found"); + return; } - this.actionPromise = undefined; + + const result = await this.memberActionsService.confirmUser(user, publicKeyResult, organization); + await this.handleMemberActionResult(result, "hasBeenConfirmed", user, confirmUserSideEffect); + } + + async revoke(user: OrganizationUserView, organization: Organization) { + const confirmed = await this.memberDialogManager.openRevokeUserConfirmationDialog(user); + + if (!confirmed) { + return false; + } + + const result = await this.memberActionsService.revokeUser(organization, user.id); + const sideEffect = async () => await this.load(organization); + await this.handleMemberActionResult(result, "revokedUserId", user, sideEffect); } async restore(user: OrganizationUserView, organization: Organization) { - this.actionPromise = this.restoreUser(user.id, organization); - try { - const result = await this.actionPromise; - if (result.success) { - this.toastService.showToast({ - variant: "success", - message: this.i18nService.t("restoredUserId", this.userNamePipe.transform(user)), - }); - await this.load(organization); - } else { - throw new Error(result.error); - } - } catch (e) { - this.validationService.showError(e); - } - this.actionPromise = undefined; + const result = await this.memberActionsService.restoreUser(organization, user.id); + const sideEffect = async () => await this.load(organization); + await this.handleMemberActionResult(result, "restoredUserId", user, sideEffect); } allowResetPassword( @@ -290,7 +298,7 @@ export class MembersComponent extends BaseMembersComponent } showEnrolledStatus( - orgUser: OrganizationUserUserDetailsResponse, + orgUser: OrganizationUserView, organization: Organization, orgResetPasswordPolicyEnabled: boolean, ): boolean { @@ -301,9 +309,15 @@ export class MembersComponent extends BaseMembersComponent ); } - private async handleInviteDialog(organization: Organization) { + async invite(organization: Organization) { const billingMetadata = await firstValueFrom(this.billingMetadata$); - const allUserEmails = this.dataSource.data?.map((user) => user.email) ?? []; + const seatLimitResult = this.billingConstraint.checkSeatLimit(organization, billingMetadata); + + if (await this.billingConstraint.seatLimitReached(seatLimitResult, organization)) { + return; + } + + const allUserEmails = this.dataSource().data?.map((user) => user.email) ?? []; const result = await this.memberDialogManager.openInviteDialog( organization, @@ -313,14 +327,6 @@ export class MembersComponent extends BaseMembersComponent if (result === MemberDialogResult.Saved) { await this.load(organization); - } - } - - async invite(organization: Organization) { - const billingMetadata = await firstValueFrom(this.billingMetadata$); - const seatLimitResult = this.billingConstraint.checkSeatLimit(organization, billingMetadata); - if (!(await this.billingConstraint.seatLimitReached(seatLimitResult, organization))) { - await this.handleInviteDialog(organization); this.organizationMetadataService.refreshMetadataCache(); } } @@ -341,7 +347,7 @@ export class MembersComponent extends BaseMembersComponent switch (result) { case MemberDialogResult.Deleted: - this.dataSource.removeUser(user); + this.dataSource().removeUser(user); break; case MemberDialogResult.Saved: case MemberDialogResult.Revoked: @@ -352,58 +358,47 @@ export class MembersComponent extends BaseMembersComponent } async bulkRemove(organization: Organization) { - if (this.actionPromise != null) { - return; - } - - await this.memberDialogManager.openBulkRemoveDialog( - organization, - this.dataSource.getCheckedUsers(), - ); + const users = this.dataSource().getCheckedUsersWithLimit(MaxCheckedCount); + await this.memberDialogManager.openBulkRemoveDialog(organization, users); this.organizationMetadataService.refreshMetadataCache(); await this.load(organization); } async bulkDelete(organization: Organization) { - if (this.actionPromise != null) { - return; - } - - await this.memberDialogManager.openBulkDeleteDialog( - organization, - this.dataSource.getCheckedUsers(), - ); + const users = this.dataSource().getCheckedUsersWithLimit(MaxCheckedCount); + await this.memberDialogManager.openBulkDeleteDialog(organization, users); await this.load(organization); } - async bulkRevoke(organization: Organization) { - await this.bulkRevokeOrRestore(true, organization); - } - - async bulkRestore(organization: Organization) { - await this.bulkRevokeOrRestore(false, organization); - } - async bulkRevokeOrRestore(isRevoking: boolean, organization: Organization) { - if (this.actionPromise != null) { - return; - } - - await this.memberDialogManager.openBulkRestoreRevokeDialog( - organization, - this.dataSource.getCheckedUsers(), - isRevoking, - ); + const users = this.dataSource().getCheckedUsersWithLimit(MaxCheckedCount); + await this.memberDialogManager.openBulkRestoreRevokeDialog(organization, users, isRevoking); await this.load(organization); } async bulkReinvite(organization: Organization) { - if (this.actionPromise != null) { - return; + let users: OrganizationUserView[]; + if (this.dataSource().isIncreasedBulkLimitEnabled()) { + users = this.dataSource().getCheckedUsersInVisibleOrder(); + } else { + users = this.dataSource().getCheckedUsers(); } - const users = this.dataSource.getCheckedUsers(); - const filteredUsers = users.filter((u) => u.status === OrganizationUserStatusType.Invited); + const allInvitedUsers = users.filter((u) => u.status === OrganizationUserStatusType.Invited); + + // Capture the original count BEFORE enforcing the limit + const originalInvitedCount = allInvitedUsers.length; + + // When feature flag is enabled, limit invited users and uncheck the excess + let filteredUsers: OrganizationUserView[]; + if (this.dataSource().isIncreasedBulkLimitEnabled()) { + filteredUsers = this.dataSource().limitAndUncheckExcess( + allInvitedUsers, + CloudBulkReinviteLimit, + ); + } else { + filteredUsers = allInvitedUsers; + } if (filteredUsers.length <= 0) { this.toastService.showToast({ @@ -414,47 +409,59 @@ export class MembersComponent extends BaseMembersComponent return; } - try { - const result = await this.memberActionsService.bulkReinvite( - organization, - filteredUsers.map((user) => user.id), - ); + const result = await this.memberActionsService.bulkReinvite( + organization, + filteredUsers.map((user) => user.id as UserId), + ); - if (!result.successful) { - throw new Error(); + if (!result.successful) { + this.validationService.showError(result.failed); + } + + // When feature flag is enabled, show toast instead of dialog + if (this.dataSource().isIncreasedBulkLimitEnabled()) { + const selectedCount = originalInvitedCount; + const invitedCount = filteredUsers.length; + + if (selectedCount > CloudBulkReinviteLimit) { + const excludedCount = selectedCount - CloudBulkReinviteLimit; + this.toastService.showToast({ + variant: "success", + message: this.i18nService.t( + "bulkReinviteLimitedSuccessToast", + CloudBulkReinviteLimit.toLocaleString(), + selectedCount.toLocaleString(), + excludedCount.toLocaleString(), + ), + }); + } else { + this.toastService.showToast({ + variant: "success", + message: this.i18nService.t("bulkReinviteSuccessToast", invitedCount.toString()), + }); } - - // Bulk Status component open + } else { + // Feature flag disabled - show legacy dialog await this.memberDialogManager.openBulkStatusDialog( users, filteredUsers, Promise.resolve(result.successful), this.i18nService.t("bulkReinviteMessage"), ); - } catch (e) { - this.validationService.showError(e); } - this.actionPromise = undefined; } async bulkConfirm(organization: Organization) { - if (this.actionPromise != null) { - return; - } - - await this.memberDialogManager.openBulkConfirmDialog( - organization, - this.dataSource.getCheckedUsers(), - ); + const users = this.dataSource().getCheckedUsersWithLimit(MaxCheckedCount); + await this.memberDialogManager.openBulkConfirmDialog(organization, users); await this.load(organization); } async bulkEnableSM(organization: Organization) { - const users = this.dataSource.getCheckedUsers(); - + const users = this.dataSource().getCheckedUsersWithLimit(MaxCheckedCount); await this.memberDialogManager.openBulkEnableSecretsManagerDialog(organization, users); - this.dataSource.uncheckAllUsers(); + this.dataSource().uncheckAllUsers(); await this.load(organization); } @@ -482,14 +489,6 @@ export class MembersComponent extends BaseMembersComponent return; } - protected async removeUserConfirmationDialog(user: OrganizationUserView) { - return await this.memberDialogManager.openRemoveUserConfirmationDialog(user); - } - - protected async revokeUserConfirmationDialog(user: OrganizationUserView) { - return await this.memberDialogManager.openRevokeUserConfirmationDialog(user); - } - async deleteUser(user: OrganizationUserView, organization: Organization) { const confirmed = await this.memberDialogManager.openDeleteUserConfirmationDialog( user, @@ -500,48 +499,72 @@ export class MembersComponent extends BaseMembersComponent return false; } - this.actionPromise = this.memberActionsService.deleteUser(organization, user.id); - try { - const result = await this.actionPromise; - if (!result.success) { - throw new Error(result.error); - } + const result = await this.memberActionsService.deleteUser(organization, user.id); + await this.handleMemberActionResult(result, "organizationUserDeleted", user, () => { + this.dataSource().removeUser(user); + }); + } + + async handleMemberActionResult( + result: MemberActionResult, + successKey: string, + user: OrganizationUserView, + sideEffect?: () => void | Promise, + ) { + if (result.error != null) { + this.toastService.showToast({ + variant: "error", + message: this.i18nService.t(result.error), + }); + this.logService.error(result.error); + return; + } + + if (result.success) { this.toastService.showToast({ variant: "success", - message: this.i18nService.t("organizationUserDeleted", this.userNamePipe.transform(user)), + message: this.i18nService.t(successKey, this.userNamePipe.transform(user)), }); - this.dataSource.removeUser(user); - } catch (e) { - this.validationService.showError(e); + + if (sideEffect) { + await sideEffect(); + } } - this.actionPromise = undefined; } - get showBulkRestoreUsers(): boolean { - return this.dataSource - .getCheckedUsers() - .every((member) => member.status == this.userStatusType.Revoked); - } - - get showBulkRevokeUsers(): boolean { - return this.dataSource - .getCheckedUsers() - .every((member) => member.status != this.userStatusType.Revoked); - } - - get showBulkRemoveUsers(): boolean { - return this.dataSource.getCheckedUsers().every((member) => !member.managedByOrganization); - } - - get showBulkDeleteUsers(): boolean { + private bulkMenuOptions(members: OrganizationUserView[]): BulkMemberFlags { const validStatuses = [ - this.userStatusType.Accepted, - this.userStatusType.Confirmed, - this.userStatusType.Revoked, + OrganizationUserStatusType.Accepted, + OrganizationUserStatusType.Confirmed, + OrganizationUserStatusType.Revoked, ]; - return this.dataSource - .getCheckedUsers() - .every((member) => member.managedByOrganization && validStatuses.includes(member.status)); + const result = { + showBulkConfirmUsers: members.every((m) => m.status == OrganizationUserStatusType.Accepted), + showBulkReinviteUsers: members.every((m) => m.status == OrganizationUserStatusType.Invited), + showBulkRestoreUsers: members.every((m) => m.status == OrganizationUserStatusType.Revoked), + showBulkRevokeUsers: members.every((m) => m.status != OrganizationUserStatusType.Revoked), + showBulkRemoveUsers: members.every((m) => !m.managedByOrganization), + showBulkDeleteUsers: members.every( + (m) => m.managedByOrganization && validStatuses.includes(m.status), + ), + }; + + return result; } + + exportMembers = () => { + const result = this.memberExportService.getMemberExport(this.dataSource().data); + if (result.success) { + this.toastService.showToast({ + variant: "success", + title: undefined, + message: this.i18nService.t("dataExportSuccess"), + }); + } + + if (result.error != null) { + this.validationService.showError(result.error.message); + } + }; } diff --git a/apps/web/src/app/admin-console/organizations/members/members.module.ts b/apps/web/src/app/admin-console/organizations/members/members.module.ts index 3b233932ed3..9fd477b1e29 100644 --- a/apps/web/src/app/admin-console/organizations/members/members.module.ts +++ b/apps/web/src/app/admin-console/organizations/members/members.module.ts @@ -17,12 +17,15 @@ import { BulkRemoveDialogComponent } from "./components/bulk/bulk-remove-dialog. import { BulkRestoreRevokeComponent } from "./components/bulk/bulk-restore-revoke.component"; import { BulkStatusComponent } from "./components/bulk/bulk-status.component"; import { UserDialogModule } from "./components/member-dialog"; +import { MembersComponent } from "./deprecated_members.component"; import { MembersRoutingModule } from "./members-routing.module"; -import { MembersComponent } from "./members.component"; +import { vNextMembersComponent } from "./members.component"; +import { UserStatusPipe } from "./pipes"; import { OrganizationMembersService, MemberActionsService, MemberDialogManagerService, + MemberExportService, } from "./services"; @NgModule({ @@ -44,13 +47,17 @@ import { BulkRestoreRevokeComponent, BulkStatusComponent, MembersComponent, + vNextMembersComponent, BulkDeleteDialogComponent, + UserStatusPipe, ], providers: [ OrganizationMembersService, MemberActionsService, BillingConstraintService, MemberDialogManagerService, + MemberExportService, + UserStatusPipe, ], }) export class MembersModule {} diff --git a/apps/web/src/app/admin-console/organizations/members/pipes/index.ts b/apps/web/src/app/admin-console/organizations/members/pipes/index.ts new file mode 100644 index 00000000000..67c485ed361 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/pipes/index.ts @@ -0,0 +1 @@ +export * from "./user-status.pipe"; diff --git a/apps/web/src/app/admin-console/organizations/members/pipes/user-status.pipe.spec.ts b/apps/web/src/app/admin-console/organizations/members/pipes/user-status.pipe.spec.ts new file mode 100644 index 00000000000..3fd05c8a2e8 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/pipes/user-status.pipe.spec.ts @@ -0,0 +1,47 @@ +import { MockProxy, mock } from "jest-mock-extended"; + +import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + +import { UserStatusPipe } from "./user-status.pipe"; + +describe("UserStatusPipe", () => { + let pipe: UserStatusPipe; + let i18nService: MockProxy; + + beforeEach(() => { + i18nService = mock(); + i18nService.t.mockImplementation((key: string) => key); + pipe = new UserStatusPipe(i18nService); + }); + + it("transforms OrganizationUserStatusType.Invited to 'invited'", () => { + expect(pipe.transform(OrganizationUserStatusType.Invited)).toBe("invited"); + expect(i18nService.t).toHaveBeenCalledWith("invited"); + }); + + it("transforms OrganizationUserStatusType.Accepted to 'accepted'", () => { + expect(pipe.transform(OrganizationUserStatusType.Accepted)).toBe("accepted"); + expect(i18nService.t).toHaveBeenCalledWith("accepted"); + }); + + it("transforms OrganizationUserStatusType.Confirmed to 'confirmed'", () => { + expect(pipe.transform(OrganizationUserStatusType.Confirmed)).toBe("confirmed"); + expect(i18nService.t).toHaveBeenCalledWith("confirmed"); + }); + + it("transforms OrganizationUserStatusType.Revoked to 'revoked'", () => { + expect(pipe.transform(OrganizationUserStatusType.Revoked)).toBe("revoked"); + expect(i18nService.t).toHaveBeenCalledWith("revoked"); + }); + + it("transforms null to 'unknown'", () => { + expect(pipe.transform(null)).toBe("unknown"); + expect(i18nService.t).toHaveBeenCalledWith("unknown"); + }); + + it("transforms undefined to 'unknown'", () => { + expect(pipe.transform(undefined)).toBe("unknown"); + expect(i18nService.t).toHaveBeenCalledWith("unknown"); + }); +}); diff --git a/apps/web/src/app/admin-console/organizations/members/pipes/user-status.pipe.ts b/apps/web/src/app/admin-console/organizations/members/pipes/user-status.pipe.ts new file mode 100644 index 00000000000..81590616027 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/pipes/user-status.pipe.ts @@ -0,0 +1,30 @@ +import { Pipe, PipeTransform } from "@angular/core"; + +import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + +@Pipe({ + name: "userStatus", + standalone: false, +}) +export class UserStatusPipe implements PipeTransform { + constructor(private i18nService: I18nService) {} + + transform(value?: OrganizationUserStatusType): string { + if (value == null) { + return this.i18nService.t("unknown"); + } + switch (value) { + case OrganizationUserStatusType.Invited: + return this.i18nService.t("invited"); + case OrganizationUserStatusType.Accepted: + return this.i18nService.t("accepted"); + case OrganizationUserStatusType.Confirmed: + return this.i18nService.t("confirmed"); + case OrganizationUserStatusType.Revoked: + return this.i18nService.t("revoked"); + default: + return this.i18nService.t("unknown"); + } + } +} diff --git a/apps/web/src/app/admin-console/organizations/members/services/index.ts b/apps/web/src/app/admin-console/organizations/members/services/index.ts index baaa33eeae9..fd6b5816513 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/index.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/index.ts @@ -1,4 +1,5 @@ export { OrganizationMembersService } from "./organization-members-service/organization-members.service"; export { MemberActionsService } from "./member-actions/member-actions.service"; export { MemberDialogManagerService } from "./member-dialog-manager/member-dialog-manager.service"; +export { MemberExportService } from "./member-export"; export { DeleteManagedMemberWarningService } from "./delete-managed-member/delete-managed-member-warning.service"; diff --git a/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.spec.ts b/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.spec.ts index e856ab7afd1..1df285d7ba2 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.spec.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.spec.ts @@ -1,42 +1,40 @@ +import { TestBed } from "@angular/core/testing"; import { MockProxy, mock } from "jest-mock-extended"; import { of } from "rxjs"; import { OrganizationUserApiService, OrganizationUserBulkResponse, + OrganizationUserService, } from "@bitwarden/admin-console/common"; +import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationManagementPreferencesService } from "@bitwarden/common/admin-console/abstractions/organization-management-preferences/organization-management-preferences.service"; import { OrganizationUserType, OrganizationUserStatusType, } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { OrganizationMetadataServiceAbstraction } from "@bitwarden/common/billing/abstractions/organization-metadata.service.abstraction"; -import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; -import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; -import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; -import { OrgKey } from "@bitwarden/common/types/key"; +import { DialogService } from "@bitwarden/components"; import { newGuid } from "@bitwarden/guid"; import { KeyService } from "@bitwarden/key-management"; import { OrganizationUserView } from "../../../core/views/organization-user.view"; -import { OrganizationUserService } from "../organization-user/organization-user.service"; -import { MemberActionsService } from "./member-actions.service"; +import { REQUESTS_PER_BATCH, MemberActionsService } from "./member-actions.service"; describe("MemberActionsService", () => { let service: MemberActionsService; let organizationUserApiService: MockProxy; let organizationUserService: MockProxy; - let keyService: MockProxy; - let encryptService: MockProxy; let configService: MockProxy; - let accountService: FakeAccountService; let organizationMetadataService: MockProxy; - const userId = newGuid() as UserId; const organizationId = newGuid() as OrganizationId; const userIdToManage = newGuid(); @@ -46,10 +44,7 @@ describe("MemberActionsService", () => { beforeEach(() => { organizationUserApiService = mock(); organizationUserService = mock(); - keyService = mock(); - encryptService = mock(); configService = mock(); - accountService = mockAccountServiceWith(userId); organizationMetadataService = mock(); mockOrganization = { @@ -68,15 +63,29 @@ describe("MemberActionsService", () => { resetPasswordEnrolled: true, } as OrganizationUserView; - service = new MemberActionsService( - organizationUserApiService, - organizationUserService, - keyService, - encryptService, - configService, - accountService, - organizationMetadataService, - ); + TestBed.configureTestingModule({ + providers: [ + MemberActionsService, + { provide: OrganizationUserApiService, useValue: organizationUserApiService }, + { provide: OrganizationUserService, useValue: organizationUserService }, + { provide: ConfigService, useValue: configService }, + { + provide: OrganizationMetadataServiceAbstraction, + useValue: organizationMetadataService, + }, + { provide: ApiService, useValue: mock() }, + { provide: DialogService, useValue: mock() }, + { provide: KeyService, useValue: mock() }, + { provide: LogService, useValue: mock() }, + { + provide: OrganizationManagementPreferencesService, + useValue: mock(), + }, + { provide: UserNamePipe, useValue: mock() }, + ], + }); + + service = TestBed.inject(MemberActionsService); }); describe("inviteUser", () => { @@ -242,8 +251,7 @@ describe("MemberActionsService", () => { describe("confirmUser", () => { const publicKey = new Uint8Array([1, 2, 3, 4, 5]); - it("should confirm user using new flow when feature flag is enabled", async () => { - configService.getFeatureFlag$.mockReturnValue(of(true)); + it("should confirm user", async () => { organizationUserService.confirmUser.mockReturnValue(of(undefined)); const result = await service.confirmUser(mockOrgUser, publicKey, mockOrganization); @@ -257,44 +265,7 @@ describe("MemberActionsService", () => { expect(organizationUserApiService.postOrganizationUserConfirm).not.toHaveBeenCalled(); }); - it("should confirm user using exising flow when feature flag is disabled", async () => { - configService.getFeatureFlag$.mockReturnValue(of(false)); - - const mockOrgKey = mock(); - const mockOrgKeys = { [organizationId]: mockOrgKey }; - keyService.orgKeys$.mockReturnValue(of(mockOrgKeys)); - - const mockEncryptedKey = new EncString("encrypted-key-data"); - encryptService.encapsulateKeyUnsigned.mockResolvedValue(mockEncryptedKey); - - organizationUserApiService.postOrganizationUserConfirm.mockResolvedValue(undefined); - - const result = await service.confirmUser(mockOrgUser, publicKey, mockOrganization); - - expect(result).toEqual({ success: true }); - expect(keyService.orgKeys$).toHaveBeenCalledWith(userId); - expect(encryptService.encapsulateKeyUnsigned).toHaveBeenCalledWith(mockOrgKey, publicKey); - expect(organizationUserApiService.postOrganizationUserConfirm).toHaveBeenCalledWith( - organizationId, - userIdToManage, - expect.objectContaining({ - key: "encrypted-key-data", - }), - ); - }); - - it("should handle missing organization keys", async () => { - configService.getFeatureFlag$.mockReturnValue(of(false)); - keyService.orgKeys$.mockReturnValue(of({})); - - const result = await service.confirmUser(mockOrgUser, publicKey, mockOrganization); - - expect(result.success).toBe(false); - expect(result.error).toContain("Organization keys not found"); - }); - it("should handle confirm errors", async () => { - configService.getFeatureFlag$.mockReturnValue(of(true)); const errorMessage = "Confirm failed"; organizationUserService.confirmUser.mockImplementation(() => { throw new Error(errorMessage); @@ -308,41 +279,308 @@ describe("MemberActionsService", () => { }); describe("bulkReinvite", () => { - const userIds = [newGuid(), newGuid(), newGuid()]; + const userIds = [newGuid() as UserId, newGuid() as UserId, newGuid() as UserId]; - it("should successfully reinvite multiple users", async () => { - const mockResponse = { - data: userIds.map((id) => ({ - id, - error: null, - })), - continuationToken: null, - } as ListResponse; - organizationUserApiService.postManyOrganizationUserReinvite.mockResolvedValue(mockResponse); - - const result = await service.bulkReinvite(mockOrganization, userIds); - - expect(result).toEqual({ - successful: mockResponse, - failed: [], + describe("when feature flag is false", () => { + beforeEach(() => { + configService.getFeatureFlag$.mockReturnValue(of(false)); + }); + + it("should successfully reinvite multiple users", async () => { + const mockResponse = new ListResponse( + { + data: userIds.map((id) => ({ + id, + error: null, + })), + continuationToken: null, + }, + OrganizationUserBulkResponse, + ); + + organizationUserApiService.postManyOrganizationUserReinvite.mockResolvedValue(mockResponse); + + const result = await service.bulkReinvite(mockOrganization, userIds); + + expect(result.failed).toEqual([]); + expect(result.successful).toBeDefined(); + expect(result.successful).toEqual(mockResponse); + expect(organizationUserApiService.postManyOrganizationUserReinvite).toHaveBeenCalledWith( + organizationId, + userIds, + ); + }); + + it("should handle bulk reinvite errors", async () => { + const errorMessage = "Bulk reinvite failed"; + organizationUserApiService.postManyOrganizationUserReinvite.mockRejectedValue( + new Error(errorMessage), + ); + + const result = await service.bulkReinvite(mockOrganization, userIds); + + expect(result.successful).toBeUndefined(); + expect(result.failed).toHaveLength(3); + expect(result.failed[0]).toEqual({ id: userIds[0], error: errorMessage }); }); - expect(organizationUserApiService.postManyOrganizationUserReinvite).toHaveBeenCalledWith( - organizationId, - userIds, - ); }); - it("should handle bulk reinvite errors", async () => { - const errorMessage = "Bulk reinvite failed"; - organizationUserApiService.postManyOrganizationUserReinvite.mockRejectedValue( - new Error(errorMessage), - ); + describe("when feature flag is true (batching behavior)", () => { + beforeEach(() => { + configService.getFeatureFlag$.mockReturnValue(of(true)); + }); + it("should process users in a single batch when count equals REQUESTS_PER_BATCH", async () => { + const userIdsBatch = Array.from({ length: REQUESTS_PER_BATCH }, () => newGuid() as UserId); + const mockResponse = new ListResponse( + { + data: userIdsBatch.map((id) => ({ + id, + error: null, + })), + continuationToken: null, + }, + OrganizationUserBulkResponse, + ); - const result = await service.bulkReinvite(mockOrganization, userIds); + organizationUserApiService.postManyOrganizationUserReinvite.mockResolvedValue(mockResponse); - expect(result.successful).toBeUndefined(); - expect(result.failed).toHaveLength(3); - expect(result.failed[0]).toEqual({ id: userIds[0], error: errorMessage }); + const result = await service.bulkReinvite(mockOrganization, userIdsBatch); + + expect(result.successful).toBeDefined(); + expect(result.successful?.response).toHaveLength(REQUESTS_PER_BATCH); + expect(result.failed).toHaveLength(0); + expect(organizationUserApiService.postManyOrganizationUserReinvite).toHaveBeenCalledTimes( + 1, + ); + expect(organizationUserApiService.postManyOrganizationUserReinvite).toHaveBeenCalledWith( + organizationId, + userIdsBatch, + ); + }); + + it("should process users in multiple batches when count exceeds REQUESTS_PER_BATCH", async () => { + const totalUsers = REQUESTS_PER_BATCH + 100; + const userIdsBatch = Array.from({ length: totalUsers }, () => newGuid() as UserId); + + const mockResponse1 = new ListResponse( + { + data: userIdsBatch.slice(0, REQUESTS_PER_BATCH).map((id) => ({ + id, + error: null, + })), + continuationToken: null, + }, + OrganizationUserBulkResponse, + ); + + const mockResponse2 = new ListResponse( + { + data: userIdsBatch.slice(REQUESTS_PER_BATCH).map((id) => ({ + id, + error: null, + })), + continuationToken: null, + }, + OrganizationUserBulkResponse, + ); + + organizationUserApiService.postManyOrganizationUserReinvite + .mockResolvedValueOnce(mockResponse1) + .mockResolvedValueOnce(mockResponse2); + + const result = await service.bulkReinvite(mockOrganization, userIdsBatch); + + expect(result.successful).toBeDefined(); + expect(result.successful?.response).toHaveLength(totalUsers); + expect(result.failed).toHaveLength(0); + expect(organizationUserApiService.postManyOrganizationUserReinvite).toHaveBeenCalledTimes( + 2, + ); + expect(organizationUserApiService.postManyOrganizationUserReinvite).toHaveBeenNthCalledWith( + 1, + organizationId, + userIdsBatch.slice(0, REQUESTS_PER_BATCH), + ); + expect(organizationUserApiService.postManyOrganizationUserReinvite).toHaveBeenNthCalledWith( + 2, + organizationId, + userIdsBatch.slice(REQUESTS_PER_BATCH), + ); + }); + + it("should aggregate results across multiple successful batches", async () => { + const totalUsers = REQUESTS_PER_BATCH + 50; + const userIdsBatch = Array.from({ length: totalUsers }, () => newGuid() as UserId); + + const mockResponse1 = new ListResponse( + { + data: userIdsBatch.slice(0, REQUESTS_PER_BATCH).map((id) => ({ + id, + error: null, + })), + continuationToken: null, + }, + OrganizationUserBulkResponse, + ); + + const mockResponse2 = new ListResponse( + { + data: userIdsBatch.slice(REQUESTS_PER_BATCH).map((id) => ({ + id, + error: null, + })), + continuationToken: null, + }, + OrganizationUserBulkResponse, + ); + + organizationUserApiService.postManyOrganizationUserReinvite + .mockResolvedValueOnce(mockResponse1) + .mockResolvedValueOnce(mockResponse2); + + const result = await service.bulkReinvite(mockOrganization, userIdsBatch); + + expect(result.successful).toBeDefined(); + expect(result.successful?.response).toHaveLength(totalUsers); + expect(result.successful?.response.slice(0, REQUESTS_PER_BATCH)).toEqual( + mockResponse1.data, + ); + expect(result.successful?.response.slice(REQUESTS_PER_BATCH)).toEqual(mockResponse2.data); + expect(result.failed).toHaveLength(0); + }); + + it("should handle mixed individual errors across multiple batches", async () => { + const totalUsers = REQUESTS_PER_BATCH + 4; + const userIdsBatch = Array.from({ length: totalUsers }, () => newGuid() as UserId); + + const mockResponse1 = new ListResponse( + { + data: userIdsBatch.slice(0, REQUESTS_PER_BATCH).map((id, index) => ({ + id, + error: index % 10 === 0 ? "Rate limit exceeded" : null, + })), + continuationToken: null, + }, + OrganizationUserBulkResponse, + ); + + const mockResponse2 = new ListResponse( + { + data: [ + { id: userIdsBatch[REQUESTS_PER_BATCH], error: null }, + { id: userIdsBatch[REQUESTS_PER_BATCH + 1], error: "Invalid email" }, + { id: userIdsBatch[REQUESTS_PER_BATCH + 2], error: null }, + { id: userIdsBatch[REQUESTS_PER_BATCH + 3], error: "User suspended" }, + ], + continuationToken: null, + }, + OrganizationUserBulkResponse, + ); + + organizationUserApiService.postManyOrganizationUserReinvite + .mockResolvedValueOnce(mockResponse1) + .mockResolvedValueOnce(mockResponse2); + + const result = await service.bulkReinvite(mockOrganization, userIdsBatch); + + // Count expected failures: every 10th index (0, 10, 20, ..., 490) in first batch + 2 explicit in second batch + // Indices 0 to REQUESTS_PER_BATCH-1 where index % 10 === 0: that's floor((BATCH_SIZE-1)/10) + 1 values + const expectedFailuresInBatch1 = Math.floor((REQUESTS_PER_BATCH - 1) / 10) + 1; + const expectedFailuresInBatch2 = 2; + const expectedTotalFailures = expectedFailuresInBatch1 + expectedFailuresInBatch2; + const expectedSuccesses = totalUsers - expectedTotalFailures; + + expect(result.successful).toBeDefined(); + expect(result.successful?.response).toHaveLength(expectedSuccesses); + expect(result.failed).toHaveLength(expectedTotalFailures); + expect(result.failed.some((f) => f.error === "Rate limit exceeded")).toBe(true); + expect(result.failed.some((f) => f.error === "Invalid email")).toBe(true); + expect(result.failed.some((f) => f.error === "User suspended")).toBe(true); + }); + + it("should aggregate all failures when all batches fail", async () => { + const totalUsers = REQUESTS_PER_BATCH + 100; + const userIdsBatch = Array.from({ length: totalUsers }, () => newGuid() as UserId); + const errorMessage = "All batches failed"; + + organizationUserApiService.postManyOrganizationUserReinvite.mockRejectedValue( + new Error(errorMessage), + ); + + const result = await service.bulkReinvite(mockOrganization, userIdsBatch); + + expect(result.successful).toBeUndefined(); + expect(result.failed).toHaveLength(totalUsers); + expect(result.failed.every((f) => f.error === errorMessage)).toBe(true); + expect(organizationUserApiService.postManyOrganizationUserReinvite).toHaveBeenCalledTimes( + 2, + ); + }); + + it("should handle empty data in batch response", async () => { + const totalUsers = REQUESTS_PER_BATCH + 50; + const userIdsBatch = Array.from({ length: totalUsers }, () => newGuid() as UserId); + + const mockResponse1 = new ListResponse( + { + data: userIdsBatch.slice(0, REQUESTS_PER_BATCH).map((id) => ({ + id, + error: null, + })), + continuationToken: null, + }, + OrganizationUserBulkResponse, + ); + + const mockResponse2 = new ListResponse( + { + data: [], + continuationToken: null, + }, + OrganizationUserBulkResponse, + ); + + organizationUserApiService.postManyOrganizationUserReinvite + .mockResolvedValueOnce(mockResponse1) + .mockResolvedValueOnce(mockResponse2); + + const result = await service.bulkReinvite(mockOrganization, userIdsBatch); + + expect(result.successful).toBeDefined(); + expect(result.successful?.response).toHaveLength(REQUESTS_PER_BATCH); + expect(result.failed).toHaveLength(0); + }); + + it("should process batches sequentially in order", async () => { + const totalUsers = REQUESTS_PER_BATCH * 2; + const userIdsBatch = Array.from({ length: totalUsers }, () => newGuid() as UserId); + const callOrder: number[] = []; + + organizationUserApiService.postManyOrganizationUserReinvite.mockImplementation( + async (orgId, ids) => { + const batchIndex = ids.includes(userIdsBatch[0]) ? 1 : 2; + callOrder.push(batchIndex); + + return new ListResponse( + { + data: ids.map((id) => ({ + id, + error: null, + })), + continuationToken: null, + }, + OrganizationUserBulkResponse, + ); + }, + ); + + await service.bulkReinvite(mockOrganization, userIdsBatch); + + expect(callOrder).toEqual([1, 2]); + expect(organizationUserApiService.postManyOrganizationUserReinvite).toHaveBeenCalledTimes( + 2, + ); + }); }); }); @@ -427,14 +665,6 @@ describe("MemberActionsService", () => { expect(result).toBe(false); }); - it("should not allow reset password when organization lacks public and private keys", () => { - const org = { ...mockOrganization, hasPublicAndPrivateKeys: false } as Organization; - - const result = service.allowResetPassword(mockOrgUser, org, resetPasswordEnabled); - - expect(result).toBe(false); - }); - it("should not allow reset password when user is not enrolled in reset password", () => { const user = { ...mockOrgUser, resetPasswordEnrolled: false } as OrganizationUserView; @@ -443,12 +673,6 @@ describe("MemberActionsService", () => { expect(result).toBe(false); }); - it("should not allow reset password when reset password is disabled", () => { - const result = service.allowResetPassword(mockOrgUser, mockOrganization, false); - - expect(result).toBe(false); - }); - it("should not allow reset password when user status is not confirmed", () => { const user = { ...mockOrgUser, @@ -460,4 +684,26 @@ describe("MemberActionsService", () => { expect(result).toBe(false); }); }); + + describe("isProcessing signal", () => { + it("should be false initially", () => { + expect(service.isProcessing()).toBe(false); + }); + + it("should be false after operation completes successfully", async () => { + organizationUserApiService.removeOrganizationUser.mockResolvedValue(undefined); + + await service.removeUser(mockOrganization, userIdToManage); + + expect(service.isProcessing()).toBe(false); + }); + + it("should be false after operation fails", async () => { + organizationUserApiService.removeOrganizationUser.mockRejectedValue(new Error("Failed")); + + await service.removeUser(mockOrganization, userIdToManage); + + expect(service.isProcessing()).toBe(false); + }); + }); }); diff --git a/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.ts b/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.ts index 5e19e26954e..5833238209c 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.ts @@ -1,27 +1,35 @@ -import { Injectable } from "@angular/core"; -import { firstValueFrom, switchMap, map } from "rxjs"; +import { inject, Injectable, signal } from "@angular/core"; +import { lastValueFrom, firstValueFrom } from "rxjs"; import { OrganizationUserApiService, OrganizationUserBulkResponse, - OrganizationUserConfirmRequest, OrganizationUserService, } from "@bitwarden/admin-console/common"; +import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { OrganizationManagementPreferencesService } from "@bitwarden/common/admin-console/abstractions/organization-management-preferences/organization-management-preferences.service"; import { OrganizationUserType, OrganizationUserStatusType, } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; -import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { assertNonNullish } from "@bitwarden/common/auth/utils"; import { OrganizationMetadataServiceAbstraction } from "@bitwarden/common/billing/abstractions/organization-metadata.service.abstraction"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { DialogService } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; +import { UserId } from "@bitwarden/user-core"; +import { ProviderUser } from "@bitwarden/web-vault/app/admin-console/common/people-table-data-source"; import { OrganizationUserView } from "../../../core/views/organization-user.view"; +import { UserConfirmComponent } from "../../../manage/user-confirm.component"; + +export const REQUESTS_PER_BATCH = 500; export interface MemberActionResult { success: boolean; @@ -35,17 +43,26 @@ export interface BulkActionResult { @Injectable() export class MemberActionsService { - private userId$ = this.accountService.activeAccount$.pipe(getUserId); + private organizationUserApiService = inject(OrganizationUserApiService); + private organizationUserService = inject(OrganizationUserService); + private configService = inject(ConfigService); + private organizationMetadataService = inject(OrganizationMetadataServiceAbstraction); + private apiService = inject(ApiService); + private dialogService = inject(DialogService); + private keyService = inject(KeyService); + private logService = inject(LogService); + private orgManagementPrefs = inject(OrganizationManagementPreferencesService); + private userNamePipe = inject(UserNamePipe); - constructor( - private organizationUserApiService: OrganizationUserApiService, - private organizationUserService: OrganizationUserService, - private keyService: KeyService, - private encryptService: EncryptService, - private configService: ConfigService, - private accountService: AccountService, - private organizationMetadataService: OrganizationMetadataServiceAbstraction, - ) {} + readonly isProcessing = signal(false); + + private startProcessing(): void { + this.isProcessing.set(true); + } + + private endProcessing(): void { + this.isProcessing.set(false); + } async inviteUser( organization: Organization, @@ -55,6 +72,7 @@ export class MemberActionsService { collections?: any[], groups?: string[], ): Promise { + this.startProcessing(); try { await this.organizationUserApiService.postOrganizationUserInvite(organization.id, { emails: [email], @@ -67,55 +85,72 @@ export class MemberActionsService { return { success: true }; } catch (error) { return { success: false, error: (error as Error).message ?? String(error) }; + } finally { + this.endProcessing(); } } async removeUser(organization: Organization, userId: string): Promise { + this.startProcessing(); try { await this.organizationUserApiService.removeOrganizationUser(organization.id, userId); this.organizationMetadataService.refreshMetadataCache(); return { success: true }; } catch (error) { return { success: false, error: (error as Error).message ?? String(error) }; + } finally { + this.endProcessing(); } } async revokeUser(organization: Organization, userId: string): Promise { + this.startProcessing(); try { await this.organizationUserApiService.revokeOrganizationUser(organization.id, userId); this.organizationMetadataService.refreshMetadataCache(); return { success: true }; } catch (error) { return { success: false, error: (error as Error).message ?? String(error) }; + } finally { + this.endProcessing(); } } async restoreUser(organization: Organization, userId: string): Promise { + this.startProcessing(); try { await this.organizationUserApiService.restoreOrganizationUser(organization.id, userId); this.organizationMetadataService.refreshMetadataCache(); return { success: true }; } catch (error) { return { success: false, error: (error as Error).message ?? String(error) }; + } finally { + this.endProcessing(); } } async deleteUser(organization: Organization, userId: string): Promise { + this.startProcessing(); try { await this.organizationUserApiService.deleteOrganizationUser(organization.id, userId); this.organizationMetadataService.refreshMetadataCache(); return { success: true }; } catch (error) { return { success: false, error: (error as Error).message ?? String(error) }; + } finally { + this.endProcessing(); } } async reinviteUser(organization: Organization, userId: string): Promise { + this.startProcessing(); try { await this.organizationUserApiService.postOrganizationUserReinvite(organization.id, userId); return { success: true }; } catch (error) { return { success: false, error: (error as Error).message ?? String(error) }; + } finally { + this.endProcessing(); } } @@ -124,58 +159,52 @@ export class MemberActionsService { publicKey: Uint8Array, organization: Organization, ): Promise { + this.startProcessing(); try { - if ( - await firstValueFrom(this.configService.getFeatureFlag$(FeatureFlag.CreateDefaultLocation)) - ) { - await firstValueFrom( - this.organizationUserService.confirmUser(organization, user.id, publicKey), - ); - } else { - const request = await firstValueFrom( - this.userId$.pipe( - switchMap((userId) => this.keyService.orgKeys$(userId)), - map((orgKeys) => { - if (orgKeys == null || orgKeys[organization.id] == null) { - throw new Error("Organization keys not found for provided User."); - } - return orgKeys[organization.id]; - }), - switchMap((orgKey) => this.encryptService.encapsulateKeyUnsigned(orgKey, publicKey)), - map((encKey) => { - const req = new OrganizationUserConfirmRequest(); - req.key = encKey.encryptedString; - return req; - }), - ), - ); - - await this.organizationUserApiService.postOrganizationUserConfirm( - organization.id, - user.id, - request, - ); - } + await firstValueFrom( + this.organizationUserService.confirmUser(organization, user.id, publicKey), + ); return { success: true }; } catch (error) { return { success: false, error: (error as Error).message ?? String(error) }; + } finally { + this.endProcessing(); } } - async bulkReinvite(organization: Organization, userIds: string[]): Promise { + async bulkReinvite(organization: Organization, userIds: UserId[]): Promise { + this.startProcessing(); try { - const result = await this.organizationUserApiService.postManyOrganizationUserReinvite( - organization.id, - userIds, + const increaseBulkReinviteLimitForCloud = await firstValueFrom( + this.configService.getFeatureFlag$(FeatureFlag.IncreaseBulkReinviteLimitForCloud), ); - return { successful: result, failed: [] }; + if (increaseBulkReinviteLimitForCloud) { + return await this.vNextBulkReinvite(organization, userIds); + } else { + const result = await this.organizationUserApiService.postManyOrganizationUserReinvite( + organization.id, + userIds, + ); + return { successful: result, failed: [] }; + } } catch (error) { return { failed: userIds.map((id) => ({ id, error: (error as Error).message ?? String(error) })), }; + } finally { + this.endProcessing(); } } + async vNextBulkReinvite( + organization: Organization, + userIds: UserId[], + ): Promise { + return this.processBatchedOperation(userIds, REQUESTS_PER_BATCH, (batch) => + this.organizationUserApiService.postManyOrganizationUserReinvite(organization.id, batch), + ); + } + allowResetPassword( orgUser: OrganizationUserView, organization: Organization, @@ -207,4 +236,98 @@ export class MemberActionsService { orgUser.status === OrganizationUserStatusType.Confirmed ); } + + /** + * Processes user IDs in sequential batches and aggregates results. + * @param userIds - Array of user IDs to process + * @param batchSize - Number of IDs to process per batch + * @param processBatch - Async function that processes a single batch and returns the result + * @returns Aggregated bulk action result + */ + private async processBatchedOperation( + userIds: UserId[], + batchSize: number, + processBatch: (batch: string[]) => Promise>, + ): Promise { + const allSuccessful: OrganizationUserBulkResponse[] = []; + const allFailed: { id: string; error: string }[] = []; + + for (let i = 0; i < userIds.length; i += batchSize) { + const batch = userIds.slice(i, i + batchSize); + + try { + const result = await processBatch(batch); + + if (result?.data) { + for (const response of result.data) { + if (response.error) { + allFailed.push({ id: response.id, error: response.error }); + } else { + allSuccessful.push(response); + } + } + } + } catch (error) { + allFailed.push( + ...batch.map((id) => ({ id, error: (error as Error).message ?? String(error) })), + ); + } + } + + const successful = + allSuccessful.length > 0 + ? new ListResponse(allSuccessful, OrganizationUserBulkResponse) + : undefined; + + return { + successful, + failed: allFailed, + }; + } + + /** + * Shared dialog workflow that returns the public key when the user accepts the selected confirmation + * action. + * + * @param user - The user to confirm (must implement ConfirmableUser interface) + * @param userNamePipe - Pipe to transform user names for display + * @param orgManagementPrefs - Service providing organization management preferences + * @returns Promise containing the pulic key that resolves when the confirm action is accepted + * or undefined when cancelled + */ + async getPublicKeyForConfirm( + user: OrganizationUserView | ProviderUser, + ): Promise { + try { + assertNonNullish(user, "Cannot confirm null user."); + + const autoConfirmFingerPrint = await firstValueFrom( + this.orgManagementPrefs.autoConfirmFingerPrints.state$, + ); + + const publicKeyResponse = await this.apiService.getUserPublicKey(user.userId); + const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey); + + if (autoConfirmFingerPrint == null || !autoConfirmFingerPrint) { + const fingerprint = await this.keyService.getFingerprint(user.userId, publicKey); + this.logService.info(`User's fingerprint: ${fingerprint.join("-")}`); + + const confirmed = UserConfirmComponent.open(this.dialogService, { + data: { + name: this.userNamePipe.transform(user), + userId: user.userId, + publicKey: publicKey, + }, + }); + + if (!(await lastValueFrom(confirmed.closed))) { + return; + } + } + + return publicKey; + } catch (e) { + this.logService.error(`Handled exception: ${e}`); + } + } } diff --git a/apps/web/src/app/admin-console/organizations/members/services/member-export/index.ts b/apps/web/src/app/admin-console/organizations/members/services/member-export/index.ts new file mode 100644 index 00000000000..acd36a91683 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/services/member-export/index.ts @@ -0,0 +1,2 @@ +export * from "./member.export"; +export * from "./member-export.service"; diff --git a/apps/web/src/app/admin-console/organizations/members/services/member-export/member-export.service.spec.ts b/apps/web/src/app/admin-console/organizations/members/services/member-export/member-export.service.spec.ts new file mode 100644 index 00000000000..08503f80f17 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/services/member-export/member-export.service.spec.ts @@ -0,0 +1,182 @@ +import { TestBed } from "@angular/core/testing"; +import { MockProxy, mock } from "jest-mock-extended"; + +import { UserTypePipe } from "@bitwarden/angular/pipes/user-type.pipe"; +import { + OrganizationUserStatusType, + OrganizationUserType, +} from "@bitwarden/common/admin-console/enums"; +import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/logging"; + +import { OrganizationUserView } from "../../../core"; +import { UserStatusPipe } from "../../pipes"; + +import { MemberExportService } from "./member-export.service"; + +describe("MemberExportService", () => { + let service: MemberExportService; + let i18nService: MockProxy; + let fileDownloadService: MockProxy; + let logService: MockProxy; + + beforeEach(() => { + i18nService = mock(); + fileDownloadService = mock(); + logService = mock(); + + // Setup common i18n translations + i18nService.t.mockImplementation((key: string) => { + const translations: Record = { + // Column headers + email: "Email", + name: "Name", + status: "Status", + role: "Role", + twoStepLogin: "Two-step Login", + accountRecovery: "Account Recovery", + secretsManager: "Secrets Manager", + groups: "Groups", + // Status values + invited: "Invited", + accepted: "Accepted", + confirmed: "Confirmed", + revoked: "Revoked", + // Role values + owner: "Owner", + admin: "Admin", + user: "User", + custom: "Custom", + // Boolean states + enabled: "Enabled", + optionEnabled: "Enabled", + disabled: "Disabled", + enrolled: "Enrolled", + notEnrolled: "Not Enrolled", + // Error messages + noMembersToExport: "No members to export", + }; + return translations[key] || key; + }); + + TestBed.configureTestingModule({ + providers: [ + MemberExportService, + { provide: FileDownloadService, useValue: fileDownloadService }, + { provide: LogService, useValue: logService }, + { provide: I18nService, useValue: i18nService }, + UserTypePipe, + UserStatusPipe, + ], + }); + + service = TestBed.inject(MemberExportService); + }); + + describe("getMemberExport", () => { + it("should export members with all fields populated", () => { + const members: OrganizationUserView[] = [ + { + email: "user1@example.com", + name: "User One", + status: OrganizationUserStatusType.Confirmed, + type: OrganizationUserType.Admin, + twoFactorEnabled: true, + resetPasswordEnrolled: true, + accessSecretsManager: true, + groupNames: ["Group A", "Group B"], + } as OrganizationUserView, + { + email: "user2@example.com", + name: "User Two", + status: OrganizationUserStatusType.Invited, + type: OrganizationUserType.User, + twoFactorEnabled: false, + resetPasswordEnrolled: false, + accessSecretsManager: false, + groupNames: ["Group C"], + } as OrganizationUserView, + ]; + + const result = service.getMemberExport(members); + + expect(result.success).toBe(true); + expect(result.error).toBeUndefined(); + expect(fileDownloadService.download).toHaveBeenCalledTimes(1); + + const downloadCall = fileDownloadService.download.mock.calls[0][0]; + expect(downloadCall.fileName).toContain("org-members"); + expect(downloadCall.fileName).toContain(".csv"); + expect(downloadCall.blobOptions).toEqual({ type: "text/plain" }); + + const csvData = downloadCall.blobData as string; + expect(csvData).toContain("Email,Name,Status,Role,Two-step Login,Account Recovery"); + expect(csvData).toContain("user1@example.com"); + expect(csvData).toContain("User One"); + expect(csvData).toContain("Confirmed"); + expect(csvData).toContain("Admin"); + expect(csvData).toContain("user2@example.com"); + expect(csvData).toContain("User Two"); + expect(csvData).toContain("Invited"); + }); + + it("should handle members with null name", () => { + const members: OrganizationUserView[] = [ + { + email: "user@example.com", + name: null, + status: OrganizationUserStatusType.Confirmed, + type: OrganizationUserType.User, + twoFactorEnabled: false, + resetPasswordEnrolled: false, + accessSecretsManager: false, + groupNames: [], + } as OrganizationUserView, + ]; + + const result = service.getMemberExport(members); + + expect(result.success).toBe(true); + expect(fileDownloadService.download).toHaveBeenCalled(); + + const csvData = fileDownloadService.download.mock.calls[0][0].blobData as string; + expect(csvData).toContain("user@example.com"); + // Empty name is represented as an empty field in CSV + expect(csvData).toContain("user@example.com,,Confirmed"); + }); + + it("should handle members with no groups", () => { + const members: OrganizationUserView[] = [ + { + email: "user@example.com", + name: "User", + status: OrganizationUserStatusType.Confirmed, + type: OrganizationUserType.User, + twoFactorEnabled: false, + resetPasswordEnrolled: false, + accessSecretsManager: false, + groupNames: null, + } as OrganizationUserView, + ]; + + const result = service.getMemberExport(members); + + expect(result.success).toBe(true); + expect(fileDownloadService.download).toHaveBeenCalled(); + + const csvData = fileDownloadService.download.mock.calls[0][0].blobData as string; + expect(csvData).toContain("user@example.com"); + expect(csvData).toBeDefined(); + }); + + it("should handle empty members array", () => { + const result = service.getMemberExport([]); + + expect(result.success).toBe(false); + expect(result.error).toBeDefined(); + expect(result.error?.message).toBe("No members to export"); + expect(fileDownloadService.download).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/web/src/app/admin-console/organizations/members/services/member-export/member-export.service.ts b/apps/web/src/app/admin-console/organizations/members/services/member-export/member-export.service.ts new file mode 100644 index 00000000000..069c7d9d148 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/services/member-export/member-export.service.ts @@ -0,0 +1,82 @@ +import { inject, Injectable } from "@angular/core"; +import * as papa from "papaparse"; + +import { UserTypePipe } from "@bitwarden/angular/pipes/user-type.pipe"; +import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { ExportHelper } from "@bitwarden/vault-export-core"; + +import { OrganizationUserView } from "../../../core"; +import { UserStatusPipe } from "../../pipes"; + +import { MemberExport } from "./member.export"; + +export interface MemberExportResult { + success: boolean; + error?: { message: string }; +} + +@Injectable() +export class MemberExportService { + private i18nService = inject(I18nService); + private userTypePipe = inject(UserTypePipe); + private userStatusPipe = inject(UserStatusPipe); + private fileDownloadService = inject(FileDownloadService); + private logService = inject(LogService); + + getMemberExport(data: OrganizationUserView[]): MemberExportResult { + try { + const members = data; + if (!members || members.length === 0) { + return { success: false, error: { message: this.i18nService.t("noMembersToExport") } }; + } + + const exportData = members.map((m) => + MemberExport.fromOrganizationUserView( + this.i18nService, + this.userTypePipe, + this.userStatusPipe, + m, + ), + ); + + const headers: string[] = [ + this.i18nService.t("email"), + this.i18nService.t("name"), + this.i18nService.t("status"), + this.i18nService.t("role"), + this.i18nService.t("twoStepLogin"), + this.i18nService.t("accountRecovery"), + this.i18nService.t("secretsManager"), + this.i18nService.t("groups"), + ]; + + const csvData = papa.unparse(exportData, { + columns: headers, + header: true, + }); + + const fileName = this.getFileName("org-members"); + + this.fileDownloadService.download({ + fileName: fileName, + blobData: csvData, + blobOptions: { type: "text/plain" }, + }); + + return { success: true }; + } catch (error) { + this.logService.error(`Failed to export members: ${error}`); + + const errorMessage = + error instanceof Error ? error.message : this.i18nService.t("unexpectedError"); + + return { success: false, error: { message: errorMessage } }; + } + } + + private getFileName(prefix: string | null = null, extension = "csv"): string { + return ExportHelper.getFileName(prefix ?? "", extension); + } +} diff --git a/apps/web/src/app/admin-console/organizations/members/services/member-export/member.export.ts b/apps/web/src/app/admin-console/organizations/members/services/member-export/member.export.ts new file mode 100644 index 00000000000..262e8ebd9fb --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/services/member-export/member.export.ts @@ -0,0 +1,43 @@ +import { UserTypePipe } from "@bitwarden/angular/pipes/user-type.pipe"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + +import { OrganizationUserView } from "../../../core"; +import { UserStatusPipe } from "../../pipes"; + +export class MemberExport { + /** + * @param user Organization user to export + * @returns a Record of each column header key, value + * All property members must be a string for export purposes. Null and undefined will appear as + * "null" in a .csv export, therefore an empty string is preferable to a nullish type. + */ + static fromOrganizationUserView( + i18nService: I18nService, + userTypePipe: UserTypePipe, + userStatusPipe: UserStatusPipe, + user: OrganizationUserView, + ): Record { + const result = { + [i18nService.t("email")]: user.email, + [i18nService.t("name")]: user.name ?? "", + [i18nService.t("status")]: userStatusPipe.transform(user.status), + [i18nService.t("role")]: userTypePipe.transform(user.type), + + [i18nService.t("twoStepLogin")]: user.twoFactorEnabled + ? i18nService.t("optionEnabled") + : i18nService.t("disabled"), + + [i18nService.t("accountRecovery")]: user.resetPasswordEnrolled + ? i18nService.t("enrolled") + : i18nService.t("notEnrolled"), + + [i18nService.t("secretsManager")]: user.accessSecretsManager + ? i18nService.t("optionEnabled") + : i18nService.t("disabled"), + + [i18nService.t("groups")]: user.groupNames?.join(", ") ?? "", + }; + + return result; + } +} diff --git a/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.ts b/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.ts index 0dc417cc2c6..b2695b7568f 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.ts @@ -1,14 +1,13 @@ import { Injectable } from "@angular/core"; import { combineLatest, firstValueFrom, from, map, switchMap } from "rxjs"; +import { CollectionService, OrganizationUserApiService } from "@bitwarden/admin-console/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { + CollectionDetailsResponse, Collection, CollectionData, - CollectionDetailsResponse, - CollectionService, - OrganizationUserApiService, -} from "@bitwarden/admin-console/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts index df5e7e8a25c..88797f86650 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/organization-user-reset-password/organization-user-reset-password.service.ts @@ -35,13 +35,10 @@ import { OrganizationUserResetPasswordEntry } from "./organization-user-reset-pa @Injectable({ providedIn: "root", }) -export class OrganizationUserResetPasswordService - implements - UserKeyRotationKeyRecoveryProvider< - OrganizationUserResetPasswordWithIdRequest, - OrganizationUserResetPasswordEntry - > -{ +export class OrganizationUserResetPasswordService implements UserKeyRotationKeyRecoveryProvider< + OrganizationUserResetPasswordWithIdRequest, + OrganizationUserResetPasswordEntry +> { constructor( private keyService: KeyService, private encryptService: EncryptService, diff --git a/apps/web/src/app/admin-console/organizations/policies/index.ts b/apps/web/src/app/admin-console/organizations/policies/index.ts index 3042be240f7..eb614e180e1 100644 --- a/apps/web/src/app/admin-console/organizations/policies/index.ts +++ b/apps/web/src/app/admin-console/organizations/policies/index.ts @@ -2,6 +2,6 @@ export { PoliciesComponent } from "./policies.component"; export { ossPolicyEditRegister } from "./policy-edit-register"; export { BasePolicyEditDefinition, BasePolicyEditComponent } from "./base-policy-edit.component"; export { POLICY_EDIT_REGISTER } from "./policy-register-token"; -export { AutoConfirmPolicyDialogComponent } from "./auto-confirm-edit-policy-dialog.component"; export { AutoConfirmPolicy } from "./policy-edit-definitions"; export { PolicyEditDialogResult } from "./policy-edit-dialog.component"; +export * from "./policy-edit-dialogs"; diff --git a/apps/web/src/app/admin-console/organizations/policies/policies.component.html b/apps/web/src/app/admin-console/organizations/policies/policies.component.html index 8df73a50e14..c38092146ab 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policies.component.html +++ b/apps/web/src/app/admin-console/organizations/policies/policies.component.html @@ -1,27 +1,34 @@ +@let organization = organization$ | async; +@let policiesEnabledMap = policiesEnabledMap$ | async; +@let organizationId = organizationId$ | async; + - @if (loading) { + @if (!organization || !policiesEnabledMap || !organizationId) { {{ "loading" | i18n }} - } - @if (!loading) { + } @else { - @for (p of policies$ | async; track p.type) { - - - - @if (policiesEnabledMap.get(p.type)) { - {{ "on" | i18n }} - } - {{ p.description | i18n }} - - + @for (p of policies$ | async; track $index) { + @if (p.display$(organization, configService) | async) { + + + + @if (policiesEnabledMap.get(p.type)) { + {{ "on" | i18n }} + } + {{ p.description | i18n }} + + + } } diff --git a/apps/web/src/app/admin-console/organizations/policies/policies.component.spec.ts b/apps/web/src/app/admin-console/organizations/policies/policies.component.spec.ts new file mode 100644 index 00000000000..125876ce05a --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/policies.component.spec.ts @@ -0,0 +1,548 @@ +import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { ActivatedRoute } from "@angular/router"; +import { mock, MockProxy } from "jest-mock-extended"; +import { BehaviorSubject, of, firstValueFrom } from "rxjs"; + +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { ListResponse } from "@bitwarden/common/models/response/list.response"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { FakeAccountService, mockAccountServiceWith } from "@bitwarden/common/spec"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { DialogService } from "@bitwarden/components"; +import { newGuid } from "@bitwarden/guid"; + +import { BasePolicyEditDefinition } from "./base-policy-edit.component"; +import { PoliciesComponent } from "./policies.component"; +import { SingleOrgPolicy } from "./policy-edit-definitions/single-org.component"; +import { PolicyEditDialogComponent } from "./policy-edit-dialog.component"; +import { PolicyListService } from "./policy-list.service"; +import { POLICY_EDIT_REGISTER } from "./policy-register-token"; + +describe("PoliciesComponent", () => { + let component: PoliciesComponent; + let fixture: ComponentFixture; + + let mockActivatedRoute: ActivatedRoute; + let mockOrganizationService: MockProxy; + let mockAccountService: FakeAccountService; + let mockPolicyApiService: MockProxy; + let mockPolicyListService: MockProxy; + let mockDialogService: MockProxy; + let mockPolicyService: MockProxy; + let mockConfigService: MockProxy; + let mockI18nService: MockProxy; + let mockPlatformUtilsService: MockProxy; + + let routeParamsSubject: BehaviorSubject; + let queryParamsSubject: BehaviorSubject; + + const mockUserId = newGuid() as UserId; + const mockOrgId = newGuid() as OrganizationId; + const mockOrg = { + id: mockOrgId, + name: "Test Organization", + enabled: true, + } as Organization; + + const mockPolicyResponse = { + id: newGuid(), + enabled: true, + object: "policy", + organizationId: mockOrgId, + type: PolicyType.SingleOrg, + data: null, + }; + + const mockPolicy = new SingleOrgPolicy(); + + beforeEach(async () => { + routeParamsSubject = new BehaviorSubject({ organizationId: mockOrgId }); + queryParamsSubject = new BehaviorSubject({}); + + mockActivatedRoute = { + params: routeParamsSubject.asObservable(), + queryParams: queryParamsSubject.asObservable(), + } as any; + + mockOrganizationService = mock(); + mockOrganizationService.organizations$.mockReturnValue(of([mockOrg])); + + mockAccountService = mockAccountServiceWith(mockUserId); + + mockPolicyApiService = mock(); + mockPolicyApiService.getPolicies.mockResolvedValue( + new ListResponse({ Data: [mockPolicyResponse], ContinuationToken: null }, PolicyResponse), + ); + + mockPolicyListService = mock(); + mockPolicyListService.getPolicies.mockReturnValue([mockPolicy]); + + mockDialogService = mock(); + mockDialogService.open.mockReturnValue({ close: jest.fn() } as any); + + mockPolicyService = mock(); + mockPolicyService.policies$.mockReturnValue(of([])); + + mockConfigService = mock(); + mockI18nService = mock(); + mockPlatformUtilsService = mock(); + + jest.spyOn(PolicyEditDialogComponent, "open").mockReturnValue({ close: jest.fn() } as any); + + await TestBed.configureTestingModule({ + imports: [PoliciesComponent], + providers: [ + { provide: ActivatedRoute, useValue: mockActivatedRoute }, + { provide: OrganizationService, useValue: mockOrganizationService }, + { provide: AccountService, useValue: mockAccountService }, + { provide: PolicyApiServiceAbstraction, useValue: mockPolicyApiService }, + { provide: PolicyListService, useValue: mockPolicyListService }, + { provide: DialogService, useValue: mockDialogService }, + { provide: PolicyService, useValue: mockPolicyService }, + { provide: ConfigService, useValue: mockConfigService }, + { provide: I18nService, useValue: mockI18nService }, + { provide: PlatformUtilsService, useValue: mockPlatformUtilsService }, + { provide: POLICY_EDIT_REGISTER, useValue: [] }, + ], + schemas: [NO_ERRORS_SCHEMA], + }) + .overrideComponent(PoliciesComponent, { + remove: { imports: [] }, + add: { template: "
    " }, + }) + .compileComponents(); + + fixture = TestBed.createComponent(PoliciesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + afterEach(() => { + if (fixture) { + fixture.destroy(); + } + jest.restoreAllMocks(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); + + describe("organizationId$", () => { + it("should extract organizationId from route params", async () => { + const orgId = await firstValueFrom(component.organizationId$); + expect(orgId).toBe(mockOrgId); + }); + + it("should emit new organizationId when route params change", (done) => { + const newOrgId = newGuid() as OrganizationId; + const emittedValues: OrganizationId[] = []; + + const subscription = component.organizationId$.subscribe((orgId) => { + emittedValues.push(orgId); + + if (emittedValues.length === 2) { + expect(emittedValues[0]).toBe(mockOrgId); + expect(emittedValues[1]).toBe(newOrgId); + subscription.unsubscribe(); + done(); + } + }); + + routeParamsSubject.next({ organizationId: newOrgId }); + }); + }); + + describe("organization$", () => { + it("should retrieve organization for current user and organizationId", async () => { + const org = await firstValueFrom(component.organization$); + expect(org).toBe(mockOrg); + expect(mockOrganizationService.organizations$).toHaveBeenCalledWith(mockUserId); + }); + + it("should throw error when organization is not found", async () => { + mockOrganizationService.organizations$.mockReturnValue(of([])); + + await expect(firstValueFrom(component.organization$)).rejects.toThrow( + "No organization found for provided userId", + ); + }); + }); + + describe("policies$", () => { + it("should return policies from PolicyListService", async () => { + const policies = await firstValueFrom(component.policies$); + + expect(policies).toBeDefined(); + expect(Array.isArray(policies)).toBe(true); + }); + }); + + describe("orgPolicies$", () => { + describe("with multiple policies", () => { + const mockPolicyResponsesData = [ + { + id: newGuid(), + organizationId: mockOrgId, + type: PolicyType.TwoFactorAuthentication, + enabled: true, + data: null, + }, + { + id: newGuid(), + organizationId: mockOrgId, + type: PolicyType.RequireSso, + enabled: false, + data: null, + }, + ]; + + beforeEach(async () => { + const listResponse = new ListResponse( + { Data: mockPolicyResponsesData, ContinuationToken: null }, + PolicyResponse, + ); + + mockPolicyApiService.getPolicies.mockResolvedValue(listResponse); + + fixture = TestBed.createComponent(PoliciesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should fetch policies from API for current organization", async () => { + const policies = await firstValueFrom(component["orgPolicies$"]); + expect(policies.length).toBe(2); + expect(mockPolicyApiService.getPolicies).toHaveBeenCalledWith(mockOrgId); + }); + }); + + describe("with no policies", () => { + beforeEach(async () => { + mockPolicyApiService.getPolicies.mockResolvedValue( + new ListResponse({ Data: [], ContinuationToken: null }, PolicyResponse), + ); + + fixture = TestBed.createComponent(PoliciesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should return empty array when API returns no data", async () => { + const policies = await firstValueFrom(component["orgPolicies$"]); + expect(policies).toEqual([]); + }); + }); + + describe("with null data", () => { + beforeEach(async () => { + mockPolicyApiService.getPolicies.mockResolvedValue( + new ListResponse({ Data: null, ContinuationToken: null }, PolicyResponse), + ); + + fixture = TestBed.createComponent(PoliciesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should return empty array when API returns null data", async () => { + const policies = await firstValueFrom(component["orgPolicies$"]); + expect(policies).toEqual([]); + }); + }); + }); + + describe("policiesEnabledMap$", () => { + describe("with multiple policies", () => { + const mockPolicyResponsesData = [ + { + id: "policy-1", + organizationId: mockOrgId, + type: PolicyType.TwoFactorAuthentication, + enabled: true, + data: null, + }, + { + id: "policy-2", + organizationId: mockOrgId, + type: PolicyType.RequireSso, + enabled: false, + data: null, + }, + { + id: "policy-3", + organizationId: mockOrgId, + type: PolicyType.SingleOrg, + enabled: true, + data: null, + }, + ]; + + beforeEach(async () => { + mockPolicyApiService.getPolicies.mockResolvedValue( + new ListResponse( + { Data: mockPolicyResponsesData, ContinuationToken: null }, + PolicyResponse, + ), + ); + + fixture = TestBed.createComponent(PoliciesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create a map of policy types to their enabled status", async () => { + const map = await firstValueFrom(component.policiesEnabledMap$); + expect(map.size).toBe(3); + expect(map.get(PolicyType.TwoFactorAuthentication)).toBe(true); + expect(map.get(PolicyType.RequireSso)).toBe(false); + expect(map.get(PolicyType.SingleOrg)).toBe(true); + }); + }); + + describe("with no policies", () => { + beforeEach(async () => { + mockPolicyApiService.getPolicies.mockResolvedValue( + new ListResponse({ Data: [], ContinuationToken: null }, PolicyResponse), + ); + + fixture = TestBed.createComponent(PoliciesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create empty map when no policies exist", async () => { + const map = await firstValueFrom(component.policiesEnabledMap$); + expect(map.size).toBe(0); + }); + }); + }); + + describe("constructor subscription", () => { + it("should subscribe to policyService.policies$ on initialization", () => { + expect(mockPolicyService.policies$).toHaveBeenCalledWith(mockUserId); + }); + + describe("when policyService emits", () => { + let policiesSubject: BehaviorSubject; + let callCount: number; + + beforeEach(async () => { + policiesSubject = new BehaviorSubject([]); + mockPolicyService.policies$.mockReturnValue(policiesSubject.asObservable()); + + callCount = 0; + mockPolicyApiService.getPolicies.mockImplementation(() => { + callCount++; + return of(new ListResponse({ Data: [], ContinuationToken: null }, PolicyResponse)); + }); + + fixture = TestBed.createComponent(PoliciesComponent); + fixture.detectChanges(); + }); + + it("should refresh policies when policyService emits", () => { + const initialCallCount = callCount; + + policiesSubject.next([{ type: PolicyType.TwoFactorAuthentication }]); + + expect(callCount).toBeGreaterThan(initialCallCount); + }); + }); + }); + + describe("handleLaunchEvent", () => { + describe("when policyId is in query params", () => { + const mockPolicyId = newGuid(); + const mockPolicy: BasePolicyEditDefinition = { + name: "Test Policy", + description: "Test Description", + type: PolicyType.TwoFactorAuthentication, + component: {} as any, + showDescription: true, + display$: () => of(true), + }; + + const mockPolicyResponseData = { + id: mockPolicyId, + organizationId: mockOrgId, + type: PolicyType.TwoFactorAuthentication, + enabled: true, + data: null, + }; + + let dialogOpenSpy: jest.SpyInstance; + + beforeEach(async () => { + queryParamsSubject.next({ policyId: mockPolicyId }); + + mockPolicyApiService.getPolicies.mockReturnValue( + of( + new ListResponse( + { Data: [mockPolicyResponseData], ContinuationToken: null }, + PolicyResponse, + ), + ), + ); + + dialogOpenSpy = jest + .spyOn(PolicyEditDialogComponent, "open") + .mockReturnValue({ close: jest.fn() } as any); + + TestBed.resetTestingModule(); + await TestBed.configureTestingModule({ + imports: [PoliciesComponent], + providers: [ + { provide: ActivatedRoute, useValue: mockActivatedRoute }, + { provide: OrganizationService, useValue: mockOrganizationService }, + { provide: AccountService, useValue: mockAccountService }, + { provide: PolicyApiServiceAbstraction, useValue: mockPolicyApiService }, + { provide: PolicyListService, useValue: mockPolicyListService }, + { provide: DialogService, useValue: mockDialogService }, + { provide: PolicyService, useValue: mockPolicyService }, + { provide: ConfigService, useValue: mockConfigService }, + { provide: I18nService, useValue: mockI18nService }, + { provide: PlatformUtilsService, useValue: mockPlatformUtilsService }, + { provide: POLICY_EDIT_REGISTER, useValue: [mockPolicy] }, + ], + schemas: [NO_ERRORS_SCHEMA], + }) + .overrideComponent(PoliciesComponent, { + remove: { imports: [] }, + add: { template: "
    " }, + }) + .compileComponents(); + + fixture = TestBed.createComponent(PoliciesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should open policy dialog when policyId is in query params", () => { + expect(dialogOpenSpy).toHaveBeenCalled(); + const callArgs = dialogOpenSpy.mock.calls[0][1]; + expect(callArgs.data?.policy.type).toBe(mockPolicy.type); + expect(callArgs.data?.organizationId).toBe(mockOrgId); + }); + }); + + it("should not open dialog when policyId is not in query params", async () => { + const editSpy = jest.spyOn(component, "edit"); + + queryParamsSubject.next({}); + + expect(editSpy).not.toHaveBeenCalled(); + }); + + it("should not open dialog when policyId does not match any org policy", async () => { + const mockPolicy: BasePolicyEditDefinition = { + name: "Test Policy", + description: "Test Description", + type: PolicyType.TwoFactorAuthentication, + component: {} as any, + showDescription: true, + display$: () => of(true), + }; + + mockPolicyListService.getPolicies.mockReturnValue([mockPolicy]); + mockPolicyApiService.getPolicies.mockResolvedValue( + new ListResponse({ Data: [], ContinuationToken: null }, PolicyResponse), + ); + + const editSpy = jest.spyOn(component, "edit"); + + queryParamsSubject.next({ policyId: "non-existent-policy-id" }); + + expect(editSpy).not.toHaveBeenCalled(); + }); + }); + + describe("edit", () => { + it("should call dialogService.open with correct parameters when no custom dialog is specified", () => { + const mockPolicy: BasePolicyEditDefinition = { + name: "Test Policy", + description: "Test Description", + type: PolicyType.TwoFactorAuthentication, + component: {} as any, + showDescription: true, + display$: () => of(true), + }; + + const openSpy = jest.spyOn(PolicyEditDialogComponent, "open"); + + component.edit(mockPolicy, mockOrgId); + + expect(openSpy).toHaveBeenCalled(); + const callArgs = openSpy.mock.calls[0]; + expect(callArgs[1]).toEqual({ + data: { + policy: mockPolicy, + organizationId: mockOrgId, + }, + }); + }); + + it("should call custom dialog open method when specified", () => { + const mockDialogRef = { close: jest.fn() }; + const mockCustomDialog = { + open: jest.fn().mockReturnValue(mockDialogRef), + }; + + const mockPolicy: BasePolicyEditDefinition = { + name: "Custom Policy", + description: "Custom Description", + type: PolicyType.RequireSso, + component: {} as any, + editDialogComponent: mockCustomDialog as any, + showDescription: true, + display$: () => of(true), + }; + + component.edit(mockPolicy, mockOrgId); + + expect(mockCustomDialog.open).toHaveBeenCalled(); + const callArgs = mockCustomDialog.open.mock.calls[0]; + expect(callArgs[1]).toEqual({ + data: { + policy: mockPolicy, + organizationId: mockOrgId, + }, + }); + expect(PolicyEditDialogComponent.open).not.toHaveBeenCalled(); + }); + + it("should pass correct organizationId to dialog", () => { + const customOrgId = newGuid() as OrganizationId; + const mockPolicy: BasePolicyEditDefinition = { + name: "Test Policy", + description: "Test Description", + type: PolicyType.SingleOrg, + component: {} as any, + showDescription: true, + display$: () => of(true), + }; + + const openSpy = jest.spyOn(PolicyEditDialogComponent, "open"); + + component.edit(mockPolicy, customOrgId); + + expect(openSpy).toHaveBeenCalled(); + const callArgs = openSpy.mock.calls[0]; + expect(callArgs[1]).toEqual({ + data: { + policy: mockPolicy, + organizationId: customOrgId, + }, + }); + }); + }); +}); diff --git a/apps/web/src/app/admin-console/organizations/policies/policies.component.ts b/apps/web/src/app/admin-console/organizations/policies/policies.component.ts index e80796fd0af..1f9a8deaa85 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policies.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policies.component.ts @@ -1,31 +1,19 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, OnInit } from "@angular/core"; +import { ChangeDetectionStrategy, Component, DestroyRef } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute } from "@angular/router"; -import { - combineLatest, - firstValueFrom, - Observable, - of, - switchMap, - first, - map, - withLatestFrom, - tap, -} from "rxjs"; +import { combineLatest, Observable, of, switchMap, first, map, shareReplay } from "rxjs"; -import { - getOrganizationById, - OrganizationService, -} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { getById } from "@bitwarden/common/platform/misc"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { DialogService } from "@bitwarden/components"; import { safeProvider } from "@bitwarden/ui-common"; @@ -37,8 +25,6 @@ import { PolicyEditDialogComponent } from "./policy-edit-dialog.component"; import { PolicyListService } from "./policy-list.service"; import { POLICY_EDIT_REGISTER } from "./policy-register-token"; -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "policies.component.html", imports: [SharedModule, HeaderModule], @@ -48,14 +34,54 @@ import { POLICY_EDIT_REGISTER } from "./policy-register-token"; deps: [POLICY_EDIT_REGISTER], }), ], + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class PoliciesComponent implements OnInit { - loading = true; - organizationId: string; - policies$: Observable; +export class PoliciesComponent { + private userId$: Observable = this.accountService.activeAccount$.pipe(getUserId); - private orgPolicies: PolicyResponse[]; - protected policiesEnabledMap: Map = new Map(); + protected organizationId$: Observable = this.route.params.pipe( + map((params) => params.organizationId), + ); + + protected organization$: Observable = combineLatest([ + this.userId$, + this.organizationId$, + ]).pipe( + switchMap(([userId, orgId]) => + this.organizationService.organizations$(userId).pipe( + getById(orgId), + map((org) => { + if (org == null) { + throw new Error("No organization found for provided userId"); + } + return org; + }), + ), + ), + ); + + protected policies$: Observable = of( + this.policyListService.getPolicies(), + ); + + private orgPolicies$: Observable = this.accountService.activeAccount$.pipe( + getUserId, + switchMap((userId) => this.policyService.policies$(userId)), + switchMap(() => this.organizationId$), + switchMap((organizationId) => this.policyApiService.getPolicies(organizationId)), + map((response) => (response.data != null && response.data.length > 0 ? response.data : [])), + shareReplay({ bufferSize: 1, refCount: true }), + ); + + protected policiesEnabledMap$: Observable> = this.orgPolicies$.pipe( + map((orgPolicies) => { + const policiesEnabledMap: Map = new Map(); + orgPolicies.forEach((op) => { + policiesEnabledMap.set(op.type, op.enabled); + }); + return policiesEnabledMap; + }), + ); constructor( private route: ActivatedRoute, @@ -66,60 +92,28 @@ export class PoliciesComponent implements OnInit { private dialogService: DialogService, private policyService: PolicyService, protected configService: ConfigService, + private destroyRef: DestroyRef, ) { - this.accountService.activeAccount$ - .pipe( - getUserId, - switchMap((userId) => this.policyService.policies$(userId)), - tap(async () => await this.load()), - takeUntilDestroyed(), - ) - .subscribe(); + this.handleLaunchEvent(); } - async ngOnInit() { - // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe - this.route.parent.parent.params.subscribe(async (params) => { - this.organizationId = params.organizationId; - const userId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); - - const organization$ = this.organizationService - .organizations$(userId) - .pipe(getOrganizationById(this.organizationId)); - - this.policies$ = organization$.pipe( - withLatestFrom(of(this.policyListService.getPolicies())), - switchMap(([organization, policies]) => { - return combineLatest( - policies.map((policy) => - policy - .display$(organization, this.configService) - .pipe(map((shouldDisplay) => ({ policy, shouldDisplay }))), - ), - ); - }), - map((results) => - results.filter((result) => result.shouldDisplay).map((result) => result.policy), - ), - ); - - await this.load(); - - // Handle policies component launch from Event message - combineLatest([this.route.queryParams.pipe(first()), this.policies$]) - /* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */ - .subscribe(async ([qParams, policies]) => { + // Handle policies component launch from Event message + private handleLaunchEvent() { + combineLatest([ + this.route.queryParams.pipe(first()), + this.policies$, + this.organizationId$, + this.orgPolicies$, + ]) + .pipe( + map(([qParams, policies, organizationId, orgPolicies]) => { if (qParams.policyId != null) { const policyIdFromEvents: string = qParams.policyId; - for (const orgPolicy of this.orgPolicies) { + for (const orgPolicy of orgPolicies) { if (orgPolicy.id === policyIdFromEvents) { for (let i = 0; i < policies.length; i++) { if (policies[i].type === orgPolicy.type) { - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.edit(policies[i]); + this.edit(policies[i], organizationId); break; } } @@ -127,27 +121,19 @@ export class PoliciesComponent implements OnInit { } } } - }); - }); + }), + takeUntilDestroyed(this.destroyRef), + ) + .subscribe(); } - async load() { - const response = await this.policyApiService.getPolicies(this.organizationId); - this.orgPolicies = response.data != null && response.data.length > 0 ? response.data : []; - this.orgPolicies.forEach((op) => { - this.policiesEnabledMap.set(op.type, op.enabled); - }); - - this.loading = false; - } - - async edit(policy: BasePolicyEditDefinition) { + edit(policy: BasePolicyEditDefinition, organizationId: OrganizationId) { const dialogComponent: PolicyDialogComponent = policy.editDialogComponent ?? PolicyEditDialogComponent; dialogComponent.open(this.dialogService, { data: { policy: policy, - organizationId: this.organizationId, + organizationId: organizationId, }, }); } diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/auto-confirm-policy.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/auto-confirm-policy.component.ts index 7fa4fc2eea7..66074918084 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/auto-confirm-policy.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/auto-confirm-policy.component.ts @@ -1,4 +1,11 @@ -import { Component, OnInit, Signal, TemplateRef, viewChild } from "@angular/core"; +import { + ChangeDetectionStrategy, + Component, + OnInit, + Signal, + TemplateRef, + viewChild, +} from "@angular/core"; import { BehaviorSubject, map, Observable } from "rxjs"; import { AutoConfirmSvg } from "@bitwarden/assets/svg"; @@ -8,8 +15,8 @@ import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { SharedModule } from "../../../../shared"; -import { AutoConfirmPolicyDialogComponent } from "../auto-confirm-edit-policy-dialog.component"; import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; +import { AutoConfirmPolicyDialogComponent } from "../policy-edit-dialogs/auto-confirm-edit-policy-dialog.component"; export class AutoConfirmPolicy extends BasePolicyEditDefinition { name = "autoConfirm"; @@ -26,11 +33,11 @@ export class AutoConfirmPolicy extends BasePolicyEditDefinition { } } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "auto-confirm-policy-edit", templateUrl: "auto-confirm-policy.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class AutoConfirmPolicyEditComponent extends BasePolicyEditComponent implements OnInit { protected readonly autoConfirmSvg = AutoConfirmSvg; diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/autotype-policy.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/autotype-policy.component.ts index ceace60cd99..809ffd39a64 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/autotype-policy.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/autotype-policy.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; @@ -18,10 +18,10 @@ export class DesktopAutotypeDefaultSettingPolicy extends BasePolicyEditDefinitio return configService.getFeatureFlag$(FeatureFlag.WindowsDesktopAutotype); } } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "autotype-policy-edit", templateUrl: "autotype-policy.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class DesktopAutotypeDefaultSettingPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/disable-send.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/disable-send.component.ts index 103420fbf51..d5d4a207598 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/disable-send.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/disable-send.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -12,10 +12,10 @@ export class DisableSendPolicy extends BasePolicyEditDefinition { component = DisableSendPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "disable-send-policy-edit", templateUrl: "disable-send.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class DisableSendPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/index.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/index.ts index 9b46e228af9..042f9771529 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/index.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/index.ts @@ -1,7 +1,10 @@ export { DisableSendPolicy } from "./disable-send.component"; export { DesktopAutotypeDefaultSettingPolicy } from "./autotype-policy.component"; export { MasterPasswordPolicy } from "./master-password.component"; -export { OrganizationDataOwnershipPolicy } from "./organization-data-ownership.component"; +export { + OrganizationDataOwnershipPolicy, + OrganizationDataOwnershipPolicyComponent, +} from "./organization-data-ownership.component"; export { PasswordGeneratorPolicy } from "./password-generator.component"; export { RemoveUnlockWithPinPolicy } from "./remove-unlock-with-pin.component"; export { RequireSsoPolicy } from "./require-sso.component"; diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.html index 63a59208cc0..f979c143a3a 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.html +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.html @@ -32,6 +32,7 @@ formControlName="minLength" id="minLength" [min]="MinPasswordLength" + [max]="MaxPasswordLength" />
    diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.spec.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.spec.ts new file mode 100644 index 00000000000..b22f5687dd2 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.spec.ts @@ -0,0 +1,69 @@ +import { NO_ERRORS_SCHEMA } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { mock } from "jest-mock-extended"; + +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; + +import { MasterPasswordPolicyComponent } from "./master-password.component"; + +describe("MasterPasswordPolicyComponent", () => { + let component: MasterPasswordPolicyComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + providers: [ + { provide: I18nService, useValue: mock() }, + { provide: OrganizationService, useValue: mock() }, + { provide: AccountService, useValue: mock() }, + ], + schemas: [NO_ERRORS_SCHEMA], + }).compileComponents(); + + fixture = TestBed.createComponent(MasterPasswordPolicyComponent); + component = fixture.componentInstance; + }); + + it("should accept minimum password length of 12", () => { + component.data.patchValue({ minLength: 12 }); + + expect(component.data.get("minLength")?.valid).toBe(true); + }); + + it("should accept maximum password length of 128", () => { + component.data.patchValue({ minLength: 128 }); + + expect(component.data.get("minLength")?.valid).toBe(true); + }); + + it("should reject password length below minimum", () => { + component.data.patchValue({ minLength: 11 }); + + expect(component.data.get("minLength")?.hasError("min")).toBe(true); + }); + + it("should reject password length above maximum", () => { + component.data.patchValue({ minLength: 129 }); + + expect(component.data.get("minLength")?.hasError("max")).toBe(true); + }); + + it("should use correct minimum from Utils", () => { + expect(component.MinPasswordLength).toBe(Utils.minimumPasswordLength); + expect(component.MinPasswordLength).toBe(12); + }); + + it("should use correct maximum from Utils", () => { + expect(component.MaxPasswordLength).toBe(Utils.maximumPasswordLength); + expect(component.MaxPasswordLength).toBe(128); + }); + + it("should have password scores from 0 to 4", () => { + const scores = component.passwordScores.filter((s) => s.value !== null).map((s) => s.value); + + expect(scores).toEqual([0, 1, 2, 3, 4]); + }); +}); diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.ts index c1223a2004b..dd2463d718d 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Component, OnInit } from "@angular/core"; +import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core"; import { FormBuilder, FormGroup, Validators } from "@angular/forms"; import { firstValueFrom } from "rxjs"; @@ -26,18 +26,22 @@ export class MasterPasswordPolicy extends BasePolicyEditDefinition { component = MasterPasswordPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "master-password-policy-edit", templateUrl: "master-password.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class MasterPasswordPolicyComponent extends BasePolicyEditComponent implements OnInit { MinPasswordLength = Utils.minimumPasswordLength; + MaxPasswordLength = Utils.maximumPasswordLength; data: FormGroup> = this.formBuilder.group({ minComplexity: [null], - minLength: [this.MinPasswordLength, [Validators.min(Utils.minimumPasswordLength)]], + minLength: [ + this.MinPasswordLength, + [Validators.min(Utils.minimumPasswordLength), Validators.max(this.MaxPasswordLength)], + ], requireUpper: [false], requireLower: [false], requireNumbers: [false], diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.html index 2b6c86b1fdc..bd2237bc2fd 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.html +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.html @@ -1,8 +1,57 @@ - - {{ "personalOwnershipExemption" | i18n }} - +

    + {{ "organizationDataOwnershipDescContent" | i18n }} + + {{ "organizationDataOwnershipContentAnchor" | i18n }}. + +

    {{ "turnOn" | i18n }} + + + + {{ "organizationDataOwnershipWarningTitle" | i18n }} + +
    + {{ "organizationDataOwnershipWarningContentTop" | i18n }} +
    +
      +
    • + {{ "organizationDataOwnershipWarning1" | i18n }} +
    • +
    • + {{ "organizationDataOwnershipWarning2" | i18n }} +
    • +
    • + {{ "organizationDataOwnershipWarning3" | i18n }} +
    • +
    +
    + {{ "organizationDataOwnershipWarningContentBottom" | i18n }} + + {{ "organizationDataOwnershipContentAnchor" | i18n }}. + +
    +
    + + + + + + +
    +
    diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.ts index d832dff158a..e4a07b7440d 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.ts @@ -1,31 +1,102 @@ -import { Component } from "@angular/core"; -import { map, Observable } from "rxjs"; +import { ChangeDetectionStrategy, Component, OnInit, TemplateRef, ViewChild } from "@angular/core"; +import { lastValueFrom, map, Observable } from "rxjs"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { PolicyRequest } from "@bitwarden/common/admin-console/models/request/policy.request"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { OrgKey } from "@bitwarden/common/types/key"; +import { CenterPositionStrategy, DialogService } from "@bitwarden/components"; +import { EncString } from "@bitwarden/sdk-internal"; import { SharedModule } from "../../../../shared"; import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; +export interface VNextPolicyRequest { + policy: PolicyRequest; + metadata: { + defaultUserCollectionName: string; + }; +} + export class OrganizationDataOwnershipPolicy extends BasePolicyEditDefinition { name = "organizationDataOwnership"; - description = "personalOwnershipPolicyDesc"; + description = "organizationDataOwnershipDesc"; type = PolicyType.OrganizationDataOwnership; component = OrganizationDataOwnershipPolicyComponent; + showDescription = false; - display$(organization: Organization, configService: ConfigService): Observable { + override display$(organization: Organization, configService: ConfigService): Observable { return configService - .getFeatureFlag$(FeatureFlag.CreateDefaultLocation) + .getFeatureFlag$(FeatureFlag.MigrateMyVaultToMyItems) .pipe(map((enabled) => !enabled)); } } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "organization-data-ownership-policy-edit", templateUrl: "organization-data-ownership.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class OrganizationDataOwnershipPolicyComponent extends BasePolicyEditComponent {} +export class OrganizationDataOwnershipPolicyComponent + extends BasePolicyEditComponent + implements OnInit +{ + constructor( + private dialogService: DialogService, + private i18nService: I18nService, + private encryptService: EncryptService, + ) { + super(); + } + + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals + @ViewChild("dialog", { static: true }) warningContent!: TemplateRef; + + override async confirm(): Promise { + if (this.policyResponse?.enabled && !this.enabled.value) { + const dialogRef = this.dialogService.open(this.warningContent, { + positionStrategy: new CenterPositionStrategy(), + }); + const result = await lastValueFrom(dialogRef.closed); + return Boolean(result); + } + return true; + } + + async buildVNextRequest(orgKey: OrgKey): Promise { + if (!this.policy) { + throw new Error("Policy was not found"); + } + + const defaultUserCollectionName = await this.getEncryptedDefaultUserCollectionName(orgKey); + + const request: VNextPolicyRequest = { + policy: { + enabled: this.enabled.value ?? false, + data: this.buildRequestData(), + }, + metadata: { + defaultUserCollectionName, + }, + }; + + return request; + } + + private async getEncryptedDefaultUserCollectionName(orgKey: OrgKey): Promise { + const defaultCollectionName = this.i18nService.t("myItems"); + const encrypted = await this.encryptService.encryptString(defaultCollectionName, orgKey); + + if (!encrypted.encryptedString) { + throw new Error("Encryption error"); + } + + return encrypted.encryptedString; + } +} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/password-generator.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/password-generator.component.ts index e3a67362cc9..b338f3ec508 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/password-generator.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/password-generator.component.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { UntypedFormBuilder, Validators } from "@angular/forms"; import { BehaviorSubject, map } from "rxjs"; @@ -19,11 +19,11 @@ export class PasswordGeneratorPolicy extends BasePolicyEditDefinition { component = PasswordGeneratorPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "password-generator-policy-edit", templateUrl: "password-generator.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class PasswordGeneratorPolicyComponent extends BasePolicyEditComponent { // these properties forward the application default settings to the UI diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.ts index ac768d47d6e..443a1792956 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -12,10 +12,10 @@ export class RemoveUnlockWithPinPolicy extends BasePolicyEditDefinition { component = RemoveUnlockWithPinPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "remove-unlock-with-pin-policy-edit", templateUrl: "remove-unlock-with-pin.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class RemoveUnlockWithPinPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/require-sso.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/require-sso.component.ts index 904c29ca70d..e7cded03f11 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/require-sso.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/require-sso.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { of } from "rxjs"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -19,10 +19,10 @@ export class RequireSsoPolicy extends BasePolicyEditDefinition { } } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "require-sso-policy-edit", templateUrl: "require-sso.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class RequireSsoPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/reset-password.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/reset-password.component.ts index bfe149048e3..4b194075aef 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/reset-password.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/reset-password.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from "@angular/core"; +import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core"; import { FormBuilder } from "@angular/forms"; import { firstValueFrom, of } from "rxjs"; @@ -26,11 +26,11 @@ export class ResetPasswordPolicy extends BasePolicyEditDefinition { } } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "reset-password-policy-edit", templateUrl: "reset-password.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class ResetPasswordPolicyComponent extends BasePolicyEditComponent implements OnInit { data = this.formBuilder.group({ diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts index 554542f8a84..279b40ef5ee 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -12,11 +12,11 @@ export class RestrictedItemTypesPolicy extends BasePolicyEditDefinition { component = RestrictedItemTypesPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "restricted-item-types-policy-edit", templateUrl: "restricted-item-types.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class RestrictedItemTypesPolicyComponent extends BasePolicyEditComponent { constructor() { diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/send-options.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/send-options.component.ts index b8a59e8f8ef..06fd672fa4e 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/send-options.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/send-options.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { UntypedFormBuilder } from "@angular/forms"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -13,11 +13,11 @@ export class SendOptionsPolicy extends BasePolicyEditDefinition { component = SendOptionsPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "send-options-policy-edit", templateUrl: "send-options.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class SendOptionsPolicyComponent extends BasePolicyEditComponent { data = this.formBuilder.group({ diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/single-org.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/single-org.component.ts index 655c5f20610..dfdb4d4ddaf 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/single-org.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/single-org.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from "@angular/core"; +import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -12,11 +12,11 @@ export class SingleOrgPolicy extends BasePolicyEditDefinition { component = SingleOrgPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "single-org-policy-edit", templateUrl: "single-org.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class SingleOrgPolicyComponent extends BasePolicyEditComponent implements OnInit { async ngOnInit() { diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/two-factor-authentication.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/two-factor-authentication.component.ts index 62f3d1f3466..8700714f6f4 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/two-factor-authentication.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/two-factor-authentication.component.ts @@ -1,4 +1,4 @@ -import { Component } from "@angular/core"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -12,10 +12,10 @@ export class TwoFactorAuthenticationPolicy extends BasePolicyEditDefinition { component = TwoFactorAuthenticationPolicyComponent; } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "two-factor-authentication-policy-edit", templateUrl: "two-factor-authentication.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class TwoFactorAuthenticationPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/uri-match-default.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/uri-match-default.component.ts index 5c0b667bea2..d88b1d0769a 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/uri-match-default.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/uri-match-default.component.ts @@ -19,6 +19,7 @@ export class UriMatchDefaultPolicy extends BasePolicyEditDefinition { component = UriMatchDefaultPolicyComponent; } @Component({ + selector: "uri-match-default-policy-edit", changeDetection: ChangeDetectionStrategy.OnPush, templateUrl: "uri-match-default.component.html", imports: [SharedModule], diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.html index bd2237bc2fd..e6c93b323c2 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.html +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.html @@ -1,57 +1,52 @@ -

    - {{ "organizationDataOwnershipDescContent" | i18n }} - - {{ "organizationDataOwnershipContentAnchor" | i18n }}. - -

    + - - - {{ "turnOn" | i18n }} - + +

    + {{ "centralizeDataOwnershipDesc" | i18n }} + + {{ "centralizeDataOwnershipContentAnchor" | i18n }} + + +

    - - - {{ "organizationDataOwnershipWarningTitle" | i18n }} - -
    - {{ "organizationDataOwnershipWarningContentTop" | i18n }} -
    -
      -
    • - {{ "organizationDataOwnershipWarning1" | i18n }} -
    • -
    • - {{ "organizationDataOwnershipWarning2" | i18n }} -
    • -
    • - {{ "organizationDataOwnershipWarning3" | i18n }} -
    • -
    -
    - {{ "organizationDataOwnershipWarningContentBottom" | i18n }} - - {{ "organizationDataOwnershipContentAnchor" | i18n }}. - -
    -
    - - - - - - -
    +
    + {{ "benefits" | i18n }}: +
      +
    • + {{ "centralizeDataOwnershipBenefit1" | i18n }} +
    • +
    • + {{ "centralizeDataOwnershipBenefit2" | i18n }} +
    • +
    • + {{ "centralizeDataOwnershipBenefit3" | i18n }} +
    • +
    +
    + + + + {{ "turnOn" | i18n }} + +
    + + +
    + + {{ "centralizeDataOwnershipWarningDesc" | i18n }} + + + {{ "centralizeDataOwnershipWarningLink" | i18n }} + + +
    diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts index a0d425d5886..e1b2f14d457 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts @@ -1,5 +1,14 @@ -import { Component, OnInit, TemplateRef, ViewChild } from "@angular/core"; -import { lastValueFrom, Observable } from "rxjs"; +import { + ChangeDetectionStrategy, + Component, + OnInit, + signal, + Signal, + TemplateRef, + viewChild, + WritableSignal, +} from "@angular/core"; +import { Observable } from "rxjs"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; @@ -9,13 +18,13 @@ import { EncryptService } from "@bitwarden/common/key-management/crypto/abstract import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { OrgKey } from "@bitwarden/common/types/key"; -import { CenterPositionStrategy, DialogService } from "@bitwarden/components"; import { EncString } from "@bitwarden/sdk-internal"; import { SharedModule } from "../../../../shared"; import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; +import { OrganizationDataOwnershipPolicyDialogComponent } from "../policy-edit-dialogs"; -interface VNextPolicyRequest { +export interface VNextPolicyRequest { policy: PolicyRequest; metadata: { defaultUserCollectionName: string; @@ -23,49 +32,40 @@ interface VNextPolicyRequest { } export class vNextOrganizationDataOwnershipPolicy extends BasePolicyEditDefinition { - name = "organizationDataOwnership"; - description = "organizationDataOwnershipDesc"; + name = "centralizeDataOwnership"; + description = "centralizeDataOwnershipDesc"; type = PolicyType.OrganizationDataOwnership; component = vNextOrganizationDataOwnershipPolicyComponent; showDescription = false; + editDialogComponent = OrganizationDataOwnershipPolicyDialogComponent; + override display$(organization: Organization, configService: ConfigService): Observable { - return configService.getFeatureFlag$(FeatureFlag.CreateDefaultLocation); + return configService.getFeatureFlag$(FeatureFlag.MigrateMyVaultToMyItems); } } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ + selector: "vnext-organization-data-ownership-policy-edit", templateUrl: "vnext-organization-data-ownership.component.html", imports: [SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class vNextOrganizationDataOwnershipPolicyComponent extends BasePolicyEditComponent implements OnInit { constructor( - private dialogService: DialogService, private i18nService: I18nService, private encryptService: EncryptService, ) { super(); } + private readonly policyForm: Signal | undefined> = viewChild("step0"); + private readonly warningContent: Signal | undefined> = viewChild("step1"); + protected readonly step: WritableSignal = signal(0); - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @ViewChild("dialog", { static: true }) warningContent!: TemplateRef; - - override async confirm(): Promise { - if (this.policyResponse?.enabled && !this.enabled.value) { - const dialogRef = this.dialogService.open(this.warningContent, { - positionStrategy: new CenterPositionStrategy(), - }); - const result = await lastValueFrom(dialogRef.closed); - return Boolean(result); - } - return true; - } + protected steps = [this.policyForm, this.warningContent]; async buildVNextRequest(orgKey: OrgKey): Promise { if (!this.policy) { @@ -97,4 +97,8 @@ export class vNextOrganizationDataOwnershipPolicyComponent return encrypted.encryptedString; } + + setStep(step: number) { + this.step.set(step); + } } diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialog.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialog.component.ts index 98b6d1c6bee..f1b3d04cc7a 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialog.component.ts @@ -14,10 +14,9 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { OrganizationId } from "@bitwarden/common/types/guid"; +import { OrgKey } from "@bitwarden/common/types/key"; import { DIALOG_DATA, DialogConfig, @@ -30,7 +29,7 @@ import { KeyService } from "@bitwarden/key-management"; import { SharedModule } from "../../../shared"; import { BasePolicyEditDefinition, BasePolicyEditComponent } from "./base-policy-edit.component"; -import { vNextOrganizationDataOwnershipPolicyComponent } from "./policy-edit-definitions/vnext-organization-data-ownership.component"; +import { VNextPolicyRequest } from "./policy-edit-definitions/organization-data-ownership.component"; export type PolicyEditDialogData = { /** @@ -75,14 +74,24 @@ export class PolicyEditDialogComponent implements AfterViewInit { private formBuilder: FormBuilder, protected dialogRef: DialogRef, protected toastService: ToastService, - private configService: ConfigService, - private keyService: KeyService, + protected keyService: KeyService, ) {} get policy(): BasePolicyEditDefinition { return this.data.policy; } + /** + * Type guard to check if the policy component has the buildVNextRequest method. + */ + private hasVNextRequest( + component: BasePolicyEditComponent, + ): component is BasePolicyEditComponent & { + buildVNextRequest: (orgKey: OrgKey) => Promise; + } { + return "buildVNextRequest" in component && typeof component.buildVNextRequest === "function"; + } + /** * Instantiates the child policy component and inserts it into the view. */ @@ -132,10 +141,7 @@ export class PolicyEditDialogComponent implements AfterViewInit { } try { - if ( - this.policyComponent instanceof vNextOrganizationDataOwnershipPolicyComponent && - (await this.isVNextEnabled()) - ) { + if (this.hasVNextRequest(this.policyComponent)) { await this.handleVNextSubmission(this.policyComponent); } else { await this.handleStandardSubmission(); @@ -154,14 +160,6 @@ export class PolicyEditDialogComponent implements AfterViewInit { } }; - private async isVNextEnabled(): Promise { - const isVNextFeatureEnabled = await firstValueFrom( - this.configService.getFeatureFlag$(FeatureFlag.CreateDefaultLocation), - ); - - return isVNextFeatureEnabled; - } - private async handleStandardSubmission(): Promise { if (!this.policyComponent) { throw new Error("PolicyComponent not initialized."); @@ -172,7 +170,9 @@ export class PolicyEditDialogComponent implements AfterViewInit { } private async handleVNextSubmission( - policyComponent: vNextOrganizationDataOwnershipPolicyComponent, + policyComponent: BasePolicyEditComponent & { + buildVNextRequest: (orgKey: OrgKey) => Promise; + }, ): Promise { const orgKey = await firstValueFrom( this.accountService.activeAccount$.pipe( @@ -187,12 +187,12 @@ export class PolicyEditDialogComponent implements AfterViewInit { throw new Error("No encryption key for this organization."); } - const vNextRequest = await policyComponent.buildVNextRequest(orgKey); + const request = await policyComponent.buildVNextRequest(orgKey); await this.policyApiService.putPolicyVNext( this.data.organizationId, this.data.policy.type, - vNextRequest, + request, ); } static open = (dialogService: DialogService, config: DialogConfig) => { diff --git a/apps/web/src/app/admin-console/organizations/policies/auto-confirm-edit-policy-dialog.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/auto-confirm-edit-policy-dialog.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/policies/auto-confirm-edit-policy-dialog.component.html rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/auto-confirm-edit-policy-dialog.component.html diff --git a/apps/web/src/app/admin-console/organizations/policies/auto-confirm-edit-policy-dialog.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/auto-confirm-edit-policy-dialog.component.ts similarity index 92% rename from apps/web/src/app/admin-console/organizations/policies/auto-confirm-edit-policy-dialog.component.ts rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/auto-confirm-edit-policy-dialog.component.ts index 99d484f04f2..fbdeffc71bb 100644 --- a/apps/web/src/app/admin-console/organizations/policies/auto-confirm-edit-policy-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/auto-confirm-edit-policy-dialog.component.ts @@ -22,7 +22,7 @@ import { tap, } from "rxjs"; -import { AutomaticUserConfirmationService } from "@bitwarden/admin-console/common"; +import { AutomaticUserConfirmationService } from "@bitwarden/auto-confirm"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; @@ -30,7 +30,6 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { PolicyRequest } from "@bitwarden/common/admin-console/models/request/policy.request"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { getById } from "@bitwarden/common/platform/misc"; import { @@ -42,20 +41,15 @@ import { } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; -import { SharedModule } from "../../../shared"; - -import { AutoConfirmPolicyEditComponent } from "./policy-edit-definitions/auto-confirm-policy.component"; +import { SharedModule } from "../../../../shared"; +import { AutoConfirmPolicyEditComponent } from "../policy-edit-definitions/auto-confirm-policy.component"; import { PolicyEditDialogComponent, PolicyEditDialogData, PolicyEditDialogResult, -} from "./policy-edit-dialog.component"; +} from "../policy-edit-dialog.component"; -export type MultiStepSubmit = { - sideEffect: () => Promise; - footerContent: Signal | undefined>; - titleContent: Signal | undefined>; -}; +import { MultiStepSubmit } from "./models"; export type AutoConfirmPolicyDialogData = PolicyEditDialogData & { firstTimeDialog?: boolean; @@ -115,7 +109,6 @@ export class AutoConfirmPolicyDialogComponent formBuilder: FormBuilder, dialogRef: DialogRef, toastService: ToastService, - configService: ConfigService, keyService: KeyService, private organizationService: OrganizationService, private policyService: PolicyService, @@ -131,7 +124,6 @@ export class AutoConfirmPolicyDialogComponent formBuilder, dialogRef, toastService, - configService, keyService, ); @@ -205,6 +197,7 @@ export class AutoConfirmPolicyDialogComponent } const autoConfirmRequest = await this.policyComponent.buildRequest(); + await this.policyApiService.putPolicy( this.data.organizationId, this.data.policy.type, @@ -238,7 +231,7 @@ export class AutoConfirmPolicyDialogComponent data: null, }; - await this.policyApiService.putPolicy( + await this.policyApiService.putPolicyVNext( this.data.organizationId, PolicyType.SingleOrg, singleOrgRequest, @@ -263,7 +256,10 @@ export class AutoConfirmPolicyDialogComponent try { const multiStepSubmit = await firstValueFrom(this.multiStepSubmit); - await multiStepSubmit[this.currentStep()].sideEffect(); + const sideEffect = multiStepSubmit[this.currentStep()].sideEffect; + if (sideEffect) { + await sideEffect(); + } if (this.currentStep() === multiStepSubmit.length - 1) { this.dialogRef.close("saved"); diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/index.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/index.ts new file mode 100644 index 00000000000..307d0da04b0 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/index.ts @@ -0,0 +1,3 @@ +export * from "./auto-confirm-edit-policy-dialog.component"; +export * from "./organization-data-ownership-edit-policy-dialog.component"; +export * from "./models"; diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/models.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/models.ts new file mode 100644 index 00000000000..86120623701 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/models.ts @@ -0,0 +1,7 @@ +import { Signal, TemplateRef } from "@angular/core"; + +export type MultiStepSubmit = { + sideEffect?: () => Promise; + footerContent: Signal | undefined>; + titleContent: Signal | undefined>; +}; diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/organization-data-ownership-edit-policy-dialog.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/organization-data-ownership-edit-policy-dialog.component.html new file mode 100644 index 00000000000..73691e94199 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/organization-data-ownership-edit-policy-dialog.component.html @@ -0,0 +1,72 @@ +
    + + + @let title = multiStepSubmit()[currentStep()]?.titleContent(); + @if (title) { + + } + + + + @if (loading) { +
    + + {{ "loading" | i18n }} +
    + } +
    + @if (policy.showDescription) { +

    {{ policy.description | i18n }}

    + } +
    + +
    + + @let footer = multiStepSubmit()[currentStep()]?.footerContent(); + @if (footer) { + + } + +
    +
    + + + {{ policy.name | i18n }} + + + + {{ "centralizeDataOwnershipWarningTitle" | i18n }} + + + + + + + + + + + + diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/organization-data-ownership-edit-policy-dialog.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/organization-data-ownership-edit-policy-dialog.component.ts new file mode 100644 index 00000000000..7869eab0063 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialogs/organization-data-ownership-edit-policy-dialog.component.ts @@ -0,0 +1,224 @@ +import { + AfterViewInit, + ChangeDetectorRef, + Component, + Inject, + signal, + TemplateRef, + viewChild, + WritableSignal, +} from "@angular/core"; +import { FormBuilder } from "@angular/forms"; +import { + catchError, + combineLatest, + defer, + firstValueFrom, + from, + map, + Observable, + of, + startWith, + switchMap, +} from "rxjs"; + +import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { assertNonNullish } from "@bitwarden/common/auth/utils"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { OrganizationId } from "@bitwarden/common/types/guid"; +import { + DIALOG_DATA, + DialogConfig, + DialogRef, + DialogService, + ToastService, +} from "@bitwarden/components"; +import { KeyService } from "@bitwarden/key-management"; + +import { SharedModule } from "../../../../shared"; +import { vNextOrganizationDataOwnershipPolicyComponent } from "../policy-edit-definitions"; +import { + PolicyEditDialogComponent, + PolicyEditDialogData, + PolicyEditDialogResult, +} from "../policy-edit-dialog.component"; + +import { MultiStepSubmit } from "./models"; + +/** + * Custom policy dialog component for Centralize Organization Data + * Ownership policy. Satisfies the PolicyDialogComponent interface + * structurally via its static open() function. + */ +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection +@Component({ + templateUrl: "organization-data-ownership-edit-policy-dialog.component.html", + imports: [SharedModule], +}) +export class OrganizationDataOwnershipPolicyDialogComponent + extends PolicyEditDialogComponent + implements AfterViewInit +{ + policyType = PolicyType; + + protected centralizeDataOwnershipEnabled$: Observable = defer(() => + from( + this.policyApiService.getPolicy( + this.data.organizationId, + PolicyType.OrganizationDataOwnership, + ), + ).pipe( + map((policy) => policy.enabled), + catchError(() => of(false)), + ), + ); + + protected readonly currentStep: WritableSignal = signal(0); + protected readonly multiStepSubmit: WritableSignal = signal([]); + + private readonly policyForm = viewChild.required>("step0"); + private readonly warningContent = viewChild.required>("step1"); + private readonly policyFormTitle = viewChild.required>("step0Title"); + private readonly warningTitle = viewChild.required>("step1Title"); + + override policyComponent: vNextOrganizationDataOwnershipPolicyComponent | undefined; + + constructor( + @Inject(DIALOG_DATA) protected data: PolicyEditDialogData, + accountService: AccountService, + policyApiService: PolicyApiServiceAbstraction, + i18nService: I18nService, + cdr: ChangeDetectorRef, + formBuilder: FormBuilder, + dialogRef: DialogRef, + toastService: ToastService, + protected keyService: KeyService, + ) { + super( + data, + accountService, + policyApiService, + i18nService, + cdr, + formBuilder, + dialogRef, + toastService, + keyService, + ); + } + + async ngAfterViewInit() { + await super.ngAfterViewInit(); + + if (this.policyComponent) { + this.saveDisabled$ = combineLatest([ + this.centralizeDataOwnershipEnabled$, + this.policyComponent.enabled.valueChanges.pipe( + startWith(this.policyComponent.enabled.value), + ), + ]).pipe(map(([policyEnabled, value]) => !policyEnabled && !value)); + } + + this.multiStepSubmit.set(this.buildMultiStepSubmit()); + } + + private buildMultiStepSubmit(): MultiStepSubmit[] { + if (this.policyComponent?.policyResponse?.enabled) { + return [ + { + sideEffect: () => this.handleSubmit(), + footerContent: this.policyForm, + titleContent: this.policyFormTitle, + }, + ]; + } + + return [ + { + footerContent: this.policyForm, + titleContent: this.policyFormTitle, + }, + { + sideEffect: () => this.handleSubmit(), + footerContent: this.warningContent, + titleContent: this.warningTitle, + }, + ]; + } + + private async handleSubmit() { + if (!this.policyComponent) { + throw new Error("PolicyComponent not initialized."); + } + + const orgKey = await firstValueFrom( + this.accountService.activeAccount$.pipe( + getUserId, + switchMap((userId) => this.keyService.orgKeys$(userId)), + ), + ); + + assertNonNullish(orgKey, "Org key not provided"); + + const request = await this.policyComponent.buildVNextRequest( + orgKey[this.data.organizationId as OrganizationId], + ); + + await this.policyApiService.putPolicyVNext( + this.data.organizationId, + this.data.policy.type, + request, + ); + + this.toastService.showToast({ + variant: "success", + message: this.i18nService.t("editedPolicyId", this.i18nService.t(this.data.policy.name)), + }); + + if (!this.policyComponent.enabled.value) { + this.dialogRef.close("saved"); + } + } + + submit = async () => { + if (!this.policyComponent) { + throw new Error("PolicyComponent not initialized."); + } + + if ((await this.policyComponent.confirm()) == false) { + this.dialogRef.close(); + return; + } + + try { + const sideEffect = this.multiStepSubmit()[this.currentStep()].sideEffect; + if (sideEffect) { + await sideEffect(); + } + + if (this.currentStep() === this.multiStepSubmit().length - 1) { + this.dialogRef.close("saved"); + return; + } + + this.currentStep.update((value) => value + 1); + this.policyComponent.setStep(this.currentStep()); + } catch (error: any) { + this.toastService.showToast({ + variant: "error", + message: error.message, + }); + } + }; + + static open = (dialogService: DialogService, config: DialogConfig) => { + return dialogService.open( + OrganizationDataOwnershipPolicyDialogComponent, + config, + ); + }; +} diff --git a/apps/web/src/app/admin-console/organizations/settings/organization-settings-routing.module.ts b/apps/web/src/app/admin-console/organizations/settings/organization-settings-routing.module.ts index a644086628c..2fb3703ee6b 100644 --- a/apps/web/src/app/admin-console/organizations/settings/organization-settings-routing.module.ts +++ b/apps/web/src/app/admin-console/organizations/settings/organization-settings-routing.module.ts @@ -57,7 +57,7 @@ const routes: Routes = [ ), canActivate: [organizationPermissionsGuard((org) => org.canAccessImport)], data: { - titleId: "importData", + titleId: "importNoun", }, }, { @@ -68,7 +68,7 @@ const routes: Routes = [ ), canActivate: [organizationPermissionsGuard((org) => org.canAccessExport)], data: { - titleId: "exportVault", + titleId: "exportNoun", }, }, ], diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.models.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.models.ts index 45691ae98d6..9755beefbf1 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.models.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector.models.ts @@ -1,13 +1,11 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { - CollectionAccessSelectionView, - OrganizationUserUserDetailsResponse, -} from "@bitwarden/admin-console/common"; +import { OrganizationUserUserDetailsResponse } from "@bitwarden/admin-console/common"; import { OrganizationUserStatusType, OrganizationUserType, } from "@bitwarden/common/admin-console/enums"; +import { CollectionAccessSelectionView } from "@bitwarden/common/admin-console/models/collections"; import { SelectItemView } from "@bitwarden/components"; import { GroupView } from "../../../core"; diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/storybook-utils.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/storybook-utils.ts index fb8bdef1d8c..9c39ca572fb 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/storybook-utils.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/storybook-utils.ts @@ -1,4 +1,4 @@ -import { action } from "@storybook/addon-actions"; +import { action } from "storybook/actions"; import { AccessItemType, AccessItemView } from "./access-selector.models"; diff --git a/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts b/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts index 7b189270e1b..4f40ea701d2 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts @@ -18,19 +18,21 @@ import { import { first } from "rxjs/operators"; import { - CollectionAccessSelectionView, CollectionAdminService, - CollectionAdminView, OrganizationUserApiService, OrganizationUserUserMiniResponse, - CollectionResponse, - CollectionView, CollectionService, } from "@bitwarden/admin-console/common"; import { getOrganizationById, OrganizationService, } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { + CollectionAccessSelectionView, + CollectionAdminView, + CollectionView, + CollectionResponse, +} from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; diff --git a/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.ts b/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.ts index 7132428c375..32a5736748c 100644 --- a/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.ts +++ b/apps/web/src/app/admin-console/organizations/shared/validators/free-org-collection-limit.validator.ts @@ -1,7 +1,7 @@ import { AbstractControl, AsyncValidatorFn, FormControl, ValidationErrors } from "@angular/forms"; import { combineLatest, map, Observable, of } from "rxjs"; -import { Collection } from "@bitwarden/admin-console/common"; +import { Collection } from "@bitwarden/common/admin-console/models/collections"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { getById } from "@bitwarden/common/platform/misc"; diff --git a/apps/web/src/app/auth/core/services/login/web-login-component.service.ts b/apps/web/src/app/auth/core/services/login/web-login-component.service.ts index 5bea0908b0a..8c1bc4bd080 100644 --- a/apps/web/src/app/auth/core/services/login/web-login-component.service.ts +++ b/apps/web/src/app/auth/core/services/login/web-login-component.service.ts @@ -61,8 +61,11 @@ export class WebLoginComponentService email: string, state: string, codeChallenge: string, + orgSsoIdentifier?: string, ): Promise { - await this.router.navigate(["/sso"]); + await this.router.navigate(["/sso"], { + queryParams: { identifier: orgSsoIdentifier }, + }); return; } diff --git a/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts b/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts index 647c9ae83d9..b09b5f0bc9a 100644 --- a/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts +++ b/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.spec.ts @@ -3,6 +3,7 @@ import { BehaviorSubject, of } from "rxjs"; import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; import { + InitializeJitPasswordCredentials, SetInitialPasswordCredentials, SetInitialPasswordService, SetInitialPasswordUserType, @@ -16,14 +17,17 @@ import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-conso import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; import { SetPasswordRequest } from "@bitwarden/common/auth/models/request/set-password.request"; import { OrganizationInviteService } from "@bitwarden/common/auth/services/organization-invite/organization-invite.service"; +import { AccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/account-cryptographic-state.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; +import { MasterPasswordSalt } from "@bitwarden/common/key-management/master-password/types/master-password.types"; import { KeysRequest } from "@bitwarden/common/models/request/keys.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { RegisterSdkService } from "@bitwarden/common/platform/abstractions/sdk/register-sdk.service"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { CsprngArray } from "@bitwarden/common/types/csprng"; -import { UserId } from "@bitwarden/common/types/guid"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { MasterKey, UserKey } from "@bitwarden/common/types/key"; import { DEFAULT_KDF_CONFIG, KdfConfigService, KeyService } from "@bitwarden/key-management"; import { RouterService } from "@bitwarden/web-vault/app/core"; @@ -45,6 +49,8 @@ describe("WebSetInitialPasswordService", () => { let userDecryptionOptionsService: MockProxy; let organizationInviteService: MockProxy; let routerService: MockProxy; + let accountCryptographicStateService: MockProxy; + let registerSdkService: MockProxy; beforeEach(() => { apiService = mock(); @@ -59,6 +65,8 @@ describe("WebSetInitialPasswordService", () => { userDecryptionOptionsService = mock(); organizationInviteService = mock(); routerService = mock(); + accountCryptographicStateService = mock(); + registerSdkService = mock(); sut = new WebSetInitialPasswordService( apiService, @@ -73,6 +81,8 @@ describe("WebSetInitialPasswordService", () => { userDecryptionOptionsService, organizationInviteService, routerService, + accountCryptographicStateService, + registerSdkService, ); }); @@ -204,4 +214,36 @@ describe("WebSetInitialPasswordService", () => { }); }); }); + + describe("initializePasswordJitPasswordUserV2Encryption(...)", () => { + it("should call routerService.getAndClearLoginRedirectUrl() and organizationInviteService.clearOrganizationInvitation()", async () => { + // Arrange + const credentials: InitializeJitPasswordCredentials = { + newPasswordHint: "newPasswordHint", + orgSsoIdentifier: "orgSsoIdentifier", + orgId: "orgId" as OrganizationId, + resetPasswordAutoEnroll: false, + newPassword: "newPassword123!", + salt: "user@example.com" as MasterPasswordSalt, + }; + const userId = "userId" as UserId; + + const superSpy = jest + .spyOn( + Object.getPrototypeOf(Object.getPrototypeOf(sut)), + "initializePasswordJitPasswordUserV2Encryption", + ) + .mockResolvedValue(undefined); + + // Act + await sut.initializePasswordJitPasswordUserV2Encryption(credentials, userId); + + // Assert + expect(superSpy).toHaveBeenCalledWith(credentials, userId); + expect(routerService.getAndClearLoginRedirectUrl).toHaveBeenCalledTimes(1); + expect(organizationInviteService.clearOrganizationInvitation).toHaveBeenCalledTimes(1); + + superSpy.mockRestore(); + }); + }); }); diff --git a/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.ts b/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.ts index 19ddbf5e260..0b8dba6c40e 100644 --- a/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.ts +++ b/apps/web/src/app/auth/core/services/password-management/set-initial-password/web-set-initial-password.service.ts @@ -1,6 +1,7 @@ import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; import { DefaultSetInitialPasswordService } from "@bitwarden/angular/auth/password-management/set-initial-password/default-set-initial-password.service.implementation"; import { + InitializeJitPasswordCredentials, SetInitialPasswordCredentials, SetInitialPasswordService, SetInitialPasswordUserType, @@ -10,9 +11,11 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction"; import { MasterPasswordApiService } from "@bitwarden/common/auth/abstractions/master-password-api.service.abstraction"; import { OrganizationInviteService } from "@bitwarden/common/auth/services/organization-invite/organization-invite.service"; +import { AccountCryptographicStateService } from "@bitwarden/common/key-management/account-cryptography/account-cryptographic-state.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { RegisterSdkService } from "@bitwarden/common/platform/abstractions/sdk/register-sdk.service"; import { UserId } from "@bitwarden/common/types/guid"; import { KdfConfigService, KeyService } from "@bitwarden/key-management"; import { RouterService } from "@bitwarden/web-vault/app/core"; @@ -34,6 +37,8 @@ export class WebSetInitialPasswordService protected userDecryptionOptionsService: InternalUserDecryptionOptionsServiceAbstraction, private organizationInviteService: OrganizationInviteService, private routerService: RouterService, + protected accountCryptographicStateService: AccountCryptographicStateService, + protected registerSdkService: RegisterSdkService, ) { super( apiService, @@ -46,6 +51,8 @@ export class WebSetInitialPasswordService organizationApiService, organizationUserApiService, userDecryptionOptionsService, + accountCryptographicStateService, + registerSdkService, ); } @@ -80,4 +87,15 @@ export class WebSetInitialPasswordService await this.routerService.getAndClearLoginRedirectUrl(); await this.organizationInviteService.clearOrganizationInvitation(); } + + override async initializePasswordJitPasswordUserV2Encryption( + credentials: InitializeJitPasswordCredentials, + userId: UserId, + ): Promise { + await super.initializePasswordJitPasswordUserV2Encryption(credentials, userId); + + // TODO: Investigate refactoring the following logic in https://bitwarden.atlassian.net/browse/PM-22615 + await this.routerService.getAndClearLoginRedirectUrl(); + await this.organizationInviteService.clearOrganizationInvitation(); + } } diff --git a/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-admin.service.ts b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-admin.service.ts index 7765d01f75c..3fc57e1a22c 100644 --- a/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-admin.service.ts +++ b/apps/web/src/app/auth/core/services/webauthn-login/webauthn-login-admin.service.ts @@ -39,9 +39,7 @@ import { WebAuthnLoginAdminApiService } from "./webauthn-login-admin-api.service /** * Service for managing WebAuthnLogin credentials. */ -export class WebauthnLoginAdminService - implements UserKeyRotationDataProvider -{ +export class WebauthnLoginAdminService implements UserKeyRotationDataProvider { static readonly MaxCredentialCount = 5; private navigatorCredentials: CredentialsContainer; diff --git a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts index b91bc932e83..80b1b27116b 100644 --- a/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts +++ b/apps/web/src/app/auth/emergency-access/services/emergency-access.service.ts @@ -45,13 +45,10 @@ import { EmergencyAccessGranteeDetailsResponse } from "../response/emergency-acc import { EmergencyAccessApiService } from "./emergency-access-api.service"; @Injectable() -export class EmergencyAccessService - implements - UserKeyRotationKeyRecoveryProvider< - EmergencyAccessWithIdRequest, - GranteeEmergencyAccessWithPublicKey - > -{ +export class EmergencyAccessService implements UserKeyRotationKeyRecoveryProvider< + EmergencyAccessWithIdRequest, + GranteeEmergencyAccessWithPublicKey +> { constructor( private emergencyAccessApiService: EmergencyAccessApiService, private apiService: ApiService, diff --git a/apps/web/src/app/auth/recover-delete.component.ts b/apps/web/src/app/auth/recover-delete.component.ts index 00b14f9a402..921d96270bf 100644 --- a/apps/web/src/app/auth/recover-delete.component.ts +++ b/apps/web/src/app/auth/recover-delete.component.ts @@ -1,21 +1,37 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Component } from "@angular/core"; -import { FormControl, FormGroup, Validators } from "@angular/forms"; -import { Router } from "@angular/router"; +import { FormControl, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms"; +import { Router, RouterLink } from "@angular/router"; +import { JslibModule } from "@bitwarden/angular/jslib.module"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { DeleteRecoverRequest } from "@bitwarden/common/models/request/delete-recover.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { ToastService } from "@bitwarden/components"; +import { + AsyncActionsModule, + ButtonModule, + FormFieldModule, + ToastService, + TypographyModule, +} from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "app-recover-delete", templateUrl: "recover-delete.component.html", - standalone: false, + imports: [ + ReactiveFormsModule, + RouterLink, + JslibModule, + AsyncActionsModule, + ButtonModule, + FormFieldModule, + I18nPipe, + TypographyModule, + ], }) export class RecoverDeleteComponent { protected recoverDeleteForm = new FormGroup({ @@ -29,7 +45,6 @@ export class RecoverDeleteComponent { constructor( private router: Router, private apiService: ApiService, - private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, private toastService: ToastService, ) {} diff --git a/apps/web/src/app/auth/recover-two-factor.component.spec.ts b/apps/web/src/app/auth/recover-two-factor.component.spec.ts index c3792cfd3f3..b7a68ff9f07 100644 --- a/apps/web/src/app/auth/recover-two-factor.component.spec.ts +++ b/apps/web/src/app/auth/recover-two-factor.component.spec.ts @@ -1,5 +1,5 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { Router } from "@angular/router"; +import { Router, provideRouter } from "@angular/router"; import { mock, MockProxy } from "jest-mock-extended"; import { @@ -7,69 +7,49 @@ import { LoginSuccessHandlerService, PasswordLoginCredentials, } from "@bitwarden/auth/common"; -import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AuthResult } from "@bitwarden/common/auth/models/domain/auth-result"; import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { ToastService } from "@bitwarden/components"; -import { KeyService } from "@bitwarden/key-management"; -import { I18nPipe } from "@bitwarden/ui-common"; import { RecoverTwoFactorComponent } from "./recover-two-factor.component"; describe("RecoverTwoFactorComponent", () => { let component: RecoverTwoFactorComponent; let fixture: ComponentFixture; - - // Mock Services let mockRouter: MockProxy; - let mockApiService: MockProxy; - let mockPlatformUtilsService: MockProxy; let mockI18nService: MockProxy; - let mockKeyService: MockProxy; let mockLoginStrategyService: MockProxy; let mockToastService: MockProxy; - let mockConfigService: MockProxy; let mockLoginSuccessHandlerService: MockProxy; let mockLogService: MockProxy; let mockValidationService: MockProxy; - beforeEach(() => { - mockRouter = mock(); - mockApiService = mock(); - mockPlatformUtilsService = mock(); + beforeEach(async () => { mockI18nService = mock(); - mockKeyService = mock(); mockLoginStrategyService = mock(); mockToastService = mock(); - mockConfigService = mock(); mockLoginSuccessHandlerService = mock(); mockLogService = mock(); mockValidationService = mock(); - TestBed.configureTestingModule({ - declarations: [RecoverTwoFactorComponent], + await TestBed.configureTestingModule({ + imports: [RecoverTwoFactorComponent], providers: [ - { provide: Router, useValue: mockRouter }, - { provide: ApiService, useValue: mockApiService }, - { provide: PlatformUtilsService, mockPlatformUtilsService }, + provideRouter([]), { provide: I18nService, useValue: mockI18nService }, - { provide: KeyService, useValue: mockKeyService }, { provide: LoginStrategyServiceAbstraction, useValue: mockLoginStrategyService }, { provide: ToastService, useValue: mockToastService }, - { provide: ConfigService, useValue: mockConfigService }, { provide: LoginSuccessHandlerService, useValue: mockLoginSuccessHandlerService }, { provide: LogService, useValue: mockLogService }, { provide: ValidationService, useValue: mockValidationService }, ], - imports: [I18nPipe], - // FIXME(PM-18598): Replace unknownElements and unknownProperties with actual imports - errorOnUnknownElements: false, - }); + }).compileComponents(); + + mockRouter = TestBed.inject(Router) as MockProxy; + jest.spyOn(mockRouter, "navigate"); fixture = TestBed.createComponent(RecoverTwoFactorComponent); component = fixture.componentInstance; diff --git a/apps/web/src/app/auth/recover-two-factor.component.ts b/apps/web/src/app/auth/recover-two-factor.component.ts index 9c033b88a75..5d160e4ed91 100644 --- a/apps/web/src/app/auth/recover-two-factor.component.ts +++ b/apps/web/src/app/auth/recover-two-factor.component.ts @@ -1,8 +1,9 @@ import { Component, DestroyRef, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; -import { FormControl, FormGroup, Validators } from "@angular/forms"; -import { Router } from "@angular/router"; +import { FormControl, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms"; +import { Router, RouterLink } from "@angular/router"; +import { JslibModule } from "@bitwarden/angular/jslib.module"; import { LoginStrategyServiceAbstraction, PasswordLoginCredentials, @@ -14,14 +15,32 @@ import { ErrorResponse } from "@bitwarden/common/models/response/error.response" import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; -import { ToastService } from "@bitwarden/components"; +import { + AsyncActionsModule, + ButtonModule, + FormFieldModule, + LinkModule, + ToastService, + TypographyModule, +} from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "app-recover-two-factor", templateUrl: "recover-two-factor.component.html", - standalone: false, + imports: [ + ReactiveFormsModule, + RouterLink, + JslibModule, + AsyncActionsModule, + ButtonModule, + FormFieldModule, + I18nPipe, + LinkModule, + TypographyModule, + ], }) export class RecoverTwoFactorComponent implements OnInit { formGroup = new FormGroup({ @@ -108,7 +127,7 @@ export class RecoverTwoFactorComponent implements OnInit { message: this.i18nService.t("twoStepRecoverDisabled"), }); - await this.loginSuccessHandlerService.run(authResult.userId); + await this.loginSuccessHandlerService.run(authResult.userId, this.masterPassword); await this.router.navigate(["/settings/security/two-factor"]); } catch (error: unknown) { diff --git a/apps/web/src/app/auth/settings/emergency-access/view/emergency-access-view.component.html b/apps/web/src/app/auth/settings/emergency-access/view/emergency-access-view.component.html index 20cc50c4d59..4aaac6aaa52 100644 --- a/apps/web/src/app/auth/settings/emergency-access/view/emergency-access-view.component.html +++ b/apps/web/src/app/auth/settings/emergency-access/view/emergency-access-view.component.html @@ -19,7 +19,7 @@ >