From 66252d7c10e29633b2f6c47bef5d7b8b0f9f8069 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 8 Jan 2026 14:12:17 +0000 Subject: [PATCH 01/35] [deps]: Update Minor github-actions updates (#17621) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [deps]: Update Minor github-actions updates * Revert bump --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel García Co-authored-by: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> --- .github/workflows/lint.yml | 2 +- .github/workflows/nx.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e8dd654d8ec..ea6894dab8e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -115,7 +115,7 @@ jobs: run: rustup --version - name: Cache cargo registry - uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2.7.7 + uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 - name: Run cargo fmt working-directory: ./apps/desktop/desktop_native diff --git a/.github/workflows/nx.yml b/.github/workflows/nx.yml index 1e23c31b033..3a7431c07f0 100644 --- a/.github/workflows/nx.yml +++ b/.github/workflows/nx.yml @@ -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/test.yml b/.github/workflows/test.yml index e8b4dc9f760..cf7251b259a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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@7b7927aa7da8b82e81e755810cb51f39941a2cc7 # v2.2.0 if: ${{ github.event.pull_request.head.repo.full_name == github.repository && !cancelled() }} with: name: Test Results From 5578c94c653e27fbf859baa776bc9c6087226992 Mon Sep 17 00:00:00 2001 From: bmbitwarden Date: Thu, 8 Jan 2026 09:27:37 -0500 Subject: [PATCH 02/35] Pm 29917 split the send access component in the web project into multiple components (#18142) * PM 29917 implemented refactor of send access component * PM-29917 refactored to new angular switch syntax * PM-29917 added mark for check --- .../send/send-access/access.component.html | 66 ++------ .../send/send-access/access.component.ts | 155 +++--------------- .../send/send-access/send-auth.component.html | 14 ++ .../send/send-access/send-auth.component.ts | 86 ++++++++++ .../send/send-access/send-view.component.html | 47 ++++++ .../send/send-access/send-view.component.ts | 131 +++++++++++++++ 6 files changed, 319 insertions(+), 180 deletions(-) create mode 100644 apps/web/src/app/tools/send/send-access/send-auth.component.html create mode 100644 apps/web/src/app/tools/send/send-access/send-auth.component.ts create mode 100644 apps/web/src/app/tools/send/send-access/send-view.component.html create mode 100644 apps/web/src/app/tools/send/send-access/send-view.component.ts diff --git a/apps/web/src/app/tools/send/send-access/access.component.html b/apps/web/src/app/tools/send/send-access/access.component.html index aec6e2a10b9..b86933410b8 100644 --- a/apps/web/src/app/tools/send/send-access/access.component.html +++ b/apps/web/src/app/tools/send/send-access/access.component.html @@ -1,52 +1,14 @@ -
- - {{ "viewSendHiddenEmailWarning" | i18n }} - {{ - "learnMore" | i18n - }}. - - - -
-

{{ "sendAccessUnavailable" | i18n }}

-
-
-

{{ "unexpectedErrorSend" | i18n }}

-
-
-

- {{ send.name }} -

-
- - - - - - - - -

- Expires: {{ expirationDate | date: "medium" }} -

-
-
- -
- - {{ "loading" | i18n }} -
-
-
+@switch (viewState) { + @case ("auth") { + + } + @case ("view") { + + } +} diff --git a/apps/web/src/app/tools/send/send-access/access.component.ts b/apps/web/src/app/tools/send/send-access/access.component.ts index 273f1c8c979..4ea469a0b1c 100644 --- a/apps/web/src/app/tools/send/send-access/access.component.ts +++ b/apps/web/src/app/tools/send/send-access/access.component.ts @@ -1,161 +1,60 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Component, OnInit } from "@angular/core"; -import { FormBuilder } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; -import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; -import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; -import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { Utils } from "@bitwarden/common/platform/misc/utils"; -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 { SendAccessResponse } from "@bitwarden/common/tools/send/models/response/send-access.response"; -import { SendAccessView } from "@bitwarden/common/tools/send/models/view/send-access.view"; -import { SEND_KDF_ITERATIONS } from "@bitwarden/common/tools/send/send-kdf"; -import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; -import { AnonLayoutWrapperDataService, ToastService } from "@bitwarden/components"; -import { KeyService } from "@bitwarden/key-management"; import { SharedModule } from "../../../shared"; -import { SendAccessFileComponent } from "./send-access-file.component"; -import { SendAccessPasswordComponent } from "./send-access-password.component"; -import { SendAccessTextComponent } from "./send-access-text.component"; +import { SendAuthComponent } from "./send-auth.component"; +import { SendViewComponent } from "./send-view.component"; + +const SendViewState = Object.freeze({ + View: "view", + Auth: "auth", +} as const); +type SendViewState = (typeof SendViewState)[keyof typeof SendViewState]; // 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-access", templateUrl: "access.component.html", - imports: [ - SendAccessFileComponent, - SendAccessTextComponent, - SendAccessPasswordComponent, - SharedModule, - ], + imports: [SendAuthComponent, SendViewComponent, SharedModule], }) export class AccessComponent implements OnInit { - protected send: SendAccessView; - protected sendType = SendType; - protected loading = true; - protected passwordRequired = false; - protected formPromise: Promise; - protected password: string; - protected unavailable = false; - protected error = false; - protected hideEmail = false; - protected decKey: SymmetricCryptoKey; - protected accessRequest: SendAccessRequest; + viewState: SendViewState = SendViewState.View; + id: string; + key: string; - protected formGroup = this.formBuilder.group({}); + sendAccessResponse: SendAccessResponse | null = null; + sendAccessRequest: SendAccessRequest = new SendAccessRequest(); - private id: string; - private key: string; - - constructor( - private cryptoFunctionService: CryptoFunctionService, - private route: ActivatedRoute, - private keyService: KeyService, - private sendApiService: SendApiService, - private toastService: ToastService, - private i18nService: I18nService, - private layoutWrapperDataService: AnonLayoutWrapperDataService, - protected formBuilder: FormBuilder, - ) {} - - protected get expirationDate() { - if (this.send == null || this.send.expirationDate == null) { - return null; - } - return this.send.expirationDate; - } - - protected get creatorIdentifier() { - if (this.send == null || this.send.creatorIdentifier == null) { - return null; - } - return this.send.creatorIdentifier; - } + constructor(private route: ActivatedRoute) {} async ngOnInit() { // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe this.route.params.subscribe(async (params) => { this.id = params.sendId; this.key = params.key; - if (this.key == null || this.id == null) { - return; + + if (this.id && this.key) { + this.viewState = SendViewState.View; + this.sendAccessResponse = null; + this.sendAccessRequest = new SendAccessRequest(); } - await this.load(); }); } - protected load = async () => { - this.unavailable = false; - this.error = false; - this.hideEmail = false; - try { - const keyArray = Utils.fromUrlB64ToArray(this.key); - this.accessRequest = new SendAccessRequest(); - if (this.password != null) { - const passwordHash = await this.cryptoFunctionService.pbkdf2( - this.password, - keyArray, - "sha256", - SEND_KDF_ITERATIONS, - ); - this.accessRequest.password = Utils.fromBufferToB64(passwordHash); - } - let sendResponse: SendAccessResponse = null; - if (this.loading) { - sendResponse = await this.sendApiService.postSendAccess(this.id, this.accessRequest); - } else { - this.formPromise = this.sendApiService.postSendAccess(this.id, this.accessRequest); - sendResponse = await this.formPromise; - } - this.passwordRequired = false; - const sendAccess = new SendAccess(sendResponse); - this.decKey = await this.keyService.makeSendKey(keyArray); - this.send = await sendAccess.decrypt(this.decKey); - } catch (e) { - if (e instanceof ErrorResponse) { - if (e.statusCode === 401) { - this.passwordRequired = true; - } else if (e.statusCode === 404) { - this.unavailable = true; - } else if (e.statusCode === 400) { - this.toastService.showToast({ - variant: "error", - title: this.i18nService.t("errorOccurred"), - message: e.message, - }); - } else { - this.error = true; - } - } else { - this.error = true; - } - } - this.loading = false; - this.hideEmail = - this.creatorIdentifier == null && - !this.passwordRequired && - !this.loading && - !this.unavailable; + onAuthRequired() { + this.viewState = SendViewState.Auth; + } - if (this.creatorIdentifier != null) { - this.layoutWrapperDataService.setAnonLayoutWrapperData({ - pageSubtitle: { - key: "sendAccessCreatorIdentifier", - placeholders: [this.creatorIdentifier], - }, - }); - } - }; - - protected setPassword(password: string) { - this.password = password; + onAccessGranted(event: { response: SendAccessResponse; request: SendAccessRequest }) { + this.sendAccessResponse = event.response; + this.sendAccessRequest = event.request; + this.viewState = SendViewState.View; } } diff --git a/apps/web/src/app/tools/send/send-access/send-auth.component.html b/apps/web/src/app/tools/send/send-access/send-auth.component.html new file mode 100644 index 00000000000..21a6de50ba8 --- /dev/null +++ b/apps/web/src/app/tools/send/send-access/send-auth.component.html @@ -0,0 +1,14 @@ +
+
+

{{ "sendAccessUnavailable" | i18n }}

+
+
+

{{ "unexpectedErrorSend" | i18n }}

+
+ + +
diff --git a/apps/web/src/app/tools/send/send-access/send-auth.component.ts b/apps/web/src/app/tools/send/send-access/send-auth.component.ts new file mode 100644 index 00000000000..b360044a8b6 --- /dev/null +++ b/apps/web/src/app/tools/send/send-access/send-auth.component.ts @@ -0,0 +1,86 @@ +import { ChangeDetectionStrategy, Component, input, output } from "@angular/core"; + +import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; +import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { SendAccessRequest } from "@bitwarden/common/tools/send/models/request/send-access.request"; +import { SendAccessResponse } from "@bitwarden/common/tools/send/models/response/send-access.response"; +import { SEND_KDF_ITERATIONS } from "@bitwarden/common/tools/send/send-kdf"; +import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; +import { ToastService } from "@bitwarden/components"; + +import { SharedModule } from "../../../shared"; + +import { SendAccessPasswordComponent } from "./send-access-password.component"; + +@Component({ + selector: "app-send-auth", + templateUrl: "send-auth.component.html", + imports: [SendAccessPasswordComponent, SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class SendAuthComponent { + readonly id = input.required(); + readonly key = input.required(); + + accessGranted = output<{ + response: SendAccessResponse; + request: SendAccessRequest; + }>(); + + loading = false; + error = false; + unavailable = false; + password?: string; + + private accessRequest!: SendAccessRequest; + + constructor( + private cryptoFunctionService: CryptoFunctionService, + private sendApiService: SendApiService, + private toastService: ToastService, + private i18nService: I18nService, + ) {} + + async onSubmit(password: string) { + this.password = password; + this.loading = true; + this.error = false; + this.unavailable = false; + + try { + const keyArray = Utils.fromUrlB64ToArray(this.key()); + this.accessRequest = new SendAccessRequest(); + + const passwordHash = await this.cryptoFunctionService.pbkdf2( + this.password, + keyArray, + "sha256", + SEND_KDF_ITERATIONS, + ); + this.accessRequest.password = Utils.fromBufferToB64(passwordHash); + + const sendResponse = await this.sendApiService.postSendAccess(this.id(), this.accessRequest); + this.accessGranted.emit({ response: sendResponse, request: this.accessRequest }); + } catch (e) { + if (e instanceof ErrorResponse) { + if (e.statusCode === 404) { + this.unavailable = true; + } else if (e.statusCode === 400) { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: e.message, + }); + } else { + this.error = true; + } + } else { + this.error = true; + } + } finally { + this.loading = false; + } + } +} diff --git a/apps/web/src/app/tools/send/send-access/send-view.component.html b/apps/web/src/app/tools/send/send-access/send-view.component.html new file mode 100644 index 00000000000..dd0b770b261 --- /dev/null +++ b/apps/web/src/app/tools/send/send-access/send-view.component.html @@ -0,0 +1,47 @@ + + {{ "viewSendHiddenEmailWarning" | i18n }} + {{ + "learnMore" | i18n + }}. + + + +
+

{{ "sendAccessUnavailable" | i18n }}

+
+
+

{{ "unexpectedErrorSend" | i18n }}

+
+
+

+ {{ send.name }} +

+
+ + + + + + + + +

+ Expires: {{ expirationDate | date: "medium" }} +

+
+
+ +
+ + {{ "loading" | i18n }} +
+
diff --git a/apps/web/src/app/tools/send/send-access/send-view.component.ts b/apps/web/src/app/tools/send/send-access/send-view.component.ts new file mode 100644 index 00000000000..0397575f021 --- /dev/null +++ b/apps/web/src/app/tools/send/send-access/send-view.component.ts @@ -0,0 +1,131 @@ +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + input, + OnInit, + output, +} from "@angular/core"; + +import { ErrorResponse } from "@bitwarden/common/models/response/error.response"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +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 { SendAccessResponse } from "@bitwarden/common/tools/send/models/response/send-access.response"; +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 { AnonLayoutWrapperDataService, ToastService } from "@bitwarden/components"; +import { KeyService } from "@bitwarden/key-management"; + +import { SharedModule } from "../../../shared"; + +import { SendAccessFileComponent } from "./send-access-file.component"; +import { SendAccessTextComponent } from "./send-access-text.component"; + +@Component({ + selector: "app-send-view", + templateUrl: "send-view.component.html", + imports: [SendAccessFileComponent, SendAccessTextComponent, SharedModule], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class SendViewComponent implements OnInit { + readonly id = input.required(); + readonly key = input.required(); + readonly sendResponse = input(null); + readonly accessRequest = input(new SendAccessRequest()); + + authRequired = output(); + + send: SendAccessView | null = null; + sendType = SendType; + loading = true; + unavailable = false; + error = false; + hideEmail = false; + decKey!: SymmetricCryptoKey; + + constructor( + private keyService: KeyService, + private sendApiService: SendApiService, + private toastService: ToastService, + private i18nService: I18nService, + private layoutWrapperDataService: AnonLayoutWrapperDataService, + private cdRef: ChangeDetectorRef, + ) {} + + get expirationDate() { + if (this.send == null || this.send.expirationDate == null) { + return null; + } + return this.send.expirationDate; + } + + get creatorIdentifier() { + if (this.send == null || this.send.creatorIdentifier == null) { + return null; + } + return this.send.creatorIdentifier; + } + + async ngOnInit() { + await this.load(); + } + + private async load() { + this.unavailable = false; + this.error = false; + this.hideEmail = false; + this.loading = true; + + let response = this.sendResponse(); + + try { + if (!response) { + response = await this.sendApiService.postSendAccess(this.id(), this.accessRequest()); + } + + const keyArray = Utils.fromUrlB64ToArray(this.key()); + const sendAccess = new SendAccess(response); + this.decKey = await this.keyService.makeSendKey(keyArray); + this.send = await sendAccess.decrypt(this.decKey); + } catch (e) { + if (e instanceof ErrorResponse) { + if (e.statusCode === 401) { + this.authRequired.emit(); + } else if (e.statusCode === 404) { + this.unavailable = true; + } else if (e.statusCode === 400) { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: e.message, + }); + } else { + this.error = true; + } + } else { + this.error = true; + } + } + + this.loading = false; + this.hideEmail = + this.creatorIdentifier == null && !this.loading && !this.unavailable && !response; + + this.hideEmail = this.send != null && this.creatorIdentifier == null; + + if (this.creatorIdentifier != null) { + this.layoutWrapperDataService.setAnonLayoutWrapperData({ + pageSubtitle: { + key: "sendAccessCreatorIdentifier", + placeholders: [this.creatorIdentifier], + }, + }); + } + + this.cdRef.markForCheck(); + } +} From 95235a2b7b07c5e2cddecf69f4053d51d57a5166 Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Thu, 8 Jan 2026 09:59:27 -0500 Subject: [PATCH 03/35] [PM-30526] remove archive option from item more options in AC (#18255) --- .../vault/components/vault-items/vault-cipher-row.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts index a723f1e942b..e437537b1cc 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.ts @@ -144,8 +144,9 @@ export class VaultCipherRowComponent implements OnInit } } + // Archive button will not show in Admin Console protected get showArchiveButton() { - if (!this.archiveEnabled()) { + if (!this.archiveEnabled() || this.viewingOrgVault) { return false; } From 0396b4e054fff25fd046a346390282b1ff300aa3 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 8 Jan 2026 16:09:52 +0100 Subject: [PATCH 04/35] Add CSV export functionality to organization members page (#17342) * Add CSV export functionality to organization members page * Remove unnecessary async from getMemberExport method * Changed button position and style * fixed button alignment * updates based on feedback from product design * refactor, cleanup * fix DI * add default to user status pipe * add missing i18n key * copy update * remove redundant copy --------- Co-authored-by: Brandon --- .../organizations/members/index.ts | 1 + .../members/members.component.html | 45 ++++-- .../members/members.component.ts | 41 ++++- .../organizations/members/members.module.ts | 5 + .../organizations/members/pipes/index.ts | 1 + .../members/pipes/user-status.pipe.spec.ts | 47 ++++++ .../members/pipes/user-status.pipe.ts | 30 ++++ .../organizations/members/services/index.ts | 1 + .../members/services/member-export/index.ts | 2 + .../member-export.service.spec.ts | 151 ++++++++++++++++++ .../member-export/member-export.service.ts | 49 ++++++ .../services/member-export/member.export.ts | 43 +++++ .../event-export/event-export.service.ts | 22 +-- apps/web/src/locales/en/messages.json | 15 ++ 14 files changed, 416 insertions(+), 37 deletions(-) create mode 100644 apps/web/src/app/admin-console/organizations/members/pipes/index.ts create mode 100644 apps/web/src/app/admin-console/organizations/members/pipes/user-status.pipe.spec.ts create mode 100644 apps/web/src/app/admin-console/organizations/members/pipes/user-status.pipe.ts create mode 100644 apps/web/src/app/admin-console/organizations/members/services/member-export/index.ts create mode 100644 apps/web/src/app/admin-console/organizations/members/services/member-export/member-export.service.spec.ts create mode 100644 apps/web/src/app/admin-console/organizations/members/services/member-export/member-export.service.ts create mode 100644 apps/web/src/app/admin-console/organizations/members/services/member-export/member.export.ts diff --git a/apps/web/src/app/admin-console/organizations/members/index.ts b/apps/web/src/app/admin-console/organizations/members/index.ts index 95bd8baf7c7..7026b9bb6ac 100644 --- a/apps/web/src/app/admin-console/organizations/members/index.ts +++ b/apps/web/src/app/admin-console/organizations/members/index.ts @@ -1 +1,2 @@ export * from "./members.module"; +export * from "./pipes"; diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.html b/apps/web/src/app/admin-console/organizations/members/members.component.html index 84e5c33d20d..921004e315d 100644 --- a/apps/web/src/app/admin-console/organizations/members/members.component.html +++ b/apps/web/src/app/admin-console/organizations/members/members.component.html @@ -102,15 +102,25 @@ {{ (organization.useGroups ? "groups" : "collections") | i18n }} {{ "role" | i18n }} {{ "policies" | i18n }} - - + +
+ + +
@@ -352,13 +362,16 @@ - +
+
+ +
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 51a2a6dafc0..e57cf54c180 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 @@ -35,6 +35,7 @@ import { OrganizationMetadataServiceAbstraction } from "@bitwarden/common/billin 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"; @@ -55,7 +56,11 @@ 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, @@ -119,6 +124,8 @@ export class MembersComponent extends BaseMembersComponent private policyService: PolicyService, private policyApiService: PolicyApiServiceAbstraction, private organizationMetadataService: OrganizationMetadataServiceAbstraction, + private memberExportService: MemberExportService, + private fileDownloadService: FileDownloadService, private configService: ConfigService, private environmentService: EnvironmentService, ) { @@ -593,4 +600,36 @@ export class MembersComponent extends BaseMembersComponent .getCheckedUsers() .every((member) => member.managedByOrganization && validStatuses.includes(member.status)); } + + exportMembers = async (): Promise => { + try { + const members = this.dataSource.data; + if (!members || members.length === 0) { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("noMembersToExport"), + }); + return; + } + + const csvData = this.memberExportService.getMemberExport(members); + const fileName = this.memberExportService.getFileName("org-members"); + + this.fileDownloadService.download({ + fileName: fileName, + blobData: csvData, + blobOptions: { type: "text/plain" }, + }); + + this.toastService.showToast({ + variant: "success", + title: undefined, + message: this.i18nService.t("dataExportSuccess"), + }); + } catch (e) { + this.validationService.showError(e); + this.logService.error(`Failed to export members: ${e}`); + } + }; } 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..65625cfd247 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 @@ -19,10 +19,12 @@ import { BulkStatusComponent } from "./components/bulk/bulk-status.component"; import { UserDialogModule } from "./components/member-dialog"; import { MembersRoutingModule } from "./members-routing.module"; import { MembersComponent } from "./members.component"; +import { UserStatusPipe } from "./pipes"; import { OrganizationMembersService, MemberActionsService, MemberDialogManagerService, + MemberExportService, } from "./services"; @NgModule({ @@ -45,12 +47,15 @@ import { BulkStatusComponent, MembersComponent, 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-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..1e229b95d24 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/services/member-export/member-export.service.spec.ts @@ -0,0 +1,151 @@ +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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; + +import { OrganizationUserView } from "../../../core"; +import { UserStatusPipe } from "../../pipes"; + +import { MemberExportService } from "./member-export.service"; + +describe("MemberExportService", () => { + let service: MemberExportService; + let i18nService: MockProxy; + + beforeEach(() => { + i18nService = 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", + disabled: "Disabled", + enrolled: "Enrolled", + notEnrolled: "Not Enrolled", + }; + return translations[key] || key; + }); + + TestBed.configureTestingModule({ + providers: [ + MemberExportService, + { 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 csvData = service.getMemberExport(members); + + 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 csvData = service.getMemberExport(members); + + 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 csvData = service.getMemberExport(members); + + expect(csvData).toContain("user@example.com"); + expect(csvData).toBeDefined(); + }); + + it("should handle empty members array", () => { + const csvData = service.getMemberExport([]); + + // When array is empty, papaparse returns an empty string + expect(csvData).toBe(""); + }); + }); +}); 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..c00881617a4 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/members/services/member-export/member-export.service.ts @@ -0,0 +1,49 @@ +import { inject, Injectable } from "@angular/core"; +import * as papa from "papaparse"; + +import { UserTypePipe } from "@bitwarden/angular/pipes/user-type.pipe"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { ExportHelper } from "@bitwarden/vault-export-core"; + +import { OrganizationUserView } from "../../../core"; +import { UserStatusPipe } from "../../pipes"; + +import { MemberExport } from "./member.export"; + +@Injectable() +export class MemberExportService { + private i18nService = inject(I18nService); + private userTypePipe = inject(UserTypePipe); + private userStatusPipe = inject(UserStatusPipe); + + getMemberExport(members: OrganizationUserView[]): string { + 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"), + ]; + + return papa.unparse(exportData, { + columns: headers, + header: true, + }); + } + + 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/tools/event-export/event-export.service.ts b/apps/web/src/app/tools/event-export/event-export.service.ts index f39b786b6d1..d888af51edf 100644 --- a/apps/web/src/app/tools/event-export/event-export.service.ts +++ b/apps/web/src/app/tools/event-export/event-export.service.ts @@ -4,6 +4,7 @@ import { Injectable } from "@angular/core"; import * as papa from "papaparse"; import { EventView } from "@bitwarden/common/models/view/event.view"; +import { ExportHelper } from "@bitwarden/vault-export-core"; import { EventExport } from "./event.export"; @@ -16,25 +17,6 @@ export class EventExportService { } getFileName(prefix: string = null, extension = "csv"): string { - const now = new Date(); - const dateString = - now.getFullYear() + - "" + - this.padNumber(now.getMonth() + 1, 2) + - "" + - this.padNumber(now.getDate(), 2) + - this.padNumber(now.getHours(), 2) + - "" + - this.padNumber(now.getMinutes(), 2) + - this.padNumber(now.getSeconds(), 2); - - return "bitwarden" + (prefix ? "_" + prefix : "") + "_export_" + dateString + "." + extension; - } - - private padNumber(num: number, width: number, padCharacter = "0"): string { - const numString = num.toString(); - return numString.length >= width - ? numString - : new Array(width - numString.length + 1).join(padCharacter) + numString; + return ExportHelper.getFileName(prefix ?? "", extension); } } diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index db30a9d1153..8024de21e56 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "There are no events to list." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, From 4866eaa2ec3950683cd4a878e6c5c86365c4aabb Mon Sep 17 00:00:00 2001 From: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> Date: Thu, 8 Jan 2026 11:09:13 -0600 Subject: [PATCH 05/35] [PM-23618] Require masterKey on makeUserKey (#17244) --- .../src/abstractions/key.service.ts | 6 ++--- libs/key-management/src/key.service.spec.ts | 26 +++++++++++++++++++ libs/key-management/src/key.service.ts | 14 +++------- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/libs/key-management/src/abstractions/key.service.ts b/libs/key-management/src/abstractions/key.service.ts index feb4a38ac27..6cf44544422 100644 --- a/libs/key-management/src/abstractions/key.service.ts +++ b/libs/key-management/src/abstractions/key.service.ts @@ -129,11 +129,11 @@ export abstract class KeyService { /** * Generates a new user key * @deprecated Interacting with the master key directly is prohibited. Use {@link makeUserKeyV1} instead. - * @throws Error when master key is null and there is no active user - * @param masterKey The user's master key. When null, grabs master key from active user. + * @throws Error when master key is null or undefined. + * @param masterKey The user's master key. * @returns A new user key and the master key protected version of it */ - abstract makeUserKey(masterKey: MasterKey | null): Promise<[UserKey, EncString]>; + abstract makeUserKey(masterKey: MasterKey): Promise<[UserKey, EncString]>; /** * Generates a new user key for a V1 user * Note: This will be replaced by a higher level function to initialize a whole users cryptographic state in the near future. diff --git a/libs/key-management/src/key.service.spec.ts b/libs/key-management/src/key.service.spec.ts index c0af62fe6e9..c0a0ab62347 100644 --- a/libs/key-management/src/key.service.spec.ts +++ b/libs/key-management/src/key.service.spec.ts @@ -177,6 +177,32 @@ describe("keyService", () => { }); }); + describe("makeUserKey", () => { + test.each([null as unknown as MasterKey, undefined as unknown as MasterKey])( + "throws when the provided masterKey is %s", + async (masterKey) => { + await expect(keyService.makeUserKey(masterKey)).rejects.toThrow("MasterKey is required"); + }, + ); + + it("encrypts the user key with the master key", async () => { + const mockUserKey = makeSymmetricCryptoKey(64); + const mockEncryptedUserKey = makeEncString("encryptedUserKey"); + + keyGenerationService.createKey.mockResolvedValue(mockUserKey); + encryptService.wrapSymmetricKey.mockResolvedValue(mockEncryptedUserKey); + const stretchedMasterKey = new SymmetricCryptoKey(new Uint8Array(64)); + keyGenerationService.stretchKey.mockResolvedValue(stretchedMasterKey); + + const result = await keyService.makeUserKey(makeSymmetricCryptoKey(32)); + + expect(encryptService.wrapSymmetricKey).toHaveBeenCalledWith(mockUserKey, stretchedMasterKey); + expect(keyGenerationService.createKey).toHaveBeenCalledWith(512); + expect(result[0]).toBe(mockUserKey); + expect(result[1]).toBe(mockEncryptedUserKey); + }); + }); + describe("everHadUserKey$", () => { let everHadUserKeyState: FakeSingleUserState; diff --git a/libs/key-management/src/key.service.ts b/libs/key-management/src/key.service.ts index 621a8135d1e..8cb072a4c2a 100644 --- a/libs/key-management/src/key.service.ts +++ b/libs/key-management/src/key.service.ts @@ -204,17 +204,9 @@ export class DefaultKeyService implements KeyServiceAbstraction { return (await firstValueFrom(this.stateProvider.getUserState$(USER_KEY, userId))) != null; } - async makeUserKey(masterKey: MasterKey | null): Promise<[UserKey, EncString]> { - if (masterKey == null) { - const userId = await firstValueFrom(this.stateProvider.activeUserId$); - if (userId == null) { - throw new Error("No active user id found."); - } - - masterKey = await firstValueFrom(this.masterPasswordService.masterKey$(userId)); - } - if (masterKey == null) { - throw new Error("No Master Key found."); + async makeUserKey(masterKey: MasterKey): Promise<[UserKey, EncString]> { + if (!masterKey) { + throw new Error("MasterKey is required"); } const newUserKey = await this.keyGenerationService.createKey(512); From de2ebc484a9205baae3a5d43c22fb97b2aefc13d Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Thu, 8 Jan 2026 11:33:24 -0600 Subject: [PATCH 06/35] exclude deleted items from at risk check (#18246) --- .../default-cipher-risk.service.spec.ts | 67 +++++++++++++++++++ .../services/default-cipher-risk.service.ts | 4 +- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/libs/common/src/vault/services/default-cipher-risk.service.spec.ts b/libs/common/src/vault/services/default-cipher-risk.service.spec.ts index e5231241462..fad5e963113 100644 --- a/libs/common/src/vault/services/default-cipher-risk.service.spec.ts +++ b/libs/common/src/vault/services/default-cipher-risk.service.spec.ts @@ -250,6 +250,38 @@ describe("DefaultCipherRiskService", () => { expect.any(Object), ); }); + + it("should filter out deleted Login ciphers", async () => { + const mockClient = sdkService.simulate.userLogin(mockUserId); + const mockCipherRiskClient = mockClient.vault.mockDeep().cipher_risk.mockDeep(); + mockCipherRiskClient.compute_risk.mockResolvedValue([]); + + const activeCipher = new CipherView(); + activeCipher.id = mockCipherId1; + activeCipher.type = CipherType.Login; + activeCipher.login = new LoginView(); + activeCipher.login.password = "password1"; + activeCipher.deletedDate = undefined; + + const deletedCipher = new CipherView(); + deletedCipher.id = mockCipherId2; + deletedCipher.type = CipherType.Login; + deletedCipher.login = new LoginView(); + deletedCipher.login.password = "password2"; + deletedCipher.deletedDate = new Date(); + + await cipherRiskService.computeRiskForCiphers([activeCipher, deletedCipher], mockUserId); + + expect(mockCipherRiskClient.compute_risk).toHaveBeenCalledWith( + [ + expect.objectContaining({ + id: expect.anything(), + password: "password1", + }), + ], + expect.any(Object), + ); + }); }); describe("buildPasswordReuseMap", () => { @@ -284,6 +316,41 @@ describe("DefaultCipherRiskService", () => { ]); expect(result).toEqual(mockReuseMap); }); + + it("should exclude deleted ciphers when building password reuse map", async () => { + const mockClient = sdkService.simulate.userLogin(mockUserId); + const mockCipherRiskClient = mockClient.vault.mockDeep().cipher_risk.mockDeep(); + + const mockReuseMap = { + password1: 1, + }; + + mockCipherRiskClient.password_reuse_map.mockReturnValue(mockReuseMap); + + const activeCipher = new CipherView(); + activeCipher.id = mockCipherId1; + activeCipher.type = CipherType.Login; + activeCipher.login = new LoginView(); + activeCipher.login.password = "password1"; + activeCipher.deletedDate = undefined; + + const deletedCipherWithSamePassword = new CipherView(); + deletedCipherWithSamePassword.id = mockCipherId2; + deletedCipherWithSamePassword.type = CipherType.Login; + deletedCipherWithSamePassword.login = new LoginView(); + deletedCipherWithSamePassword.login.password = "password1"; + deletedCipherWithSamePassword.deletedDate = new Date(); + + const result = await cipherRiskService.buildPasswordReuseMap( + [activeCipher, deletedCipherWithSamePassword], + mockUserId, + ); + + expect(mockCipherRiskClient.password_reuse_map).toHaveBeenCalledWith([ + expect.objectContaining({ password: "password1" }), + ]); + expect(result).toEqual(mockReuseMap); + }); }); describe("computeCipherRiskForUser", () => { diff --git a/libs/common/src/vault/services/default-cipher-risk.service.ts b/libs/common/src/vault/services/default-cipher-risk.service.ts index 4b4558e5e7a..5f424fdd7a2 100644 --- a/libs/common/src/vault/services/default-cipher-risk.service.ts +++ b/libs/common/src/vault/services/default-cipher-risk.service.ts @@ -71,7 +71,6 @@ export class DefaultCipherRiskService implements CipherRiskServiceAbstraction { passwordMap, checkExposed, }); - return results[0]; } @@ -103,7 +102,8 @@ export class DefaultCipherRiskService implements CipherRiskServiceAbstraction { return ( cipher.type === CipherType.Login && cipher.login?.password != null && - cipher.login.password !== "" + cipher.login.password !== "" && + !cipher.isDeleted ); }) .map( From 0dd4ed702624249c72c90dccf055b049a0d2f443 Mon Sep 17 00:00:00 2001 From: Leslie Tilton <23057410+Banrion@users.noreply.github.com> Date: Thu, 8 Jan 2026 12:54:39 -0600 Subject: [PATCH 07/35] Use official latest phishing sources from phish.co.za (#18271) --- .../src/dirt/phishing-detection/phishing-resources.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/browser/src/dirt/phishing-detection/phishing-resources.ts b/apps/browser/src/dirt/phishing-detection/phishing-resources.ts index 262d6cf833b..4cd155c8ae3 100644 --- a/apps/browser/src/dirt/phishing-detection/phishing-resources.ts +++ b/apps/browser/src/dirt/phishing-detection/phishing-resources.ts @@ -18,8 +18,7 @@ export const PHISHING_RESOURCES: Record Date: Thu, 8 Jan 2026 15:01:03 -0500 Subject: [PATCH 08/35] Added batch encrypt many method and used that in imports (#18266) --- .../abstractions/cipher-encryption.service.ts | 10 +++ .../src/vault/abstractions/cipher.service.ts | 9 +++ .../src/vault/services/cipher.service.ts | 18 ++++++ .../default-cipher-encryption.service.spec.ts | 62 +++++++++++++++++++ .../default-cipher-encryption.service.ts | 38 ++++++++++++ libs/importer/src/services/import.service.ts | 24 ++++--- 6 files changed, 154 insertions(+), 7 deletions(-) diff --git a/libs/common/src/vault/abstractions/cipher-encryption.service.ts b/libs/common/src/vault/abstractions/cipher-encryption.service.ts index fdd42c0acf2..a3b824fd46e 100644 --- a/libs/common/src/vault/abstractions/cipher-encryption.service.ts +++ b/libs/common/src/vault/abstractions/cipher-encryption.service.ts @@ -20,6 +20,16 @@ export abstract class CipherEncryptionService { */ abstract encrypt(model: CipherView, userId: UserId): Promise; + /** + * Encrypts multiple ciphers using the SDK for the given userId. + * + * @param models The cipher views to encrypt + * @param userId The user ID to initialize the SDK client with + * + * @returns A promise that resolves to an array of encryption contexts + */ + abstract encryptMany(models: CipherView[], userId: UserId): Promise; + /** * Move the cipher to the specified organization by re-encrypting its keys with the organization's key. * The cipher.organizationId will be updated to the new organizationId. diff --git a/libs/common/src/vault/abstractions/cipher.service.ts b/libs/common/src/vault/abstractions/cipher.service.ts index 0d3a0b99fcb..203984075f7 100644 --- a/libs/common/src/vault/abstractions/cipher.service.ts +++ b/libs/common/src/vault/abstractions/cipher.service.ts @@ -50,6 +50,15 @@ export abstract class CipherService implements UserKeyRotationDataProvider; + /** + * Encrypts multiple ciphers for the given user. + * + * @param models The cipher views to encrypt + * @param userId The user ID to encrypt for + * + * @returns A promise that resolves to an array of encryption contexts + */ + abstract encryptMany(models: CipherView[], userId: UserId): Promise; abstract encryptFields(fieldsModel: FieldView[], key: SymmetricCryptoKey): Promise; abstract encryptField(fieldModel: FieldView, key: SymmetricCryptoKey): Promise; abstract get(id: string, userId: UserId): Promise; diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index d25aa62ea3a..2e0adc892e3 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -340,6 +340,24 @@ export class CipherService implements CipherServiceAbstraction { } } + async encryptMany(models: CipherView[], userId: UserId): Promise { + const sdkEncryptionEnabled = await this.configService.getFeatureFlag( + FeatureFlag.PM22136_SdkCipherEncryption, + ); + + if (sdkEncryptionEnabled) { + return await this.cipherEncryptionService.encryptMany(models, userId); + } + + // Fallback to sequential encryption if SDK disabled + const results: EncryptionContext[] = []; + for (const model of models) { + const result = await this.encrypt(model, userId); + results.push(result); + } + return results; + } + async encryptAttachments( attachmentsModel: AttachmentView[], key: SymmetricCryptoKey, diff --git a/libs/common/src/vault/services/default-cipher-encryption.service.spec.ts b/libs/common/src/vault/services/default-cipher-encryption.service.spec.ts index f54dfa17a38..a0ca4833b92 100644 --- a/libs/common/src/vault/services/default-cipher-encryption.service.spec.ts +++ b/libs/common/src/vault/services/default-cipher-encryption.service.spec.ts @@ -253,6 +253,68 @@ describe("DefaultCipherEncryptionService", () => { }); }); + describe("encryptMany", () => { + it("should encrypt multiple ciphers", async () => { + const cipherView2 = new CipherView(cipherObj); + cipherView2.name = "test-name-2"; + const cipherView3 = new CipherView(cipherObj); + cipherView3.name = "test-name-3"; + + const ciphers = [cipherViewObj, cipherView2, cipherView3]; + + const expectedCipher1: Cipher = { + id: cipherId as string, + type: CipherType.Login, + name: "encrypted-name-1", + } as unknown as Cipher; + + const expectedCipher2: Cipher = { + id: cipherId as string, + type: CipherType.Login, + name: "encrypted-name-2", + } as unknown as Cipher; + + const expectedCipher3: Cipher = { + id: cipherId as string, + type: CipherType.Login, + name: "encrypted-name-3", + } as unknown as Cipher; + + mockSdkClient.vault().ciphers().encrypt.mockReturnValue({ + cipher: sdkCipher, + encryptedFor: userId, + }); + + jest + .spyOn(Cipher, "fromSdkCipher") + .mockReturnValueOnce(expectedCipher1) + .mockReturnValueOnce(expectedCipher2) + .mockReturnValueOnce(expectedCipher3); + + const results = await cipherEncryptionService.encryptMany(ciphers, userId); + + expect(results).toBeDefined(); + expect(results.length).toBe(3); + expect(results[0].cipher).toEqual(expectedCipher1); + expect(results[1].cipher).toEqual(expectedCipher2); + expect(results[2].cipher).toEqual(expectedCipher3); + + expect(mockSdkClient.vault().ciphers().encrypt).toHaveBeenCalledTimes(3); + + expect(results[0].encryptedFor).toBe(userId); + expect(results[1].encryptedFor).toBe(userId); + expect(results[2].encryptedFor).toBe(userId); + }); + + it("should handle empty array", async () => { + const results = await cipherEncryptionService.encryptMany([], userId); + + expect(results).toBeDefined(); + expect(results.length).toBe(0); + expect(mockSdkClient.vault().ciphers().encrypt).not.toHaveBeenCalled(); + }); + }); + describe("encryptCipherForRotation", () => { it("should call the sdk method to encrypt the cipher with a new key for rotation", async () => { mockSdkClient.vault().ciphers().encrypt_cipher_for_rotation.mockReturnValue({ diff --git a/libs/common/src/vault/services/default-cipher-encryption.service.ts b/libs/common/src/vault/services/default-cipher-encryption.service.ts index f1b737ed50f..588265846e0 100644 --- a/libs/common/src/vault/services/default-cipher-encryption.service.ts +++ b/libs/common/src/vault/services/default-cipher-encryption.service.ts @@ -51,6 +51,44 @@ export class DefaultCipherEncryptionService implements CipherEncryptionService { ); } + async encryptMany(models: CipherView[], userId: UserId): Promise { + if (!models || models.length === 0) { + return []; + } + + return firstValueFrom( + this.sdkService.userClient$(userId).pipe( + map((sdk) => { + if (!sdk) { + throw new Error("SDK not available"); + } + + using ref = sdk.take(); + + const results: EncryptionContext[] = []; + + // TODO: https://bitwarden.atlassian.net/browse/PM-30580 + // Replace this loop with a native SDK encryptMany method for better performance. + for (const model of models) { + const sdkCipherView = this.toSdkCipherView(model, ref.value); + const encryptionContext = ref.value.vault().ciphers().encrypt(sdkCipherView); + + results.push({ + cipher: Cipher.fromSdkCipher(encryptionContext.cipher)!, + encryptedFor: uuidAsString(encryptionContext.encryptedFor) as UserId, + }); + } + + return results; + }), + catchError((error: unknown) => { + this.logService.error(`Failed to encrypt ciphers in batch: ${error}`); + return EMPTY; + }), + ), + ); + } + async moveToOrganization( model: CipherView, organizationId: OrganizationId, diff --git a/libs/importer/src/services/import.service.ts b/libs/importer/src/services/import.service.ts index 400beae5179..829bd04e994 100644 --- a/libs/importer/src/services/import.service.ts +++ b/libs/importer/src/services/import.service.ts @@ -374,10 +374,13 @@ export class ImportService implements ImportServiceAbstraction { private async handleIndividualImport(importResult: ImportResult, userId: UserId) { const request = new ImportCiphersRequest(); - for (let i = 0; i < importResult.ciphers.length; i++) { - const c = await this.cipherService.encrypt(importResult.ciphers[i], userId); - request.ciphers.push(new CipherRequest(c)); + + const encryptedCiphers = await this.cipherService.encryptMany(importResult.ciphers, userId); + + for (const encryptedCipher of encryptedCiphers) { + request.ciphers.push(new CipherRequest(encryptedCipher)); } + const userKey = await firstValueFrom(this.keyService.userKey$(userId)); if (importResult.folders != null) { @@ -400,11 +403,18 @@ export class ImportService implements ImportServiceAbstraction { userId: UserId, ) { const request = new ImportOrganizationCiphersRequest(); - for (let i = 0; i < importResult.ciphers.length; i++) { - importResult.ciphers[i].organizationId = organizationId; - const c = await this.cipherService.encrypt(importResult.ciphers[i], userId); - request.ciphers.push(new CipherRequest(c)); + + // Set organization ID on all ciphers before batch encryption + importResult.ciphers.forEach((cipher) => { + cipher.organizationId = organizationId; + }); + + const encryptedCiphers = await this.cipherService.encryptMany(importResult.ciphers, userId); + + for (const encryptedCipher of encryptedCiphers) { + request.ciphers.push(new CipherRequest(encryptedCipher)); } + if (importResult.collections != null) { for (let i = 0; i < importResult.collections.length; i++) { importResult.collections[i].organizationId = organizationId; From 6579e31374cc96fc0b0d4debea8c7e3a429c0bf6 Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Thu, 8 Jan 2026 16:16:08 -0500 Subject: [PATCH 09/35] [PM-30537] add tab nav to restart premium link (#18269) --- apps/web/src/app/vault/individual-vault/vault.component.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/vault/individual-vault/vault.component.html b/apps/web/src/app/vault/individual-vault/vault.component.html index df1b727154f..cb5332d07d8 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.html +++ b/apps/web/src/app/vault/individual-vault/vault.component.html @@ -43,7 +43,9 @@
{{ "premiumSubscriptionEndedDesc" | i18n }}
- {{ "restartPremium" | i18n }} + + {{ "restartPremium" | i18n }} +
} From eedc36cf3959f8c8da1d350c45697960e16e1e65 Mon Sep 17 00:00:00 2001 From: blackwood Date: Thu, 8 Jan 2026 16:46:13 -0500 Subject: [PATCH 10/35] Revert "Display autofill overlay for zoom.us signin password (#16900)" (#18261) --- .../services/autofill-overlay-content.service.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) 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 2087b0640fb..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; } From 4aa69a769be8228c4001225c2b448d2a092b6927 Mon Sep 17 00:00:00 2001 From: Zhaolin Liang Date: Fri, 9 Jan 2026 07:19:56 +0800 Subject: [PATCH 11/35] [PM-25402] auto-assign new logins to current folder/collection (#16268) * auto-assign selected collection for new vault items * Ensure a selected collectionId in the vault filter is passed on to a newly created cipher. Fixes #15485 * Assign selected folder and collection when creating a new cipher Added here to prevent a regression whenever we switch over to this componet and deprecate vault-v2.component * account for null folderIds --------- Co-authored-by: Daniel James Smith Co-authored-by: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Co-authored-by: jaasen-livefront --- apps/desktop/src/vault/app/vault-v3/vault.component.ts | 10 +++++----- apps/desktop/src/vault/app/vault/vault-v2.component.ts | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.ts b/apps/desktop/src/vault/app/vault-v3/vault.component.ts index 21ba7547f8b..a16ef93e230 100644 --- a/apps/desktop/src/vault/app/vault-v3/vault.component.ts +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.ts @@ -34,7 +34,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag 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, OrganizationId, UserId } from "@bitwarden/common/types/guid"; +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"; @@ -158,7 +158,7 @@ export class VaultComponent cipherId: string | null = null; favorites = false; type: CipherType | null = null; - folderId: string | null = null; + folderId: string | null | undefined = null; collectionId: string | null = null; organizationId: string | null = null; myVaultOnly = false; @@ -980,9 +980,7 @@ export class VaultComponent // clear out organizationId when the user switches to a personal vault filter this.addOrganizationId = null; } - if (this.activeFilter.selectedFolderId && this.activeFilter.selectedFolder) { - this.folderId = this.activeFilter.selectedFolderId; - } + this.folderId = this.activeFilter.selectedFolderId; if (this.config == null) { return; @@ -990,7 +988,9 @@ export class VaultComponent this.config.initialValues = { ...this.config.initialValues, + folderId: this.folderId, organizationId: this.addOrganizationId as OrganizationId, + collectionIds: this.addCollectionIds as CollectionId[], }; } diff --git a/apps/desktop/src/vault/app/vault/vault-v2.component.ts b/apps/desktop/src/vault/app/vault/vault-v2.component.ts index ade4af928fc..eedcb4dde83 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.ts @@ -45,7 +45,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag 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, OrganizationId, UserId } from "@bitwarden/common/types/guid"; +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"; @@ -172,7 +172,7 @@ export class VaultV2Component cipherId: string | null = null; favorites = false; type: CipherType | null = null; - folderId: string | null = null; + folderId: string | null | undefined = null; collectionId: string | null = null; organizationId: OrganizationId | null = null; myVaultOnly = false; @@ -1016,9 +1016,7 @@ export class VaultV2Component // clear out organizationId when the user switches to a personal vault filter this.addOrganizationId = null; } - if (this.activeFilter.selectedFolderId && this.activeFilter.selectedFolder) { - this.folderId = this.activeFilter.selectedFolderId; - } + this.folderId = this.activeFilter.selectedFolderId; if (this.config == null) { return; @@ -1027,6 +1025,8 @@ export class VaultV2Component this.config.initialValues = { ...this.config.initialValues, organizationId: this.addOrganizationId as OrganizationId, + folderId: this.folderId, + collectionIds: this.addCollectionIds as CollectionId[], }; } From 1022d21654fd54223eb460d03e2773aa14e52d8f Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:21:32 -0800 Subject: [PATCH 12/35] use custom auth wrapper for at-risk-passwords (#18055) --- apps/browser/src/popup/app-routing.module.ts | 3 ++- .../popup/guards/at-risk-passwords.guard.ts | 26 ++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 6838d4940ab..1f1d4d25b40 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -86,6 +86,7 @@ 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"; @@ -723,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/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); From 95100b6f239c033cda677ffece75e44a07ac1e0b Mon Sep 17 00:00:00 2001 From: Isaac Ivins Date: Fri, 9 Jan 2026 03:41:15 -0500 Subject: [PATCH 13/35] Feature/pm 28788 desktop header UI migration (#18221) Add desktop header component --- apps/desktop/src/app/app-routing.module.ts | 1 + .../header/desktop-header.component.html | 21 +++ .../header/desktop-header.component.spec.ts | 122 ++++++++++++++++++ .../layout/header/desktop-header.component.ts | 47 +++++++ apps/desktop/src/app/layout/header/index.ts | 1 + .../app/tools/send-v2/send-v2.component.html | 59 ++++----- .../tools/send-v2/send-v2.component.spec.ts | 7 + .../app/tools/send-v2/send-v2.component.ts | 2 + .../components/src/header/header.component.ts | 3 + 9 files changed, 229 insertions(+), 34 deletions(-) create mode 100644 apps/desktop/src/app/layout/header/desktop-header.component.html create mode 100644 apps/desktop/src/app/layout/header/desktop-header.component.spec.ts create mode 100644 apps/desktop/src/app/layout/header/desktop-header.component.ts create mode 100644 apps/desktop/src/app/layout/header/index.ts diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index 6077afa8c12..f75f6ccdc20 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -361,6 +361,7 @@ const routes: Routes = [ { path: "new-sends", component: SendV2Component, + data: { pageTitle: { key: "send" } } satisfies RouteDataProperties, }, ], }, 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..efee5e21d9b --- /dev/null +++ b/apps/desktop/src/app/layout/header/desktop-header.component.html @@ -0,0 +1,21 @@ +
+ + + + + + + + + + + + + + + + + + + +
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/tools/send-v2/send-v2.component.html b/apps/desktop/src/app/tools/send-v2/send-v2.component.html index 659e4be9c5b..05c1332f1e7 100644 --- a/apps/desktop/src/app/tools/send-v2/send-v2.component.html +++ b/apps/desktop/src/app/tools/send-v2/send-v2.component.html @@ -1,40 +1,31 @@
-
- -
-

{{ "send" | i18n }}

- @if (!disableSend()) { - - } -
- + + + @if (!disableSend()) { + + } + +
-
- - - -
+ + +
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 index 58c9ce8e0b4..713915e3cf7 100644 --- 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 @@ -3,6 +3,7 @@ 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"; @@ -89,6 +90,12 @@ describe("SendV2Component", () => { }, { provide: MessagingService, useValue: mock() }, { provide: ConfigService, useValue: mock() }, + { + provide: ActivatedRoute, + useValue: { + data: of({}), + }, + }, ], }).compileComponents(); 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 index 1b2ccb30247..6a44713d309 100644 --- a/apps/desktop/src/app/tools/send-v2/send-v2.component.ts +++ b/apps/desktop/src/app/tools/send-v2/send-v2.component.ts @@ -35,6 +35,7 @@ import { } 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({ @@ -56,6 +57,7 @@ type Action = (typeof Action)[keyof typeof Action]; AddEditComponent, SendListComponent, NewSendDropdownV2Component, + DesktopHeaderComponent, ], providers: [ { diff --git a/libs/components/src/header/header.component.ts b/libs/components/src/header/header.component.ts index 08cd91ea206..44b0c063d89 100644 --- a/libs/components/src/header/header.component.ts +++ b/libs/components/src/header/header.component.ts @@ -1,8 +1,11 @@ import { ChangeDetectionStrategy, Component, input } from "@angular/core"; +import { TypographyDirective } from "../typography/typography.directive"; + @Component({ selector: "bit-header", templateUrl: "./header.component.html", + imports: [TypographyDirective], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, }) From bcdf3a52bb5c89537239d6436a1f6054f2c58f9a Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 9 Jan 2026 15:22:20 +0100 Subject: [PATCH 14/35] Autosync the updated translations (#18276) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/af/messages.json | 4 +-- apps/desktop/src/locales/ar/messages.json | 4 +-- apps/desktop/src/locales/az/messages.json | 4 +-- apps/desktop/src/locales/be/messages.json | 4 +-- apps/desktop/src/locales/bg/messages.json | 4 +-- apps/desktop/src/locales/bn/messages.json | 4 +-- apps/desktop/src/locales/bs/messages.json | 4 +-- apps/desktop/src/locales/ca/messages.json | 4 +-- apps/desktop/src/locales/cs/messages.json | 2 +- apps/desktop/src/locales/cy/messages.json | 4 +-- apps/desktop/src/locales/da/messages.json | 4 +-- apps/desktop/src/locales/de/messages.json | 16 ++++++------ apps/desktop/src/locales/el/messages.json | 4 +-- apps/desktop/src/locales/en_GB/messages.json | 4 +-- apps/desktop/src/locales/en_IN/messages.json | 4 +-- apps/desktop/src/locales/eo/messages.json | 4 +-- apps/desktop/src/locales/es/messages.json | 4 +-- apps/desktop/src/locales/et/messages.json | 4 +-- apps/desktop/src/locales/eu/messages.json | 4 +-- apps/desktop/src/locales/fa/messages.json | 4 +-- apps/desktop/src/locales/fi/messages.json | 4 +-- apps/desktop/src/locales/fil/messages.json | 4 +-- apps/desktop/src/locales/fr/messages.json | 4 +-- apps/desktop/src/locales/gl/messages.json | 4 +-- apps/desktop/src/locales/he/messages.json | 4 +-- apps/desktop/src/locales/hi/messages.json | 4 +-- apps/desktop/src/locales/hr/messages.json | 4 +-- apps/desktop/src/locales/hu/messages.json | 4 +-- apps/desktop/src/locales/id/messages.json | 4 +-- apps/desktop/src/locales/it/messages.json | 26 ++++++++++---------- apps/desktop/src/locales/ja/messages.json | 4 +-- apps/desktop/src/locales/ka/messages.json | 4 +-- apps/desktop/src/locales/km/messages.json | 4 +-- apps/desktop/src/locales/kn/messages.json | 4 +-- apps/desktop/src/locales/ko/messages.json | 4 +-- apps/desktop/src/locales/lt/messages.json | 4 +-- apps/desktop/src/locales/lv/messages.json | 4 +-- apps/desktop/src/locales/me/messages.json | 4 +-- apps/desktop/src/locales/ml/messages.json | 4 +-- apps/desktop/src/locales/mr/messages.json | 4 +-- apps/desktop/src/locales/my/messages.json | 4 +-- apps/desktop/src/locales/nb/messages.json | 4 +-- apps/desktop/src/locales/ne/messages.json | 4 +-- apps/desktop/src/locales/nl/messages.json | 4 +-- apps/desktop/src/locales/nn/messages.json | 4 +-- apps/desktop/src/locales/or/messages.json | 4 +-- apps/desktop/src/locales/pl/messages.json | 4 +-- apps/desktop/src/locales/pt_BR/messages.json | 4 +-- apps/desktop/src/locales/pt_PT/messages.json | 4 +-- apps/desktop/src/locales/ro/messages.json | 4 +-- apps/desktop/src/locales/ru/messages.json | 4 +-- apps/desktop/src/locales/si/messages.json | 4 +-- apps/desktop/src/locales/sk/messages.json | 4 +-- apps/desktop/src/locales/sl/messages.json | 4 +-- apps/desktop/src/locales/sr/messages.json | 4 +-- apps/desktop/src/locales/sv/messages.json | 6 ++--- apps/desktop/src/locales/ta/messages.json | 4 +-- apps/desktop/src/locales/te/messages.json | 4 +-- apps/desktop/src/locales/th/messages.json | 4 +-- apps/desktop/src/locales/tr/messages.json | 4 +-- apps/desktop/src/locales/uk/messages.json | 4 +-- apps/desktop/src/locales/vi/messages.json | 4 +-- apps/desktop/src/locales/zh_CN/messages.json | 4 +-- apps/desktop/src/locales/zh_TW/messages.json | 4 +-- 64 files changed, 145 insertions(+), 145 deletions(-) diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index c879ae0cc70..cb186f31d35 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index 77a73a6c0b4..a33c6b301b6 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index 31735e88ef1..d3cdbba9e23 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -4267,8 +4267,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": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Yararsız qısayol" diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index 6bb3bf31013..4f441d20781 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index aceff28455f..10702ea4aa9 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -4267,8 +4267,8 @@ "typeShortcut": { "message": "Комбинация за въвеждане" }, - "editAutotypeShortcutDescription": { - "message": "Използвайте един или повече от модификаторите Ctrl, Alt, Win или Shift, заедно с някоя буква." + "editAutotypeKeyboardModifiersDescription": { + "message": "Използвайте един или повече от модификаторите Ctrl, Alt или Win, заедно с някоя буква." }, "invalidShortcut": { "message": "Неправилна комбинация" diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index 01a9f1d57d6..607418bcb46 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index a90b00d16e5..9f13b809760 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index 41283482c62..1b03ad6fa1e 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index 436839c4d8b..437f42f840f 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -4267,7 +4267,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": { diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index e00bfb52e41..f04f6625529 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index 5129d83839c..f022c4cee33 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index b5deafa055e..2783b39ca69 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -4267,8 +4267,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" @@ -4307,7 +4307,7 @@ "message": "Wiederherstellen" }, "archived": { - "message": "Archived" + "message": "Archiviert" }, "itemsInArchive": { "message": "Einträge im Archiv" @@ -4331,19 +4331,19 @@ "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?" }, "unArchiveAndSave": { - "message": "Unarchive and save" + "message": "Nicht mehr archivieren und speichern" }, "restartPremium": { - "message": "Restart Premium" + "message": "Premium neu starten" }, "premiumSubscriptionEnded": { - "message": "Your Premium subscription ended" + "message": "Dein Premium-Abonnement ist abgelaufen" }, "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." + "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": "Item has been restored" + "message": "Eintrag wurde wiederhergestellt" }, "zipPostalCodeLabel": { "message": "PLZ / Postleitzahl" diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index d8329d7d04b..9a4d2b736be 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index d2f191f08c3..f7020c63bf1 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index 399087bad95..d4e497f41d3 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index ebbdce12c3d..ab07b9db9af 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index 0b456800964..af61f7a97a0 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -4267,8 +4267,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": "Atajo inválido" diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index f009785e27e..b013a55ffd7 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index 47b3c7ac33f..49b7bad76de 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index 977e383fd06..10be871914f 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -4267,8 +4267,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": "میانبر نامعتبر" diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index 30588bf9ddc..2c2275f3afa 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index 0a8b0e89fd9..ff7562680e1 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index 577dfad3511..566e6fc7429 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index 90eee288681..5a5a1715d7e 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index f723b37d85f..5ce8db992f2 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -4267,8 +4267,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": "קיצור דרך לא חוקי" diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index 2c7e0394c3e..4c376c957d5 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index ce8eaddc1a3..71aa2a5a67a 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -4267,8 +4267,8 @@ "typeShortcut": { "message": "Vrsta prečaca" }, - "editAutotypeShortcutDescription": { - "message": "Uključi jedan ili dva modifikatora: Ctrl, Alt, Win ili Shift i slovo." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Nevažeći prečac" diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index 374136ee556..fd0667d7aa8 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index 487a84c11b6..eb84f8bd747 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index 175b6b19772..d27efd0789a 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -101,7 +101,7 @@ } }, "new": { - "message": "New", + "message": "Nuovo", "description": "for adding new items" }, "newUri": { @@ -2416,7 +2416,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLink": { - "message": "Copy Send link", + "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": { @@ -4040,14 +4040,14 @@ "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" + "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": "Clear filters or try another search term" + "message": "Elimina i filtri di ricerca o prova con altri termini" }, "generatorNudgeTitle": { "message": "Crea rapidamente password sicure" @@ -4267,8 +4267,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": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Scorciatoia non valida" @@ -4307,7 +4307,7 @@ "message": "Rimuovi dall'Archivio" }, "archived": { - "message": "Archived" + "message": "Archiviato" }, "itemsInArchive": { "message": "Elementi archiviati" @@ -4331,19 +4331,19 @@ "message": "Gli elementi archiviati sono esclusi dai risultati di ricerca e dall'auto-riempimento. Vuoi davvero archiviare questo elemento?" }, "unArchiveAndSave": { - "message": "Unarchive and save" + "message": "Togli dall'archivio e salva" }, "restartPremium": { - "message": "Restart Premium" + "message": "Riavvia Premium" }, "premiumSubscriptionEnded": { - "message": "Your Premium subscription ended" + "message": "Il tuo abbonamento Premium è terminato" }, "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." + "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": "Item has been restored" + "message": "L'elemento è stato ripristinato" }, "zipPostalCodeLabel": { "message": "CAP / codice postale" @@ -4421,7 +4421,7 @@ "message": "Timeout della sessione" }, "resizeSideNavigation": { - "message": "Resize side navigation" + "message": "Ridimensiona la navigazione laterale" }, "sessionTimeoutSettingsManagedByOrganization": { "message": "Questa impostazione è gestita dalla tua organizzazione." diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index 438828aa1ed..24395bf30d7 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index d1fe2e2b05f..831367b12a8 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index 90eee288681..5a5a1715d7e 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index b4509a61d57..d7602b05af2 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index 3717ffbdc68..700439e6030 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/lt/messages.json b/apps/desktop/src/locales/lt/messages.json index 71bd35ef34f..e959141e0c9 100644 --- a/apps/desktop/src/locales/lt/messages.json +++ b/apps/desktop/src/locales/lt/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index 3f4aede37f1..12e548f50b9 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index 312af4a6c1d..fea8b6d97a5 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index debdac6c8c7..f8eb1f682f9 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index 90eee288681..5a5a1715d7e 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index db7180a84b9..189044c4c40 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index 4338e219145..508e4bbdfbc 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index 6d103b6bda6..8d688575099 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index 185007135c5..bfbac0c2e65 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index 2a1c43ae342..9119a018cbd 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index 9f95f1c0d27..4ffca3c1e6e 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index 2f75826a7b5..50a39b462d9 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index b0705f9b7c2..a0696b63c1e 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index 09037dd73b5..3b6a818977e 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index 651c6386d7c..b188aa96d1c 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index d367051deaf..6d7b8bf1c23 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -4267,8 +4267,8 @@ "typeShortcut": { "message": "Введите сочетание клавиш" }, - "editAutotypeShortcutDescription": { - "message": "Включите один или два из следующих модификаторов: Ctrl, Alt, Win или Shift и букву." + "editAutotypeKeyboardModifiersDescription": { + "message": "Включите один или два из следующих модификаторов: Ctrl, Alt, Win и букву." }, "invalidShortcut": { "message": "Недопустимое сочетание клавиш" diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index 982d73dd5c6..37f2897c919 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index af256e6d883..b3398629da3 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index 3cb8df78bbc..6fa6d25285f 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index dd9ce034124..885ed8410db 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -4267,8 +4267,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": "Неважећа пречица" diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index ee60c850ce7..f96fff1153c 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -4267,8 +4267,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" @@ -4343,7 +4343,7 @@ "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" + "message": "Objektet har återställts" }, "zipPostalCodeLabel": { "message": "Postnummer" diff --git a/apps/desktop/src/locales/ta/messages.json b/apps/desktop/src/locales/ta/messages.json index f64c237a7c3..d95440ca1a2 100644 --- a/apps/desktop/src/locales/ta/messages.json +++ b/apps/desktop/src/locales/ta/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index 90eee288681..5a5a1715d7e 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index bc72ae0fc14..b87ed307efc 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -4267,8 +4267,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" diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index 3be00ac1393..59995a02096 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -4267,8 +4267,8 @@ "typeShortcut": { "message": "Kısayolu yazın" }, - "editAutotypeShortcutDescription": { - "message": "Aşağıdaki değiştirici tuşlardan birini veya ikisini (Ctrl, Alt, Win ya da Shift) ve bir harf kullanın." + "editAutotypeKeyboardModifiersDescription": { + "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Geçersiz kısayol" diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index ebf6a74ff9b..ed9c76b6ecd 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -4267,8 +4267,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": "Недійсна комбінація клавіш" diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index 9ec16480d73..ff711c154bc 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -4267,8 +4267,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": "Include one or two of the following modifiers: Ctrl, Alt, Win, and a letter." }, "invalidShortcut": { "message": "Phím tắt không hợp lệ" diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index c883192768e..028e7836c41 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -4267,8 +4267,8 @@ "typeShortcut": { "message": "输入快捷键" }, - "editAutotypeShortcutDescription": { - "message": "包含以下一个或两个修饰符:Ctrl、Alt、Win 或 Shift,外加一个字母。" + "editAutotypeKeyboardModifiersDescription": { + "message": "包含以下修饰键中的一个或两个:Ctrl、Alt、Win,以及一个字母。" }, "invalidShortcut": { "message": "无效的快捷键" diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index 17c7b0867ee..7a3ff01e115 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -4267,8 +4267,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": "無效的捷徑" From b4c1d1c149035fc6b3d8d32246e8787ac8d0e4b4 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 9 Jan 2026 15:22:43 +0100 Subject: [PATCH 15/35] Autosync the updated translations (#18278) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/af/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/ar/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/az/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/be/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/bg/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/bn/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/bs/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/ca/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/cs/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/cy/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/da/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/de/messages.json | 93 +++++++++++++++++++- apps/web/src/locales/el/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/en_GB/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/en_IN/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/eo/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/es/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/et/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/eu/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/fa/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/fi/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/fil/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/fr/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/gl/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/he/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/hi/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/hr/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/hu/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/id/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/it/messages.json | 97 ++++++++++++++++++++- apps/web/src/locales/ja/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/ka/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/km/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/kn/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/ko/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/lv/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/ml/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/mr/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/my/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/nb/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/ne/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/nl/messages.json | 91 +++++++++++++++++++- apps/web/src/locales/nn/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/or/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/pl/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/pt_BR/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/pt_PT/messages.json | 97 ++++++++++++++++++++- apps/web/src/locales/ro/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/ru/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/si/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/sk/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/sl/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/sr_CS/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/sr_CY/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/sv/messages.json | 91 +++++++++++++++++++- apps/web/src/locales/ta/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/te/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/th/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/tr/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/uk/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/vi/messages.json | 89 ++++++++++++++++++++ apps/web/src/locales/zh_CN/messages.json | 103 +++++++++++++++++++++-- apps/web/src/locales/zh_TW/messages.json | 89 ++++++++++++++++++++ 63 files changed, 5626 insertions(+), 19 deletions(-) diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index 850ab193da8..6175da95ec8 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Daar is geen gebeure om te lys nie." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Geaktiveer" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Herroep" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send-skakel", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Onttrek van rekeningterugstel" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index af84960c021..dd114daf78e 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "ليس هناك أعضاء لعرضهم." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "لا توجد أية أحداث لعرضها." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "تم التفعيل" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "استعادة الوصول" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "ملغاة" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "إرسال رابط", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index f14a17a60d0..0cb76ce4cb8 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Sadalanacaq heç bir üzv yoxdur." }, + "noMembersToExport": { + "message": ".dillonvince767@gmail.com" + }, "noEventsInList": { "message": "Sadalanacaq heç bir tədbir yoxdur." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Fəallaşdırıldı" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Erişimi bərpa et" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Ləğv edildi" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "\"Send\" keçidi", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Hesab geri qaytarılmasına yazıldınız" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Hesab geri qaytarılması üzrə razılığı geri götür" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Tam onlayn təhlükəsizlik" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index d01e7131107..600eb8cea92 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "У спісе адсутнічаюць удзельнікі." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "У спісе адсутнічаюць падзеі." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Уключана" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Аднавіць доступ" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Адклікана" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Спасылка на Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Зарэгістравацца на аднаўленне ўліковага запісу" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Адклікаць аднаўленне ўліковага запісу" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index dedee053730..dc2dfab4488 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Няма членове за показване." }, + "noMembersToExport": { + "message": "Няма членове за изнасяне." + }, "noEventsInList": { "message": "Няма събития за показване." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Включено" }, + "optionEnabled": { + "message": "Включено" + }, "restoreAccess": { "message": "Възстановяване на достъпа" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Отнет достъп" }, + "accepted": { + "message": "Прието" + }, "sendLink": { "message": "Изпращане на връзката", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Включен във възстановяването на профили" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Оттегляне от възстановяването на профили" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Пълна сигурност в Интернет" + }, + "updatePayment": { + "message": "Актуализиране на плащанията" + }, + "weCouldNotProcessYourPayment": { + "message": "Плащането не беше успешно. Моля, актуализирайте разплащателния си метод или се свържете с екипа по поддръжката за съдействие." + }, + "yourSubscriptionHasExpired": { + "message": "Абонаментът Ви е изтекъл. Моля, свържете се с екипа по поддръжката за съдействие." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Абонаментът Ви по план ще бъде преустановен на $DATE$. Можете да го подновите по всяко време преди това.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Споделяйте още повече със Семейния план, или преминете към подсилената защита на паролите с Екипния план или този за големи организации." + }, + "youHaveAGracePeriod": { + "message": "След като просрочите периода на абонамента си, разполагате с още $DAYS$ дни,. Моля, заплатете старите фактури до $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Управление на фактурите" + }, + "yourNextChargeIsFor": { + "message": "Следващото Ви таксуване ще бъде" + }, + "dueOn": { + "message": "с крайна дата" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Абонаментът Ви ще бъде спрян на" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Абонаментът Ви беше спрян на" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Абонаментът Ви ще бъде прекратен на" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Абонаментът Ви беше прекратен на" + }, + "storageFull": { + "message": "Mястото за съхранение е пълно" + }, + "storageUsedDescription": { + "message": "Използвали сте $USED$ от $AVAILABLE$ GB от наличното си място за съхранение на шифровани данни.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "Използвали сте всичките си $GB$ GB от наличното си място за съхранение на шифровани данни. Ако искате да продължите да добавяте файлове, добавете повече място за съхранение." } } diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index 149ac3ebbca..bf8fbc1e603 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "There are no events to list." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "লিঙ্ক পাঠান", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index c162a5464ec..be24796ae06 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Nema događaja za prikaz." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index cb7be2b2bae..9a5f5244f35 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "No hi ha cap membre a llistar." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "No hi ha cap esdeveniment a llistar." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Habilitat" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restaura l'accés" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revocat" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Enllaç Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Inscrit en la recuperació del compte" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Retirar-se de la recuperació del compte" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index a74167ebe0e..5fcfa8d8edc 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Žádní členové k zobrazení." }, + "noMembersToExport": { + "message": "Žádní členové pro export." + }, "noEventsInList": { "message": "Žádné události k zobrazení." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Zapnuto" }, + "optionEnabled": { + "message": "Povoleno" + }, "restoreAccess": { "message": "Obnovit přístup" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Odvoláno" }, + "accepted": { + "message": "Přijato" + }, "sendLink": { "message": "Odkaz pro Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Zapsán do obnovení účtu" }, + "enrolled": { + "message": "Zapsán" + }, + "notEnrolled": { + "message": "Nezapsán" + }, "withdrawAccountRecovery": { "message": "Odstoupit z obnovení účtu" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Dokončit online zabezpečení" + }, + "updatePayment": { + "message": "Aktualizovat platbu" + }, + "weCouldNotProcessYourPayment": { + "message": "Nemohli jsme zpracovat Vaši platbu. Aktualizujte způsob platby nebo kontaktujte tým podpory pro pomoc." + }, + "yourSubscriptionHasExpired": { + "message": "Vaše předplatné vypršelo. Kontaktujte tým podpory pro pomoc." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Zrušení Vašeho předplatného je naplánováno na $DATE$. Před tím ho můžete kdykoli obnovit.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Sdílejte ještě více s rodinami, nebo získejte mocné, důvěryhodné heslo s týmy nebo Enterprise." + }, + "youHaveAGracePeriod": { + "message": "Máte lhůtu k odkladu $DAYS$ dnů od data vypršení předplatného. Vyřešte poslední splatné faktury do $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Spravovat faktury" + }, + "yourNextChargeIsFor": { + "message": "Vaše další platba je za" + }, + "dueOn": { + "message": "splatná" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Vaše předplatné bude pozastaveno" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Vaše předplatné bylo pozastaveno dne" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Vaše předplatné bude zrušeno dne" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Vaše předplatné bylo zrušeno dne" + }, + "storageFull": { + "message": "Úložiště je plné" + }, + "storageUsedDescription": { + "message": "Využili jste $USED$ z $AVAILABLE$ GB Vašeho šifrovaného úložiště.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "Využili jste celých $GB$ GB Vašeho šifrovaného úložiště. Chcete-li pokračovat v ukládání souborů, přidejte další úložiště." } } diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index 0aaf4d25956..f8028a95b2d 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "There are no events to list." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index 48b77348359..8526782a2e5 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Ingen medlemmer at vise." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Der er ingen begivenheder at vise." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Aktiveret" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Gendan adgang" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Tilbagekaldt" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send-link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Indrulleret i kontogendannelse" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Afmeld fra kontogendannelse" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index 8e5de82e539..40f3e64ec1f 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Keine Mitglieder zum Anzeigen vorhanden." }, + "noMembersToExport": { + "message": "Es gibt keine Mitglieder zum Exportieren." + }, "noEventsInList": { "message": "Keine Ereignisse vorhanden." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Aktiviert" }, + "optionEnabled": { + "message": "Aktiviert" + }, "restoreAccess": { "message": "Zugriff wiederherstellen" }, @@ -3147,7 +3153,7 @@ "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": "Item has been restored" + "message": "Eintrag wurde wiederhergestellt" }, "restartPremium": { "message": "Premium neu starten" @@ -5649,6 +5655,9 @@ "revoked": { "message": "Widerrufen" }, + "accepted": { + "message": "Akzeptiert" + }, "sendLink": { "message": "Send-Link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Für Kontowiederherstellung registriert" }, + "enrolled": { + "message": "Registriert" + }, + "notEnrolled": { + "message": "Nicht registriert" + }, "withdrawAccountRecovery": { "message": "Von Kontowiederherstellung abmelden" }, @@ -11613,7 +11628,7 @@ "message": "Nicht mehr archivieren" }, "archived": { - "message": "Archived" + "message": "Archiviert" }, "unArchiveAndSave": { "message": "Nicht mehr archivieren und speichern" @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Umfassende Online-Sicherheit" + }, + "updatePayment": { + "message": "Zahlungsmethode aktualisieren" + }, + "weCouldNotProcessYourPayment": { + "message": "Wir konnten deine Zahlung nicht verarbeiten. Bitte aktualisiere deine Zahlungsmethode oder wende dich an das Support-Team, um Hilfe zu erhalten." + }, + "yourSubscriptionHasExpired": { + "message": "Dein Abonnement ist abgelaufen. Bitte kontaktiere das Support-Team für Hilfe." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Dein Abonnement wird am $DATE$ gekündigt. Du kannst es davor jederzeit wieder reaktivieren.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Teile noch mehr mit Families oder erhalte eine leistungsstarke und vertrauenswürdige Passwortsicherheit mit Teams oder Enterprise." + }, + "youHaveAGracePeriod": { + "message": "Du hast eine Nachfrist von $DAYS$ Tagen ab Ablaufdatum deines Abonnements. Bitte begleiche die letzten fälligen Rechnungen bis zum $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Rechnungen verwalten" + }, + "yourNextChargeIsFor": { + "message": "Deine nächste Abbuchung erfolgt am" + }, + "dueOn": { + "message": "fällig am" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Dein Abonnement wird deaktiviert am" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Dein Abonnement wurde deaktiviert am" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Dein Abonnement wird gekündigt am" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Dein Abonnement wurde gekündigt am" + }, + "storageFull": { + "message": "Speicher voll" + }, + "storageUsedDescription": { + "message": "Du hast $USED$ von $AVAILABLE$ GB deines verschlüsselten Datenspeichers verwendet.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "Du hast die gesamten $GB$ GB deines verschlüsselten Speichers verwendet. Um mit dem Speichern von Dateien fortzufahren, füge mehr Speicher hinzu." } } diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index d273e8d8df4..22fef811575 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Δεν υπάρχουν μέλη προς εμφάνιση." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Δεν υπάρχουν γεγονότα στη λίστα." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Ενεργοποιημένο" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Επαναφορά πρόσβασης" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Ανακλήθηκαν" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Αποστολή Συνδέσμου", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Έγινε εγγραφή στην ανάκτηση λογαριασμού" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Απόσυρση από την ανάκτηση λογαριασμού" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index 0f2f657e8c4..e940b18682e 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "There are no events to list." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be cancelled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was cancelled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index f222b832edb..a4e904c4e3f 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "There are no events to list." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Enabled" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send Link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be cancelled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was cancelled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index 961aa447cce..ecf4ea16a31 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Estas neniu membro por listi." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Estas neniu evento por listi." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Ŝaltita" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Rehavigi aliron" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Sendi ligilon", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index 3ffce558575..1ed117a249c 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "No hay miembros para mostrar." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "No hay eventos que listar." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Activado" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Recuperar el acceso" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revocado" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Enlace Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Inscrito en la recuperación de la cuenta" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Retirarse de la recuperación de la cuenta" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index 92eed1f84e4..a657243582b 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Puuduvad kasutajad, keda kuvada." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Puuduvad sündmused, mida kuvada." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Sisselülitatud" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Taasta ligipääsu luba" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Eemaldatud" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Sendi link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index 3ab60e246c0..c5fbb39790b 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Ez dago erakusteko gertakaririk." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Gaituta" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Sarbidea berreskuratu" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Ezeztatuak" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send esteka", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index 40fefff18cf..17c4ba2a1e9 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "هیچ عضوی برای نمایش وجود ندارد." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "هیچ مناسبتی برای نمایش وجود ندارد." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "روشن شده" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "بازیابی دسترسی" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "لغو شد" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "ارسال پیوند", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "در بازیابی حساب کاربری ثبت نام شد" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "برداشت از بازیابی حساب" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index fa5bf0b1f92..6c72705fd8e 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Näytettäviä jäseniä ei ole." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Näytettäviä tapahtumia ei ole." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Käytössä" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Palauta käyttöoikeudet" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Mitätöidyt" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send-linkki", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Liitetty tilin palautusapuun" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Eroa tilin palautusavusta" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index f89d13f6804..c7c0fb18775 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Walang maililistang miyembro." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Walang maililistang kaganapan." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Nakabukas" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Ibalik ang access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Binawi ang" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Ipadala ang link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index c4150f0b1e9..cbf4edfe4b8 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Il n'y a pas de membres à répertorier." }, + "noMembersToExport": { + "message": "Il n'y a aucun membre à exporter." + }, "noEventsInList": { "message": "Aucun événement à afficher." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Activé" }, + "optionEnabled": { + "message": "Activé" + }, "restoreAccess": { "message": "Restaurer l'Accès" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Révoqué" }, + "accepted": { + "message": "Accepté" + }, "sendLink": { "message": "Lien du Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Inscrit à la récupération du compte" }, + "enrolled": { + "message": "Inscrit" + }, + "notEnrolled": { + "message": "Non inscrit" + }, "withdrawAccountRecovery": { "message": "Retirer de la récupération du compte" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Sécurité en ligne complète" + }, + "updatePayment": { + "message": "Mettre à jour le paiement" + }, + "weCouldNotProcessYourPayment": { + "message": "Nous n'avons pas pu traiter votre paiement. Veuillez mettre à jour votre méthode de paiement ou contactez l'équipe de support pour obtenir de l'assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Votre abonnement a expiré. Veuillez contacter l'équipe de suuport pour obtenir de l'assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Votre abonnement est programmé pour terminer le $DATE$. Vous pouvez le rétablir à tout moment avant cette date.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Partagez encore plus avec Familles, ou obtenez une sécurité de mot de passe puissante et fiable avec Équipes ou Entreprise." + }, + "youHaveAGracePeriod": { + "message": "Vous bénéficiez d'une période de grace de $DAYS$ jours suivants la date d'expiration de votre abonnement. Veuillez régler les paiements en souffrance pour les factures dont les échéances sont passées d'ici le $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Gérer les factures" + }, + "yourNextChargeIsFor": { + "message": "Votre prochaine charge est de" + }, + "dueOn": { + "message": "dû le" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Votre abonnement sera suspendu le" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Votre abonnement a été suspendu le" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Votre abonnement sera annulé le" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Votre abonnement a été annulé le" + }, + "storageFull": { + "message": "Stockage plein" + }, + "storageUsedDescription": { + "message": "Vous avez utilisé $USED$ sur $AVAILABLE$ Go de votre stockage de fichiers chiffré.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "Vous avez utilisé tous les $GB$ Go de votre stockage chiffré. Pour continuer à stocker des fichiers, ajoutez plus de stockage." } } diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index b386b769064..facbb21f5a4 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "There are no events to list." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index 3774cbe3b3e..5f1969206c2 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "אין חברים להצגה ברשימה." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "אין אירועים להצגה ברשימה." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "מופעל" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "שחזר גישה" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "מבוטל" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "קישור סֵנְד", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "נרשם לשחזור חשבון" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "לסגת משחזור חשבון" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index 17fc058b781..eb1fc912225 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "There are no events to list." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index c68b3a6279d..14d469d5d36 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Nema članova za prikaz." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Nema događaja za prikaz." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Omogućeno" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Vrati pristup" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Opozvano" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Veza na Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Oporavak računa uključen" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Isključi oporavak računa" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index 3ff717d534c..76cf76f205c 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Nincsenek megjeleníthető tagok." }, + "noMembersToExport": { + "message": "Nincsenek exportálható tagok." + }, "noEventsInList": { "message": "Nincsenek megjeleníthető események." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Engedélyezve" }, + "optionEnabled": { + "message": "Engedélyezve" + }, "restoreAccess": { "message": "Hozzáférés helyreállítás" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "A visszavonás megtörtént." }, + "accepted": { + "message": "Elfogadva" + }, "sendLink": { "message": "Send hivatkozás", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Megtörtént a regisztráció fiók helyreállításra." }, + "enrolled": { + "message": "Feliratkozott" + }, + "notEnrolled": { + "message": "Nem feliratkozott" + }, "withdrawAccountRecovery": { "message": "Kilépés a fiók helyreállításból" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Teljes körű online biztonság" + }, + "updatePayment": { + "message": "Fizetés frissítése" + }, + "weCouldNotProcessYourPayment": { + "message": "Nem lehetett feldolgozni a fizetést. Frissítsük a fizetési módot vagy forduljunk segítségért az ügyfélszolgálathoz." + }, + "yourSubscriptionHasExpired": { + "message": "Az előfizetés lejárt. Forduljunk a támogató csapathoz segítségért." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Az előfizetés az időzítés szerint lejár: $DATE$. Ezt megelőzően bármikor visszaállíthatjuk.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Osszunk meg még többet a Családi csomaggal vagy kapjunk hatékony, megbízható jelszóbiztonságot a Teams vagy az Enterprise segítségével." + }, + "youHaveAGracePeriod": { + "message": "Az előfizetés lejárati dátumától számítva $DAYS$ nap türelmi időszak áll rendelkezésére az előfizetés fenntartásához. Rendezzük a lejárt számlákat a következő időpontig: $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Számlák kezelése" + }, + "yourNextChargeIsFor": { + "message": "Következő terhelés" + }, + "dueOn": { + "message": "esedékes" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Az előfizetés felfüggesztésre kerül:" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Az előfizetés felfüggesztésre került:" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Az előfizetés törlésre kerül:" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Az előfizetés törlésre került:" + }, + "storageFull": { + "message": "A tárhely megtelt." + }, + "storageUsedDescription": { + "message": "$USED$ / $AVAILABLE$ GB lett felhasználva a titkosított fájl tárolóból.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "A titkosított tárhely összes $GB$ mérete felhasználásra került. A fájlok tárolásának folytatásához adjunk hozzá további tárhelyet." } } diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index 73a1d220918..95d393d82b0 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Tidak ada anggota untuk ditampilkan." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Tidak ada acara untuk dicantumkan." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Diaktifkan" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Pulihkan Akses" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Dicabut" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Kirim Tautan", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index b9a2b98dc18..63a659662f8 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Nessun membro da mostrare." }, + "noMembersToExport": { + "message": "Nessun elemento da esportare." + }, "noEventsInList": { "message": "Nessun evento da mostrare." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Attivato" }, + "optionEnabled": { + "message": "Attivo" + }, "restoreAccess": { "message": "Ripristina accesso" }, @@ -2635,7 +2641,7 @@ "message": "Chiave" }, "unnamedKey": { - "message": "Unnamed key" + "message": "Chiave senza nome" }, "twoStepAuthenticatorEnterCodeV2": { "message": "Codice di verifica" @@ -3147,7 +3153,7 @@ "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": "Item has been restored" + "message": "L'elemento è stato ripristinato" }, "restartPremium": { "message": "Riavvia Premium" @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revocato" }, + "accepted": { + "message": "Approvato" + }, "sendLink": { "message": "Link del Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Iscritto al recupero dell'account" }, + "enrolled": { + "message": "Registrato" + }, + "notEnrolled": { + "message": "Non registrato" + }, "withdrawAccountRecovery": { "message": "Rifiuta il recupero dell'account" }, @@ -11613,7 +11628,7 @@ "message": "Togli dall'archivio" }, "archived": { - "message": "Archived" + "message": "Archiviato" }, "unArchiveAndSave": { "message": "Togli dall'archivio e salva" @@ -12308,7 +12323,7 @@ "message": "Verifica dell'utente non riuscita." }, "resizeSideNavigation": { - "message": "Resize side navigation" + "message": "Ridimensiona la navigazione laterale" }, "recoveryDeleteCiphersTitle": { "message": "Elimina gli oggetti della cassaforte non recuperabili" @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Sicurezza online completa" + }, + "updatePayment": { + "message": "Aggiorna il metodo di pagamento" + }, + "weCouldNotProcessYourPayment": { + "message": "Non è stato possibile elaborare la transazione. Aggiorna il metodo di pagamento o contatta l'assistenza." + }, + "yourSubscriptionHasExpired": { + "message": "Il tuo abbonamento è scaduto. Contatta il team di supporto per ricevere assistenza." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Il tuo abbonamento è programmato per terminare il $DATE$. Puoi ripristinarlo in qualsiasi momento prima di quella data.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Condividi ancora di più con il piano famiglie, o rafforza le protezioni con Team o Enterprise." + }, + "youHaveAGracePeriod": { + "message": "Hai un periodo di grazia di $DAYS$ giorni dalla fine dell'abbonamento. Ti preghiamo di provvedere ai pagamenti entro il $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Gestisci le fatture" + }, + "yourNextChargeIsFor": { + "message": "Il tuo prossimo addebito è programmato" + }, + "dueOn": { + "message": "per" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Il tuo abbonamento sarà sospeso il" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Il tuo abbonamento è stato sospeso il" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Il tuo abbonamento terminerà il" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Il tuo abbonamento è terminato il" + }, + "storageFull": { + "message": "Spazio di archiviazione pieno" + }, + "storageUsedDescription": { + "message": "Hai usato $USED$ GB su $AVAILABLE$ del tuo archivio file crittografato.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "Hai usato tutti i $GB$ GB del tuo spazio di archiviazione crittografato. Per archiviare altri file, aggiungi altro spazio." } } diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index 2e290f0e4b9..79e65adbd7b 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "表示できるメンバーがいません。" }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "表示するイベントがありません" }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "有効化されました" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "アクセスを復元する" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "取り消し済み" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send リンク", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "アカウント回復に登録しました" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "アカウント回復から登録解除する" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index 4c6c8a6572c..a3806147491 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "არაა წევრები ჩამოსათველად." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "არაა მოვლენები ჩამოსათველად." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index 09f111c30c5..8024de21e56 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "There are no events to list." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index b0e03d029cf..c0fe31906ee 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "ಪಟ್ಟಿ ಮಾಡಲು ಯಾವುದೇ ಘಟನೆಗಳಿಲ್ಲ." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "ಸಕ್ರಿಯಗೊಳಿಸಿದೆ" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "ಲಿಂಕ್ ಕಳುಹಿಸಿ", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index a8eb53841ec..3f56c424333 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "이벤트가 없습니다." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "활성화됨" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send 링크", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index dc6ddbce784..f1ba805ae78 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Nav dalībnieku, ko uzskaitīt." }, + "noMembersToExport": { + "message": "Nav dalībnieku, ko izgūt." + }, "noEventsInList": { "message": "Nav notikumu, ko parādīt." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Iespējots" }, + "optionEnabled": { + "message": "Iespējots" + }, "restoreAccess": { "message": "Atjaunot piekļuvi" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Atsauktie" }, + "accepted": { + "message": "Pieņemts" + }, "sendLink": { "message": "Send saite", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Pieteicies konta atkopei" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Atsaukt konta atkopi" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Pilnīga drošība tiešsaistē" + }, + "updatePayment": { + "message": "Atjaunināt maksājumu" + }, + "weCouldNotProcessYourPayment": { + "message": "Mēs nevarējām apstrādāt maksājumu. Lūgums atjaunināt savu maksājumu veidu vai sazināties ar atbalsta komandu, lai iegūtu palīdzību." + }, + "yourSubscriptionHasExpired": { + "message": "Abonements ir beidzies. Lūgums sazināties ar atbalsta komandu, lai iegūtu palīdzību." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Abonements ir ierindots atcelšanai $DATE$. Līdz tam jebkurā brīdī to var atjaunot.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index dcf3e950dfe..a58e8e310ad 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "പ്രദർശിപ്പിക്കാൻ ഇവന്റുകളൊന്നുമില്ല." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "പ്രവർത്തനക്ഷമമാക്കി" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "ലിങ്ക് അയയ്‌ക്കുക", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index 70771c9fbed..eb6d5b917c7 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "There are no events to list." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index 09f111c30c5..8024de21e56 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "There are no events to list." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index 8bf79f74f07..04f22415ac3 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Det er ingen medlemmer å vise." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Det er ingen hendelser å liste opp." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Aktivert" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Gjenopprett tilgang" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Opphevet" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send lenke", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index 75f4b5524c3..70b3478386f 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "There are no events to list." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index f2f2cebb236..ea802c9aca6 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -1747,7 +1747,10 @@ "message": "Er zijn geen gebruikers om weer te geven." }, "noMembersInList": { - "message": "Er zijn geen leden op weer te geven." + "message": "Er zijn geen leden om weer te geven." + }, + "noMembersToExport": { + "message": "Er zijn geen leden om te exporteren." }, "noEventsInList": { "message": "Er zijn geen gebeurtenissen om weer te geven." @@ -2537,6 +2540,9 @@ "enabled": { "message": "Ingeschakeld" }, + "optionEnabled": { + "message": "Ingeschakeld" + }, "restoreAccess": { "message": "Toegang herstellen" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Ingetrokken" }, + "accepted": { + "message": "Geaccepteerd" + }, "sendLink": { "message": "Send-koppeling", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Aangemeld bij accountherstel" }, + "enrolled": { + "message": "Ingeschreven" + }, + "notEnrolled": { + "message": "Niet ingeschreven" + }, "withdrawAccountRecovery": { "message": "Afmelden voor accountherstel" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Online beveiliging voltooien" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index 1e4f376f083..44d7dbf39c1 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Det er ingen hendingar å syna." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index 09f111c30c5..8024de21e56 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "There are no events to list." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index 272b53eac23..7f2e8da75bd 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Brak członków do wyświetlenia." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Brak wydarzeń do wyświetlenia." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Włączone" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Przywróć dostęp" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Unieważnieni" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Link wysyłki", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Dołączono do odzyskiwania konta" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Wycofaj z odzyskiwania konta" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index 5f5d4500bef..3faa9ef7729 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Não há membros para listar." }, + "noMembersToExport": { + "message": "Não há membros para exportar." + }, "noEventsInList": { "message": "Não há eventos para listar." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Ativado" }, + "optionEnabled": { + "message": "Ativado" + }, "restoreAccess": { "message": "Restaurar acesso" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revogados" }, + "accepted": { + "message": "Aceito" + }, "sendLink": { "message": "Link do Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Inscrito na recuperação de conta" }, + "enrolled": { + "message": "Inscrito" + }, + "notEnrolled": { + "message": "Não inscrito" + }, "withdrawAccountRecovery": { "message": "Retirar-se da recuperação de conta" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Segurança on-line completa" + }, + "updatePayment": { + "message": "Atualizar pagamento" + }, + "weCouldNotProcessYourPayment": { + "message": "Não pudemos processar seu pagamento. Atualize seu método de pagamento ou entre em contato com a equipe de suporte para assistência." + }, + "yourSubscriptionHasExpired": { + "message": "Sua assinatura expirou. Entre em contato com a equipe de suporte para obter assistência." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Sua assinatura está agendada para ser cancelada em $DATE$. Você pode reestabelecê-la a qualquer momento antes disso.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Compartilhe ainda mais com o Famílias, ou receba segurança poderosa e confiável de senhas com o Equipes ou o Empresarial." + }, + "youHaveAGracePeriod": { + "message": "Você tem um período de tolerância de $DAYS$ dias até a data de expiração da sua assinatura. Resolva as faturas atrasadas até $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Gerenciar faturas" + }, + "yourNextChargeIsFor": { + "message": "Sua próxima cobrança é de" + }, + "dueOn": { + "message": "para" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Sua assinatura será suspensa em" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Sua assinatura foi suspensa em" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Sua assinatura será cancelada em" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Sua assinatura foi cancelada em" + }, + "storageFull": { + "message": "Armazenamento cheio" + }, + "storageUsedDescription": { + "message": "Você usou $USED$ dos $AVAILABLE$ GB do seu armazenamento de arquivos criptografados.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "Você usou todos os $GB$ GB do seu armazenamento criptografado. Para continuar armazenando arquivos, adicione mais armazenamento." } } diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index ae6d11eab21..1915d4b49f3 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Não existem membros para listar." }, + "noMembersToExport": { + "message": "Não existem membros para exportar." + }, "noEventsInList": { "message": "Não existem eventos para listar." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Ativado" }, + "optionEnabled": { + "message": "Ativado" + }, "restoreAccess": { "message": "Restaurar o acesso" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revogado" }, + "accepted": { + "message": "Aceite" + }, "sendLink": { "message": "Link do Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Inscrito na recuperação de conta" }, + "enrolled": { + "message": "Inscrito" + }, + "notEnrolled": { + "message": "Não inscrito" + }, "withdrawAccountRecovery": { "message": "Retirar-se da recuperação de conta" }, @@ -8839,7 +8854,7 @@ "message": "Ajuda na licença de funcionalidades pagas" }, "selfHostGracePeriodHelp": { - "message": "Após a expiração da sua subscrição, tem 60 dias para aplicar um ficheiro de licença atualizado à sua organização. O período de carência termina a $GRACE_PERIOD_END_DATE$.", + "message": "Após a expiração da sua subscrição, dispõe de 60 dias para aplicar um ficheiro de licença atualizado à sua organização. O período de tolerância termina a $GRACE_PERIOD_END_DATE$.", "placeholders": { "GRACE_PERIOD_END_DATE": { "content": "$1", @@ -10040,7 +10055,7 @@ "description": "The date header used when a subscription is past due." }, "pastDueWarningForChargeAutomatically": { - "message": "Dispõe de um período de carência de $DAYS$ dias a partir da data de expiração da sua subscrição para manter a sua subscrição. Por favor, resolva as faturas vencidas até $SUSPENSION_DATE$.", + "message": "Dispõe de um período de tolerância de $DAYS$ dias a partir da data de expiração da sua subscrição para manter a subscrição ativa. Por favor, resolva as faturas em atraso até $SUSPENSION_DATE$.", "placeholders": { "days": { "content": "$1", @@ -10054,7 +10069,7 @@ "description": "A warning shown to the user when their subscription is past due and they are charged automatically." }, "pastDueWarningForSendInvoice": { - "message": "Dispõe de um período de carência de $DAYS$ dias a partir da data de vencimento da sua primeira fatura não paga para manter a sua subscrição. Por favor, resolva as faturas vencidas até $SUSPENSION_DATE$.", + "message": "Dispõe de um período de tolerância de $DAYS$ dias a partir da data de vencimento da sua primeira fatura em atraso para manter a subscrição ativa. Por favor, resolva as faturas em atraso até $SUSPENSION_DATE$.", "placeholders": { "days": { "content": "$1", @@ -11468,7 +11483,7 @@ } }, "resellerPastDueWarningMsg": { - "message": "A fatura da sua subscrição não foi paga. Para garantir um serviço ininterrupto, contacte a $RESELLER$ para confirmar a sua renovação antes de $GRACE_PERIOD_END$.", + "message": "A fatura da sua subscrição não foi paga. Para garantir a continuidade do serviço, contacte a $RESELLER$ para confirmar a sua renovação antes de $GRACE_PERIOD_END$.", "placeholders": { "reseller": { "content": "$1", @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Segurança total online" + }, + "updatePayment": { + "message": "Atualizar pagamento" + }, + "weCouldNotProcessYourPayment": { + "message": "Não foi possível processar o seu pagamento. Por favor, atualize o seu método de pagamento ou contacte a equipa de suporte para obter assistência." + }, + "yourSubscriptionHasExpired": { + "message": "A sua subscrição expirou. Por favor, contacte a equipa de suporte para obter assistência." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "A sua subscrição está agendada para ser cancelada em $DATE$. Pode reativá-la a qualquer momento até essa data.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Partilhe ainda mais com o plano Familiar ou obtenha uma segurança de palavras-passe poderosa e fiável com os planos Equipas ou Empresarial." + }, + "youHaveAGracePeriod": { + "message": "Dispõe de um período de tolerância de $DAYS$ dias a partir da data de expiração da sua subscrição. Por favor, resolva as faturas em atraso até $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Gerir faturas" + }, + "yourNextChargeIsFor": { + "message": "O seu próximo pagamento é de" + }, + "dueOn": { + "message": "com vencimento a" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "A sua subscrição será suspensa a" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "A sua subscrição foi suspensa a" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "A sua subscrição será cancelada a" + }, + "yourSubscriptionWasCanceledOn": { + "message": "A sua subscrição foi cancelada a" + }, + "storageFull": { + "message": "Armazenamento cheio" + }, + "storageUsedDescription": { + "message": "Utilizou $USED$ de $AVAILABLE$ GB do seu armazenamento de ficheiros encriptados.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "Utilizou os $GB$ GB do seu armazenamento encriptado. Para continuar a guardar ficheiros, adicione mais espaço de armazenamento." } } diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index 490b33dd8ae..b8be7a0f162 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Niciun eveniment de afișat." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Activat" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restaurare acces" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revocat" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Link Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index 5fe8d27139a..91f6ecf9946 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Нет участников для отображения." }, + "noMembersToExport": { + "message": "Нет участников для экспорта." + }, "noEventsInList": { "message": "Нет событий для отображения." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Включено" }, + "optionEnabled": { + "message": "Включено" + }, "restoreAccess": { "message": "Восстановить доступ" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Отозвано" }, + "accepted": { + "message": "Принято" + }, "sendLink": { "message": "Ссылка на Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Зарегистрирован на восстановление аккаунта" }, + "enrolled": { + "message": "Зарегистрировано" + }, + "notEnrolled": { + "message": "Не зарегистрировано" + }, "withdrawAccountRecovery": { "message": "Сняться с восстановления аккаунта" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Полная онлайн-защищенность" + }, + "updatePayment": { + "message": "Обновить платежную информацию" + }, + "weCouldNotProcessYourPayment": { + "message": "Нам не удалось обработать ваш платеж. Пожалуйста, обновите свой способ оплаты или обратитесь за помощью в службу поддержки." + }, + "yourSubscriptionHasExpired": { + "message": "Срок действия вашей подписки истек. Пожалуйста, обратитесь за помощью в службу поддержки." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Ваша подписка будет отменена $DATE$. Вы сможете восстановить ее в любое время до этого момента.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Делитесь еще большим количеством информации с семьями или обеспечьте надежную защиту паролем с командами или организациями." + }, + "youHaveAGracePeriod": { + "message": "У вас есть льготный период $DAYS$ дней с даты истечения срока действия вашей подписки. Пожалуйста, оплатите просроченные счета к $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Управление счетами" + }, + "yourNextChargeIsFor": { + "message": "Ваша следующая оплата будет за" + }, + "dueOn": { + "message": "срок" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Ваша подписка будет приостановлена в" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Ваша подписка была приостановлена" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Ваша подписка будет отменена в" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Ваша подписка была отменена" + }, + "storageFull": { + "message": "Хранилище заполнено" + }, + "storageUsedDescription": { + "message": "Вы использовали $USED$ из $AVAILABLE$ ГБ вашего зашифрованного файлового хранилища.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "Вы использовали все $GB$ вашего зашифрованного хранилища. Чтобы продолжить хранение файлов, добавьте дополнительное хранилище." } } diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index 34f5fdb6a56..4f0dc60ad6d 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "There are no events to list." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index b881394a479..159f90dec1d 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Neexistujú žiadni členovia na zobrazenie." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Neexistujú žiadne udalosti na zobrazenie." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Povolené" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Obnoviť prístup" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Zrušený prístup" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Odkaz na Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Zapísaný na obnovu konta" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Odhlásiť sa z obnovy konta" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index edd02eaf75d..62edf2df74a 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Ni članov za prikaz." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Ni dogodkov za prikaz." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Omogočeno" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Obnovi dostop" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Povezava pošiljke", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index cd0749da2ad..cd9c52f3f02 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "There are no events to list." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Omogućeno" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/sr_CY/messages.json b/apps/web/src/locales/sr_CY/messages.json index 526557d97e3..d1a4b3776ef 100644 --- a/apps/web/src/locales/sr_CY/messages.json +++ b/apps/web/src/locales/sr_CY/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Нема чланова за приказивање." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Нема догађаја у листи." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Омогућено" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Врати притуп" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Опозвано" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "УРЛ „Send“", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Уписан/а у опоравак налога" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Повуците са опоравка налога" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index b39eeaf1fac..909a17ef7c4 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Det finns inga medlemmar att visa." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Det finns inga händelser att visa." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Aktiverad" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Återställ åtkomst" }, @@ -3147,7 +3153,7 @@ "message": "För att återfå åtkomst till ditt arkiv, starta om Premium-prenumerationen. Om du redigerar detaljer för ett arkiverat objekt innan du startar om kommer det att flyttas tillbaka till ditt valv." }, "itemRestored": { - "message": "Item has been restored" + "message": "Objektet har återställts" }, "restartPremium": { "message": "Starta om Premium" @@ -5649,6 +5655,9 @@ "revoked": { "message": "Återkallad" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send-länk", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Registrerad i kontoåterställning" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Uttag från kontoåterställning" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Komplett säkerhet online" + }, + "updatePayment": { + "message": "Uppdatera betalning" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Ditt abonnemang har löpt ut. Kontakta supportteamet för hjälp." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Ditt abonnemang är planerat att avslutas den $DATE$. Du kan återställa det när som helst innan dess.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Hantera fakturor" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Lagringen är full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/ta/messages.json b/apps/web/src/locales/ta/messages.json index 348147d044c..7b6eb08c614 100644 --- a/apps/web/src/locales/ta/messages.json +++ b/apps/web/src/locales/ta/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "பட்டியலிட உறுப்பினர்கள் யாரும் இல்லை." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "பட்டியலிட நிகழ்வுகள் எதுவும் இல்லை." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "இயக்கப்பட்டது" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "அணுகலை மீட்டமை" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "திரும்பப் பெறப்பட்டது" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send இணைப்பு", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "கணக்கு மீட்டெடுப்பில் பதிவுசெய்யப்பட்டது" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "கணக்கு மீட்டெடுப்பிலிருந்து விலகு" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index 09f111c30c5..8024de21e56 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "There are no events to list." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index 355dfda0f49..ea103206532 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "There are no members to list." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "ไม่มีเหตุการณ์สำหรับแสดง" }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Turned on" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Restore access" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Revoked" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send link", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Enrolled in account recovery" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Withdraw from account recovery" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index fe000f73ed7..97181dff010 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Listelenecek üye yok." }, + "noMembersToExport": { + "message": "Dışa aktarılacak üye yok." + }, "noEventsInList": { "message": "Listelenecek olay yok." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Etkinleştirildi" }, + "optionEnabled": { + "message": "Etkinleştirildi" + }, "restoreAccess": { "message": "Erişimi geri getir" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "İptal edildi" }, + "accepted": { + "message": "Kabul edildi" + }, "sendLink": { "message": "Send bağlantısı", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Hesap kurtarmaya kaydolundu" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Hesap kurtarmadan ayrıl" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Eksiksiz çevrimiçi güvenlik" + }, + "updatePayment": { + "message": "Ödemeyi güncelle" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Faturaları yönet" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Depolama alanı dolu" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index e9d1a3e1551..155b0eed971 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Список не містить учасників." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Немає подій." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Увімкнено" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Відновити доступ" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Відкликані" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Посилання на відправлення", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Розгорнуто на відновлення облікового запису" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Відкликати відновлення облікового запису" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index 3e1f74bccba..803c283fa52 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "Không có người nào để liệt kê." }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "Chưa có sự kiện nào." }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "Kích hoạt" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "Khôi phục quyền truy cập" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "Đã thu hồi" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Liên kết Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "Đã đăng ký khôi phục tài khoản" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "Rút khỏi khôi phục tài khoản" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "Complete online security" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index f6b2adfc3da..a5cca606972 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -236,7 +236,7 @@ "message": "标记为关键的应用程序" }, "criticalApplicationsMarkedSuccess": { - "message": "$COUNT$ 个标记为关键的应用程序", + "message": "$COUNT$ 个应用程序标记为关键", "placeholders": { "count": { "content": "$1", @@ -341,7 +341,7 @@ "message": "总的应用程序" }, "applicationsNeedingReview": { - "message": "应用程序需要审查" + "message": "需要审查的应用程序" }, "newApplicationsCardTitle": { "message": "审查新应用程序" @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "没有可列出的成员。" }, + "noMembersToExport": { + "message": "没有可导出的成员。" + }, "noEventsInList": { "message": "没有可列出的事件。" }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "已启用" }, + "optionEnabled": { + "message": "已启用" + }, "restoreAccess": { "message": "恢复访问权限" }, @@ -3219,7 +3225,7 @@ "message": "您的方案包含了 7 天的免费试用。在试用期结束前,不会从您的付款方式中扣款。您可以随时取消。" }, "paymentInformation": { - "message": "支付信息" + "message": "付款信息" }, "billingInformation": { "message": "计费信息" @@ -3228,7 +3234,7 @@ "message": "在 7 天免费试用期间,不会从您的付款方式中扣款。" }, "creditCard": { - "message": "支付卡" + "message": "信用卡" }, "paypalClickSubmit": { "message": "选择 PayPal 按钮登录您的 PayPal 账户,然后点击下面的「提交」按钮继续。" @@ -5235,7 +5241,7 @@ "message": "您的 API 密钥可用于验证 Bitwarden 公共 API。" }, "apiKeyRotateDesc": { - "message": "轮换 API 密钥将使前一个密钥失效。如果您认为当前密钥不再安全,可以轮换 API 密钥。" + "message": "轮换 API 密钥将使之前的密钥失效。如果您认为当前密钥不再安全,您可以轮换您的 API 密钥。" }, "apiKeyWarning": { "message": "您的 API 密钥拥有组织的全部访问权限。请严格保密。" @@ -5649,6 +5655,9 @@ "revoked": { "message": "已撤销" }, + "accepted": { + "message": "已接受" + }, "sendLink": { "message": "Send 链接", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -5941,7 +5950,7 @@ "message": "自动用户确认可能对您的组织数据带来安全风险。" }, "autoConfirmAcceptSecurityRiskLearnMore": { - "message": "进一步了解此风险", + "message": "了解此风险", "description": "The is the link copy for the first check box option in the edit policy dialog" }, "autoConfirmSingleOrgRequired": { @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "已注册账户恢复" }, + "enrolled": { + "message": "已注册" + }, + "notEnrolled": { + "message": "未注册" + }, "withdrawAccountRecovery": { "message": "撤销账户恢复" }, @@ -10852,7 +10867,7 @@ "message": "进一步了解搜索密码库" }, "learnMoreAboutYourAccountFingerprintPhrase": { - "message": "进一步了解账户指纹短语" + "message": "了解您的账户指纹短语" }, "impactOfRotatingYourEncryptionKey": { "message": "轮换加密密钥的影响" @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "全面的在线安全防护" + }, + "updatePayment": { + "message": "更新付款信息" + }, + "weCouldNotProcessYourPayment": { + "message": "我们无法处理您的付款。请更新您的付款方式或联系支持团队寻求帮助。" + }, + "yourSubscriptionHasExpired": { + "message": "您的订阅已过期。请联系支持团队寻求帮助。" + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "您的订阅将于 $DATE$ 取消。在此日期之前,您可以随时恢复订阅。", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "使用家庭版共享更多内容,或使用团队版或企业版获得强大、可信赖的密码安全防护。" + }, + "youHaveAGracePeriod": { + "message": "从您的订阅到期之日起,您有 $DAYS$ 天的宽限期。请在 $DATE$ 之前处理逾期未支付的账单。", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "管理账单" + }, + "yourNextChargeIsFor": { + "message": "您的下一次收费是用于" + }, + "dueOn": { + "message": "到期日期为" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "您的订阅将被暂停于" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "您的订阅被暂停于" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "您的订阅将被取消于" + }, + "yourSubscriptionWasCanceledOn": { + "message": "您的订阅被取消于" + }, + "storageFull": { + "message": "存储空间已满" + }, + "storageUsedDescription": { + "message": "总计 $AVAILABLE$ GB 加密文件存储空间,您已使用 $USED$ GB。", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "您已使用了全部的 $GB$ GB 加密存储空间。要继续存储文件,请添加更多存储空间。" } } diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index 9b44b0c3b27..5fc80815483 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -1749,6 +1749,9 @@ "noMembersInList": { "message": "沒有可列出的成員。" }, + "noMembersToExport": { + "message": "There are no members to export." + }, "noEventsInList": { "message": "沒有可列出的事件。" }, @@ -2537,6 +2540,9 @@ "enabled": { "message": "已啟用" }, + "optionEnabled": { + "message": "Enabled" + }, "restoreAccess": { "message": "還原存取權限" }, @@ -5649,6 +5655,9 @@ "revoked": { "message": "已撤銷" }, + "accepted": { + "message": "Accepted" + }, "sendLink": { "message": "Send 連結", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -6307,6 +6316,12 @@ "enrolledAccountRecovery": { "message": "已注冊帳戶復原" }, + "enrolled": { + "message": "Enrolled" + }, + "notEnrolled": { + "message": "Not enrolled" + }, "withdrawAccountRecovery": { "message": "撤銷帳戶復原" }, @@ -12491,5 +12506,79 @@ }, "planDescPremium": { "message": "完整的線上安全防護" + }, + "updatePayment": { + "message": "Update payment" + }, + "weCouldNotProcessYourPayment": { + "message": "We could not process your payment. Please update your payment method or contact the support team for assistance." + }, + "yourSubscriptionHasExpired": { + "message": "Your subscription has expired. Please contact the support team for assistance." + }, + "yourSubscriptionIsScheduledToCancel": { + "message": "Your subscription is scheduled to cancel on $DATE$. You can reinstate it anytime before then.", + "placeholders": { + "date": { + "content": "$1", + "example": "Dec. 22, 2025" + } + } + }, + "premiumShareEvenMore": { + "message": "Share even more with Families, or get powerful, trusted password security with Teams or Enterprise." + }, + "youHaveAGracePeriod": { + "message": "You have a grace period of $DAYS$ days from your subscription expiration date. Please resolve the past due invoices by $DATE$.", + "placeholders": { + "days": { + "content": "$1", + "example": "14" + }, + "date": { + "content": "$2", + "example": "Dec. 22, 2025" + } + } + }, + "manageInvoices": { + "message": "Manage invoices" + }, + "yourNextChargeIsFor": { + "message": "Your next charge is for" + }, + "dueOn": { + "message": "due on" + }, + "yourSubscriptionWillBeSuspendedOn": { + "message": "Your subscription will be suspended on" + }, + "yourSubscriptionWasSuspendedOn": { + "message": "Your subscription was suspended on" + }, + "yourSubscriptionWillBeCanceledOn": { + "message": "Your subscription will be canceled on" + }, + "yourSubscriptionWasCanceledOn": { + "message": "Your subscription was canceled on" + }, + "storageFull": { + "message": "Storage full" + }, + "storageUsedDescription": { + "message": "You have used $USED$ out of $AVAILABLE$ GB of your encrypted file storage.", + "placeholders": { + "used": { + "content": "$1", + "example": "1" + }, + "available": { + "content": "$2", + "example": "5" + } + } + }, + "storageFullDescription": { + "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } } From f7f4ac0bcc6fc4cd9c891db14300596040718440 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 9 Jan 2026 15:22:56 +0100 Subject: [PATCH 16/35] Autosync the updated translations (#18277) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/ar/messages.json | 18 +++++++++++++ apps/browser/src/_locales/az/messages.json | 18 +++++++++++++ apps/browser/src/_locales/be/messages.json | 18 +++++++++++++ apps/browser/src/_locales/bg/messages.json | 18 +++++++++++++ apps/browser/src/_locales/bn/messages.json | 18 +++++++++++++ apps/browser/src/_locales/bs/messages.json | 18 +++++++++++++ apps/browser/src/_locales/ca/messages.json | 18 +++++++++++++ apps/browser/src/_locales/cs/messages.json | 18 +++++++++++++ apps/browser/src/_locales/cy/messages.json | 18 +++++++++++++ apps/browser/src/_locales/da/messages.json | 18 +++++++++++++ apps/browser/src/_locales/de/messages.json | 24 ++++++++++++++--- apps/browser/src/_locales/el/messages.json | 18 +++++++++++++ apps/browser/src/_locales/en_GB/messages.json | 18 +++++++++++++ apps/browser/src/_locales/en_IN/messages.json | 18 +++++++++++++ apps/browser/src/_locales/es/messages.json | 18 +++++++++++++ apps/browser/src/_locales/et/messages.json | 18 +++++++++++++ apps/browser/src/_locales/eu/messages.json | 18 +++++++++++++ apps/browser/src/_locales/fa/messages.json | 18 +++++++++++++ apps/browser/src/_locales/fi/messages.json | 18 +++++++++++++ apps/browser/src/_locales/fil/messages.json | 18 +++++++++++++ apps/browser/src/_locales/fr/messages.json | 18 +++++++++++++ apps/browser/src/_locales/gl/messages.json | 18 +++++++++++++ apps/browser/src/_locales/he/messages.json | 18 +++++++++++++ apps/browser/src/_locales/hi/messages.json | 18 +++++++++++++ apps/browser/src/_locales/hr/messages.json | 18 +++++++++++++ apps/browser/src/_locales/hu/messages.json | 18 +++++++++++++ apps/browser/src/_locales/id/messages.json | 18 +++++++++++++ apps/browser/src/_locales/it/messages.json | 26 ++++++++++++++++--- apps/browser/src/_locales/ja/messages.json | 18 +++++++++++++ apps/browser/src/_locales/ka/messages.json | 18 +++++++++++++ apps/browser/src/_locales/km/messages.json | 18 +++++++++++++ apps/browser/src/_locales/kn/messages.json | 18 +++++++++++++ apps/browser/src/_locales/ko/messages.json | 18 +++++++++++++ apps/browser/src/_locales/lt/messages.json | 18 +++++++++++++ apps/browser/src/_locales/lv/messages.json | 18 +++++++++++++ apps/browser/src/_locales/ml/messages.json | 18 +++++++++++++ apps/browser/src/_locales/mr/messages.json | 18 +++++++++++++ apps/browser/src/_locales/my/messages.json | 18 +++++++++++++ apps/browser/src/_locales/nb/messages.json | 18 +++++++++++++ apps/browser/src/_locales/ne/messages.json | 18 +++++++++++++ apps/browser/src/_locales/nl/messages.json | 18 +++++++++++++ apps/browser/src/_locales/nn/messages.json | 18 +++++++++++++ apps/browser/src/_locales/or/messages.json | 18 +++++++++++++ apps/browser/src/_locales/pl/messages.json | 22 ++++++++++++++-- apps/browser/src/_locales/pt_BR/messages.json | 18 +++++++++++++ apps/browser/src/_locales/pt_PT/messages.json | 18 +++++++++++++ apps/browser/src/_locales/ro/messages.json | 18 +++++++++++++ apps/browser/src/_locales/ru/messages.json | 18 +++++++++++++ apps/browser/src/_locales/si/messages.json | 18 +++++++++++++ apps/browser/src/_locales/sk/messages.json | 18 +++++++++++++ apps/browser/src/_locales/sl/messages.json | 18 +++++++++++++ apps/browser/src/_locales/sr/messages.json | 18 +++++++++++++ apps/browser/src/_locales/sv/messages.json | 20 +++++++++++++- apps/browser/src/_locales/ta/messages.json | 18 +++++++++++++ apps/browser/src/_locales/te/messages.json | 18 +++++++++++++ apps/browser/src/_locales/th/messages.json | 18 +++++++++++++ apps/browser/src/_locales/tr/messages.json | 18 +++++++++++++ apps/browser/src/_locales/uk/messages.json | 18 +++++++++++++ apps/browser/src/_locales/vi/messages.json | 18 +++++++++++++ apps/browser/src/_locales/zh_CN/messages.json | 18 +++++++++++++ apps/browser/src/_locales/zh_TW/messages.json | 18 +++++++++++++ 61 files changed, 1108 insertions(+), 10 deletions(-) diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index debe3f82c1b..bfea785fc63 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index 547ebee9500..f94b0bfc501 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -4811,6 +4811,24 @@ "adminConsole": { "message": "Admin Konsolu" }, + "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" + }, "accountSecurity": { "message": "Hesab güvənliyi" }, diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 5f765c0045d..81f7fefdd9f 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Бяспеке акаўнта" }, diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index c94e8ff2fdc..4fcca092639 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -4811,6 +4811,24 @@ "adminConsole": { "message": "Административна конзола" }, + "admin": { + "message": "Администратор" + }, + "automaticUserConfirmation": { + "message": "Автоматично потвърждение на потребителите" + }, + "automaticUserConfirmationHint": { + "message": "Автоматично потвърждение на потребителите, когато това устройство е отключено" + }, + "autoConfirmOnboardingCallout": { + "message": "Спестете време с автоматичното потвърждение на потребителите" + }, + "autoConfirmWarning": { + "message": "Това може да се отрази на сигурността на данните в организацията Ви. " + }, + "autoConfirmWarningLink": { + "message": "Научете повече за рисковете" + }, "accountSecurity": { "message": "Защита на регистрацията" }, diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 106b61dd9f8..753418ab603 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index ac17bac7097..a9a6c77e7ae 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 96944f45b5b..c195fd8c0b2 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Seguretat del compte" }, diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index 1d00f81a62c..1087cf67c7c 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Zabezpečení účtu" }, diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index c6a380da1a6..0b8d45e4042 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Diogelwch eich cyfrif" }, diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 4871e7e7100..8b35b2049ca 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Kontosikkerhed" }, diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index d996c1d0835..8363d97c5e2 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -586,7 +586,7 @@ "message": "Für die Nutzung des Archivs ist eine Premium-Mitgliedschaft erforderlich." }, "itemRestored": { - "message": "Item has been restored" + "message": "Eintrag wurde wiederhergestellt" }, "edit": { "message": "Bearbeiten" @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Kontosicherheit" }, @@ -5668,10 +5686,10 @@ "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": "Vulnerable password." + "message": "Gefährdetes Passwort." }, "changeNow": { - "message": "Change now" + "message": "Jetzt ändern" }, "missingWebsite": { "message": "Fehlende Website" diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index e05cc5f4d6a..ea86a8137a8 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Ασφάλεια λογαριασμού" }, diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 1e22c5ffa34..4b773754d24 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index cbb2851f872..d841974a386 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 1160899a4d3..fab3e5f5bd9 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Seguridad de la cuenta" }, diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 69fa7ef8bfc..674fc7f85e9 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index d5aeb2ce295..175dff36dce 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 510b71de2ee..770c83f1d0e 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "امنیت حساب کاربری" }, diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 011c5fb9026..3ad41bc0b78 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Tilin suojaus" }, diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 260449921bd..d6d34948cf4 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 0cd6abfee60..56604fa3a10 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Sécurité du compte" }, diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 642659da268..efd1396eac9 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Seguridade da conta" }, diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index b59499f1d6d..2b466187b70 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "אבטחת החשבון" }, diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index 637c1943174..5458ff6f671 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index bef42a97294..d0a46669fd9 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Sigurnost računa" }, diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index 915f2241efd..38cf27cfdc2 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Fiókbiztonság" }, diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 643b72125a2..9d50cc7f048 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Keamanan akun" }, diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index a74b4aa4757..2fd85c2096e 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -586,7 +586,7 @@ "message": "Per utilizzare Archivio è necessario un abbonamento premium." }, "itemRestored": { - "message": "Item has been restored" + "message": "L'elemento è stato ripristinato" }, "edit": { "message": "Modifica" @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Sicurezza dell'account" }, @@ -5668,10 +5686,10 @@ "message": "Questo login è a rischio e non contiene un sito web. Aggiungi un sito web e cambia la password per maggiore sicurezza." }, "vulnerablePassword": { - "message": "Vulnerable password." + "message": "Password vulnerabile." }, "changeNow": { - "message": "Change now" + "message": "Cambiala subito!" }, "missingWebsite": { "message": "Sito web mancante" @@ -6050,6 +6068,6 @@ "message": "Perché vedo questo avviso?" }, "resizeSideNavigation": { - "message": "Resize side navigation" + "message": "Ridimensiona la navigazione laterale" } } diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index 8233240d728..ed377335590 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "アカウントのセキュリティ" }, diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index 7b79332d906..b54ca90d920 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "ანგარიშის უსაფრთხოება" }, diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index ea4f2b08a85..26184ebf9c1 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 6b5c8251bf1..ec287d84d9d 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 8841a307e5a..14c97b086fe 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "계정 보안" }, diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index bb95441b30a..44574d5bae8 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Paskyros saugumas" }, diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 9ef89e918b1..eca6a7f92af 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Konta drošība" }, diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index bb025788e18..49afe2b7db5 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 8440297105c..db264f4d571 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index ea4f2b08a85..26184ebf9c1 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index fcf1a3f14d9..c588aa0ef39 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Kontosikkerhet" }, diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index ea4f2b08a85..26184ebf9c1 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index ac465690dcd..d4bb4a8f6d9 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Accountbeveiliging" }, diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index ea4f2b08a85..26184ebf9c1 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index ea4f2b08a85..26184ebf9c1 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 5162829669d..4392b744e97 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -586,7 +586,7 @@ "message": "A premium membership is required to use Archive." }, "itemRestored": { - "message": "Item has been restored" + "message": "Element został przywrócony" }, "edit": { "message": "Edytuj" @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Bezpieczeństwo konta" }, @@ -5671,7 +5689,7 @@ "message": "Vulnerable password." }, "changeNow": { - "message": "Change now" + "message": "Zmień teraz" }, "missingWebsite": { "message": "Brak strony internetowej" diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index 2a35a6f0c64..a21fefd54a5 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Segurança da conta" }, diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index fdf3ba2d164..69c97e5ad78 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -4811,6 +4811,24 @@ "adminConsole": { "message": "Consola de administração" }, + "admin": { + "message": "Administrador" + }, + "automaticUserConfirmation": { + "message": "Confirmação automática do utilizador" + }, + "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" + }, "accountSecurity": { "message": "Segurança da conta" }, diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 44c4abba934..40faec2782a 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 2b96b2038a5..64e9c877c48 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -4811,6 +4811,24 @@ "adminConsole": { "message": "консоли администратора" }, + "admin": { + "message": "Администратор" + }, + "automaticUserConfirmation": { + "message": "Автоматическое подтверждение пользователя" + }, + "automaticUserConfirmationHint": { + "message": "Автоматически подтверждать ожидающих пользователей пока это устройство разблокировано" + }, + "autoConfirmOnboardingCallout": { + "message": "Экономьте время благодаря автоматическому подтверждению пользователей" + }, + "autoConfirmWarning": { + "message": "Это может повлиять на безопасность данных вашей организации. " + }, + "autoConfirmWarningLink": { + "message": "Узнайте о рисках" + }, "accountSecurity": { "message": "Безопасность аккаунта" }, diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index c06249e55cb..79ff6de2618 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index 9c7cdec8c8f..6b8d752a7fc 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Zabezpečenie účtu" }, diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 7d9eef643a3..3f82082adf2 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 223d5909d41..59e1ce9335e 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Безбедност налога" }, diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 4a9fc27dd84..81a7fda2e39 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -586,7 +586,7 @@ "message": "Ett premium-medlemskap krävs för att använda Arkiv." }, "itemRestored": { - "message": "Item has been restored" + "message": "Objektet har återställts" }, "edit": { "message": "Redigera" @@ -4811,6 +4811,24 @@ "adminConsole": { "message": "Adminkonsol" }, + "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" + }, "accountSecurity": { "message": "Kontosäkerhet" }, diff --git a/apps/browser/src/_locales/ta/messages.json b/apps/browser/src/_locales/ta/messages.json index d11b2329b3f..db9e2f519af 100644 --- a/apps/browser/src/_locales/ta/messages.json +++ b/apps/browser/src/_locales/ta/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "கணக்கு பாதுகாப்பு" }, diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index ea4f2b08a85..26184ebf9c1 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Account security" }, diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index f12bed9ea18..53a2c56ae94 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "ความปลอดภัยของบัญชี" }, diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index 84b240c2397..d2356eb133f 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -4811,6 +4811,24 @@ "adminConsole": { "message": "Yönetici Konsolu" }, + "admin": { + "message": "Yönetici" + }, + "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" + }, "accountSecurity": { "message": "Hesap güvenliği" }, diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index 2688995d6a7..854c43872e5 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "Безпека облікового запису" }, diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index e00aae84e50..df7d6c00d7b 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -4811,6 +4811,24 @@ "adminConsole": { "message": "Bảng điều khiển dành cho quản trị viên" }, + "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" + }, "accountSecurity": { "message": "Bảo mật tài khoản" }, diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index ef2ac258078..1658ce23944 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -4811,6 +4811,24 @@ "adminConsole": { "message": "管理控制台" }, + "admin": { + "message": "管理员" + }, + "automaticUserConfirmation": { + "message": "自动用户确认" + }, + "automaticUserConfirmationHint": { + "message": "当此设备已解锁时,自动确认待处理的用户" + }, + "autoConfirmOnboardingCallout": { + "message": "通过自动用户确认节省时间" + }, + "autoConfirmWarning": { + "message": "这可能会影响您组织的数据安全。" + }, + "autoConfirmWarningLink": { + "message": "了解此风险" + }, "accountSecurity": { "message": "账户安全" }, diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index b43739639c5..5a772b12476 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -4811,6 +4811,24 @@ "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" + }, "accountSecurity": { "message": "帳戶安全性" }, From c6f704bd219609d5630d5f105ee6350c77c30625 Mon Sep 17 00:00:00 2001 From: Daniel Riera Date: Fri, 9 Jan 2026 10:37:16 -0500 Subject: [PATCH 17/35] [PM-29518] Remove @ts-strict-ignore in overlay/inline-menu/content/autofill-inline-menu-content.service.ts (#18155) * Initialized the observers directly in the constructor and removed setupMutationObserver * explicitly initialize timers as null * removed redundant checks for inlineMenuEnabled and tracked the button and list so TS knows they are definitely assigned * early returns for processContainerElementMutation list and button checks, last child now has a fallback to 0 for undefined * Update apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts Co-authored-by: Jonathan Prusik --------- Co-authored-by: Jonathan Prusik --- .../autofill-inline-menu-content.service.ts | 79 ++++++++----------- 1 file changed, 32 insertions(+), 47 deletions(-) diff --git a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts index b61e5e19d53..c2f872d7ba5 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { InlineMenuElementPosition, InlineMenuPosition, @@ -62,8 +60,8 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte */ private inlineMenuEnabled = true; private mutationObserverIterations = 0; - private mutationObserverIterationsResetTimeout: number | NodeJS.Timeout; - private handlePersistentLastChildOverrideTimeout: number | NodeJS.Timeout; + private mutationObserverIterationsResetTimeout: number | NodeJS.Timeout | null = null; + private handlePersistentLastChildOverrideTimeout: number | NodeJS.Timeout | null = null; private lastElementOverrides: WeakMap = new WeakMap(); private readonly customElementDefaultStyles: Partial = { all: "initial", @@ -77,7 +75,21 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte }; constructor() { - this.setupMutationObserver(); + /** + * Sets up mutation observers for the inline menu elements, the menu container, and + * the document element. The mutation observers are used to remove any styles that + * are added to the inline menu elements by the website. They are also used to ensure + * that the inline menu elements are always present at the bottom of the menu container. + */ + this.htmlMutationObserver = new MutationObserver(this.handlePageMutations); + this.bodyMutationObserver = new MutationObserver(this.handlePageMutations); + this.inlineMenuElementsMutationObserver = new MutationObserver( + this.handleInlineMenuElementMutationObserverUpdate, + ); + this.containerElementMutationObserver = new MutationObserver( + this.handleContainerElementMutationObserverUpdate, + ); + this.observePageAttributes(); } /** @@ -181,12 +193,8 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * Updates the position of the inline menu button. */ private async appendButtonElement(): Promise { - if (!this.inlineMenuEnabled) { - return; - } - if (!this.buttonElement) { - this.createButtonElement(); + this.buttonElement = this.createButtonElement(); this.updateCustomElementDefaultStyles(this.buttonElement); } @@ -201,12 +209,8 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * Updates the position of the inline menu list. */ private async appendListElement(): Promise { - if (!this.inlineMenuEnabled) { - return; - } - if (!this.listElement) { - this.createListElement(); + this.listElement = this.createListElement(); this.updateCustomElementDefaultStyles(this.listElement); } @@ -257,16 +261,12 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * to create the element if it already exists in the DOM. */ private createButtonElement() { - if (!this.inlineMenuEnabled) { - return; - } - if (this.isFirefoxBrowser) { this.buttonElement = globalThis.document.createElement("div"); this.buttonElement.setAttribute("popover", "manual"); new AutofillInlineMenuButtonIframe(this.buttonElement); - return; + return this.buttonElement; } const customElementName = this.generateRandomCustomElementName(); @@ -282,6 +282,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte this.buttonElement = globalThis.document.createElement(customElementName); this.buttonElement.setAttribute("popover", "manual"); + return this.buttonElement; } /** @@ -289,16 +290,12 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * to create the element if it already exists in the DOM. */ private createListElement() { - if (!this.inlineMenuEnabled) { - return; - } - if (this.isFirefoxBrowser) { this.listElement = globalThis.document.createElement("div"); this.listElement.setAttribute("popover", "manual"); new AutofillInlineMenuListIframe(this.listElement); - return; + return this.listElement; } const customElementName = this.generateRandomCustomElementName(); @@ -314,6 +311,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte this.listElement = globalThis.document.createElement(customElementName); this.listElement.setAttribute("popover", "manual"); + return this.listElement; } /** @@ -330,27 +328,6 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte this.observeCustomElements(); } - /** - * Sets up mutation observers for the inline menu elements, the menu container, and - * the document element. The mutation observers are used to remove any styles that - * are added to the inline menu elements by the website. They are also used to ensure - * that the inline menu elements are always present at the bottom of the menu container. - */ - private setupMutationObserver = () => { - this.htmlMutationObserver = new MutationObserver(this.handlePageMutations); - this.bodyMutationObserver = new MutationObserver(this.handlePageMutations); - - this.inlineMenuElementsMutationObserver = new MutationObserver( - this.handleInlineMenuElementMutationObserverUpdate, - ); - - this.containerElementMutationObserver = new MutationObserver( - this.handleContainerElementMutationObserverUpdate, - ); - - this.observePageAttributes(); - }; - /** * Sets up mutation observers to verify that the inline menu * elements are not modified by the website. @@ -652,6 +629,10 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte return; } + if (!this.buttonElement) { + return; + } + const lastChild = containerElement.lastElementChild; const secondToLastChild = lastChild?.previousElementSibling; const lastChildIsInlineMenuList = lastChild === this.listElement; @@ -667,7 +648,8 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte this.lastElementOverrides.set(lastChild, lastChildEncounterCount + 1); } - if (this.lastElementOverrides.get(lastChild) >= 3) { + const lastChildEncounterCountAfterUpdate = this.lastElementOverrides.get(lastChild) || 0; + if (lastChildEncounterCountAfterUpdate >= 3) { this.handlePersistentLastChildOverride(lastChild); return; @@ -686,6 +668,9 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte (lastChildIsInlineMenuList && !secondToLastChildIsInlineMenuButton) || (lastChildIsInlineMenuButton && isInlineMenuListVisible) ) { + if (!this.listElement) { + return; + } containerElement.insertBefore(this.buttonElement, this.listElement); return; } From 392794b560cdc598c322a99930eff0dc50a9253f Mon Sep 17 00:00:00 2001 From: Brandon Treston Date: Fri, 9 Jan 2026 12:41:39 -0500 Subject: [PATCH 18/35] Ac/pm 26365 auto confirm extension one time setup dialog (#17339) * create nav link for auto confirm in settings page * wip * WIP * create auto confirm library * migrate auto confirm files to lib * update imports * fix tests * fix nudge * cleanup, add documentation * clean up * cleanup * fix import * fix more imports * implement one time dialog * add tests * design changes * fix styles * edit copy * fix tests * fix tw issue * fix typo, add tests * CR feedback * more clean up, fix race condition * CR feedback, cache policies, refactor tests * run prettier with updated version * clean up duplicate logic * clean up * add missing export * fix test * fix dialog position * add tests --- apps/browser/src/_locales/en/messages.json | 15 +++ .../vault-v2/vault-v2.component.spec.ts | 103 ++++++++++++++++++ .../components/vault-v2/vault-v2.component.ts | 39 +++++++ ...auto-confirm-extension-dialog.component.ts | 78 +++++++++++++ .../auto-confirm-warning-dialog.component.ts | 11 +- libs/auto-confirm/src/components/index.ts | 1 + 6 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 libs/auto-confirm/src/components/auto-confirm-extension-dialog.component.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 29b39863bc6..1613373bd62 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -4829,6 +4829,21 @@ "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" }, 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 883d17b61c3..e6dffdaff08 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 @@ -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"; @@ -136,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 @@ -145,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(); @@ -222,6 +232,13 @@ describe("VaultV2Component", () => { 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({ @@ -275,6 +292,10 @@ describe("VaultV2Component", () => { provide: SearchService, useValue: { isCipherSearching$: of(false) }, }, + { + provide: AutomaticUserConfirmationService, + useValue: autoConfirmSvc, + }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); @@ -588,4 +609,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 30d1d21abfb..761b366bcd2 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 @@ -15,6 +15,7 @@ import { shareReplay, switchMap, take, + withLatestFrom, tap, BehaviorSubject, } from "rxjs"; @@ -25,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"; @@ -41,6 +47,7 @@ import { ButtonModule, DialogService, NoItemsModule, + ToastService, TypographyModule, } from "@bitwarden/components"; import { @@ -267,6 +274,8 @@ 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, @@ -329,6 +338,36 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { }); }); + 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); diff --git a/libs/auto-confirm/src/components/auto-confirm-extension-dialog.component.ts b/libs/auto-confirm/src/components/auto-confirm-extension-dialog.component.ts new file mode 100644 index 00000000000..c04d8b5209b --- /dev/null +++ b/libs/auto-confirm/src/components/auto-confirm-extension-dialog.component.ts @@ -0,0 +1,78 @@ +import { DialogRef } from "@angular/cdk/dialog"; +import { CommonModule } from "@angular/common"; +import { ChangeDetectionStrategy, Component } from "@angular/core"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { + BadgeComponent, + ButtonModule, + CenterPositionStrategy, + DialogModule, + DialogService, +} from "@bitwarden/components"; + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` + +
+
+ {{ "availableNow" | i18n }} +
+
+

+ + {{ "autoConfirmSetup" | i18n }} + +

+ + {{ "autoConfirmSetupDesc" | i18n }} + +
+
+ +
+ + + + + {{ "autoConfirmSetupHint" | i18n }} + + + +
+
+
+ `, + imports: [ButtonModule, DialogModule, CommonModule, JslibModule, BadgeComponent], +}) +export class AutoConfirmExtensionSetupDialogComponent { + constructor(public dialogRef: DialogRef) {} + + static open(dialogService: DialogService) { + return dialogService.open(AutoConfirmExtensionSetupDialogComponent, { + positionStrategy: new CenterPositionStrategy(), + }); + } +} diff --git a/libs/auto-confirm/src/components/auto-confirm-warning-dialog.component.ts b/libs/auto-confirm/src/components/auto-confirm-warning-dialog.component.ts index f126ce3b92c..877a0fe918a 100644 --- a/libs/auto-confirm/src/components/auto-confirm-warning-dialog.component.ts +++ b/libs/auto-confirm/src/components/auto-confirm-warning-dialog.component.ts @@ -2,7 +2,12 @@ import { DialogRef } from "@angular/cdk/dialog"; import { CommonModule } from "@angular/common"; import { ChangeDetectionStrategy, Component } from "@angular/core"; -import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components"; +import { + ButtonModule, + CenterPositionStrategy, + DialogModule, + DialogService, +} from "@bitwarden/components"; import { I18nPipe } from "@bitwarden/ui-common"; @Component({ @@ -14,6 +19,8 @@ export class AutoConfirmWarningDialogComponent { constructor(public dialogRef: DialogRef) {} static open(dialogService: DialogService) { - return dialogService.open(AutoConfirmWarningDialogComponent); + return dialogService.open(AutoConfirmWarningDialogComponent, { + positionStrategy: new CenterPositionStrategy(), + }); } } diff --git a/libs/auto-confirm/src/components/index.ts b/libs/auto-confirm/src/components/index.ts index a0310e805c6..1cddd1d7e59 100644 --- a/libs/auto-confirm/src/components/index.ts +++ b/libs/auto-confirm/src/components/index.ts @@ -1 +1,2 @@ +export * from "./auto-confirm-extension-dialog.component"; export * from "./auto-confirm-warning-dialog.component"; From 1b76ce5b7c19703429de6ec0b6f8d831c688d9df Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Fri, 9 Jan 2026 10:31:08 -0800 Subject: [PATCH 19/35] [PM-30264] - fix exact match dialog show logic (#18216) * fix exact match dialog show logic * fix logic for uri matching * simplify exact match dialog show logic --- .../item-more-options.component.spec.ts | 187 ++++++++++-------- .../item-more-options.component.ts | 11 +- 2 files changed, 111 insertions(+), 87 deletions(-) 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 bd9ce108522..6728249b788 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 @@ -158,14 +158,6 @@ 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 () => { - 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(() => { uriMatchStrategy$.next(UriMatchStrategy.Domain); @@ -236,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(); @@ -266,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", () => { @@ -273,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, @@ -292,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(); }); }); 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 c4353e17bef..ce797d9755e 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 @@ -204,12 +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 uriMatchStrategy = await firstValueFrom(this.uriMatchStrategy$); - if (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" }, From 92190d734c84c4d545c8fe147c2586984969bac7 Mon Sep 17 00:00:00 2001 From: bmbitwarden Date: Fri, 9 Jan 2026 13:39:26 -0500 Subject: [PATCH 20/35] Pm 28182 add success page (#17814) * PM-28182-implemented send confirmation drawer * PM-28182 resolved lint issue * PM-28182 resolved pr comment * PM-28182 put behind feature flag * Fix feature flag checks in send component * Fix feature flag checks in send dropdown component * Add SendUIRefresh feature flag * PM-28182 resolved lint issues * PM-28182 resolved N bug in drawer message * PM28182 resolved expirationDate replaced with delettionDate * PM-28182 resolved build issue * PM-28182 resolved failling tests * PM-28182 resolved pr comment to consolidate expression * chore: rerun web build * PM-28182 removed unneeded export --- .../new-send-dropdown.component.spec.ts | 3 + .../new-send/new-send-dropdown.component.ts | 23 ++++-- apps/web/src/app/tools/send/send.component.ts | 18 ++++- apps/web/src/app/tools/send/shared/index.ts | 1 + .../send-success-drawer-dialog.component.html | 45 +++++++++++ .../send-success-drawer-dialog.component.ts | 75 +++++++++++++++++++ apps/web/src/locales/en/messages.json | 33 +++++++- .../send-add-edit-dialog.component.ts | 12 +-- 8 files changed, 197 insertions(+), 13 deletions(-) create mode 100644 apps/web/src/app/tools/send/shared/index.ts create mode 100644 apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.html create mode 100644 apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.ts diff --git a/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.spec.ts b/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.spec.ts index 4f5dda1745e..134eaac2956 100644 --- a/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.spec.ts +++ b/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.spec.ts @@ -72,6 +72,7 @@ describe("NewSendDropdownComponent", () => { const openSpy = jest.spyOn(SendAddEditDialogComponent, "open"); const openDrawerSpy = jest.spyOn(SendAddEditDialogComponent, "openDrawer"); mockConfigService.getFeatureFlag.mockResolvedValue(false); + openSpy.mockReturnValue({ closed: of({}) } as any); await component.createSend(SendType.Text); @@ -85,6 +86,8 @@ describe("NewSendDropdownComponent", () => { mockConfigService.getFeatureFlag.mockImplementation(async (key) => key === FeatureFlag.SendUIRefresh ? true : false, ); + const mockRef = { closed: of({}) }; + openDrawerSpy.mockReturnValue(mockRef as any); await component.createSend(SendType.Text); diff --git a/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.ts b/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.ts index 22f07e4fe92..dca70dca4b8 100644 --- a/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.ts +++ b/apps/web/src/app/tools/send/new-send/new-send-dropdown.component.ts @@ -1,6 +1,6 @@ import { CommonModule } from "@angular/common"; import { Component, Input } from "@angular/core"; -import { firstValueFrom, Observable, of, switchMap } from "rxjs"; +import { firstValueFrom, Observable, of, switchMap, lastValueFrom } from "rxjs"; import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; import { JslibModule } from "@bitwarden/angular/jslib.module"; @@ -10,7 +10,13 @@ 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 { ButtonModule, DialogService, MenuModule } from "@bitwarden/components"; -import { DefaultSendFormConfigService, SendAddEditDialogComponent } from "@bitwarden/send-ui"; +import { + DefaultSendFormConfigService, + SendAddEditDialogComponent, + SendItemDialogResult, +} from "@bitwarden/send-ui"; + +import { SendSuccessDrawerDialogComponent } from "../shared"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @@ -60,12 +66,19 @@ export class NewSendDropdownComponent { if (!(await firstValueFrom(this.canAccessPremium$)) && type === SendType.File) { return; } - const formConfig = await this.addEditFormConfigService.buildConfig("add", undefined, type); - const useRefresh = await this.configService.getFeatureFlag(FeatureFlag.SendUIRefresh); + if (useRefresh) { - SendAddEditDialogComponent.openDrawer(this.dialogService, { formConfig }); + const dialogRef = SendAddEditDialogComponent.openDrawer(this.dialogService, { formConfig }); + if (dialogRef) { + const result = await lastValueFrom(dialogRef.closed); + if (result?.result === SendItemDialogResult.Saved && result?.send) { + this.dialogService.openDrawer(SendSuccessDrawerDialogComponent, { + data: result.send, + }); + } + } } else { SendAddEditDialogComponent.open(this.dialogService, { formConfig }); } diff --git a/apps/web/src/app/tools/send/send.component.ts b/apps/web/src/app/tools/send/send.component.ts index 7c0e03e3e21..eb3d92ebe26 100644 --- a/apps/web/src/app/tools/send/send.component.ts +++ b/apps/web/src/app/tools/send/send.component.ts @@ -39,6 +39,7 @@ import { HeaderModule } from "../../layouts/header/header.module"; import { SharedModule } from "../../shared"; import { NewSendDropdownComponent } from "./new-send/new-send-dropdown.component"; +import { SendSuccessDrawerDialogComponent } from "./shared"; const BroadcasterSubscriptionId = "SendComponent"; @@ -172,12 +173,25 @@ export class SendComponent extends BaseSendComponent implements OnInit, OnDestro }); } - const result = await lastValueFrom(this.sendItemDialogRef.closed); + const result: SendItemDialogResult = await lastValueFrom(this.sendItemDialogRef.closed); this.sendItemDialogRef = undefined; // If the dialog was closed by deleting the cipher, refresh the vault. - if (result === SendItemDialogResult.Deleted || result === SendItemDialogResult.Saved) { + if ( + result?.result === SendItemDialogResult.Deleted || + result?.result === SendItemDialogResult.Saved + ) { await this.load(); } + + if ( + result?.result === SendItemDialogResult.Saved && + result?.send && + (await this.configService.getFeatureFlag(FeatureFlag.SendUIRefresh)) + ) { + this.dialogService.openDrawer(SendSuccessDrawerDialogComponent, { + data: result.send, + }); + } } } diff --git a/apps/web/src/app/tools/send/shared/index.ts b/apps/web/src/app/tools/send/shared/index.ts new file mode 100644 index 00000000000..afc507ee464 --- /dev/null +++ b/apps/web/src/app/tools/send/shared/index.ts @@ -0,0 +1 @@ +export { SendSuccessDrawerDialogComponent } from "./send-success-drawer-dialog.component"; diff --git a/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.html b/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.html new file mode 100644 index 00000000000..b9326ca08ac --- /dev/null +++ b/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.html @@ -0,0 +1,45 @@ + + + {{ dialogTitle() | i18n }} + + +
+
+
+ +
+
+ +

+ {{ "sendCreatedSuccessfully" | i18n }} +

+ +

+ {{ "sendCreatedDescription" | i18n: formattedExpirationTime }} +

+ + + {{ "sendLink" | i18n }} + + + +
+
+ + + + + +
diff --git a/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.ts b/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.ts new file mode 100644 index 00000000000..1cea9b83428 --- /dev/null +++ b/apps/web/src/app/tools/send/shared/send-success-drawer-dialog.component.ts @@ -0,0 +1,75 @@ +import { Component, ChangeDetectionStrategy, Inject, signal, computed } from "@angular/core"; +import { firstValueFrom } from "rxjs"; + +import { ActiveSendIcon } from "@bitwarden/assets/svg"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; +import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; +import { DIALOG_DATA, DialogModule, ToastService, TypographyModule } from "@bitwarden/components"; +import { SharedModule } from "@bitwarden/web-vault/app/shared"; + +@Component({ + imports: [SharedModule, DialogModule, TypographyModule], + templateUrl: "./send-success-drawer-dialog.component.html", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class SendSuccessDrawerDialogComponent { + readonly sendLink = signal(""); + activeSendIcon = ActiveSendIcon; + + // Computed property to get the dialog title based on send type + readonly dialogTitle = computed(() => { + return this.send.type === SendType.Text ? "newTextSend" : "newFileSend"; + }); + + constructor( + @Inject(DIALOG_DATA) public send: SendView, + private environmentService: EnvironmentService, + private i18nService: I18nService, + private platformUtilsService: PlatformUtilsService, + private toastService: ToastService, + ) { + void this.initLink(); + } + + async initLink() { + const env = await firstValueFrom(this.environmentService.environment$); + this.sendLink.set(env.getSendUrl() + this.send.accessId + "/" + this.send.urlB64Key); + } + + get formattedExpirationTime(): string { + if (!this.send.deletionDate) { + return ""; + } + const hoursAvailable = this.getHoursAvailable(this.send); + if (hoursAvailable < 24) { + return hoursAvailable === 1 + ? this.i18nService.t("oneHour").toLowerCase() + : this.i18nService.t("durationTimeHours", String(hoursAvailable)).toLowerCase(); + } + const daysAvailable = Math.ceil(hoursAvailable / 24); + return daysAvailable === 1 + ? this.i18nService.t("oneDay").toLowerCase() + : this.i18nService.t("days", String(daysAvailable)).toLowerCase(); + } + + private getHoursAvailable(send: SendView): number { + const now = new Date().getTime(); + const deletionDate = new Date(send.deletionDate).getTime(); + return Math.max(0, Math.ceil((deletionDate - now) / (1000 * 60 * 60))); + } + + copyLink() { + const link = this.sendLink(); + if (!link) { + return; + } + this.platformUtilsService.copyToClipboard(link); + this.toastService.showToast({ + variant: "success", + message: this.i18nService.t("valueCopied", this.i18nService.t("sendLink")), + }); + } +} diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 8024de21e56..5952abef7fc 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -5616,6 +5616,37 @@ "message": "Send saved", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, + "sendCreatedSuccessfully": { + "message": "Send created successfully!", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "sendCreatedDescription": { + "message": "Copy and share this Send link. It can be viewed by the people you specified for the next $TIME$.", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated.", + "placeholders": { + "time": { + "content": "$1", + "example": "7 days" + } + } + }, + "durationTimeHours": { + "message": "$HOURS$ hours", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + } + } + }, + "newTextSend": { + "message": "New Text Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, + "newFileSend": { + "message": "New File Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "editedSend": { "message": "Send saved", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -12581,4 +12612,4 @@ "storageFullDescription": { "message": "You have used all $GB$ GB of your encrypted storage. To continue storing files, add more storage." } -} +} \ No newline at end of file diff --git a/libs/tools/send/send-ui/src/add-edit/send-add-edit-dialog.component.ts b/libs/tools/send/send-ui/src/add-edit/send-add-edit-dialog.component.ts index 38257df603a..d2f2c2204b9 100644 --- a/libs/tools/send/send-ui/src/add-edit/send-add-edit-dialog.component.ts +++ b/libs/tools/send/send-ui/src/add-edit/send-add-edit-dialog.component.ts @@ -44,8 +44,10 @@ export const SendItemDialogResult = Object.freeze({ } as const); /** A result of the Send add/edit dialog. */ -export type SendItemDialogResult = (typeof SendItemDialogResult)[keyof typeof SendItemDialogResult]; - +export type SendItemDialogResult = { + result: (typeof SendItemDialogResult)[keyof typeof SendItemDialogResult]; + send?: SendView; +}; /** * Component for adding or editing a send item. */ @@ -93,7 +95,7 @@ export class SendAddEditDialogComponent { */ async onSendCreated(send: SendView) { // FIXME Add dialogService.open send-created dialog - this.dialogRef.close(SendItemDialogResult.Saved); + this.dialogRef.close({ result: SendItemDialogResult.Saved, send }); return; } @@ -101,14 +103,14 @@ export class SendAddEditDialogComponent { * Handles the event when the send is updated. */ async onSendUpdated(send: SendView) { - this.dialogRef.close(SendItemDialogResult.Saved); + this.dialogRef.close({ result: SendItemDialogResult.Saved }); } /** * Handles the event when the send is deleted. */ async onSendDeleted() { - this.dialogRef.close(SendItemDialogResult.Deleted); + this.dialogRef.close({ result: SendItemDialogResult.Deleted }); this.toastService.showToast({ variant: "success", From 881afacdede0b907ba3b7ad0aa2a23f367461e80 Mon Sep 17 00:00:00 2001 From: Isaiah Inuwa Date: Fri, 9 Jan 2026 14:18:17 -0600 Subject: [PATCH 21/35] Enable cross-compilation and packaging of Windows Appx from macOS (#17976) * Enable cross-compilation and packaging of Windows Appx from macOS * Consolidate cargo build execution into a single function in native build script * Install cargo-xwin when needed * Install Appx tools when needed * Consolidate command execution into a single function in native build script * Only include the native node modules for the appropriate platform electron-builder's globs interact strangely, so we can't exclude all the .node files in the global config and then include the platform-specific files in the platform configuration. * Always copy Rust binaries to dist folder * Log source and destination when copying files * Update copyright * Match Electron version in Beta build --- apps/desktop/custom-appx-manifest.xml | 111 +++++++++++ apps/desktop/desktop_native/build.js | 120 ++++++++---- apps/desktop/electron-builder.beta.json | 18 +- apps/desktop/electron-builder.json | 16 +- apps/desktop/package.json | 2 +- apps/desktop/scripts/after-pack.js | 5 +- apps/desktop/scripts/appx-cross-build.ps1 | 226 ++++++++++++++++++++++ apps/desktop/scripts/before-pack.js | 31 +++ 8 files changed, 474 insertions(+), 55 deletions(-) create mode 100644 apps/desktop/custom-appx-manifest.xml create mode 100755 apps/desktop/scripts/appx-cross-build.ps1 create mode 100644 apps/desktop/scripts/before-pack.js 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/build.js b/apps/desktop/desktop_native/build.js index 54a6dba8326..b20aa7e5af8 100644 --- a/apps/desktop/desktop_native/build.js +++ b/apps/desktop/desktop_native/build.js @@ -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, mode === "release"); - buildProxyBin(target, mode === "release"); - buildImporterBinaries(target, mode === "release"); + buildNapiModule(target, isRelease); + buildProxyBin(target, isRelease); + buildImporterBinaries(target, isRelease); buildProcessIsolation(); }); diff --git a/apps/desktop/electron-builder.beta.json b/apps/desktop/electron-builder.beta.json index 0c95c7f01a6..2d7d76827f1 100644 --- a/apps/desktop/electron-builder.beta.json +++ b/apps/desktop/electron-builder.beta.json @@ -13,14 +13,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 +35,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,6 +59,7 @@ "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", diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json index a4e1c44dc5b..c42c3cc4202 100644 --- a/apps/desktop/electron-builder.json +++ b/apps/desktop/electron-builder.json @@ -13,12 +13,13 @@ }, "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": "39.2.6", "generateUpdatesFilesForAllChannels": true, @@ -94,11 +95,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,6 +173,7 @@ "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", diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 17322c42a84..93d016f8791 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -29,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", 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/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`); + } + } +} From eb12758c993f4655d1768d33b53f3a79ba1a64f3 Mon Sep 17 00:00:00 2001 From: Addison Beck Date: Fri, 9 Jan 2026 15:22:54 -0500 Subject: [PATCH 22/35] fix(nx): use relative paths in tsconfig.base.json for TypeScript 7 compatibility (#18295) Update the NX library generator to prefix paths with './' when adding entries to tsconfig.base.json. This ensures compatibility with TypeScript 7 and tsgo, which require relative paths to explicitly start with './'. --- libs/nx-plugin/src/generators/basic-lib.spec.ts | 2 +- libs/nx-plugin/src/generators/basic-lib.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/nx-plugin/src/generators/basic-lib.spec.ts b/libs/nx-plugin/src/generators/basic-lib.spec.ts index 9fd7a702375..2018593046b 100644 --- a/libs/nx-plugin/src/generators/basic-lib.spec.ts +++ b/libs/nx-plugin/src/generators/basic-lib.spec.ts @@ -24,7 +24,7 @@ describe("basic-lib generator", () => { expect(tsconfigContent).not.toBeNull(); const tsconfig = JSON.parse(tsconfigContent?.toString() ?? ""); expect(tsconfig.compilerOptions.paths[`@bitwarden/${options.name}`]).toEqual([ - `libs/test/src/index.ts`, + `./libs/test/src/index.ts`, ]); }); diff --git a/libs/nx-plugin/src/generators/basic-lib.ts b/libs/nx-plugin/src/generators/basic-lib.ts index 4f2f542ac89..c0d8a528841 100644 --- a/libs/nx-plugin/src/generators/basic-lib.ts +++ b/libs/nx-plugin/src/generators/basic-lib.ts @@ -82,7 +82,7 @@ function updateTsConfigPath(tree: Tree, name: string, srcRoot: string) { updateJson(tree, "tsconfig.base.json", (json) => { const paths = json.compilerOptions.paths || {}; - paths[`@bitwarden/${name}`] = [`${srcRoot}/index.ts`]; + paths[`@bitwarden/${name}`] = [`./${srcRoot}/index.ts`]; json.compilerOptions.paths = paths; return json; From 00882c331a43580343742fe4f80d029cdc61fa7a Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Fri, 9 Jan 2026 15:39:18 -0500 Subject: [PATCH 23/35] [PM-30611] show deleted archived items in trash (#18272) --- libs/angular/src/vault/components/vault-items.component.ts | 7 ++++++- .../src/vault/vault-filter/models/vault-filter.model.ts | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/libs/angular/src/vault/components/vault-items.component.ts b/libs/angular/src/vault/components/vault-items.component.ts index 0254ddabf2b..563fd48028d 100644 --- a/libs/angular/src/vault/components/vault-items.component.ts +++ b/libs/angular/src/vault/components/vault-items.component.ts @@ -194,7 +194,12 @@ export class VaultItemsComponent implements OnDestroy return this.searchService.searchCiphers( userId, searchText, - [filter, this.deletedFilter, this.archivedFilter, restrictedTypeFilter], + [ + filter, + this.deletedFilter, + ...(this.deleted ? [] : [this.archivedFilter]), + restrictedTypeFilter, + ], allCiphers, ); }), diff --git a/libs/angular/src/vault/vault-filter/models/vault-filter.model.ts b/libs/angular/src/vault/vault-filter/models/vault-filter.model.ts index 87536036644..83693c85239 100644 --- a/libs/angular/src/vault/vault-filter/models/vault-filter.model.ts +++ b/libs/angular/src/vault/vault-filter/models/vault-filter.model.ts @@ -51,7 +51,8 @@ export class VaultFilter { cipherPassesFilter = CipherViewLikeUtils.isDeleted(cipher); } if (this.status === "archive" && cipherPassesFilter) { - cipherPassesFilter = CipherViewLikeUtils.isArchived(cipher); + cipherPassesFilter = + CipherViewLikeUtils.isArchived(cipher) && !CipherViewLikeUtils.isDeleted(cipher); } if (this.cipherType != null && cipherPassesFilter) { cipherPassesFilter = CipherViewLikeUtils.getType(cipher) === this.cipherType; From 711036bd6027cd190e700d8ad5c600bc507cfbe5 Mon Sep 17 00:00:00 2001 From: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> Date: Fri, 9 Jan 2026 14:53:48 -0600 Subject: [PATCH 24/35] [PM-27325] Deprecate user account crypto init methods (#18188) * deprecate account crypto init methods * Add deprecation notice for new use cases on makeKeyPair --- .../src/abstractions/key.service.ts | 17 +++++++---------- libs/key-management/src/key.service.ts | 5 ----- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/libs/key-management/src/abstractions/key.service.ts b/libs/key-management/src/abstractions/key.service.ts index 6cf44544422..bc065155fdb 100644 --- a/libs/key-management/src/abstractions/key.service.ts +++ b/libs/key-management/src/abstractions/key.service.ts @@ -128,18 +128,13 @@ export abstract class KeyService { /** * Generates a new user key - * @deprecated Interacting with the master key directly is prohibited. Use {@link makeUserKeyV1} instead. + * @deprecated Interacting with the master key directly is prohibited. + * For new features please use the KM provided SDK methods for user cryptography initialization or reach out to the KM team. * @throws Error when master key is null or undefined. * @param masterKey The user's master key. * @returns A new user key and the master key protected version of it */ abstract makeUserKey(masterKey: MasterKey): Promise<[UserKey, EncString]>; - /** - * Generates a new user key for a V1 user - * Note: This will be replaced by a higher level function to initialize a whole users cryptographic state in the near future. - * @returns A new user key - */ - abstract makeUserKeyV1(): Promise; /** * Clears the user's stored version of the user key * @param userId The desired user @@ -334,9 +329,9 @@ export abstract class KeyService { abstract getFingerprint(fingerprintMaterial: string, publicKey: Uint8Array): Promise; /** * Generates a new keypair - * @param key A key to encrypt the private key with. If not provided, - * defaults to the user key - * @returns A new keypair: [publicKey in Base64, encrypted privateKey] + * @deprecated New use-cases of this function are prohibited. Low-level cryptographic constructions and initialization should be done in the SDK. + * @param key A symmetric key to wrap the newly created private key with. + * @returns A new keypair: [publicKey in Base64, wrapped privateKey] * @throws If the provided key is a null-ish value. */ abstract makeKeyPair(key: SymmetricCryptoKey): Promise<[string, EncString]>; @@ -361,6 +356,8 @@ export abstract class KeyService { /** * Initialize all necessary crypto keys needed for a new account. * Warning! This completely replaces any existing keys! + * @deprecated New use cases for cryptography initialization should be done in the SDK. + * Current usage is actively being migrated see PM-21771 for details. * @param userId The user id of the target user. * @returns The user's newly created public key, private key, and encrypted private key * @throws An error if the userId is null or undefined. diff --git a/libs/key-management/src/key.service.ts b/libs/key-management/src/key.service.ts index 8cb072a4c2a..752a89e5fcd 100644 --- a/libs/key-management/src/key.service.ts +++ b/libs/key-management/src/key.service.ts @@ -213,11 +213,6 @@ export class DefaultKeyService implements KeyServiceAbstraction { return this.buildProtectedSymmetricKey(masterKey, newUserKey); } - async makeUserKeyV1(): Promise { - const newUserKey = await this.keyGenerationService.createKey(512); - return newUserKey as UserKey; - } - /** * Clears the user key. Clears all stored versions of the user keys as well, such as the biometrics key * @param userId The desired user From a199744e2456fde1863dba0d89320ac659d04e32 Mon Sep 17 00:00:00 2001 From: neuronull <9162534+neuronull@users.noreply.github.com> Date: Fri, 9 Jan 2026 12:56:36 -0800 Subject: [PATCH 25/35] Inform user if Desktop client already running (#17846) --- apps/desktop/src/main/window.main.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/main/window.main.ts b/apps/desktop/src/main/window.main.ts index bbdd2ad0a0f..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"; @@ -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 { From 494a4a59322fd05f3ab80c31e8ea06a78f11a914 Mon Sep 17 00:00:00 2001 From: Isaiah Inuwa Date: Fri, 9 Jan 2026 15:24:16 -0600 Subject: [PATCH 26/35] Allow local Electron app signing for Windows dev builds [PM-18325] (#17973) --- apps/desktop/sign.js | 62 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/apps/desktop/sign.js b/apps/desktop/sign.js index 6a42666c46f..b8da98a882b 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 && ext == "exe") { 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.`, + ); + } } }; From 1714660bdec5289003ebb36786ac41f50b30466d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 10 Jan 2026 08:19:13 +1000 Subject: [PATCH 27/35] [deps] AC: Update bufferutil to v4.1.0 (#18280) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index eec3487b6d4..32d5abebb91 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,7 @@ "big-integer": "1.6.52", "braintree-web-drop-in": "1.46.0", "buffer": "6.0.3", - "bufferutil": "4.0.9", + "bufferutil": "4.1.0", "chalk": "4.1.2", "commander": "14.0.0", "core-js": "3.47.0", @@ -19321,9 +19321,9 @@ "license": "MIT" }, "node_modules/bufferutil": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz", - "integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.1.0.tgz", + "integrity": "sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 1cfddb16c42..7aba2035dce 100644 --- a/package.json +++ b/package.json @@ -174,7 +174,7 @@ "big-integer": "1.6.52", "braintree-web-drop-in": "1.46.0", "buffer": "6.0.3", - "bufferutil": "4.0.9", + "bufferutil": "4.1.0", "chalk": "4.1.2", "commander": "14.0.0", "core-js": "3.47.0", From 404d925f845eed52991053438fa839eabaac9526 Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Fri, 9 Jan 2026 16:39:22 -0800 Subject: [PATCH 28/35] [PM-24560] - Add Archive UI Element to View and Edit Item Cards (#16954) * finalize new UI elements for archive/unarchive * add tests * add missing service * add tests * updates to edit and view pages * use structureClone * fix lint * fix typo * clean up return types * fixes to archive UI * fix tests * use @if and userId$ --- apps/browser/src/_locales/en/messages.json | 3 + .../add-edit/add-edit-v2.component.html | 38 +++- .../add-edit/add-edit-v2.component.spec.ts | 139 +++++++++++++- .../add-edit/add-edit-v2.component.ts | 68 ++++++- .../vault-v2/view-v2/view-v2.component.html | 70 ++++--- .../view-v2/view-v2.component.spec.ts | 171 ++++++++++++++++- .../vault-v2/view-v2/view-v2.component.ts | 26 +++ .../vault-item-dialog.component.html | 25 ++- .../vault-item-dialog.component.spec.ts | 174 +++++++++++++++++- .../vault-item-dialog.component.ts | 110 ++++++++--- apps/web/src/locales/en/messages.json | 3 + .../abstractions/cipher-archive.service.ts | 6 +- .../src/vault/models/view/cipher.view.ts | 4 + .../default-cipher-archive.service.spec.ts | 4 + .../default-cipher-archive.service.ts | 19 +- .../archive-cipher-utilities.service.spec.ts | 6 +- .../archive-cipher-utilities.service.ts | 80 ++++---- 17 files changed, 824 insertions(+), 122 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 1613373bd62..d3a393ecc37 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -573,6 +573,9 @@ "itemWasSentToArchive": { "message": "Item was sent to archive" }, + "itemWasUnarchived": { + "message": "Item was unarchived" + }, "itemUnarchived": { "message": "Item was unarchived" }, 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..7230c565a48 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 @@ -31,14 +31,34 @@ {{ "cancel" | i18n }} - + + @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 f2c9d470816..4ffe44133d7 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,7 +1,8 @@ import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; import { ActivatedRoute, Router } from "@angular/router"; import { mock, MockProxy } from "jest-mock-extended"; -import { BehaviorSubject } from "rxjs"; +import { BehaviorSubject, of } from "rxjs"; import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -12,13 +13,16 @@ 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 { AddEditCipherInfo } from "@bitwarden/common/vault/types/add-edit-cipher-info"; +import { DialogService } from "@bitwarden/components"; import { + ArchiveCipherUtilitiesService, CipherFormConfig, CipherFormConfigService, CipherFormMode, @@ -45,15 +49,15 @@ 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 openSimpleDialog = jest.fn().mockResolvedValue(true); + const cipherArchiveService = mock(); beforeEach(async () => { buildConfig.mockClear(); @@ -61,6 +65,10 @@ describe("AddEditV2Component", () => { navigate.mockClear(); back.mockClear(); collect.mockClear(); + openSimpleDialog.mockClear(); + + cipherArchiveService.hasArchiveFlagEnabled$ = of(true); + cipherArchiveService.userCanArchive$.mockReturnValue(of(false)); addEditCipherInfo$ = new BehaviorSubject(null); cipherServiceMock = mock({ @@ -83,10 +91,21 @@ describe("AddEditV2Component", () => { { provide: CipherAuthorizationService, useValue: { - canDeleteCipher$: jest.fn().mockReturnValue(true), + canDeleteCipher$: jest.fn().mockReturnValue(of(true)), }, }, { provide: AccountService, useValue: mockAccountServiceWith("UserId" as UserId) }, + { + provide: CipherArchiveService, + useValue: cipherArchiveService, + }, + { + provide: ArchiveCipherUtilitiesService, + useValue: { + archiveCipher: jest.fn().mockResolvedValue(null), + unarchiveCipher: jest.fn().mockResolvedValue(null), + }, + }, ], }) .overrideProvider(CipherFormConfigService, { @@ -94,6 +113,11 @@ describe("AddEditV2Component", () => { buildConfig, }, }) + .overrideProvider(DialogService, { + useValue: { + openSimpleDialog, + }, + }) .compileComponents(); fixture = TestBed.createComponent(AddEditV2Component); @@ -356,6 +380,111 @@ describe("AddEditV2Component", () => { }); }); + 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 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 22aad854dd0..8704694fd53 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, OnDestroy } from "@angular/core"; +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"; @@ -31,6 +32,8 @@ import { ToastService, } from "@bitwarden/components"; import { + ArchiveCipherUtilitiesService, + CipherFormComponent, CipherFormConfig, CipherFormConfigService, CipherFormGenerationService, @@ -159,6 +162,7 @@ export type AddEditQueryParams = Partial>; ], }) export class AddEditV2Component implements OnInit, OnDestroy { + readonly cipherFormComponent = viewChild(CipherFormComponent); headerText: string; config: CipherFormConfig; canDeleteCipher$: Observable; @@ -171,6 +175,18 @@ export class AddEditV2Component implements OnInit, OnDestroy { 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; @@ -182,6 +198,16 @@ export class AddEditV2Component implements OnInit, OnDestroy { return BrowserPopupUtils.inSingleActionPopout(window, VaultPopoutType.addEditVaultItem); } + protected archiveFlagEnabled$ = this.archiveService.hasArchiveFlagEnabled$; + + /** + * Flag to indicate if the user can archive items. + * @protected + */ + protected userCanArchive$ = this.accountService.activeAccount$.pipe( + switchMap((account) => this.archiveService.userCanArchive$(account.id)), + ); + constructor( private route: ActivatedRoute, private i18nService: I18nService, @@ -196,6 +222,8 @@ export class AddEditV2Component implements OnInit, OnDestroy { private dialogService: DialogService, protected cipherAuthorizationService: CipherAuthorizationService, private accountService: AccountService, + private archiveService: CipherArchiveService, + private archiveCipherUtilsService: ArchiveCipherUtilitiesService, ) { this.subscribeToParams(); } @@ -322,6 +350,10 @@ export class AddEditV2Component implements OnInit, OnDestroy { await BrowserApi.sendMessage("addEditCipherSubmitted"); } + get isEditMode(): boolean { + return ["edit", "partial-edit"].includes(this.config?.mode); + } + subscribeToParams(): void { this.route.queryParams .pipe( @@ -430,6 +462,40 @@ export class AddEditV2Component implements OnInit, OnDestroy { 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" }, 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..d2a4aaab3f0 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 @@ -3,37 +3,47 @@ - + @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..9c536a7e85a 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,9 @@ describe("ViewV2Component", () => { username: "test-username", password: "test-password", totp: "123", + uris: [], }, + card: {}, } as unknown as CipherView; const mockPasswordRepromptService = { @@ -84,6 +102,8 @@ describe("ViewV2Component", () => { softDeleteWithServer: jest.fn().mockResolvedValue(undefined), }; + const cipherArchiveService = mock(); + beforeEach(async () => { mockCipherService.cipherViews$.mockClear(); mockCipherService.deleteWithServer.mockClear(); @@ -97,6 +117,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], @@ -142,6 +166,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 +233,7 @@ describe("ViewV2Component", () => { fixture = TestBed.createComponent(ViewV2Component); component = fixture.componentInstance; fixture.detectChanges(); + (component as any).showFooter$ = of(true); }); describe("queryParams", () => { @@ -352,6 +432,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; 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..64fa42bb2ba 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 @@ -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"; @@ -42,6 +43,7 @@ import { ToastService, } from "@bitwarden/components"; import { + ArchiveCipherUtilitiesService, ChangeLoginPasswordService, CipherViewComponent, CopyCipherFieldService, @@ -114,6 +116,10 @@ export class ViewV2Component { senderTabId?: number; 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 +137,8 @@ export class ViewV2Component { protected cipherAuthorizationService: CipherAuthorizationService, private copyCipherFieldService: CopyCipherFieldService, private popupScrollPositionService: VaultPopupScrollPositionService, + private archiveService: CipherArchiveService, + private archiveCipherUtilsService: ArchiveCipherUtilitiesService, ) { this.subscribeToParams(); } @@ -272,6 +280,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/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.html b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.html index 16256ab875a..c863608ba10 100644 --- a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.html +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.html @@ -2,7 +2,8 @@ {{ title }} - @if (cipherIsArchived) { + + @if (isCipherArchived) { {{ "archived" | i18n }} } @@ -83,8 +84,28 @@ } - @if (showDelete) { + @if (showActionButtons) {
+ @if (userCanArchive$ | async) { + @if (isCipherArchived) { + + } + @if (cipher.canBeArchived) { + + } + }