From 460d66d62499f0a5d336d9a651f049d9611cf024 Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Wed, 29 Oct 2025 07:41:35 -0500 Subject: [PATCH 01/66] Remove FF: `pm-17772-admin-initiated-sponsorships` (#16873) * Remove FF * Fix test --- .../free-families-policy.service.spec.ts | 25 ------------------- .../services/free-families-policy.service.ts | 13 ++-------- libs/common/src/enums/feature-flag.enum.ts | 2 -- 3 files changed, 2 insertions(+), 38 deletions(-) diff --git a/apps/web/src/app/billing/services/free-families-policy.service.spec.ts b/apps/web/src/app/billing/services/free-families-policy.service.spec.ts index 10ccc448986..5b39a5a848a 100644 --- a/apps/web/src/app/billing/services/free-families-policy.service.spec.ts +++ b/apps/web/src/app/billing/services/free-families-policy.service.spec.ts @@ -38,7 +38,6 @@ describe("FreeFamiliesPolicyService", () => { describe("showSponsoredFamiliesDropdown$", () => { it("should return true when all conditions are met", async () => { // Configure mocks - configService.getFeatureFlag$.mockReturnValue(of(true)); policyService.policiesByType$.mockReturnValue(of([])); // Create a test organization that meets all criteria @@ -58,7 +57,6 @@ describe("FreeFamiliesPolicyService", () => { it("should return false when organization is not Enterprise", async () => { // Configure mocks - configService.getFeatureFlag$.mockReturnValue(of(true)); policyService.policiesByType$.mockReturnValue(of([])); // Create a test organization that is not Enterprise tier @@ -74,27 +72,8 @@ describe("FreeFamiliesPolicyService", () => { expect(result).toBe(false); }); - it("should return false when feature flag is disabled", async () => { - // Configure mocks to disable feature flag - configService.getFeatureFlag$.mockReturnValue(of(false)); - policyService.policiesByType$.mockReturnValue(of([])); - - // Create a test organization that meets other criteria - const organization = { - id: "org-id", - productTierType: ProductTierType.Enterprise, - useAdminSponsoredFamilies: true, - isAdmin: true, - } as Organization; - - // Test the method - const result = await firstValueFrom(service.showSponsoredFamiliesDropdown$(of(organization))); - expect(result).toBe(false); - }); - it("should return false when families feature is disabled by policy", async () => { // Configure mocks with a policy that disables the feature - configService.getFeatureFlag$.mockReturnValue(of(true)); policyService.policiesByType$.mockReturnValue( of([{ organizationId: "org-id", enabled: true } as Policy]), ); @@ -114,7 +93,6 @@ describe("FreeFamiliesPolicyService", () => { it("should return false when useAdminSponsoredFamilies is false", async () => { // Configure mocks - configService.getFeatureFlag$.mockReturnValue(of(true)); policyService.policiesByType$.mockReturnValue(of([])); // Create a test organization with useAdminSponsoredFamilies set to false @@ -132,7 +110,6 @@ describe("FreeFamiliesPolicyService", () => { it("should return true when user is an owner but not admin", async () => { // Configure mocks - configService.getFeatureFlag$.mockReturnValue(of(true)); policyService.policiesByType$.mockReturnValue(of([])); // Create a test organization where user is owner but not admin @@ -152,7 +129,6 @@ describe("FreeFamiliesPolicyService", () => { it("should return true when user can manage users but is not admin or owner", async () => { // Configure mocks - configService.getFeatureFlag$.mockReturnValue(of(true)); policyService.policiesByType$.mockReturnValue(of([])); // Create a test organization where user can manage users but is not admin or owner @@ -172,7 +148,6 @@ describe("FreeFamiliesPolicyService", () => { it("should return false when user has no admin permissions", async () => { // Configure mocks - configService.getFeatureFlag$.mockReturnValue(of(true)); policyService.policiesByType$.mockReturnValue(of([])); // Create a test organization where user has no admin permissions diff --git a/apps/web/src/app/billing/services/free-families-policy.service.ts b/apps/web/src/app/billing/services/free-families-policy.service.ts index 52041936e50..68e333d53ba 100644 --- a/apps/web/src/app/billing/services/free-families-policy.service.ts +++ b/apps/web/src/app/billing/services/free-families-policy.service.ts @@ -8,8 +8,6 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { ProductTierType } from "@bitwarden/common/billing/enums"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; interface EnterpriseOrgStatus { isFreeFamilyPolicyEnabled: boolean; @@ -23,7 +21,6 @@ export class FreeFamiliesPolicyService { private policyService: PolicyService, private organizationService: OrganizationService, private accountService: AccountService, - private configService: ConfigService, ) {} organizations$ = this.accountService.activeAccount$.pipe( @@ -58,20 +55,14 @@ export class FreeFamiliesPolicyService { userId, ); - return combineLatest([ - enterpriseOrganization$, - this.configService.getFeatureFlag$(FeatureFlag.PM17772_AdminInitiatedSponsorships), - organization, - policies$, - ]).pipe( - map(([isEnterprise, featureFlagEnabled, org, policies]) => { + return combineLatest([enterpriseOrganization$, organization, policies$]).pipe( + map(([isEnterprise, org, policies]) => { const familiesFeatureDisabled = policies.some( (policy) => policy.organizationId === org.id && policy.enabled, ); return ( isEnterprise && - featureFlagEnabled && !familiesFeatureDisabled && org.useAdminSponsoredFamilies && (org.isAdmin || org.isOwner || org.canManageUsers) diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index d9cd1dbfab3..085731b034e 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -23,7 +23,6 @@ export enum FeatureFlag { /* Billing */ TrialPaymentOptional = "PM-8163-trial-payment", - PM17772_AdminInitiatedSponsorships = "pm-17772-admin-initiated-sponsorships", PM21821_ProviderPortalTakeover = "pm-21821-provider-portal-takeover", PM22415_TaxIDWarnings = "pm-22415-tax-id-warnings", PM24032_NewNavigationPremiumUpgradeButton = "pm-24032-new-navigation-premium-upgrade-button", @@ -109,7 +108,6 @@ export const DefaultFeatureFlagValue = { /* Billing */ [FeatureFlag.TrialPaymentOptional]: FALSE, - [FeatureFlag.PM17772_AdminInitiatedSponsorships]: FALSE, [FeatureFlag.PM21821_ProviderPortalTakeover]: FALSE, [FeatureFlag.PM22415_TaxIDWarnings]: FALSE, [FeatureFlag.PM24032_NewNavigationPremiumUpgradeButton]: FALSE, From 5b815c4ae4b997151b7f2cf247d1110925dce14f Mon Sep 17 00:00:00 2001 From: Bryan Cunningham Date: Wed, 29 Oct 2025 09:49:16 -0400 Subject: [PATCH 02/66] [CL-879] use tooltip on icon button (#16576) * Add tooltip to icon button to display label * remove legacy cdr variable * create overlay on focus or hover * attach describdedby ids * fix type errors * remove aria-describedby when not necessary * fix failing tests * implement Claude feedback * fixing broken specs * remove host attr binding * Simplify directive aria logic * Move id to statis number * do not render empty tooltip * pass id to tooltip component * remove pointer-events none to allow tooltip on normal buttons * exclude some tooltip stories * change describedby input name * add story with tooltip on regular button * enhanced tooltip docs * set model directly * change model to input --- .../components/src/button/button.component.ts | 1 - .../src/icon-button/icon-button.component.ts | 19 ++++-- .../src/tooltip/tooltip.component.html | 18 +++--- .../src/tooltip/tooltip.component.ts | 1 + .../src/tooltip/tooltip.directive.ts | 64 +++++++++++++------ libs/components/src/tooltip/tooltip.mdx | 19 +++++- libs/components/src/tooltip/tooltip.spec.ts | 9 ++- .../components/src/tooltip/tooltip.stories.ts | 40 ++++++++++-- .../delete-attachment.component.spec.ts | 2 +- .../uri-option.component.spec.ts | 10 ++- .../download-attachment.component.spec.ts | 2 +- 11 files changed, 137 insertions(+), 48 deletions(-) diff --git a/libs/components/src/button/button.component.ts b/libs/components/src/button/button.component.ts index 350d493f832..6ef5309b018 100644 --- a/libs/components/src/button/button.component.ts +++ b/libs/components/src/button/button.component.ts @@ -92,7 +92,6 @@ export class ButtonComponent implements ButtonLikeAbstraction { "hover:!tw-text-muted", "aria-disabled:tw-cursor-not-allowed", "hover:tw-no-underline", - "aria-disabled:tw-pointer-events-none", ] : [], ) diff --git a/libs/components/src/icon-button/icon-button.component.ts b/libs/components/src/icon-button/icon-button.component.ts index f1edee7c089..9887c0bde8b 100644 --- a/libs/components/src/icon-button/icon-button.component.ts +++ b/libs/components/src/icon-button/icon-button.component.ts @@ -17,6 +17,7 @@ import { setA11yTitleAndAriaLabel } from "../a11y/set-a11y-title-and-aria-label" import { ButtonLikeAbstraction } from "../shared/button-like.abstraction"; import { FocusableElement } from "../shared/focusable-element"; import { SpinnerComponent } from "../spinner"; +import { TooltipDirective } from "../tooltip"; import { ariaDisableElement } from "../utils"; export type IconButtonType = "primary" | "danger" | "contrast" | "main" | "muted" | "nav-contrast"; @@ -100,7 +101,10 @@ const sizes: Record = { */ "[attr.bitIconButton]": "icon()", }, - hostDirectives: [AriaDisableDirective], + hostDirectives: [ + AriaDisableDirective, + { directive: TooltipDirective, inputs: ["tooltipPosition"] }, + ], }) export class BitIconButtonComponent implements ButtonLikeAbstraction, FocusableElement { readonly icon = model.required({ alias: "bitIconButton" }); @@ -109,6 +113,9 @@ export class BitIconButtonComponent implements ButtonLikeAbstraction, FocusableE readonly size = model("default"); + private elementRef = inject(ElementRef); + private tooltip = inject(TooltipDirective, { host: true, optional: true }); + /** * label input will be used to set the `aria-label` attributes on the button. * This is for accessibility purposes, as it provides a text alternative for the icon button. @@ -186,8 +193,6 @@ export class BitIconButtonComponent implements ButtonLikeAbstraction, FocusableE return this.elementRef.nativeElement; } - private elementRef = inject(ElementRef); - constructor() { const element = this.elementRef.nativeElement; @@ -198,9 +203,15 @@ export class BitIconButtonComponent implements ButtonLikeAbstraction, FocusableE effect(() => { setA11yTitleAndAriaLabel({ element: this.elementRef.nativeElement, - title: originalTitle ?? this.label(), + title: undefined, label: this.label(), }); + + const tooltipContent: string = originalTitle || this.label(); + + if (tooltipContent) { + this.tooltip?.tooltipContent.set(tooltipContent); + } }); } } diff --git a/libs/components/src/tooltip/tooltip.component.html b/libs/components/src/tooltip/tooltip.component.html index 4d354fc2765..ce9f1ceeffe 100644 --- a/libs/components/src/tooltip/tooltip.component.html +++ b/libs/components/src/tooltip/tooltip.component.html @@ -1,9 +1,11 @@ -
- +} diff --git a/libs/components/src/tooltip/tooltip.component.ts b/libs/components/src/tooltip/tooltip.component.ts index 34c67015004..79e2dfd7973 100644 --- a/libs/components/src/tooltip/tooltip.component.ts +++ b/libs/components/src/tooltip/tooltip.component.ts @@ -15,6 +15,7 @@ type TooltipData = { content: Signal; isVisible: Signal; tooltipPosition: Signal; + id: Signal; }; export const TOOLTIP_DATA = new InjectionToken("TOOLTIP_DATA"); diff --git a/libs/components/src/tooltip/tooltip.directive.ts b/libs/components/src/tooltip/tooltip.directive.ts index b2c1621d710..bcf9fc5e174 100644 --- a/libs/components/src/tooltip/tooltip.directive.ts +++ b/libs/components/src/tooltip/tooltip.directive.ts @@ -8,8 +8,9 @@ import { ElementRef, Injector, input, - effect, signal, + model, + computed, } from "@angular/core"; import { TooltipPositionIdentifier, tooltipPositions } from "./tooltip-positions"; @@ -26,30 +27,39 @@ import { TooltipComponent, TOOLTIP_DATA } from "./tooltip.component"; "(mouseleave)": "hideTooltip()", "(focus)": "showTooltip()", "(blur)": "hideTooltip()", + "[attr.aria-describedby]": "resolvedDescribedByIds()", }, }) export class TooltipDirective implements OnInit { + private static nextId = 0; /** * The value of this input is forwarded to the tooltip.component to render */ - readonly bitTooltip = input.required(); + readonly tooltipContent = model("", { alias: "bitTooltip" }); /** * The value of this input is forwarded to the tooltip.component to set its position explicitly. * @default "above-center" */ readonly tooltipPosition = input("above-center"); + /** + * Input so the consumer can choose to add the tooltip id to the aria-describedby attribute of the host element. + */ + readonly addTooltipToDescribedby = input(false); + private readonly isVisible = signal(false); private overlayRef: OverlayRef | undefined; - private elementRef = inject(ElementRef); + private elementRef = inject>(ElementRef); private overlay = inject(Overlay); private viewContainerRef = inject(ViewContainerRef); - private injector = inject(Injector); private positionStrategy = this.overlay .position() .flexibleConnectedTo(this.elementRef) .withFlexibleDimensions(false) .withPush(true); + private tooltipId = `bit-tooltip-${TooltipDirective.nextId++}`; + private currentDescribedByIds = + this.elementRef.nativeElement.getAttribute("aria-describedby") || null; private tooltipPortal = new ComponentPortal( TooltipComponent, @@ -59,23 +69,50 @@ export class TooltipDirective implements OnInit { { provide: TOOLTIP_DATA, useValue: { - content: this.bitTooltip, + content: this.tooltipContent, isVisible: this.isVisible, tooltipPosition: this.tooltipPosition, + id: signal(this.tooltipId), }, }, ], }), ); + private destroyTooltip = () => { + this.overlayRef?.dispose(); + this.overlayRef = undefined; + this.isVisible.set(false); + }; + private showTooltip = () => { + if (!this.overlayRef) { + this.overlayRef = this.overlay.create({ + ...this.defaultPopoverConfig, + positionStrategy: this.positionStrategy, + }); + + this.overlayRef.attach(this.tooltipPortal); + } this.isVisible.set(true); }; private hideTooltip = () => { - this.isVisible.set(false); + this.destroyTooltip(); }; + private readonly resolvedDescribedByIds = computed(() => { + if (this.addTooltipToDescribedby()) { + if (this.currentDescribedByIds) { + return `${this.currentDescribedByIds || ""} ${this.tooltipId}`; + } else { + return this.tooltipId; + } + } else { + return this.currentDescribedByIds; + } + }); + private computePositions(tooltipPosition: TooltipPositionIdentifier) { const chosenPosition = tooltipPositions.find((position) => position.id === tooltipPosition); @@ -91,20 +128,5 @@ export class TooltipDirective implements OnInit { ngOnInit() { this.positionStrategy.withPositions(this.computePositions(this.tooltipPosition())); - - this.overlayRef = this.overlay.create({ - ...this.defaultPopoverConfig, - positionStrategy: this.positionStrategy, - }); - - this.overlayRef.attach(this.tooltipPortal); - - effect( - () => { - this.positionStrategy.withPositions(this.computePositions(this.tooltipPosition())); - this.overlayRef?.updatePosition(); - }, - { injector: this.injector }, - ); } } diff --git a/libs/components/src/tooltip/tooltip.mdx b/libs/components/src/tooltip/tooltip.mdx index 4b6f10d97f8..13e159c98eb 100644 --- a/libs/components/src/tooltip/tooltip.mdx +++ b/libs/components/src/tooltip/tooltip.mdx @@ -11,7 +11,20 @@ import { TooltipDirective } from "@bitwarden/components"; <Description /> -NOTE: The `TooltipComponent` can't be used on its own. It must be applied via the `TooltipDirective` +### Tooltip usage + +The `TooltipComponent` can't be used on its own. It must be applied via the `TooltipDirective`. + +The `IconButtonComponent` will automatically apply a tooltip based on the component's `label` input. + +#### Adding the tooltip to the host element's `aria-describedby` list + +The `addTooltipToDescribedby="true"` model input can be used to add the tooltip id to the list of +the host element's `aria-describedby` element IDs. + +NOTE: This behavior is not always necessary and could be redundant if the host element's aria +attributes already convey the same message as the tooltip. Use only when the tooltip is extra, +non-essential contextual information. <Primary /> <Controls /> @@ -29,3 +42,7 @@ NOTE: The `TooltipComponent` can't be used on its own. It must be applied via th ### On disabled element <Canvas of={stories.OnDisabledButton} /> + +### On a Button + +<Canvas of={stories.OnNonIconButton} /> diff --git a/libs/components/src/tooltip/tooltip.spec.ts b/libs/components/src/tooltip/tooltip.spec.ts index b6a49acbc77..a88424de3bb 100644 --- a/libs/components/src/tooltip/tooltip.spec.ts +++ b/libs/components/src/tooltip/tooltip.spec.ts @@ -59,7 +59,14 @@ describe("TooltipDirective (visibility only)", () => { }; const overlayRefStub: OverlayRefStub = { - attach: jest.fn(() => ({})), + attach: jest.fn(() => ({ + changeDetectorRef: { detectChanges: jest.fn() }, + location: { + nativeElement: { + querySelector: jest.fn().mockReturnValue({ id: "tip-123" }), + }, + }, + })), updatePosition: jest.fn(), }; diff --git a/libs/components/src/tooltip/tooltip.stories.ts b/libs/components/src/tooltip/tooltip.stories.ts index 8ea3f52f913..73dad5801f3 100644 --- a/libs/components/src/tooltip/tooltip.stories.ts +++ b/libs/components/src/tooltip/tooltip.stories.ts @@ -72,7 +72,6 @@ type Story = StoryObj<TooltipDirective>; export const Default: Story = { args: { - bitTooltip: "This is a tooltip", tooltipPosition: "above-center", }, render: (args) => ({ @@ -81,6 +80,7 @@ export const Default: Story = { <div class="tw-p-4"> <button bitIconButton="bwi-ellipsis-v" + label="Your tooltip content here" ${formatArgsForCodeSnippet<TooltipDirective>(args)} > Button label here @@ -98,26 +98,29 @@ export const Default: Story = { export const AllPositions: Story = { render: () => ({ + parameters: { + chromatic: { disableSnapshot: true }, + }, template: ` <div class="tw-p-16 tw-grid tw-grid-cols-2 tw-gap-8 tw-place-items-center"> <button bitIconButton="bwi-angle-up" - bitTooltip="Top tooltip" + label="Top tooltip" tooltipPosition="above-center" ></button> <button bitIconButton="bwi-angle-right" - bitTooltip="Right tooltip" + label="Right tooltip" tooltipPosition="right-center" ></button> <button bitIconButton="bwi-angle-left" - bitTooltip="Left tooltip" + label="Left tooltip" tooltipPosition="left-center" ></button> <button bitIconButton="bwi-angle-down" - bitTooltip="Bottom tooltip" + label="Bottom tooltip" tooltipPosition="below-center" ></button> </div> @@ -127,11 +130,14 @@ export const AllPositions: Story = { export const LongContent: Story = { render: () => ({ + parameters: { + chromatic: { disableSnapshot: true }, + }, template: ` <div class="tw-p-16 tw-flex tw-items-center tw-justify-center"> <button bitIconButton="bwi-ellipsis-v" - bitTooltip="This is a very long tooltip that will wrap to multiple lines to demonstrate how the tooltip handles long content. This is not recommended for usability." + label="This is a very long tooltip that will wrap to multiple lines to demonstrate how the tooltip handles long content. This is not recommended for usability." ></button> </div> `, @@ -140,14 +146,34 @@ export const LongContent: Story = { export const OnDisabledButton: Story = { render: () => ({ + parameters: { + chromatic: { disableSnapshot: true }, + }, template: ` <div class="tw-p-16 tw-flex tw-items-center tw-justify-center"> <button bitIconButton="bwi-ellipsis-v" - bitTooltip="Tooltip on disabled button" + label="Tooltip on disabled button" [disabled]="true" ></button> </div> `, }), }; + +export const OnNonIconButton: Story = { + render: () => ({ + parameters: { + chromatic: { disableSnapshot: true }, + }, + template: ` + <div class="tw-p-16 tw-flex tw-items-center tw-justify-center"> + <button + bitButton + addTooltipToDescribedby="true" + bitTooltip="Some additional tooltip text to describe the button" + >Button label</button> + </div> + `, + }), +}; diff --git a/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.spec.ts b/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.spec.ts index 941b3740952..4e3899407d2 100644 --- a/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.spec.ts +++ b/libs/vault/src/cipher-form/components/attachments/delete-attachment/delete-attachment.component.spec.ts @@ -68,7 +68,7 @@ describe("DeleteAttachmentComponent", () => { it("renders delete button", () => { const deleteButton = fixture.debugElement.query(By.css("button")); - expect(deleteButton.attributes["title"]).toBe("deleteAttachmentName"); + expect(deleteButton.attributes["aria-label"]).toBe("deleteAttachmentName"); }); it("does not delete when the user cancels the dialog", async () => { diff --git a/libs/vault/src/cipher-form/components/autofill-options/uri-option.component.spec.ts b/libs/vault/src/cipher-form/components/autofill-options/uri-option.component.spec.ts index 0d7f3663967..2d06f5dcc29 100644 --- a/libs/vault/src/cipher-form/components/autofill-options/uri-option.component.spec.ts +++ b/libs/vault/src/cipher-form/components/autofill-options/uri-option.component.spec.ts @@ -149,13 +149,17 @@ describe("UriOptionComponent", () => { expect(getMatchDetectionSelect()).not.toBeNull(); }); - it("should update the match detection button title when the toggle is clicked", () => { + it("should update the match detection button aria-label when the toggle is clicked", () => { component.writeValue({ uri: "https://example.com", matchDetection: UriMatchStrategy.Exact }); fixture.detectChanges(); - expect(getToggleMatchDetectionBtn().title).toBe("showMatchDetection https://example.com"); + expect(getToggleMatchDetectionBtn().getAttribute("aria-label")).toBe( + "showMatchDetection https://example.com", + ); getToggleMatchDetectionBtn().click(); fixture.detectChanges(); - expect(getToggleMatchDetectionBtn().title).toBe("hideMatchDetection https://example.com"); + expect(getToggleMatchDetectionBtn().getAttribute("aria-label")).toBe( + "hideMatchDetection https://example.com", + ); }); }); diff --git a/libs/vault/src/components/download-attachment/download-attachment.component.spec.ts b/libs/vault/src/components/download-attachment/download-attachment.component.spec.ts index 8ba7b29a526..ec5a9ce96fd 100644 --- a/libs/vault/src/components/download-attachment/download-attachment.component.spec.ts +++ b/libs/vault/src/components/download-attachment/download-attachment.component.spec.ts @@ -108,7 +108,7 @@ describe("DownloadAttachmentComponent", () => { it("renders delete button", () => { const deleteButton = fixture.debugElement.query(By.css("button")); - expect(deleteButton.attributes["title"]).toBe("downloadAttachmentName"); + expect(deleteButton.attributes["aria-label"]).toBe("downloadAttachmentName"); }); describe("download attachment", () => { From 4d00d0caa5c4a728952a67efeabc1abf763ec272 Mon Sep 17 00:00:00 2001 From: SmithThe4th <gsmith@bitwarden.com> Date: Wed, 29 Oct 2025 10:31:21 -0400 Subject: [PATCH 03/66] Fixed edit menu on admin console and removed favorite item on the admin console (#16982) --- .../organizations/collections/vault.component.ts | 5 ++++- .../vault-items/vault-cipher-row.component.html | 10 ++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts index eb4e47e0ffd..f827dda9a9b 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts @@ -794,6 +794,9 @@ export class VaultComponent implements OnInit, OnDestroy { case "viewEvents": await this.viewEvents(event.item); break; + case "editCipher": + await this.editCipher(event.item); + break; } } finally { this.processingEvent$.next(false); @@ -856,7 +859,7 @@ export class VaultComponent implements OnInit, OnDestroy { * @param cipherView - When set, the cipher to be edited * @param cloneCipher - `true` when the cipher should be cloned. */ - async editCipher(cipher: CipherView | undefined, cloneCipher: boolean) { + async editCipher(cipher: CipherView | undefined, cloneCipher?: boolean) { if ( cipher && cipher.reprompt !== 0 && diff --git a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html index 43ce8530d55..c09553dab9c 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html +++ b/apps/web/src/app/vault/components/vault-items/vault-cipher-row.component.html @@ -169,10 +169,12 @@ <bit-menu-divider *ngIf="showMenuDivider"></bit-menu-divider> - <button bitMenuItem type="button" (click)="toggleFavorite()"> - <i class="bwi bwi-fw bwi-star" aria-hidden="true"></i> - {{ (cipher.favorite ? "unfavorite" : "favorite") | i18n }} - </button> + @if (!viewingOrgVault) { + <button bitMenuItem type="button" (click)="toggleFavorite()"> + <i class="bwi bwi-fw bwi-star" aria-hidden="true"></i> + {{ (cipher.favorite ? "unfavorite" : "favorite") | i18n }} + </button> + } <button bitMenuItem type="button" (click)="editCipher()" *ngIf="canEditCipher"> <i class="bwi bwi-fw bwi-pencil-square" aria-hidden="true"></i> {{ "edit" | i18n }} From d31b9211693debcfd1823b9c229ef0b03e1c2e97 Mon Sep 17 00:00:00 2001 From: Daniel Riera <driera@livefront.com> Date: Wed, 29 Oct 2025 10:40:27 -0400 Subject: [PATCH 04/66] PM-27364 delete bar.scss and drop bar.html internals (#17023) * PM-27364 delete bar.scss and drop bar.html internals * no longer clear document --- .../src/autofill/notification/bar.html | 52 +-- .../src/autofill/notification/bar.scss | 304 ------------------ apps/browser/src/autofill/notification/bar.ts | 2 - 3 files changed, 1 insertion(+), 357 deletions(-) delete mode 100644 apps/browser/src/autofill/notification/bar.scss diff --git a/apps/browser/src/autofill/notification/bar.html b/apps/browser/src/autofill/notification/bar.html index 90df670d29c..c0b57de612e 100644 --- a/apps/browser/src/autofill/notification/bar.html +++ b/apps/browser/src/autofill/notification/bar.html @@ -5,55 +5,5 @@ <title>Bitwarden - - -
-
- - - -
-
-
- -
-
- - - - - - - + diff --git a/apps/browser/src/autofill/notification/bar.scss b/apps/browser/src/autofill/notification/bar.scss deleted file mode 100644 index c91c5f3ebac..00000000000 --- a/apps/browser/src/autofill/notification/bar.scss +++ /dev/null @@ -1,304 +0,0 @@ -@import "../shared/styles/variables"; - -body { - margin: 0; - padding: 0; - height: 100%; - font-size: 14px; - line-height: 16px; - font-family: $font-family-sans-serif; - - @include themify($themes) { - color: themed("textColor"); - background-color: themed("backgroundColor"); - } -} - -img { - margin: 0; - padding: 0; - border: 0; -} - -button, -select { - font-size: $font-size-base; - font-family: $font-family-sans-serif; -} - -.outer-wrapper { - display: block; - position: relative; - padding: 8px; - min-height: 42px; - border: 1px solid transparent; - border-bottom: 2px solid transparent; - border-radius: 4px; - box-sizing: border-box; - - @include themify($themes) { - border-color: themed("borderColor"); - border-bottom-color: themed("primaryColor"); - } - - &.success-event { - @include themify($themes) { - border-bottom-color: themed("successColor"); - } - } - - &.error-event { - @include themify($themes) { - border-bottom-color: themed("errorColor"); - } - } -} - -.inner-wrapper { - display: grid; - grid-template-columns: auto max-content; -} - -.outer-wrapper > *, -.inner-wrapper > * { - align-self: center; -} - -#logo { - width: 24px; - height: 24px; - display: block; -} - -.logo-wrapper { - position: absolute; - top: 8px; - left: 10px; - overflow: hidden; -} - -#close-button { - display: flex; - align-items: center; - justify-content: center; - width: 30px; - height: 30px; - margin-right: 10px; - padding: 0; - - &:hover { - @include themify($themes) { - border-color: rgba(themed("textColor"), 0.2); - background-color: rgba(themed("textColor"), 0.2); - } - } -} - -#close { - display: block; - width: 16px; - height: 16px; - - > path { - @include themify($themes) { - fill: themed("textColor"); - } - } -} - -.notification-close { - position: absolute; - top: 6px; - right: 6px; -} - -#content .inner-wrapper { - display: flex; - flex-wrap: wrap; - align-items: flex-start; - - .notification-body { - width: 100%; - padding: 4px 38px 24px 42px; - font-weight: 400; - } - - .notification-actions { - display: flex; - width: 100%; - align-items: stretch; - justify-content: flex-end; - - #never-save { - margin-right: auto; - padding: 0; - font-size: 16px; - font-weight: 500; - letter-spacing: 0.5px; - } - - #select-folder { - width: 125px; - margin-right: 6px; - font-size: 12px; - appearance: none; - background-repeat: no-repeat; - background-position: center right 4px; - background-size: 16px; - - @include themify($themes) { - color: themed("mutedTextColor"); - border-color: themed("mutedTextColor"); - } - - &:not([disabled]) { - display: block; - } - } - - .primary, - .secondary { - font-size: 12px; - } - - .secondary { - margin-right: 6px; - border-width: 1px; - } - - .primary { - margin-right: 2px; - } - - &.success-message, - &.error-message { - padding: 4px 36px 6px 42px; - } - } -} - -button { - padding: 4px 8px; - border-radius: $border-radius; - border: 1px solid transparent; - cursor: pointer; -} - -button.primary:not(.neutral) { - @include themify($themes) { - background-color: themed("primaryColor"); - color: themed("textContrast"); - border-color: themed("primaryColor"); - } - - &:hover { - @include themify($themes) { - background-color: darken(themed("primaryColor"), 1.5%); - color: darken(themed("textContrast"), 6%); - } - } -} - -button.secondary:not(.neutral) { - @include themify($themes) { - background-color: themed("backgroundColor"); - color: themed("mutedTextColor"); - border-color: themed("mutedTextColor"); - } - - &:hover { - @include themify($themes) { - background-color: themed("backgroundOffsetColor"); - color: darken(themed("mutedTextColor"), 6%); - } - } -} - -button.link, -button.neutral { - @include themify($themes) { - background-color: transparent; - color: themed("primaryColor"); - } - - &:hover { - text-decoration: underline; - - @include themify($themes) { - color: darken(themed("primaryColor"), 6%); - } - } -} - -select { - padding: 4px 6px; - border: 1px solid #000000; - border-radius: $border-radius; - - @include themify($themes) { - color: themed("textColor"); - background-color: themed("inputBackgroundColor"); - border-color: themed("inputBorderColor"); - } -} - -.success-message { - display: flex; - align-items: center; - justify-content: center; - - @include themify($themes) { - color: themed("successColor"); - } - - svg { - margin-right: 8px; - - path { - @include themify($themes) { - fill: themed("successColor"); - } - } - } -} - -.error-message { - @include themify($themes) { - color: themed("errorColor"); - } -} - -.success-event, -.error-event { - .notification-body { - display: none; - } -} - -@media screen and (max-width: 768px) { - #select-folder { - display: none; - } -} - -@media print { - body { - display: none; - } -} - -.theme_light { - #content .inner-wrapper { - #select-folder { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB3aWR0aD0nMTYnIGhlaWdodD0nMTYnIGZpbGw9J25vbmUnPjxwYXRoIHN0cm9rZT0nIzIxMjUyOScgZD0nbTUgNiAzIDMgMy0zJy8+PC9zdmc+"); - } - } -} - -.theme_dark { - #content .inner-wrapper { - #select-folder { - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxNicgaGVpZ2h0PScxNicgZmlsbD0nbm9uZSc+PHBhdGggc3Ryb2tlPScjZmZmZmZmJyBkPSdtNSA2IDMgMyAzLTMnLz48L3N2Zz4="); - } - } -} diff --git a/apps/browser/src/autofill/notification/bar.ts b/apps/browser/src/autofill/notification/bar.ts index fcf91ca2e91..3673a9f7321 100644 --- a/apps/browser/src/autofill/notification/bar.ts +++ b/apps/browser/src/autofill/notification/bar.ts @@ -187,8 +187,6 @@ async function initNotificationBar(message: NotificationBarWindowMessage) { const notificationTestId = getNotificationTestId(notificationType); appendHeaderMessageToTitle(headerMessage); - document.body.innerHTML = ""; - if (isVaultLocked) { const notificationConfig = { ...notificationBarIframeInitData, From b1738cc6b207ed2f004757742385412293b5a188 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 29 Oct 2025 15:51:50 +0100 Subject: [PATCH 05/66] [PM-26340] Add linux biometrics v2 (#16660) * Extract windows biometrics v2 changes Co-authored-by: Bernd Schoolmann * Address some code review feedback * cargo fmt * rely on zeroizing allocator * Handle TDE edge cases * Update windows default * Make windows rust code async and fix restoring focus freezes * fix formatting * cleanup native logging * Add unit test coverage * Add missing logic to edge case for PIN disable. * Address code review feedback * fix test * code review changes * fix clippy warning * Swap to unimplemented on each method * Implement encrypted memory store * Make dpapi secure key container pub(super) * Add linux biometrics v2 * Run cargo fmt * Fix cargo lock * Undo AC changes * Undo change * Fix build * Cargo fmt --------- Co-authored-by: Thomas Avery Co-authored-by: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> --- .../core/src/biometric_v2/linux.rs | 141 ++++++++++++++++++ .../core/src/biometric_v2/mod.rs | 2 +- .../core/src/secure_memory/mod.rs | 2 +- 3 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 apps/desktop/desktop_native/core/src/biometric_v2/linux.rs diff --git a/apps/desktop/desktop_native/core/src/biometric_v2/linux.rs b/apps/desktop/desktop_native/core/src/biometric_v2/linux.rs new file mode 100644 index 00000000000..44cba4a9e5b --- /dev/null +++ b/apps/desktop/desktop_native/core/src/biometric_v2/linux.rs @@ -0,0 +1,141 @@ +//! This file implements Polkit based system unlock. +//! +//! # Security +//! This section describes the assumed security model and security guarantees achieved. In the required security +//! guarantee is that a locked vault - a running app - cannot be unlocked when the device (user-space) +//! is compromised in this state. +//! +//! When first unlocking the app, the app sends the user-key to this module, which holds it in secure memory, +//! protected by memfd_secret. This makes it inaccessible to other processes, even if they compromise root, a kernel compromise +//! has circumventable best-effort protections. While the app is running this key is held in memory, even if locked. +//! When unlocking, the app will prompt the user via `polkit` to get a yes/no decision on whether to release the key to the app. + +use anyhow::{anyhow, Result}; +use std::sync::Arc; +use tokio::sync::Mutex; +use tracing::{debug, warn}; +use zbus::Connection; +use zbus_polkit::policykit1::{AuthorityProxy, CheckAuthorizationFlags, Subject}; + +use crate::secure_memory::*; + +pub struct BiometricLockSystem { + // The userkeys that are held in memory MUST be protected from memory dumping attacks, to ensure + // locked vaults cannot be unlocked + secure_memory: Arc>, +} + +impl BiometricLockSystem { + pub fn new() -> Self { + Self { + secure_memory: Arc::new(Mutex::new( + crate::secure_memory::encrypted_memory_store::EncryptedMemoryStore::new(), + )), + } + } +} + +impl Default for BiometricLockSystem { + fn default() -> Self { + Self::new() + } +} + +impl super::BiometricTrait for BiometricLockSystem { + async fn authenticate(&self, _hwnd: Vec, _message: String) -> Result { + polkit_authenticate_bitwarden_policy().await + } + + async fn authenticate_available(&self) -> Result { + polkit_is_bitwarden_policy_available().await + } + + async fn enroll_persistent(&self, _user_id: &str, _key: &[u8]) -> Result<()> { + // Not implemented + Ok(()) + } + + async fn provide_key(&self, user_id: &str, key: &[u8]) { + self.secure_memory + .lock() + .await + .put(user_id.to_string(), key); + } + + async fn unlock(&self, user_id: &str, _hwnd: Vec) -> Result> { + if !polkit_authenticate_bitwarden_policy().await? { + return Err(anyhow!("Authentication failed")); + } + + self.secure_memory + .lock() + .await + .get(user_id) + .ok_or(anyhow!("No key found")) + } + + async fn unlock_available(&self, user_id: &str) -> Result { + Ok(self.secure_memory.lock().await.has(user_id)) + } + + async fn has_persistent(&self, _user_id: &str) -> Result { + Ok(false) + } + + async fn unenroll(&self, user_id: &str) -> Result<(), anyhow::Error> { + self.secure_memory.lock().await.remove(user_id); + Ok(()) + } +} + +/// Perform a polkit authorization against the bitwarden unlock policy. Note: This relies on no custom +/// rules in the system skipping the authorization check, in which case this counts as UV / authentication. +async fn polkit_authenticate_bitwarden_policy() -> Result { + debug!("[Polkit] Authenticating / performing UV"); + + let connection = Connection::system().await?; + let proxy = AuthorityProxy::new(&connection).await?; + let subject = Subject::new_for_owner(std::process::id(), None, None)?; + let details = std::collections::HashMap::new(); + let authorization_result = proxy + .check_authorization( + &subject, + "com.bitwarden.Bitwarden.unlock", + &details, + CheckAuthorizationFlags::AllowUserInteraction.into(), + "", + ) + .await; + + match authorization_result { + Ok(result) => Ok(result.is_authorized), + Err(e) => { + warn!("[Polkit] Error performing authentication: {:?}", e); + Ok(false) + } + } +} + +async fn polkit_is_bitwarden_policy_available() -> Result { + let connection = Connection::system().await?; + let proxy = AuthorityProxy::new(&connection).await?; + let actions = proxy.enumerate_actions("en").await?; + for action in actions { + if action.action_id == "com.bitwarden.Bitwarden.unlock" { + return Ok(true); + } + } + Ok(false) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + #[ignore] + async fn test_polkit_authenticate() { + let result = polkit_authenticate_bitwarden_policy().await; + assert!(result.is_ok()); + } +} diff --git a/apps/desktop/desktop_native/core/src/biometric_v2/mod.rs b/apps/desktop/desktop_native/core/src/biometric_v2/mod.rs index e37a101e2ae..669267b7829 100644 --- a/apps/desktop/desktop_native/core/src/biometric_v2/mod.rs +++ b/apps/desktop/desktop_native/core/src/biometric_v2/mod.rs @@ -1,7 +1,7 @@ use anyhow::Result; #[allow(clippy::module_inception)] -#[cfg_attr(target_os = "linux", path = "unimplemented.rs")] +#[cfg_attr(target_os = "linux", path = "linux.rs")] #[cfg_attr(target_os = "macos", path = "unimplemented.rs")] #[cfg_attr(target_os = "windows", path = "windows.rs")] mod biometric_v2; diff --git a/apps/desktop/desktop_native/core/src/secure_memory/mod.rs b/apps/desktop/desktop_native/core/src/secure_memory/mod.rs index 8695904758e..d4323ce40dd 100644 --- a/apps/desktop/desktop_native/core/src/secure_memory/mod.rs +++ b/apps/desktop/desktop_native/core/src/secure_memory/mod.rs @@ -1,7 +1,7 @@ #[cfg(target_os = "windows")] pub(crate) mod dpapi; -mod encrypted_memory_store; +pub(crate) mod encrypted_memory_store; mod secure_key; /// The secure memory store provides an ephemeral key-value store for sensitive data. From d567530e159c083cbf986dbd0f68f498a7199d2c Mon Sep 17 00:00:00 2001 From: cyprain-okeke <108260115+cyprain-okeke@users.noreply.github.com> Date: Wed, 29 Oct 2025 16:02:59 +0100 Subject: [PATCH 06/66] resolve the button name (#17094) --- .../billing/individual/premium/premium-vnext.component.html | 2 +- .../upgrade/upgrade-account/upgrade-account.component.spec.ts | 2 +- .../upgrade/upgrade-account/upgrade-account.component.ts | 2 +- .../upgrade/upgrade-payment/upgrade-payment.component.ts | 2 +- apps/web/src/locales/en/messages.json | 3 +++ 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/billing/individual/premium/premium-vnext.component.html b/apps/web/src/app/billing/individual/premium/premium-vnext.component.html index bf5d0f60861..ee2bef9baa3 100644 --- a/apps/web/src/app/billing/individual/premium/premium-vnext.component.html +++ b/apps/web/src/app/billing/individual/premium/premium-vnext.component.html @@ -38,7 +38,7 @@ diff --git a/apps/web/src/app/billing/individual/upgrade/upgrade-account/upgrade-account.component.spec.ts b/apps/web/src/app/billing/individual/upgrade/upgrade-account/upgrade-account.component.spec.ts index 27e69fcf0d4..a6038873e83 100644 --- a/apps/web/src/app/billing/individual/upgrade/upgrade-account/upgrade-account.component.spec.ts +++ b/apps/web/src/app/billing/individual/upgrade/upgrade-account/upgrade-account.component.spec.ts @@ -98,7 +98,7 @@ describe("UpgradeAccountComponent", () => { expect(sut["familiesCardDetails"].price.amount).toBe(40 / 12); expect(sut["familiesCardDetails"].price.cadence).toBe("monthly"); expect(sut["familiesCardDetails"].button.type).toBe("secondary"); - expect(sut["familiesCardDetails"].button.text).toBe("upgradeToFamilies"); + expect(sut["familiesCardDetails"].button.text).toBe("startFreeFamiliesTrial"); expect(sut["familiesCardDetails"].features).toEqual(["Feature A", "Feature B", "Feature C"]); }); diff --git a/apps/web/src/app/billing/individual/upgrade/upgrade-account/upgrade-account.component.ts b/apps/web/src/app/billing/individual/upgrade/upgrade-account/upgrade-account.component.ts index be09505d190..780b6bed433 100644 --- a/apps/web/src/app/billing/individual/upgrade/upgrade-account/upgrade-account.component.ts +++ b/apps/web/src/app/billing/individual/upgrade/upgrade-account/upgrade-account.component.ts @@ -119,7 +119,7 @@ export class UpgradeAccountComponent implements OnInit { }, button: { text: this.i18nService.t( - this.isFamiliesPlan(tier.id) ? "upgradeToFamilies" : "upgradeToPremium", + this.isFamiliesPlan(tier.id) ? "startFreeFamiliesTrial" : "upgradeToPremium", ), type: buttonType, }, diff --git a/apps/web/src/app/billing/individual/upgrade/upgrade-payment/upgrade-payment.component.ts b/apps/web/src/app/billing/individual/upgrade/upgrade-payment/upgrade-payment.component.ts index a0ba480fe1e..a80ff5d720a 100644 --- a/apps/web/src/app/billing/individual/upgrade/upgrade-payment/upgrade-payment.component.ts +++ b/apps/web/src/app/billing/individual/upgrade/upgrade-payment/upgrade-payment.component.ts @@ -161,7 +161,7 @@ export class UpgradePaymentComponent implements OnInit, AfterViewInit { }; this.upgradeToMessage = this.i18nService.t( - this.isFamiliesPlan ? "upgradeToFamilies" : "upgradeToPremium", + this.isFamiliesPlan ? "startFreeFamiliesTrial" : "upgradeToPremium", ); } else { this.complete.emit({ status: UpgradePaymentStatus.Closed, organizationId: null }); diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index aa0353e754d..c1026af4b6e 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -11975,5 +11975,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } From 687f3d144cc0c236b826f26b22719dece8c14caf Mon Sep 17 00:00:00 2001 From: Vijay Oommen Date: Wed, 29 Oct 2025 10:58:38 -0500 Subject: [PATCH 07/66] [PM-17577] Inactive two-step login report - check hostname and domain name (#16823) --- ...active-two-factor-report.component.spec.ts | 149 ++++++++++++++++++ .../inactive-two-factor-report.component.ts | 11 ++ 2 files changed, 160 insertions(+) diff --git a/apps/web/src/app/dirt/reports/pages/inactive-two-factor-report.component.spec.ts b/apps/web/src/app/dirt/reports/pages/inactive-two-factor-report.component.spec.ts index acc34232571..80893737ffd 100644 --- a/apps/web/src/app/dirt/reports/pages/inactive-two-factor-report.component.spec.ts +++ b/apps/web/src/app/dirt/reports/pages/inactive-two-factor-report.component.spec.ts @@ -121,4 +121,153 @@ describe("InactiveTwoFactorReportComponent", () => { it("should call fullSync method of syncService", () => { expect(syncServiceMock.fullSync).toHaveBeenCalledWith(false); }); + + describe("isInactive2faCipher", () => { + beforeEach(() => { + // Add both domain and host to services map + component.services.set("example.com", "https://example.com/2fa-doc"); + component.services.set("sub.example.com", "https://sub.example.com/2fa-doc"); + fixture.detectChanges(); + }); + it("should return true and documentation for cipher with matching domain", () => { + const cipher = createCipherView({ + login: { + uris: [{ uri: "https://example.com/login" }], + }, + }); + const [doc, isInactive] = (component as any).isInactive2faCipher(cipher); + expect(isInactive).toBe(true); + expect(doc).toBe("https://example.com/2fa-doc"); + }); + + it("should return true and documentation for cipher with matching host", () => { + const cipher = createCipherView({ + login: { + uris: [{ uri: "https://sub.example.com/login" }], + }, + }); + const [doc, isInactive] = (component as any).isInactive2faCipher(cipher); + expect(isInactive).toBe(true); + expect(doc).toBe("https://sub.example.com/2fa-doc"); + }); + + it("should return false for cipher with non-matching domain or host", () => { + const cipher = createCipherView({ + login: { + uris: [{ uri: "https://otherdomain.com/login" }], + }, + }); + const [doc, isInactive] = (component as any).isInactive2faCipher(cipher); + expect(isInactive).toBe(false); + expect(doc).toBe(""); + }); + + it("should return false if cipher type is not Login", () => { + const cipher = createCipherView({ + type: 2, + login: { + uris: [{ uri: "https://example.com/login" }], + }, + }); + const [doc, isInactive] = (component as any).isInactive2faCipher(cipher); + expect(isInactive).toBe(false); + expect(doc).toBe(""); + }); + + it("should return false if cipher has TOTP", () => { + const cipher = createCipherView({ + login: { + totp: "some-totp", + uris: [{ uri: "https://example.com/login" }], + }, + }); + const [doc, isInactive] = (component as any).isInactive2faCipher(cipher); + expect(isInactive).toBe(false); + expect(doc).toBe(""); + }); + + it("should return false if cipher is deleted", () => { + const cipher = createCipherView({ + isDeleted: true, + login: { + uris: [{ uri: "https://example.com/login" }], + }, + }); + const [doc, isInactive] = (component as any).isInactive2faCipher(cipher); + expect(isInactive).toBe(false); + expect(doc).toBe(""); + }); + + it("should return false if cipher does not have edit access and no organization", () => { + component.organization = null; + const cipher = createCipherView({ + edit: false, + login: { + uris: [{ uri: "https://example.com/login" }], + }, + }); + const [doc, isInactive] = (component as any).isInactive2faCipher(cipher); + expect(isInactive).toBe(false); + expect(doc).toBe(""); + }); + + it("should return false if cipher does not have viewPassword", () => { + const cipher = createCipherView({ + viewPassword: false, + login: { + uris: [{ uri: "https://example.com/login" }], + }, + }); + const [doc, isInactive] = (component as any).isInactive2faCipher(cipher); + expect(isInactive).toBe(false); + expect(doc).toBe(""); + }); + + it("should check all uris and return true if any matches domain or host", () => { + const cipher = createCipherView({ + login: { + uris: [ + { uri: "https://otherdomain.com/login" }, + { uri: "https://sub.example.com/dashboard" }, + ], + }, + }); + const [doc, isInactive] = (component as any).isInactive2faCipher(cipher); + expect(isInactive).toBe(true); + expect(doc).toBe("https://sub.example.com/2fa-doc"); + }); + + it("should return false if uris array is empty", () => { + const cipher = createCipherView({ + login: { + uris: [], + }, + }); + const [doc, isInactive] = (component as any).isInactive2faCipher(cipher); + expect(isInactive).toBe(false); + expect(doc).toBe(""); + }); + + function createCipherView({ + type = 1, + login = {}, + isDeleted = false, + edit = true, + viewPassword = true, + }: any): any { + return { + id: "test-id", + type, + login: { + totp: null, + hasUris: true, + uris: [], + ...login, + }, + isDeleted, + edit, + viewPassword, + }; + } + }); }); diff --git a/apps/web/src/app/dirt/reports/pages/inactive-two-factor-report.component.ts b/apps/web/src/app/dirt/reports/pages/inactive-two-factor-report.component.ts index 8b0fdda70e3..2a8ec12ac6a 100644 --- a/apps/web/src/app/dirt/reports/pages/inactive-two-factor-report.component.ts +++ b/apps/web/src/app/dirt/reports/pages/inactive-two-factor-report.component.ts @@ -109,7 +109,18 @@ export class InactiveTwoFactorReportComponent extends CipherReportComponent impl const u = login.uris[i]; if (u.uri != null && u.uri !== "") { const uri = u.uri.replace("www.", ""); + const host = Utils.getHost(uri); const domain = Utils.getDomain(uri); + // check host first + if (host != null && this.services.has(host)) { + if (this.services.get(host) != null) { + docFor2fa = this.services.get(host) || ""; + } + isInactive2faCipher = true; + break; + } + + // then check domain if (domain != null && this.services.has(domain)) { if (this.services.get(domain) != null) { docFor2fa = this.services.get(domain) || ""; From ee420258e6cd01384eeb7233736ccabb6d523d4b Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 29 Oct 2025 17:46:50 +0100 Subject: [PATCH 08/66] Remove deprecated encstring usage from dirt code (#17100) --- .../member-access-report.component.ts | 5 +++- .../member-access-report.service.spec.ts | 24 +++++++++++++++++-- .../services/member-access-report.service.ts | 22 ++++++++++++++--- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/member-access-report.component.ts b/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/member-access-report.component.ts index ad15edd84df..445cee6683c 100644 --- a/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/member-access-report.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/member-access-report.component.ts @@ -8,12 +8,15 @@ import { BehaviorSubject, debounceTime, firstValueFrom, lastValueFrom } from "rx import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe"; import { safeProvider } from "@bitwarden/angular/platform/utils/safe-provider"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { OrganizationMetadataServiceAbstraction } from "@bitwarden/common/billing/abstractions/organization-metadata.service.abstraction"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { FileDownloadService } from "@bitwarden/common/platform/abstractions/file-download/file-download.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { OrganizationId } from "@bitwarden/common/types/guid"; import { DialogService, SearchModule, TableDataSource } from "@bitwarden/components"; +import { KeyService } from "@bitwarden/key-management"; import { ExportHelper } from "@bitwarden/vault-export-core"; import { CoreOrganizationModule } from "@bitwarden/web-vault/app/admin-console/organizations/core"; import { @@ -41,7 +44,7 @@ import { MemberAccessReportView } from "./view/member-access-report.view"; safeProvider({ provide: MemberAccessReportServiceAbstraction, useClass: MemberAccessReportService, - deps: [MemberAccessReportApiService, I18nService], + deps: [MemberAccessReportApiService, I18nService, EncryptService, KeyService, AccountService], }), ], }) diff --git a/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/services/member-access-report.service.spec.ts b/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/services/member-access-report.service.spec.ts index ad388cfed04..615e6d079b2 100644 --- a/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/services/member-access-report.service.spec.ts +++ b/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/services/member-access-report.service.spec.ts @@ -1,7 +1,13 @@ import { mock } from "jest-mock-extended"; +import { of } from "rxjs"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { OrganizationId } from "@bitwarden/common/types/guid"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { mockAccountServiceWith } from "@bitwarden/common/spec"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { newGuid } from "@bitwarden/guid"; +import { KeyService } from "@bitwarden/key-management"; import { MemberAccessReportApiService } from "./member-access-report-api.service"; import { @@ -9,9 +15,14 @@ import { memberAccessWithoutAccessDetailsReportsMock, } from "./member-access-report.mock"; import { MemberAccessReportService } from "./member-access-report.service"; + describe("ImportService", () => { const mockOrganizationId = "mockOrgId" as OrganizationId; const reportApiService = mock(); + const mockEncryptService = mock(); + const userId = newGuid() as UserId; + const mockAccountService = mockAccountServiceWith(userId); + const mockKeyService = mock(); let memberAccessReportService: MemberAccessReportService; const i18nMock = mock({ t(key) { @@ -20,10 +31,19 @@ describe("ImportService", () => { }); beforeEach(() => { + mockKeyService.orgKeys$.mockReturnValue( + of({ mockOrgId: new SymmetricCryptoKey(new Uint8Array(64)) }), + ); reportApiService.getMemberAccessData.mockImplementation(() => Promise.resolve(memberAccessReportsMock), ); - memberAccessReportService = new MemberAccessReportService(reportApiService, i18nMock); + memberAccessReportService = new MemberAccessReportService( + reportApiService, + i18nMock, + mockEncryptService, + mockKeyService, + mockAccountService, + ); }); describe("generateMemberAccessReportView", () => { diff --git a/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/services/member-access-report.service.ts b/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/services/member-access-report.service.ts index caa27a75b82..f6d1139f619 100644 --- a/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/services/member-access-report.service.ts +++ b/bitwarden_license/bit-web/src/app/dirt/reports/member-access-report/services/member-access-report.service.ts @@ -1,11 +1,16 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { Injectable } from "@angular/core"; +import { firstValueFrom, map } from "rxjs"; import { CollectionAccessSelectionView } from "@bitwarden/admin-console/common"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Guid, OrganizationId } from "@bitwarden/common/types/guid"; +import { KeyService } from "@bitwarden/key-management"; import { getPermissionList, convertToPermission, @@ -22,6 +27,9 @@ export class MemberAccessReportService { constructor( private reportApiService: MemberAccessReportApiService, private i18nService: I18nService, + private encryptService: EncryptService, + private keyService: KeyService, + private accountService: AccountService, ) {} /** * Transforms user data into a MemberAccessReportView. @@ -78,14 +86,22 @@ export class MemberAccessReportService { async generateUserReportExportItems( organizationId: OrganizationId, ): Promise { + const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + const organizationSymmetricKey = await firstValueFrom( + this.keyService.orgKeys$(activeUserId).pipe(map((keys) => keys[organizationId])), + ); + const memberAccessReports = await this.reportApiService.getMemberAccessData(organizationId); const collectionNames = memberAccessReports.map((item) => item.collectionName.encryptedString); const collectionNameMap = new Map(collectionNames.map((col) => [col, ""])); for await (const key of collectionNameMap.keys()) { - const decrypted = new EncString(key); - await decrypted.decrypt(organizationId); - collectionNameMap.set(key, decrypted.decryptedValue); + const encryptedCollectionName = new EncString(key); + const collectionName = await this.encryptService.decryptString( + encryptedCollectionName, + organizationSymmetricKey, + ); + collectionNameMap.set(key, collectionName); } const exportItems = memberAccessReports.map((report) => { From 6896c77332cfc740aabdc39409ef04b1071237f5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:21:07 -0400 Subject: [PATCH 09/66] [deps] UI Foundation: Update axe-playwright to v2.2.2 (#16629) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Bryan Cunningham --- 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 e131618ee4a..e39aeea5605 100644 --- a/package-lock.json +++ b/package-lock.json @@ -125,7 +125,7 @@ "@yao-pkg/pkg": "6.5.1", "angular-eslint": "19.6.0", "autoprefixer": "10.4.21", - "axe-playwright": "2.1.0", + "axe-playwright": "2.2.2", "babel-loader": "9.2.1", "base64-loader": "1.0.0", "browserslist": "4.23.2", @@ -16966,9 +16966,9 @@ } }, "node_modules/axe-playwright": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/axe-playwright/-/axe-playwright-2.1.0.tgz", - "integrity": "sha512-tY48SX56XaAp16oHPyD4DXpybz8Jxdz9P7exTjF/4AV70EGUavk+1fUPWirM0OYBR+YyDx6hUeDvuHVA6fB9YA==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/axe-playwright/-/axe-playwright-2.2.2.tgz", + "integrity": "sha512-h350/grzDCPgpuWV7eEOqr/f61Xn07Gi9f9B3Ew4rW6/nFtpdEJYW6jgRATorgAGXjEAYFTnaY3sEys39wDw4A==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 2c02ff68824..0190bc37bec 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "@yao-pkg/pkg": "6.5.1", "angular-eslint": "19.6.0", "autoprefixer": "10.4.21", - "axe-playwright": "2.1.0", + "axe-playwright": "2.2.2", "babel-loader": "9.2.1", "base64-loader": "1.0.0", "browserslist": "4.23.2", From 4bdfefd001b553849c9f69bff52a3ea4dc637ac7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:51:25 -0400 Subject: [PATCH 10/66] [deps] UI Foundation: Update chromatic to v13.3.1 (#16630) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Bryan Cunningham --- 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 e39aeea5605..d1858d4d508 100644 --- a/package-lock.json +++ b/package-lock.json @@ -129,7 +129,7 @@ "babel-loader": "9.2.1", "base64-loader": "1.0.0", "browserslist": "4.23.2", - "chromatic": "13.1.2", + "chromatic": "13.3.1", "concurrently": "9.2.0", "copy-webpack-plugin": "13.0.0", "cross-env": "10.1.0", @@ -18492,9 +18492,9 @@ } }, "node_modules/chromatic": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-13.1.2.tgz", - "integrity": "sha512-jgVptQabJHOnzmmvLjbtfutREkWGhDDk2gVqMH6N+V7z56oIy4Sd2/U7ZxNvnVFPinZQMSjSdUce4b6JIP64Dg==", + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-13.3.1.tgz", + "integrity": "sha512-qJ/el70Wo7jFgiXPpuukqxCEc7IKiH/e8MjTzIF9uKw+3XZ6GghOTTLC7lGfeZtosiQBMkRlYet77tC4KKHUng==", "dev": true, "license": "MIT", "bin": { diff --git a/package.json b/package.json index 0190bc37bec..32056a174b1 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "babel-loader": "9.2.1", "base64-loader": "1.0.0", "browserslist": "4.23.2", - "chromatic": "13.1.2", + "chromatic": "13.3.1", "concurrently": "9.2.0", "copy-webpack-plugin": "13.0.0", "cross-env": "10.1.0", From d85b9986d0f97362d22cf30259dd4abaf3e91b6a Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Wed, 29 Oct 2025 19:42:19 +0100 Subject: [PATCH 11/66] [CL-901] [CL-903] Unowned - Prefer signal & change detection (#16949) --- apps/browser/src/popup/app.component.ts | 2 ++ ...ktop-sync-verification-dialog.component.ts | 2 ++ apps/browser/src/popup/tabs-v2.component.ts | 2 ++ .../src/app/accounts/settings.component.ts | 2 ++ apps/desktop/src/app/app.component.ts | 14 ++++++++++ .../src/app/components/avatar.component.ts | 18 +++++++++++++ ...wser-sync-verification-dialog.component.ts | 2 ++ .../components/user-verification.component.ts | 2 ++ ...erify-native-messaging-dialog.component.ts | 2 ++ .../app/layout/account-switcher.component.ts | 2 ++ .../src/app/layout/header.component.ts | 2 ++ apps/desktop/src/app/layout/nav.component.ts | 2 ++ .../src/app/layout/search/search.component.ts | 2 ++ apps/web/src/app/app.component.ts | 2 ++ .../components/dynamic-avatar.component.ts | 12 +++++++++ .../environment-selector.component.ts | 2 ++ .../request-sm-access.component.ts | 2 ++ .../sm-landing.component.ts | 2 ++ .../app/settings/domain-rules.component.ts | 2 ++ .../src/app/settings/preferences.component.ts | 2 ++ .../bit-browser/src/popup/app.component.ts | 2 ++ .../bit-web/src/app/app.component.ts | 2 ++ .../activity/activity-card.component.ts | 6 +++++ .../guards/project-access.guard.spec.ts | 4 +++ .../integrations.component.spec.ts | 4 +++ .../integrations/integrations.component.ts | 2 ++ .../layout/layout.component.ts | 2 ++ .../layout/navigation.component.ts | 2 ++ .../overview/overview.component.ts | 2 ++ .../overview/section.component.ts | 4 +++ .../dialog/project-delete-dialog.component.ts | 2 ++ .../dialog/project-dialog.component.ts | 2 ++ .../project/project-people.component.ts | 2 ++ .../project/project-secrets.component.ts | 2 ++ .../project-service-accounts.component.ts | 2 ++ .../projects/project/project.component.ts | 2 ++ .../projects/projects/projects.component.ts | 2 ++ .../secrets/dialog/secret-delete.component.ts | 2 ++ .../secrets/dialog/secret-dialog.component.ts | 2 ++ .../dialog/secret-view-dialog.component.ts | 2 ++ .../secrets/secrets.component.ts | 2 ++ .../access/access-list.component.ts | 8 ++++++ .../access/access-tokens.component.ts | 2 ++ .../access-token-create-dialog.component.ts | 2 ++ .../dialogs/access-token-dialog.component.ts | 2 ++ .../dialogs/expiration-options.component.ts | 6 +++++ .../config/config.component.ts | 2 ++ ...service-account-delete-dialog.component.ts | 2 ++ .../service-account-dialog.component.ts | 2 ++ .../service-accounts-events.component.ts | 2 ++ .../service-account-access.guard.spec.ts | 4 +++ .../service-account-people.component.ts | 2 ++ .../service-account-projects.component.ts | 2 ++ .../service-account.component.ts | 2 ++ .../service-accounts-list.component.ts | 14 ++++++++++ .../service-accounts.component.ts | 2 ++ .../sm-import-error-dialog.component.ts | 2 ++ .../settings/porting/sm-export.component.ts | 2 ++ .../settings/porting/sm-import.component.ts | 2 ++ .../access-policy-selector.component.ts | 22 ++++++++++++++++ .../bulk-confirmation-dialog.component.ts | 2 ++ .../dialogs/bulk-status-dialog.component.ts | 2 ++ .../shared/new-menu.component.ts | 2 ++ .../shared/org-suspended.component.ts | 2 ++ .../shared/projects-list.component.ts | 16 ++++++++++++ .../shared/secrets-list.component.ts | 26 +++++++++++++++++++ .../dialog/secret-hard-delete.component.ts | 2 ++ .../trash/dialog/secret-restore.component.ts | 2 ++ .../secrets-manager/trash/trash.component.ts | 2 ++ eslint.config.mjs | 6 ++--- .../src/components/callout.component.ts | 14 ++++++++++ .../modal/dynamic-modal.component.ts | 4 +++ .../src/directives/api-action.directive.ts | 2 ++ .../src/directives/copy-text.directive.ts | 2 ++ .../src/directives/fallback-src.directive.ts | 2 ++ .../directives/if-feature.directive.spec.ts | 2 ++ .../src/directives/if-feature.directive.ts | 4 +++ .../directives/input-verbatim.directive.ts | 2 ++ .../src/directives/launch-click.directive.ts | 2 ++ .../src/directives/text-drag.directive.ts | 2 ++ .../directives/true-false-value.directive.ts | 4 +++ .../src/card/base-card/base-card.component.ts | 2 ++ .../src/card/card-content.component.ts | 2 ++ 83 files changed, 315 insertions(+), 3 deletions(-) diff --git a/apps/browser/src/popup/app.component.ts b/apps/browser/src/popup/app.component.ts index b85da665fa0..8f00569b720 100644 --- a/apps/browser/src/popup/app.component.ts +++ b/apps/browser/src/popup/app.component.ts @@ -67,6 +67,8 @@ import { initPopupClosedListener } from "../platform/services/popup-view-cache-b import { routerTransition } from "./app-routing.animations"; import { DesktopSyncVerificationDialogComponent } from "./components/desktop-sync-verification-dialog.component"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "app-root", styles: [], diff --git a/apps/browser/src/popup/components/desktop-sync-verification-dialog.component.ts b/apps/browser/src/popup/components/desktop-sync-verification-dialog.component.ts index 2ca24da6c75..510348927ce 100644 --- a/apps/browser/src/popup/components/desktop-sync-verification-dialog.component.ts +++ b/apps/browser/src/popup/components/desktop-sync-verification-dialog.component.ts @@ -15,6 +15,8 @@ export type DesktopSyncVerificationDialogParams = { fingerprint: string[]; }; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "desktop-sync-verification-dialog.component.html", imports: [JslibModule, ButtonModule, DialogModule], diff --git a/apps/browser/src/popup/tabs-v2.component.ts b/apps/browser/src/popup/tabs-v2.component.ts index f1e42799b35..1c409fee639 100644 --- a/apps/browser/src/popup/tabs-v2.component.ts +++ b/apps/browser/src/popup/tabs-v2.component.ts @@ -17,6 +17,8 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { NavButton } from "../platform/popup/layout/popup-tab-navigation.component"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "app-tabs-v2", templateUrl: "./tabs-v2.component.html", diff --git a/apps/desktop/src/app/accounts/settings.component.ts b/apps/desktop/src/app/accounts/settings.component.ts index 7666e9bef1b..abebdfa5fc3 100644 --- a/apps/desktop/src/app/accounts/settings.component.ts +++ b/apps/desktop/src/app/accounts/settings.component.ts @@ -67,6 +67,8 @@ import { DesktopSettingsService } from "../../platform/services/desktop-settings import { DesktopPremiumUpgradePromptService } from "../../services/desktop-premium-upgrade-prompt.service"; import { NativeMessagingManifestService } from "../services/native-messaging-manifest.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "app-settings", templateUrl: "settings.component.html", diff --git a/apps/desktop/src/app/app.component.ts b/apps/desktop/src/app/app.component.ts index 7f7eddcfe95..4b6dcab0dff 100644 --- a/apps/desktop/src/app/app.component.ts +++ b/apps/desktop/src/app/app.component.ts @@ -91,6 +91,8 @@ const BroadcasterSubscriptionId = "AppComponent"; const IdleTimeout = 60000 * 10; // 10 minutes const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours +// 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-root", styles: [], @@ -115,14 +117,26 @@ const SyncInterval = 6 * 60 * 60 * 1000; // 6 hours standalone: false, }) export class AppComponent implements OnInit, OnDestroy { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("settings", { read: ViewContainerRef, static: true }) settingsRef: ViewContainerRef; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("premium", { read: ViewContainerRef, static: true }) premiumRef: ViewContainerRef; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("passwordHistory", { read: ViewContainerRef, static: true }) passwordHistoryRef: ViewContainerRef; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("exportVault", { read: ViewContainerRef, static: true }) exportVaultModalRef: ViewContainerRef; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("appGenerator", { read: ViewContainerRef, static: true }) generatorModalRef: ViewContainerRef; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("loginApproval", { read: ViewContainerRef, static: true }) loginApprovalModalRef: ViewContainerRef; diff --git a/apps/desktop/src/app/components/avatar.component.ts b/apps/desktop/src/app/components/avatar.component.ts index 1fba864686c..d17ebb5b942 100644 --- a/apps/desktop/src/app/components/avatar.component.ts +++ b/apps/desktop/src/app/components/avatar.component.ts @@ -5,20 +5,38 @@ import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +// 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-avatar", template: ``, standalone: false, }) export class AvatarComponent implements OnChanges, OnInit { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() size = 45; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() charCount = 2; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() fontSize = 20; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() dynamic = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() circle = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() color?: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() id?: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() text?: string; private svgCharCount = 2; diff --git a/apps/desktop/src/app/components/browser-sync-verification-dialog.component.ts b/apps/desktop/src/app/components/browser-sync-verification-dialog.component.ts index 713dc07e803..5d3c777f333 100644 --- a/apps/desktop/src/app/components/browser-sync-verification-dialog.component.ts +++ b/apps/desktop/src/app/components/browser-sync-verification-dialog.component.ts @@ -7,6 +7,8 @@ export type BrowserSyncVerificationDialogParams = { fingerprint: string[]; }; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "browser-sync-verification-dialog.component.html", imports: [JslibModule, ButtonModule, DialogModule], diff --git a/apps/desktop/src/app/components/user-verification.component.ts b/apps/desktop/src/app/components/user-verification.component.ts index 31d38b10183..e19916c3d6b 100644 --- a/apps/desktop/src/app/components/user-verification.component.ts +++ b/apps/desktop/src/app/components/user-verification.component.ts @@ -11,6 +11,8 @@ import { FormFieldModule } from "@bitwarden/components"; * @deprecated Jan 24, 2024: Use new libs/auth UserVerificationDialogComponent or UserVerificationFormInputComponent instead. * Each client specific component should eventually be converted over to use one of these new components. */ +// 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-user-verification", imports: [CommonModule, JslibModule, ReactiveFormsModule, FormFieldModule, FormsModule], diff --git a/apps/desktop/src/app/components/verify-native-messaging-dialog.component.ts b/apps/desktop/src/app/components/verify-native-messaging-dialog.component.ts index 72284d007b6..14c2b137d73 100644 --- a/apps/desktop/src/app/components/verify-native-messaging-dialog.component.ts +++ b/apps/desktop/src/app/components/verify-native-messaging-dialog.component.ts @@ -7,6 +7,8 @@ export type VerifyNativeMessagingDialogData = { applicationName: string; }; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "verify-native-messaging-dialog.component.html", imports: [JslibModule, ButtonModule, DialogModule], diff --git a/apps/desktop/src/app/layout/account-switcher.component.ts b/apps/desktop/src/app/layout/account-switcher.component.ts index a54674c3a1e..6a7e274ade4 100644 --- a/apps/desktop/src/app/layout/account-switcher.component.ts +++ b/apps/desktop/src/app/layout/account-switcher.component.ts @@ -31,6 +31,8 @@ type InactiveAccount = ActiveAccount & { authenticationStatus: AuthenticationStatus; }; +// 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-account-switcher", templateUrl: "account-switcher.component.html", diff --git a/apps/desktop/src/app/layout/header.component.ts b/apps/desktop/src/app/layout/header.component.ts index 9aef093423f..9630e3b1914 100644 --- a/apps/desktop/src/app/layout/header.component.ts +++ b/apps/desktop/src/app/layout/header.component.ts @@ -1,5 +1,7 @@ import { Component } from "@angular/core"; +// 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-header", templateUrl: "header.component.html", diff --git a/apps/desktop/src/app/layout/nav.component.ts b/apps/desktop/src/app/layout/nav.component.ts index bcc2b57fb17..72064a4de51 100644 --- a/apps/desktop/src/app/layout/nav.component.ts +++ b/apps/desktop/src/app/layout/nav.component.ts @@ -4,6 +4,8 @@ import { RouterLink, RouterLinkActive } from "@angular/router"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "app-nav", templateUrl: "nav.component.html", diff --git a/apps/desktop/src/app/layout/search/search.component.ts b/apps/desktop/src/app/layout/search/search.component.ts index 70196d74dda..c0b088a13d9 100644 --- a/apps/desktop/src/app/layout/search/search.component.ts +++ b/apps/desktop/src/app/layout/search/search.component.ts @@ -8,6 +8,8 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { SearchBarService, SearchBarState } from "./search-bar.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "app-search", templateUrl: "search.component.html", diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 60911173308..13c4207992c 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -33,6 +33,8 @@ import { KeyService, BiometricStateService } from "@bitwarden/key-management"; const BroadcasterSubscriptionId = "AppComponent"; const IdleTimeout = 60000 * 10; // 10 minutes +// 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-root", templateUrl: "app.component.html", diff --git a/apps/web/src/app/components/dynamic-avatar.component.ts b/apps/web/src/app/components/dynamic-avatar.component.ts index 8cd73862151..ddaaa21758b 100644 --- a/apps/web/src/app/components/dynamic-avatar.component.ts +++ b/apps/web/src/app/components/dynamic-avatar.component.ts @@ -8,6 +8,8 @@ import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.servic import { SharedModule } from "../shared"; type SizeTypes = "xlarge" | "large" | "default" | "small" | "xsmall"; +// 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: "dynamic-avatar", imports: [SharedModule], @@ -25,10 +27,20 @@ type SizeTypes = "xlarge" | "large" | "default" | "small" | "xsmall"; `, }) export class DynamicAvatarComponent implements OnDestroy { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() border = false; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() id: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() text: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() title: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() size: SizeTypes = "default"; private destroy$ = new Subject(); diff --git a/apps/web/src/app/components/environment-selector/environment-selector.component.ts b/apps/web/src/app/components/environment-selector/environment-selector.component.ts index 37e5ae0c3d8..4f77cc96bf7 100644 --- a/apps/web/src/app/components/environment-selector/environment-selector.component.ts +++ b/apps/web/src/app/components/environment-selector/environment-selector.component.ts @@ -12,6 +12,8 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; import { SharedModule } 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 @Component({ selector: "environment-selector", templateUrl: "environment-selector.component.html", diff --git a/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts b/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts index 0e32321a0b3..afac3b059a8 100644 --- a/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts +++ b/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts @@ -19,6 +19,8 @@ import { RequestSMAccessRequest } from "../models/requests/request-sm-access.req import { SmLandingApiService } from "./sm-landing-api.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "app-request-sm-access", templateUrl: "request-sm-access.component.html", diff --git a/apps/web/src/app/secrets-manager/secrets-manager-landing/sm-landing.component.ts b/apps/web/src/app/secrets-manager/secrets-manager-landing/sm-landing.component.ts index 301e6f7dfad..c1cc2b63e28 100644 --- a/apps/web/src/app/secrets-manager/secrets-manager-landing/sm-landing.component.ts +++ b/apps/web/src/app/secrets-manager/secrets-manager-landing/sm-landing.component.ts @@ -12,6 +12,8 @@ import { NoItemsModule, SearchModule } from "@bitwarden/components"; import { HeaderModule } from "../../layouts/header/header.module"; import { SharedModule } from "../../shared/shared.module"; +// 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-sm-landing", imports: [SharedModule, SearchModule, NoItemsModule, HeaderModule], diff --git a/apps/web/src/app/settings/domain-rules.component.ts b/apps/web/src/app/settings/domain-rules.component.ts index 6c4cb13d5fa..0e9d2f422d9 100644 --- a/apps/web/src/app/settings/domain-rules.component.ts +++ b/apps/web/src/app/settings/domain-rules.component.ts @@ -12,6 +12,8 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { HeaderModule } from "../layouts/header/header.module"; import { SharedModule } 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 @Component({ selector: "app-domain-rules", templateUrl: "domain-rules.component.html", diff --git a/apps/web/src/app/settings/preferences.component.ts b/apps/web/src/app/settings/preferences.component.ts index 58a072ce76a..c1e8fce98ca 100644 --- a/apps/web/src/app/settings/preferences.component.ts +++ b/apps/web/src/app/settings/preferences.component.ts @@ -39,6 +39,8 @@ import { PermitCipherDetailsPopoverComponent } from "@bitwarden/vault"; import { HeaderModule } from "../layouts/header/header.module"; import { SharedModule } 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 @Component({ selector: "app-preferences", templateUrl: "preferences.component.html", diff --git a/bitwarden_license/bit-browser/src/popup/app.component.ts b/bitwarden_license/bit-browser/src/popup/app.component.ts index 339681d66da..f880b946cfa 100644 --- a/bitwarden_license/bit-browser/src/popup/app.component.ts +++ b/bitwarden_license/bit-browser/src/popup/app.component.ts @@ -2,6 +2,8 @@ import { Component, OnInit } from "@angular/core"; import { AppComponent as BaseAppComponent } from "@bitwarden/browser/popup/app.component"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "app-root", templateUrl: "../../../../apps/browser/src/popup/app.component.html", diff --git a/bitwarden_license/bit-web/src/app/app.component.ts b/bitwarden_license/bit-web/src/app/app.component.ts index abfb79b8f18..caa1e4b0cf6 100644 --- a/bitwarden_license/bit-web/src/app/app.component.ts +++ b/bitwarden_license/bit-web/src/app/app.component.ts @@ -2,6 +2,8 @@ import { Component } from "@angular/core"; import { AppComponent as BaseAppComponent } from "@bitwarden/web-vault/app/app.component"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "app-root", templateUrl: "../../../../apps/web/src/app/app.component.html", diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-card.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-card.component.ts index 84c763841b5..427e7262f50 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-card.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-card.component.ts @@ -39,11 +39,15 @@ export class ActivityCardComponent { /** * The text to display for the action link */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() actionText: string = ""; /** * Show action link */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showActionLink: boolean = false; /** @@ -78,6 +82,8 @@ export class ActivityCardComponent { /** * Event emitted when action link is clicked */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() actionClick = new EventEmitter(); constructor(private router: Router) {} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/guards/project-access.guard.spec.ts b/bitwarden_license/bit-web/src/app/secrets-manager/guards/project-access.guard.spec.ts index f442c85f46d..79c022e8fd2 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/guards/project-access.guard.spec.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/guards/project-access.guard.spec.ts @@ -20,12 +20,16 @@ import { ProjectService } from "../projects/project.service"; import { projectAccessGuard } from "./project-access.guard"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ template: "", standalone: false, }) export class GuardedRouteTestComponent {} +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ template: "", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts index 978cfeb1aa4..0e8c46c8864 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.spec.ts @@ -21,6 +21,8 @@ import { IntegrationGridComponent } from "../../dirt/organization-integrations/i import { IntegrationsComponent } from "./integrations.component"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "app-header", template: "
", @@ -28,6 +30,8 @@ import { IntegrationsComponent } from "./integrations.component"; }) class MockHeaderComponent {} +// 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: "sm-new-menu", template: "
", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts index b2279775191..37c7a93d27f 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/integrations/integrations.component.ts @@ -3,6 +3,8 @@ import { Component } from "@angular/core"; import { Integration } from "@bitwarden/bit-common/dirt/organization-integrations/models/integration"; import { IntegrationType } from "@bitwarden/common/enums"; +// 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: "sm-integrations", templateUrl: "./integrations.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/layout/layout.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/layout/layout.component.ts index b50e586c337..00a4c6cc4d4 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/layout/layout.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/layout/layout.component.ts @@ -1,5 +1,7 @@ import { Component, OnInit } from "@angular/core"; +// 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: "sm-layout", templateUrl: "./layout.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/layout/navigation.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/layout/navigation.component.ts index a714bc0d543..be9124ee3e1 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/layout/navigation.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/layout/navigation.component.ts @@ -31,6 +31,8 @@ import { ServiceAccountService } from "../service-accounts/service-account.servi import { SecretsManagerPortingApiService } from "../settings/services/sm-porting-api.service"; import { CountService } from "../shared/counts/count.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-navigation", templateUrl: "./navigation.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts index e301c0462c3..12a5432c4b8 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts @@ -75,6 +75,8 @@ type OrganizationTasks = { createServiceAccount: boolean; }; +// 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: "sm-overview", templateUrl: "./overview.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/overview/section.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/overview/section.component.ts index 6b71c81f09e..0691ed9dd73 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/overview/section.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/overview/section.component.ts @@ -1,11 +1,15 @@ import { Component, Input } from "@angular/core"; +// 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: "sm-section", templateUrl: "./section.component.html", standalone: false, }) export class SectionComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() open = true; /** diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-delete-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-delete-dialog.component.ts index 8cdb1bb4d69..3ddf3233b38 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-delete-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-delete-dialog.component.ts @@ -25,6 +25,8 @@ export interface ProjectDeleteOperation { projects: ProjectListView[]; } +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "./project-delete-dialog.component.html", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts index 819f2107fcf..2f6b2229d75 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/dialog/project-dialog.component.ts @@ -25,6 +25,8 @@ export interface ProjectOperation { projectId?: string; } +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "./project-dialog.component.html", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-people.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-people.component.ts index ec7397a22a8..49b016e921c 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-people.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-people.component.ts @@ -24,6 +24,8 @@ import { import { ApItemEnum } from "../../shared/access-policies/access-policy-selector/models/enums/ap-item.enum"; import { AccessPolicyService } from "../../shared/access-policies/access-policy.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-project-people", templateUrl: "./project-people.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-secrets.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-secrets.component.ts index 5c83f784431..7112a28010f 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-secrets.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-secrets.component.ts @@ -41,6 +41,8 @@ import { import { SecretService } from "../../secrets/secret.service"; import { SecretsListComponent } from "../../shared/secrets-list.component"; import { ProjectService } from "../project.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-project-secrets", templateUrl: "./project-secrets.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-service-accounts.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-service-accounts.component.ts index fc3a489bce9..e2fd8556621 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-service-accounts.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-service-accounts.component.ts @@ -22,6 +22,8 @@ import { } from "../../shared/access-policies/access-policy-selector/models/ap-item-view.type"; import { AccessPolicyService } from "../../shared/access-policies/access-policy.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-project-service-accounts", templateUrl: "./project-service-accounts.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project.component.ts index c79ebd733c0..7c1812e3f26 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project.component.ts @@ -34,6 +34,8 @@ import { } from "../dialog/project-dialog.component"; import { ProjectService } from "../project.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-project", templateUrl: "./project.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/projects/projects.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/projects/projects.component.ts index 81a568f0c65..10e75cfb75a 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/projects/projects.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/projects/projects.component.ts @@ -40,6 +40,8 @@ import { } from "../dialog/project-dialog.component"; import { ProjectService } from "../project.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-projects", templateUrl: "./projects.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-delete.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-delete.component.ts index 6340cc42f3b..344a20f02c2 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-delete.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-delete.component.ts @@ -18,6 +18,8 @@ export interface SecretDeleteOperation { secrets: SecretListView[]; } +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "./secret-delete.component.html", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts index 9172d44965d..6376b58423d 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts @@ -67,6 +67,8 @@ export interface SecretOperation { organizationEnabled: boolean; } +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "./secret-dialog.component.html", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-view-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-view-dialog.component.ts index b719014a382..ace8db4e6ba 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-view-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-view-dialog.component.ts @@ -10,6 +10,8 @@ export interface SecretViewDialogParams { secretId: string; } +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "./secret-view-dialog.component.html", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts index ca093f449c9..46cccb1d95d 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts @@ -34,6 +34,8 @@ import { } from "./dialog/secret-view-dialog.component"; import { SecretService } from "./secret.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-secrets", templateUrl: "./secrets.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-list.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-list.component.ts index a714729d96f..7a8c0b37408 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-list.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-list.component.ts @@ -5,12 +5,16 @@ import { Component, EventEmitter, Input, Output } from "@angular/core"; import { AccessTokenView } from "../models/view/access-token.view"; +// 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: "sm-access-list", templateUrl: "./access-list.component.html", standalone: false, }) export class AccessListComponent { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() get tokens(): AccessTokenView[] { return this._tokens; @@ -21,7 +25,11 @@ export class AccessListComponent { } private _tokens: AccessTokenView[]; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() newAccessTokenEvent = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() revokeAccessTokensEvent = new EventEmitter(); protected selection = new SelectionModel(true, []); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-tokens.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-tokens.component.ts index b9643ce8fd8..4e9069cd6cb 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-tokens.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/access-tokens.component.ts @@ -24,6 +24,8 @@ import { ServiceAccountService } from "../service-account.service"; import { AccessService } from "./access.service"; import { AccessTokenCreateDialogComponent } from "./dialogs/access-token-create-dialog.component"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-access-tokens", templateUrl: "./access-tokens.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/dialogs/access-token-create-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/dialogs/access-token-create-dialog.component.ts index dfbe0a1511d..3aca93572ef 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/dialogs/access-token-create-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/dialogs/access-token-create-dialog.component.ts @@ -15,6 +15,8 @@ export interface AccessTokenOperation { serviceAccountView: ServiceAccountView; } +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "./access-token-create-dialog.component.html", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/dialogs/access-token-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/dialogs/access-token-dialog.component.ts index 0259b8d6e90..cf5118c5062 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/dialogs/access-token-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/dialogs/access-token-dialog.component.ts @@ -12,6 +12,8 @@ export interface AccessTokenDetails { accessToken: string; } +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "./access-token-dialog.component.html", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/dialogs/expiration-options.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/dialogs/expiration-options.component.ts index 891501874ff..a0db42d03b0 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/dialogs/expiration-options.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/access/dialogs/expiration-options.component.ts @@ -18,6 +18,8 @@ import { Subject, takeUntil } from "rxjs"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-expiration-options", templateUrl: "./expiration-options.component.html", @@ -40,8 +42,12 @@ export class ExpirationOptionsComponent { private destroy$ = new Subject(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() expirationDayOptions: number[]; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() set touched(val: boolean) { if (val) { this.form.markAllAsTouched(); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/config/config.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/config/config.component.ts index f85cde90306..18ef397c6ae 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/config/config.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/config/config.component.ts @@ -24,6 +24,8 @@ class ServiceAccountConfig { projects: ProjectListView[]; } +// 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: "sm-service-account-config", templateUrl: "./config.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-delete-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-delete-dialog.component.ts index 5edc57d8c74..638ee6862a3 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-delete-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-delete-dialog.component.ts @@ -25,6 +25,8 @@ export interface ServiceAccountDeleteOperation { serviceAccounts: ServiceAccountView[]; } +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "./service-account-delete-dialog.component.html", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts index 250e0870ecf..5c6072807a6 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/dialog/service-account-dialog.component.ts @@ -24,6 +24,8 @@ export interface ServiceAccountOperation { organizationEnabled: boolean; } +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "./service-account-dialog.component.html", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/event-logs/service-accounts-events.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/event-logs/service-accounts-events.component.ts index 2e364df1423..5968933064d 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/event-logs/service-accounts-events.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/event-logs/service-accounts-events.component.ts @@ -17,6 +17,8 @@ import { EventExportService } from "@bitwarden/web-vault/app/tools/event-export" import { ServiceAccountEventLogApiService } from "./service-account-event-log-api.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-service-accounts-events", templateUrl: "./service-accounts-events.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/guards/service-account-access.guard.spec.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/guards/service-account-access.guard.spec.ts index e0bcad8d6e9..e7b258ed1c2 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/guards/service-account-access.guard.spec.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/guards/service-account-access.guard.spec.ts @@ -20,12 +20,16 @@ import { ServiceAccountService } from "../service-account.service"; import { serviceAccountAccessGuard } from "./service-account-access.guard"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ template: "", standalone: false, }) export class GuardedRouteTestComponent {} +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ template: "", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/people/service-account-people.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/people/service-account-people.component.ts index 4449757167d..42ab2ec613b 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/people/service-account-people.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/people/service-account-people.component.ts @@ -25,6 +25,8 @@ import { ApItemEnum } from "../../shared/access-policies/access-policy-selector/ import { ApPermissionEnum } from "../../shared/access-policies/access-policy-selector/models/enums/ap-permission.enum"; import { AccessPolicyService } from "../../shared/access-policies/access-policy.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-service-account-people", templateUrl: "./service-account-people.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/projects/service-account-projects.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/projects/service-account-projects.component.ts index af334b22c63..6d4490bad3c 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/projects/service-account-projects.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/projects/service-account-projects.component.ts @@ -22,6 +22,8 @@ import { } from "../../shared/access-policies/access-policy-selector/models/ap-item-view.type"; import { AccessPolicyService } from "../../shared/access-policies/access-policy.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-service-account-projects", templateUrl: "./service-account-projects.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.component.ts index 5eb074e3e99..285f03acb01 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-account.component.ts @@ -15,6 +15,8 @@ import { AccessService } from "./access/access.service"; import { AccessTokenCreateDialogComponent } from "./access/dialogs/access-token-create-dialog.component"; import { ServiceAccountService } from "./service-account.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-service-account", templateUrl: "./service-account.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.ts index 21f11d6bfed..4febda9ea28 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts-list.component.ts @@ -21,6 +21,8 @@ import { ServiceAccountView, } from "../models/view/service-account.view"; +// 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: "sm-service-accounts-list", templateUrl: "./service-accounts-list.component.html", @@ -29,6 +31,8 @@ import { export class ServiceAccountsListComponent implements OnDestroy, OnInit { protected dataSource = new TableDataSource(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() get serviceAccounts(): ServiceAccountSecretsDetailsView[] { return this._serviceAccounts; @@ -40,15 +44,25 @@ export class ServiceAccountsListComponent implements OnDestroy, OnInit { } private _serviceAccounts: ServiceAccountSecretsDetailsView[]; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() set search(search: string) { this.selection.clear(); this.dataSource.filter = search; } + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() newServiceAccountEvent = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() deleteServiceAccountsEvent = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onServiceAccountCheckedEvent = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() editServiceAccountEvent = new EventEmitter(); private destroy$: Subject = new Subject(); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.component.ts index 345fff03876..5d6b4fd49de 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/service-accounts/service-accounts.component.ts @@ -30,6 +30,8 @@ import { } from "./dialog/service-account-dialog.component"; import { ServiceAccountService } from "./service-account.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-service-accounts", templateUrl: "./service-accounts.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/settings/dialog/sm-import-error-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/settings/dialog/sm-import-error-dialog.component.ts index 0bed0355a8c..85e054d998b 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/settings/dialog/sm-import-error-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/settings/dialog/sm-import-error-dialog.component.ts @@ -10,6 +10,8 @@ export interface SecretsManagerImportErrorDialogOperation { error: SecretsManagerImportError; } +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "./sm-import-error-dialog.component.html", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-export.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-export.component.ts index c2b726803c5..e2b66d9ffa6 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-export.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-export.component.ts @@ -26,6 +26,8 @@ type ExportFormat = { fileExtension: string; }; +// 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: "sm-export", templateUrl: "./sm-export.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-import.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-import.component.ts index 65075d12bf6..c2ffe5536b8 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-import.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/settings/porting/sm-import.component.ts @@ -18,6 +18,8 @@ import { import { SecretsManagerImportError } from "../models/error/sm-import-error"; import { SecretsManagerPortingApiService } from "../services/sm-porting-api.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-import", templateUrl: "./sm-import.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.ts index fba3ff03ee0..2bb4d6cb37f 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/access-policies/access-policy-selector/access-policy-selector.component.ts @@ -20,6 +20,8 @@ import { ApItemViewType } from "./models/ap-item-view.type"; import { ApItemEnumUtil, ApItemEnum } from "./models/enums/ap-item.enum"; import { ApPermissionEnum } from "./models/enums/ap-permission.enum"; +// 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: "sm-access-policy-selector", templateUrl: "access-policy-selector.component.html", @@ -108,23 +110,43 @@ export class AccessPolicySelectorComponent implements ControlValueAccessor, OnIn disabled: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() loading: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() addButtonMode: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() label: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() hint: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() columnTitle: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() emptyMessage: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() permissionList = [ { perm: ApPermissionEnum.CanRead, labelId: "canRead" }, { perm: ApPermissionEnum.CanReadWrite, labelId: "canReadWrite" }, ]; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() initialPermission = ApPermissionEnum.CanRead; // Pass in a static permission that wil be the only option for a given selector instance. // Will ignore permissionList and initialPermission. + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() staticPermission: ApPermissionEnum; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() get items(): ApItemViewType[] { return this.selectionList.allItems; diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/dialogs/bulk-confirmation-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/dialogs/bulk-confirmation-dialog.component.ts index 9d2a3715e16..0f0991d52a9 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/dialogs/bulk-confirmation-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/dialogs/bulk-confirmation-dialog.component.ts @@ -22,6 +22,8 @@ export enum BulkConfirmationResult { Cancel, } +// 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: "sm-bulk-confirmation-dialog", templateUrl: "./bulk-confirmation-dialog.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/dialogs/bulk-status-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/dialogs/bulk-status-dialog.component.ts index fc7890f1654..8e27b551e55 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/dialogs/bulk-status-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/dialogs/bulk-status-dialog.component.ts @@ -18,6 +18,8 @@ export class BulkOperationStatus { errorMessage?: string; } +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "./bulk-status-dialog.component.html", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/new-menu.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/new-menu.component.ts index 18823130d22..6c3d4228c06 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/new-menu.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/new-menu.component.ts @@ -26,6 +26,8 @@ import { ServiceAccountOperation, } from "../service-accounts/dialog/service-account-dialog.component"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-new-menu", templateUrl: "./new-menu.component.html", diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/org-suspended.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/org-suspended.component.ts index 6777df7ef7a..f2e0d48fe1d 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/org-suspended.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/org-suspended.component.ts @@ -10,6 +10,8 @@ import { import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "./org-suspended.component.html", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/projects-list.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/projects-list.component.ts index 31114bcd1c4..5d3c806f386 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/projects-list.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/projects-list.component.ts @@ -20,12 +20,16 @@ import { openEntityEventsDialog } from "@bitwarden/web-vault/app/admin-console/o import { ProjectListView } from "../models/view/project-list.view"; import { ProjectView } from "../models/view/project.view"; +// 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: "sm-projects-list", templateUrl: "./projects-list.component.html", standalone: false, }) export class ProjectsListComponent implements OnInit { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() get projects(): ProjectListView[] { return this._projects; @@ -40,17 +44,29 @@ export class ProjectsListComponent implements OnInit { protected isAdmin$: Observable; private destroy$: Subject = new Subject(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showMenus?: boolean = true; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() set search(search: string) { this.selection.clear(); this.dataSource.filter = search; } + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() editProjectEvent = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() deleteProjectEvent = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() newProjectEvent = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() copiedProjectUUIdEvent = new EventEmitter(); selection = new SelectionModel(true, []); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.ts index 4ef7dbf22e7..05e38baff69 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.ts @@ -21,6 +21,8 @@ import { SecretListView } from "../models/view/secret-list.view"; import { SecretView } from "../models/view/secret.view"; import { SecretService } from "../secrets/secret.service"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-secrets-list", templateUrl: "./secrets-list.component.html", @@ -29,6 +31,8 @@ import { SecretService } from "../secrets/secret.service"; export class SecretsListComponent implements OnDestroy, OnInit { protected dataSource = new TableDataSource(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() get secrets(): SecretListView[] { return this._secrets; @@ -40,22 +44,44 @@ export class SecretsListComponent implements OnDestroy, OnInit { } private _secrets: SecretListView[]; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() set search(search: string) { this.selection.clear(); this.dataSource.filter = search; } + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() trash: boolean; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() editSecretEvent = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() viewSecretEvent = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() copySecretNameEvent = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() copySecretValueEvent = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() copySecretUuidEvent = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onSecretCheckedEvent = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() deleteSecretsEvent = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() newSecretEvent = new EventEmitter(); + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() restoreSecretsEvent = new EventEmitter(); private destroy$: Subject = new Subject(); diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/trash/dialog/secret-hard-delete.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/trash/dialog/secret-hard-delete.component.ts index 29f9a85250c..521550185f1 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/trash/dialog/secret-hard-delete.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/trash/dialog/secret-hard-delete.component.ts @@ -13,6 +13,8 @@ export interface SecretHardDeleteOperation { organizationId: string; } +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "./secret-hard-delete.component.html", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/trash/dialog/secret-restore.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/trash/dialog/secret-restore.component.ts index 712757445be..034b6f8de00 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/trash/dialog/secret-restore.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/trash/dialog/secret-restore.component.ts @@ -13,6 +13,8 @@ export interface SecretRestoreOperation { organizationId: string; } +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "./secret-restore.component.html", standalone: false, diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/trash/trash.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/trash/trash.component.ts index 4392ae8b1bb..b4da7769127 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/trash/trash.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/trash/trash.component.ts @@ -21,6 +21,8 @@ import { SecretRestoreOperation, } from "./dialog/secret-restore.component"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "sm-trash", templateUrl: "./trash.component.html", diff --git a/eslint.config.mjs b/eslint.config.mjs index d8b2094c37c..656972d2421 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -72,9 +72,9 @@ export default tseslint.config( "@angular-eslint/no-output-on-prefix": 0, "@angular-eslint/no-output-rename": 0, "@angular-eslint/no-outputs-metadata-property": 0, - "@angular-eslint/prefer-on-push-component-change-detection": "warn", - "@angular-eslint/prefer-output-emitter-ref": "warn", - "@angular-eslint/prefer-signals": "warn", + "@angular-eslint/prefer-on-push-component-change-detection": "error", + "@angular-eslint/prefer-output-emitter-ref": "error", + "@angular-eslint/prefer-signals": "error", "@angular-eslint/prefer-standalone": 0, "@angular-eslint/use-lifecycle-interface": "error", "@angular-eslint/use-pipe-transform-interface": 0, diff --git a/libs/angular/src/components/callout.component.ts b/libs/angular/src/components/callout.component.ts index 215de49f676..9630b761076 100644 --- a/libs/angular/src/components/callout.component.ts +++ b/libs/angular/src/components/callout.component.ts @@ -9,17 +9,31 @@ import { CalloutTypes } from "@bitwarden/components"; /** * @deprecated use the CL's `CalloutComponent` instead */ +// 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-callout", templateUrl: "callout.component.html", standalone: false, }) export class DeprecatedCalloutComponent implements OnInit { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() type: CalloutTypes = "info"; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() icon: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() title: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() enforcedPolicyOptions: MasterPasswordPolicyOptions; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() enforcedPolicyMessage: string; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() useAlertRole = false; calloutStyle: string; diff --git a/libs/angular/src/components/modal/dynamic-modal.component.ts b/libs/angular/src/components/modal/dynamic-modal.component.ts index 77491193916..ea40dd1a877 100644 --- a/libs/angular/src/components/modal/dynamic-modal.component.ts +++ b/libs/angular/src/components/modal/dynamic-modal.component.ts @@ -15,6 +15,8 @@ import { import { ModalRef } from "./modal.ref"; +// 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-modal", template: "", @@ -23,6 +25,8 @@ import { ModalRef } from "./modal.ref"; export class DynamicModalComponent implements AfterViewInit, OnDestroy { componentRef: ComponentRef; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @ViewChild("modalContent", { read: ViewContainerRef, static: true }) modalContentRef: ViewContainerRef; diff --git a/libs/angular/src/directives/api-action.directive.ts b/libs/angular/src/directives/api-action.directive.ts index 85ba8a7489c..6873e448589 100644 --- a/libs/angular/src/directives/api-action.directive.ts +++ b/libs/angular/src/directives/api-action.directive.ts @@ -18,6 +18,8 @@ import { ValidationService } from "@bitwarden/common/platform/abstractions/valid standalone: false, }) export class ApiActionDirective implements OnChanges { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() appApiAction: Promise; constructor( diff --git a/libs/angular/src/directives/copy-text.directive.ts b/libs/angular/src/directives/copy-text.directive.ts index 0f9018e19ad..aefb26ef07e 100644 --- a/libs/angular/src/directives/copy-text.directive.ts +++ b/libs/angular/src/directives/copy-text.directive.ts @@ -15,6 +15,8 @@ export class CopyTextDirective { private platformUtilsService: PlatformUtilsService, ) {} + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input("appCopyText") copyText: string; @HostListener("copy") onCopy() { diff --git a/libs/angular/src/directives/fallback-src.directive.ts b/libs/angular/src/directives/fallback-src.directive.ts index f1225245912..b63dc8671cf 100644 --- a/libs/angular/src/directives/fallback-src.directive.ts +++ b/libs/angular/src/directives/fallback-src.directive.ts @@ -7,6 +7,8 @@ import { Directive, ElementRef, HostListener, Input } from "@angular/core"; standalone: false, }) export class FallbackSrcDirective { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input("appFallbackSrc") appFallbackSrc: string; /** Only try setting the fallback once. This prevents an infinite loop if the fallback itself is missing. */ diff --git a/libs/angular/src/directives/if-feature.directive.spec.ts b/libs/angular/src/directives/if-feature.directive.spec.ts index d7c49994045..357209b0e64 100644 --- a/libs/angular/src/directives/if-feature.directive.spec.ts +++ b/libs/angular/src/directives/if-feature.directive.spec.ts @@ -13,6 +13,8 @@ const testBooleanFeature: FeatureFlag = "boolean-feature" as FeatureFlag; const testStringFeature: FeatureFlag = "string-feature" as FeatureFlag; const testStringFeatureValue = "test-value"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ template: `
diff --git a/libs/angular/src/directives/if-feature.directive.ts b/libs/angular/src/directives/if-feature.directive.ts index aa10c9e8081..28cf1d5c35f 100644 --- a/libs/angular/src/directives/if-feature.directive.ts +++ b/libs/angular/src/directives/if-feature.directive.ts @@ -20,12 +20,16 @@ export class IfFeatureDirective implements OnInit { /** * The feature flag to check. */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() appIfFeature: FeatureFlag; /** * Optional value to compare against the value of the feature flag in the config service. * @default true */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() appIfFeatureValue: AllowedFeatureFlagTypes = true; private hasView = false; diff --git a/libs/angular/src/directives/input-verbatim.directive.ts b/libs/angular/src/directives/input-verbatim.directive.ts index 7bd18b12659..1240523d2bf 100644 --- a/libs/angular/src/directives/input-verbatim.directive.ts +++ b/libs/angular/src/directives/input-verbatim.directive.ts @@ -7,6 +7,8 @@ import { Directive, ElementRef, Input, OnInit, Renderer2 } from "@angular/core"; standalone: false, }) export class InputVerbatimDirective implements OnInit { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() set appInputVerbatim(condition: boolean | string) { this.disableComplete = condition === "" || condition === true; } diff --git a/libs/angular/src/directives/launch-click.directive.ts b/libs/angular/src/directives/launch-click.directive.ts index b270dbba5e3..ce44648dc37 100644 --- a/libs/angular/src/directives/launch-click.directive.ts +++ b/libs/angular/src/directives/launch-click.directive.ts @@ -10,6 +10,8 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; export class LaunchClickDirective { constructor(private platformUtilsService: PlatformUtilsService) {} + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input("appLaunchClick") uriToLaunch = ""; @HostListener("click") onClick() { diff --git a/libs/angular/src/directives/text-drag.directive.ts b/libs/angular/src/directives/text-drag.directive.ts index 6202c552a87..aade2798dc7 100644 --- a/libs/angular/src/directives/text-drag.directive.ts +++ b/libs/angular/src/directives/text-drag.directive.ts @@ -8,6 +8,8 @@ import { Directive, HostListener, Input } from "@angular/core"; }, }) export class TextDragDirective { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ alias: "appTextDrag", required: true, diff --git a/libs/angular/src/directives/true-false-value.directive.ts b/libs/angular/src/directives/true-false-value.directive.ts index 5d25ac2a385..78c1b4647c6 100644 --- a/libs/angular/src/directives/true-false-value.directive.ts +++ b/libs/angular/src/directives/true-false-value.directive.ts @@ -14,7 +14,11 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; standalone: false, }) export class TrueFalseValueDirective implements ControlValueAccessor { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() trueValue: boolean | string = true; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals @Input() falseValue: boolean | string = false; constructor( diff --git a/libs/components/src/card/base-card/base-card.component.ts b/libs/components/src/card/base-card/base-card.component.ts index 44f82a32c47..8c4dd80f2d1 100644 --- a/libs/components/src/card/base-card/base-card.component.ts +++ b/libs/components/src/card/base-card/base-card.component.ts @@ -6,6 +6,8 @@ import { BaseCardDirective } from "./base-card.directive"; * The base card component is a container that applies our standard card border and box-shadow. * In most cases using our `` component should suffice. */ +// 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: "bit-base-card", template: ``, diff --git a/libs/components/src/card/card-content.component.ts b/libs/components/src/card/card-content.component.ts index 60be20e78f0..650a2665475 100644 --- a/libs/components/src/card/card-content.component.ts +++ b/libs/components/src/card/card-content.component.ts @@ -1,5 +1,7 @@ import { Component } from "@angular/core"; +// 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: "bit-card-content", template: `
`, From 75846e8fb1cd2932b20a7dec8bc2dd69c574f35e Mon Sep 17 00:00:00 2001 From: Brandon Treston Date: Wed, 29 Oct 2025 15:04:37 -0400 Subject: [PATCH 12/66] add decryption logic (#17106) --- .../organization-members.service.spec.ts | 52 +++++++++++++++++ .../organization-members.service.ts | 56 +++++++++++++++---- 2 files changed, 98 insertions(+), 10 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.spec.ts b/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.spec.ts index 615d2ece463..aef4dd00312 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.spec.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.spec.ts @@ -1,13 +1,17 @@ import { TestBed } from "@angular/core/testing"; +import { of } from "rxjs"; import { + CollectionService, OrganizationUserApiService, OrganizationUserUserDetailsResponse, } from "@bitwarden/admin-console/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { OrganizationId } from "@bitwarden/common/types/guid"; +import { KeyService } from "@bitwarden/key-management"; import { GroupApiService } from "../../../core"; @@ -18,6 +22,9 @@ describe("OrganizationMembersService", () => { let organizationUserApiService: jest.Mocked; let groupService: jest.Mocked; let apiService: jest.Mocked; + let keyService: jest.Mocked; + let accountService: jest.Mocked; + let collectionService: jest.Mocked; const mockOrganizationId = "org-123" as OrganizationId; @@ -51,6 +58,7 @@ describe("OrganizationMembersService", () => { const createMockCollection = (id: string, name: string) => ({ id, name, + organizationId: mockOrganizationId, }); beforeEach(() => { @@ -66,12 +74,27 @@ describe("OrganizationMembersService", () => { getCollections: jest.fn(), } as any; + keyService = { + orgKeys$: jest.fn(), + } as any; + + accountService = { + activeAccount$: of({ id: "user-123" } as any), + } as any; + + collectionService = { + decryptMany$: jest.fn(), + } as any; + TestBed.configureTestingModule({ providers: [ OrganizationMembersService, { provide: OrganizationUserApiService, useValue: organizationUserApiService }, { provide: GroupApiService, useValue: groupService }, { provide: ApiService, useValue: apiService }, + { provide: KeyService, useValue: keyService }, + { provide: AccountService, useValue: accountService }, + { provide: CollectionService, useValue: collectionService }, ], }); @@ -88,11 +111,15 @@ describe("OrganizationMembersService", () => { data: [mockUser], } as any; const mockCollections = [createMockCollection("col-1", "Collection 1")]; + const mockOrgKey = { [mockOrganizationId]: {} as any }; + const mockDecryptedCollections = [{ id: "col-1", name: "Collection 1" }]; organizationUserApiService.getAllUsers.mockResolvedValue(mockUsersResponse); apiService.getCollections.mockResolvedValue({ data: mockCollections, } as any); + keyService.orgKeys$.mockReturnValue(of(mockOrgKey)); + collectionService.decryptMany$.mockReturnValue(of(mockDecryptedCollections as any)); const result = await service.loadUsers(organization); @@ -171,11 +198,19 @@ describe("OrganizationMembersService", () => { createMockCollection("col-2", "Alpha Collection"), createMockCollection("col-3", "Beta Collection"), ]; + const mockOrgKey = { [mockOrganizationId]: {} as any }; + const mockDecryptedCollections = [ + { id: "col-1", name: "Zebra Collection" }, + { id: "col-2", name: "Alpha Collection" }, + { id: "col-3", name: "Beta Collection" }, + ]; organizationUserApiService.getAllUsers.mockResolvedValue(mockUsersResponse); apiService.getCollections.mockResolvedValue({ data: mockCollections, } as any); + keyService.orgKeys$.mockReturnValue(of(mockOrgKey)); + collectionService.decryptMany$.mockReturnValue(of(mockDecryptedCollections as any)); const result = await service.loadUsers(organization); @@ -223,11 +258,19 @@ describe("OrganizationMembersService", () => { // col-2 is missing - should be filtered out createMockCollection("col-3", "Collection 3"), ]; + const mockOrgKey = { [mockOrganizationId]: {} as any }; + const mockDecryptedCollections = [ + { id: "col-1", name: "Collection 1" }, + // col-2 is missing - should be filtered out + { id: "col-3", name: "Collection 3" }, + ]; organizationUserApiService.getAllUsers.mockResolvedValue(mockUsersResponse); apiService.getCollections.mockResolvedValue({ data: mockCollections, } as any); + keyService.orgKeys$.mockReturnValue(of(mockOrgKey)); + collectionService.decryptMany$.mockReturnValue(of(mockDecryptedCollections as any)); const result = await service.loadUsers(organization); @@ -269,11 +312,14 @@ describe("OrganizationMembersService", () => { const mockUsersResponse: ListResponse = { data: null as any, } as any; + const mockOrgKey = { [mockOrganizationId]: {} as any }; organizationUserApiService.getAllUsers.mockResolvedValue(mockUsersResponse); apiService.getCollections.mockResolvedValue({ data: [], } as any); + keyService.orgKeys$.mockReturnValue(of(mockOrgKey)); + collectionService.decryptMany$.mockReturnValue(of([])); const result = await service.loadUsers(organization); @@ -285,11 +331,14 @@ describe("OrganizationMembersService", () => { const mockUsersResponse: ListResponse = { data: undefined as any, } as any; + const mockOrgKey = { [mockOrganizationId]: {} as any }; organizationUserApiService.getAllUsers.mockResolvedValue(mockUsersResponse); apiService.getCollections.mockResolvedValue({ data: [], } as any); + keyService.orgKeys$.mockReturnValue(of(mockOrgKey)); + collectionService.decryptMany$.mockReturnValue(of([])); const result = await service.loadUsers(organization); @@ -322,11 +371,14 @@ describe("OrganizationMembersService", () => { const mockUsersResponse: ListResponse = { data: [mockUser], } as any; + const mockOrgKey = { [mockOrganizationId]: {} as any }; organizationUserApiService.getAllUsers.mockResolvedValue(mockUsersResponse); apiService.getCollections.mockResolvedValue({ data: [], } as any); + keyService.orgKeys$.mockReturnValue(of(mockOrgKey)); + collectionService.decryptMany$.mockReturnValue(of([])); const result = await service.loadUsers(organization); diff --git a/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.ts b/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.ts index 613c7c1b9c0..0dc417cc2c6 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/organization-members-service/organization-members.service.ts @@ -1,8 +1,18 @@ import { Injectable } from "@angular/core"; +import { combineLatest, firstValueFrom, from, map, switchMap } from "rxjs"; -import { OrganizationUserApiService } from "@bitwarden/admin-console/common"; +import { + Collection, + CollectionData, + CollectionDetailsResponse, + CollectionService, + OrganizationUserApiService, +} from "@bitwarden/admin-console/common"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { KeyService } from "@bitwarden/key-management"; import { GroupApiService } from "../../../core"; import { OrganizationUserView } from "../../../core/views/organization-user.view"; @@ -13,6 +23,9 @@ export class OrganizationMembersService { private organizationUserApiService: OrganizationUserApiService, private groupService: GroupApiService, private apiService: ApiService, + private keyService: KeyService, + private accountService: AccountService, + private collectionService: CollectionService, ) {} async loadUsers(organization: Organization): Promise { @@ -62,15 +75,38 @@ export class OrganizationMembersService { } private async getCollectionNameMap(organization: Organization): Promise> { - const response = this.apiService - .getCollections(organization.id) - .then((res) => - res.data.map((r: { id: string; name: string }) => ({ id: r.id, name: r.name })), - ); + const collections$ = from(this.apiService.getCollections(organization.id)).pipe( + map((response) => { + return response.data.map((r) => + Collection.fromCollectionData(new CollectionData(r as CollectionDetailsResponse)), + ); + }), + ); - const collections = await response; - const collectionMap = new Map(); - collections.forEach((c: { id: string; name: string }) => collectionMap.set(c.id, c.name)); - return collectionMap; + const orgKey$ = this.accountService.activeAccount$.pipe( + getUserId, + switchMap((userId) => this.keyService.orgKeys$(userId)), + map((orgKeys) => { + if (orgKeys == null) { + throw new Error("Organization keys not found for provided User."); + } + return orgKeys; + }), + ); + + return await firstValueFrom( + combineLatest([orgKey$, collections$]).pipe( + switchMap(([orgKey, collections]) => + this.collectionService.decryptMany$(collections, orgKey), + ), + map((decryptedCollections) => { + const collectionMap: Map = new Map(); + decryptedCollections.forEach((c) => { + collectionMap.set(c.id, c.name); + }); + return collectionMap; + }), + ), + ); } } From 66052b6dd366c6b0de088f5afa2fa66c0263cfd0 Mon Sep 17 00:00:00 2001 From: Vijay Oommen Date: Wed, 29 Oct 2025 14:06:18 -0500 Subject: [PATCH 13/66] PM-26676 change the org should update the access intelligence report (#17053) --- .../services/domain/risk-insights-orchestrator.service.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts index f52ab68985b..2435fe12038 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts @@ -165,6 +165,7 @@ export class RiskInsightsOrchestratorService { initializeForOrganization(organizationId: OrganizationId) { this.logService.debug("[RiskInsightsOrchestratorService] Initializing for org", organizationId); this._initializeOrganizationTriggerSubject.next(organizationId); + this.fetchReport(); } removeCriticalApplication$(criticalApplication: string): Observable { @@ -587,7 +588,7 @@ export class RiskInsightsOrchestratorService { private _setupEnrichedReportData() { // Setup the enriched report data pipeline const enrichmentSubscription = combineLatest([ - this.rawReportData$.pipe(filter((data) => !!data && !!data?.data)), + this.rawReportData$, this._ciphers$.pipe(filter((data) => !!data)), ]).pipe( switchMap(([rawReportData, ciphers]) => { @@ -627,7 +628,7 @@ export class RiskInsightsOrchestratorService { .pipe( withLatestFrom(this._userId$), filter(([orgId, userId]) => !!orgId && !!userId), - exhaustMap(([orgId, userId]) => + switchMap(([orgId, userId]) => this.organizationService.organizations$(userId!).pipe( getOrganizationById(orgId), map((org) => ({ organizationId: orgId!, organizationName: org?.name ?? "" })), @@ -725,7 +726,7 @@ export class RiskInsightsOrchestratorService { scan((prevState: ReportState, currState: ReportState) => ({ ...prevState, ...currState, - data: currState.data !== null ? currState.data : prevState.data, + data: currState.data, })), startWith({ loading: false, error: null, data: null }), shareReplay({ bufferSize: 1, refCount: true }), From e333c0a8bcc6735bb8705d4ee5e334de11446e6a Mon Sep 17 00:00:00 2001 From: John Harrington <84741727+harr1424@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:49:31 -0700 Subject: [PATCH 14/66] Preserve export type across export source selections (#16922) --- .../vault-export.service.abstraction.ts | 27 +++++++++++ .../src/services/vault-export.service.ts | 29 +++++++++++- .../src/components/export.component.html | 2 +- .../src/components/export.component.ts | 45 ++++++++++++------- 4 files changed, 85 insertions(+), 18 deletions(-) diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.abstraction.ts b/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.abstraction.ts index e25fec6eb82..0d58f168671 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.abstraction.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.abstraction.ts @@ -1,3 +1,5 @@ +import { Observable } from "rxjs"; + import { UserId, OrganizationId } from "@bitwarden/common/types/guid"; import { ExportedVault } from "../types"; @@ -5,6 +7,24 @@ import { ExportedVault } from "../types"; export const EXPORT_FORMATS = ["csv", "json", "encrypted_json", "zip"] as const; export type ExportFormat = (typeof EXPORT_FORMATS)[number]; +/** + * Options that determine which export formats are available + */ +export type FormatOptions = { + /** Whether the export is for the user's personal vault */ + isMyVault: boolean; +}; + +/** + * Metadata describing an available export format + */ +export type ExportFormatMetadata = { + /** Display name for the format (e.g., ".json", ".csv") */ + name: string; + /** The export format identifier */ + format: ExportFormat; +}; + export abstract class VaultExportServiceAbstraction { abstract getExport: ( userId: UserId, @@ -18,4 +38,11 @@ export abstract class VaultExportServiceAbstraction { password: string, onlyManagedCollections?: boolean, ) => Promise; + + /** + * Get available export formats based on vault context + * @param options Options determining which formats are available + * @returns Observable stream of available export formats + */ + abstract formats$(options: FormatOptions): Observable; } diff --git a/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.ts b/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.ts index b601478d06d..38d71136006 100644 --- a/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.ts +++ b/libs/tools/export/vault-export/vault-export-core/src/services/vault-export.service.ts @@ -1,4 +1,4 @@ -import { firstValueFrom } from "rxjs"; +import { firstValueFrom, Observable, of } from "rxjs"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; @@ -9,7 +9,12 @@ import { ExportedVault } from "../types"; import { IndividualVaultExportServiceAbstraction } from "./individual-vault-export.service.abstraction"; import { OrganizationVaultExportServiceAbstraction } from "./org-vault-export.service.abstraction"; -import { ExportFormat, VaultExportServiceAbstraction } from "./vault-export.service.abstraction"; +import { + ExportFormat, + ExportFormatMetadata, + FormatOptions, + VaultExportServiceAbstraction, +} from "./vault-export.service.abstraction"; export class VaultExportService implements VaultExportServiceAbstraction { constructor( @@ -85,6 +90,26 @@ export class VaultExportService implements VaultExportServiceAbstraction { ); } + /** + * Get available export formats based on vault context + * @param options Options determining which formats are available + * @returns Observable stream of available export formats + */ + formats$(options: FormatOptions): Observable { + const baseFormats: ExportFormatMetadata[] = [ + { name: ".json", format: "json" }, + { name: ".csv", format: "csv" }, + { name: ".json (Encrypted)", format: "encrypted_json" }, + ]; + + // ZIP format with attachments is only available for individual vault exports + if (options.isMyVault) { + return of([...baseFormats, { name: ".zip (with attachments)", format: "zip" }]); + } + + return of(baseFormats); + } + /** Checks if the provided userId matches the currently authenticated user * @param userId The userId to check * @throws Error if the userId does not match the currently authenticated user diff --git a/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.html b/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.html index c638e5d7dde..f41375edd5a 100644 --- a/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.html +++ b/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.html @@ -35,7 +35,7 @@ {{ "fileFormat" | i18n }} - + diff --git a/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts b/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts index 19921b35162..610f30c1f67 100644 --- a/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts +++ b/libs/tools/export/vault-export/vault-export-ui/src/components/export.component.ts @@ -67,7 +67,11 @@ import { } from "@bitwarden/components"; import { GeneratorServicesModule } from "@bitwarden/generator-components"; import { CredentialGeneratorService, GenerateRequest, Type } from "@bitwarden/generator-core"; -import { ExportedVault, VaultExportServiceAbstraction } from "@bitwarden/vault-export-core"; +import { + ExportedVault, + ExportFormatMetadata, + VaultExportServiceAbstraction, +} from "@bitwarden/vault-export-core"; import { EncryptedExportType } from "../enums/encrypted-export-type.enum"; @@ -231,11 +235,11 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit { fileEncryptionType: [EncryptedExportType.AccountEncrypted], }); - formatOptions = [ - { name: ".json", value: "json" }, - { name: ".csv", value: "csv" }, - { name: ".json (Encrypted)", value: "encrypted_json" }, - ]; + /** + * Observable stream of available export format options + * Dynamically updates based on vault selection (My Vault vs Organization) + */ + formatOptions$: Observable; private destroy$ = new Subject(); private onlyManagedCollections = true; @@ -338,17 +342,28 @@ export class ExportComponent implements OnInit, OnDestroy, AfterViewInit { } private observeFormSelections(): void { - this.exportForm.controls.vaultSelector.valueChanges - .pipe(takeUntil(this.destroy$)) - .subscribe((value) => { - this.organizationId = value !== "myVault" ? value : undefined; + // Set up dynamic format options based on vault selection + this.formatOptions$ = this.exportForm.controls.vaultSelector.valueChanges.pipe( + startWith(this.exportForm.controls.vaultSelector.value), + map((vaultSelection) => { + const isMyVault = vaultSelection === "myVault"; + // Update organizationId based on vault selection + this.organizationId = isMyVault ? undefined : vaultSelection; + return { isMyVault }; + }), + switchMap((options) => this.exportService.formats$(options)), + tap((formats) => { + // Preserve the current format selection if it's still available in the new format list + const currentFormat = this.exportForm.get("format").value; + const isFormatAvailable = formats.some((f) => f.format === currentFormat); - this.formatOptions = this.formatOptions.filter((option) => option.value !== "zip"); - this.exportForm.get("format").setValue("json"); - if (value === "myVault") { - this.formatOptions.push({ name: ".zip (with attachments)", value: "zip" }); + // Only reset to json if the current format is no longer available + if (!isFormatAvailable) { + this.exportForm.get("format").setValue("json"); } - }); + }), + shareReplay({ bufferSize: 1, refCount: true }), + ); } /** From 94f778006fb7d15d7e24aeff5a7702a7b994165a Mon Sep 17 00:00:00 2001 From: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> Date: Wed, 29 Oct 2025 14:49:48 -0500 Subject: [PATCH 15/66] Fix lint (#17113) --- .../policies/session-timeout-confirmation-never.component.ts | 2 ++ .../app/key-management/policies/session-timeout.component.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/bitwarden_license/bit-web/src/app/key-management/policies/session-timeout-confirmation-never.component.ts b/bitwarden_license/bit-web/src/app/key-management/policies/session-timeout-confirmation-never.component.ts index a909baf1c77..884cbd10cac 100644 --- a/bitwarden_license/bit-web/src/app/key-management/policies/session-timeout-confirmation-never.component.ts +++ b/bitwarden_license/bit-web/src/app/key-management/policies/session-timeout-confirmation-never.component.ts @@ -3,6 +3,8 @@ import { Component } from "@angular/core"; import { DialogRef, DialogService } from "@bitwarden/components"; import { SharedModule } from "@bitwarden/web-vault/app/shared"; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ imports: [SharedModule], templateUrl: "./session-timeout-confirmation-never.component.html", diff --git a/bitwarden_license/bit-web/src/app/key-management/policies/session-timeout.component.ts b/bitwarden_license/bit-web/src/app/key-management/policies/session-timeout.component.ts index 3e40b9f0d80..9c6129f64df 100644 --- a/bitwarden_license/bit-web/src/app/key-management/policies/session-timeout.component.ts +++ b/bitwarden_license/bit-web/src/app/key-management/policies/session-timeout.component.ts @@ -40,6 +40,8 @@ export class SessionTimeoutPolicy extends BasePolicyEditDefinition { const DEFAULT_HOURS = 8; const DEFAULT_MINUTES = 0; +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ templateUrl: "session-timeout.component.html", imports: [SharedModule], From c05ea23ce4a3e6361fc47a08e14817fd62f0def2 Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:55:23 -0700 Subject: [PATCH 16/66] [PM-25083][26650][26651][26652] - Autofill confirmation dialog (#16835) * add autofill confirmation dialog * fix key * better handle bad uris * add specs * adjustments to autofill confirmation to include exact match dialog. fix gradient * update logic. add tests --- apps/browser/src/_locales/en/messages.json | 48 ++++ ...utofill-confirmation-dialog.component.html | 68 +++++ ...fill-confirmation-dialog.component.spec.ts | 192 ++++++++++++++ .../autofill-confirmation-dialog.component.ts | 100 ++++++++ .../item-more-options.component.html | 14 +- .../item-more-options.component.spec.ts | 241 ++++++++++++++++++ .../item-more-options.component.ts | 95 +++++-- .../services/vault-popup-items.service.ts | 7 + libs/common/src/enums/feature-flag.enum.ts | 2 + 9 files changed, 749 insertions(+), 18 deletions(-) create mode 100644 apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html create mode 100644 apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.spec.ts create mode 100644 apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.ts create mode 100644 apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.spec.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 29601bfa70c..4f230dd9883 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -588,6 +588,9 @@ "view": { "message": "View" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1031,18 @@ "editedItem": { "message": "Item saved" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Do you really want to send to the trash?" }, @@ -1676,9 +1691,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3276,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4050,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html new file mode 100644 index 00000000000..77801edc8fe --- /dev/null +++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html @@ -0,0 +1,68 @@ + + {{ "confirmAutofill" | i18n }} +
+

+ {{ "confirmAutofillDesc" | i18n }} +

+ @if (savedUrls.length === 1) { +

+ {{ "savedWebsite" | i18n }} +

+ +
+ {{ savedUrls[0] }} +
+
+ } + @if (savedUrls.length > 1) { +
+

+ {{ "savedWebsites" | i18n: savedUrls.length }} +

+ +
+
+
+ +
+ {{ url }} +
+
+
+
+ } +

+ {{ "currentWebsite" | i18n }} +

+ +
+ {{ currentUrl }} +
+
+
+ + + +
+
+
diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.spec.ts new file mode 100644 index 00000000000..1fe3dfaf25a --- /dev/null +++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.spec.ts @@ -0,0 +1,192 @@ +import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { provideNoopAnimations } from "@angular/platform-browser/animations"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { DIALOG_DATA, DialogRef, DialogService } from "@bitwarden/components"; + +import { + AutofillConfirmationDialogComponent, + AutofillConfirmationDialogResult, + AutofillConfirmationDialogParams, +} from "./autofill-confirmation-dialog.component"; + +describe("AutofillConfirmationDialogComponent", () => { + let fixture: ComponentFixture; + let component: AutofillConfirmationDialogComponent; + + const dialogRef = { + close: jest.fn(), + } as unknown as DialogRef; + + const params: AutofillConfirmationDialogParams = { + currentUrl: "https://example.com/path?q=1", + savedUrls: ["https://one.example.com/a", "https://two.example.com/b", "not-a-url.example"], + }; + + beforeEach(async () => { + jest.spyOn(Utils, "getHostname").mockImplementation((value: string | null | undefined) => { + if (typeof value !== "string" || !value) { + return ""; + } + try { + // handle non-URL host strings gracefully + if (!value.includes("://")) { + return value; + } + return new URL(value).hostname; + } catch { + return ""; + } + }); + + await TestBed.configureTestingModule({ + imports: [AutofillConfirmationDialogComponent], + providers: [ + provideNoopAnimations(), + { provide: DIALOG_DATA, useValue: params }, + { provide: DialogRef, useValue: dialogRef }, + { provide: I18nService, useValue: { t: (key: string) => key } }, + { provide: DialogService, useValue: {} }, + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }).compileComponents(); + + fixture = TestBed.createComponent(AutofillConfirmationDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it("normalizes currentUrl and savedUrls via Utils.getHostname", () => { + expect(Utils.getHostname).toHaveBeenCalledTimes(1 + (params.savedUrls?.length ?? 0)); + // current + expect(component.currentUrl).toBe("example.com"); + // saved + expect(component.savedUrls).toEqual([ + "one.example.com", + "two.example.com", + "not-a-url.example", + ]); + }); + + it("renders normalized values into the template (shallow check)", () => { + const text = fixture.nativeElement.textContent as string; + expect(text).toContain("example.com"); + expect(text).toContain("one.example.com"); + expect(text).toContain("two.example.com"); + expect(text).toContain("not-a-url.example"); + }); + + it("emits Canceled on close()", () => { + const spy = jest.spyOn(dialogRef, "close"); + component["close"](); + expect(spy).toHaveBeenCalledWith(AutofillConfirmationDialogResult.Canceled); + }); + + it("emits AutofillAndUrlAdded on autofillAndAddUrl()", () => { + const spy = jest.spyOn(dialogRef, "close"); + component["autofillAndAddUrl"](); + expect(spy).toHaveBeenCalledWith(AutofillConfirmationDialogResult.AutofillAndUrlAdded); + }); + + it("emits AutofilledOnly on autofillOnly()", () => { + const spy = jest.spyOn(dialogRef, "close"); + component["autofillOnly"](); + expect(spy).toHaveBeenCalledWith(AutofillConfirmationDialogResult.AutofilledOnly); + }); + + it("applies collapsed list gradient class by default, then clears it after viewAllSavedUrls()", () => { + const initial = component["savedUrlsListClass"]; + expect(initial).toContain("gradient"); + + component["viewAllSavedUrls"](); + fixture.detectChanges(); + + const expanded = component["savedUrlsListClass"]; + expect(expanded).toBe(""); + }); + + it("handles empty savedUrls gracefully", async () => { + const newParams: AutofillConfirmationDialogParams = { + currentUrl: "https://bitwarden.com/help", + savedUrls: [], + }; + + const newFixture = TestBed.createComponent(AutofillConfirmationDialogComponent); + const newInstance = newFixture.componentInstance; + + (newInstance as any).params = newParams; + const fresh = new AutofillConfirmationDialogComponent( + newParams as any, + dialogRef, + ) as AutofillConfirmationDialogComponent; + + expect(fresh.savedUrls).toEqual([]); + expect(fresh.currentUrl).toBe("bitwarden.com"); + }); + + it("handles undefined savedUrls by defaulting to [] and empty strings from Utils.getHostname", () => { + const localParams: AutofillConfirmationDialogParams = { + currentUrl: "https://sub.domain.tld/x", + }; + + const local = new AutofillConfirmationDialogComponent(localParams as any, dialogRef); + + expect(local.savedUrls).toEqual([]); + expect(local.currentUrl).toBe("sub.domain.tld"); + }); + + it("filters out falsy/invalid values from Utils.getHostname in savedUrls", () => { + (Utils.getHostname as jest.Mock).mockImplementationOnce(() => "example.com"); + (Utils.getHostname as jest.Mock) + .mockImplementationOnce(() => "ok.example") + .mockImplementationOnce(() => "") + .mockImplementationOnce(() => undefined as unknown as string); + + const edgeParams: AutofillConfirmationDialogParams = { + currentUrl: "https://example.com", + savedUrls: ["https://ok.example", "://bad", "%%%"], + }; + + const edge = new AutofillConfirmationDialogComponent(edgeParams as any, dialogRef); + + expect(edge.currentUrl).toBe("example.com"); + expect(edge.savedUrls).toEqual(["ok.example"]); + }); + + it("renders one current-url callout and N saved-url callouts", () => { + const callouts = Array.from( + fixture.nativeElement.querySelectorAll("bit-callout"), + ) as HTMLElement[]; + expect(callouts.length).toBe(1 + params.savedUrls!.length); + }); + + it("renders normalized hostnames into the DOM text", () => { + const text = (fixture.nativeElement.textContent as string).replace(/\s+/g, " "); + expect(text).toContain("example.com"); + expect(text).toContain("one.example.com"); + expect(text).toContain("two.example.com"); + }); + + it("shows the 'view all' button when savedUrls > 1 and hides it after click", () => { + const findViewAll = () => + fixture.nativeElement.querySelector( + "button.tw-text-sm.tw-font-bold.tw-cursor-pointer", + ) as HTMLButtonElement | null; + + let btn = findViewAll(); + expect(btn).toBeTruthy(); + + btn!.click(); + fixture.detectChanges(); + + btn = findViewAll(); + expect(btn).toBeFalsy(); + expect(component.savedUrlsExpanded).toBe(true); + }); +}); diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.ts b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.ts new file mode 100644 index 00000000000..cc2fc546ae6 --- /dev/null +++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.ts @@ -0,0 +1,100 @@ +import { CommonModule } from "@angular/common"; +import { Component, Inject } from "@angular/core"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; +import { + DIALOG_DATA, + DialogConfig, + DialogRef, + ButtonModule, + DialogService, + DialogModule, + TypographyModule, + CalloutComponent, + LinkModule, +} from "@bitwarden/components"; + +export interface AutofillConfirmationDialogParams { + savedUrls?: string[]; + currentUrl: string; +} + +export const AutofillConfirmationDialogResult = Object.freeze({ + AutofillAndUrlAdded: "added", + AutofilledOnly: "autofilled", + Canceled: "canceled", +} as const); + +export type AutofillConfirmationDialogResultType = UnionOfValues< + typeof AutofillConfirmationDialogResult +>; + +@Component({ + templateUrl: "./autofill-confirmation-dialog.component.html", + imports: [ + ButtonModule, + CalloutComponent, + CommonModule, + DialogModule, + LinkModule, + TypographyModule, + JslibModule, + ], +}) +export class AutofillConfirmationDialogComponent { + AutofillConfirmationDialogResult = AutofillConfirmationDialogResult; + + currentUrl: string = ""; + savedUrls: string[] = []; + savedUrlsExpanded = false; + + constructor( + @Inject(DIALOG_DATA) protected params: AutofillConfirmationDialogParams, + private dialogRef: DialogRef, + ) { + this.currentUrl = Utils.getHostname(params.currentUrl); + this.savedUrls = + params.savedUrls?.map((url) => Utils.getHostname(url) ?? "").filter(Boolean) ?? []; + } + + protected get savedUrlsListClass(): string { + return this.savedUrlsExpanded + ? "" + : `tw-relative + tw-max-h-24 + tw-overflow-hidden + after:tw-pointer-events-none after:tw-content-[''] + after:tw-absolute after:tw-inset-x-0 after:tw-bottom-0 + after:tw-h-8 after:tw-bg-gradient-to-t + after:tw-from-background after:tw-to-transparent + `; + } + + protected viewAllSavedUrls() { + this.savedUrlsExpanded = true; + } + + protected close() { + this.dialogRef.close(AutofillConfirmationDialogResult.Canceled); + } + + protected autofillAndAddUrl() { + this.dialogRef.close(AutofillConfirmationDialogResult.AutofillAndUrlAdded); + } + + protected autofillOnly() { + this.dialogRef.close(AutofillConfirmationDialogResult.AutofilledOnly); + } + + static open( + dialogService: DialogService, + config: DialogConfig, + ) { + return dialogService.open( + AutofillConfirmationDialogComponent, + { ...config }, + ); + } +} diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html index 3a48f7eb449..b05d19498ac 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html @@ -13,9 +13,17 @@ - + + @if (!(showAutofillConfirmation$ | async)) { + + } diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.spec.ts new file mode 100644 index 00000000000..15a9ba8f8e3 --- /dev/null +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.spec.ts @@ -0,0 +1,241 @@ +import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; +import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; +import { NoopAnimationsModule } from "@angular/platform-browser/animations"; +import { Router } from "@angular/router"; +import { mock } from "jest-mock-extended"; +import { BehaviorSubject, of } from "rxjs"; + +import { CollectionService } from "@bitwarden/admin-console/common"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; +import { + UriMatchStrategy, + UriMatchStrategySetting, +} from "@bitwarden/common/models/domain/domain-service"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CipherType } from "@bitwarden/common/vault/enums"; +import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; +import { DialogService, ToastService } from "@bitwarden/components"; +import { PasswordRepromptService } from "@bitwarden/vault"; + +import { VaultPopupAutofillService } from "../../../services/vault-popup-autofill.service"; +import { VaultPopupItemsService } from "../../../services/vault-popup-items.service"; +import { + AutofillConfirmationDialogComponent, + AutofillConfirmationDialogResult, +} from "../autofill-confirmation-dialog/autofill-confirmation-dialog.component"; + +import { ItemMoreOptionsComponent } from "./item-more-options.component"; + +describe("ItemMoreOptionsComponent", () => { + let fixture: ComponentFixture; + let component: ItemMoreOptionsComponent; + + const dialogService = { + openSimpleDialog: jest.fn().mockResolvedValue(true), + open: jest.fn(), + }; + const featureFlag$ = new BehaviorSubject(false); + const configService = { + getFeatureFlag$: jest.fn().mockImplementation(() => featureFlag$.asObservable()), + }; + const cipherService = { + getFullCipherView: jest.fn(), + encrypt: jest.fn(), + updateWithServer: jest.fn(), + softDeleteWithServer: jest.fn(), + }; + const autofillSvc = { + doAutofill: jest.fn(), + doAutofillAndSave: jest.fn(), + currentAutofillTab$: new BehaviorSubject<{ url?: string | null } | null>(null), + autofillAllowed$: new BehaviorSubject(true), + }; + + const uriMatchStrategy$ = new BehaviorSubject(UriMatchStrategy.Domain); + + const domainSettingsService = { + resolvedDefaultUriMatchStrategy$: uriMatchStrategy$.asObservable(), + }; + + const hasSearchText$ = new BehaviorSubject(false); + const vaultPopupItemsService = { + hasSearchText$: hasSearchText$.asObservable(), + }; + + const baseCipher = { + id: "cipher-1", + login: { + uris: [ + { uri: "https://one.example.com" }, + { uri: "" }, + { uri: undefined as unknown as string }, + { uri: "https://two.example.com/a" }, + ], + username: "user", + }, + favorite: false, + reprompt: 0, + type: CipherType.Login, + viewPassword: true, + edit: true, + } as any; + + beforeEach(waitForAsync(async () => { + jest.clearAllMocks(); + + cipherService.getFullCipherView.mockImplementation(async (c) => ({ ...baseCipher, ...c })); + + TestBed.configureTestingModule({ + imports: [ItemMoreOptionsComponent, NoopAnimationsModule], + providers: [ + { provide: ConfigService, useValue: configService }, + { provide: CipherService, useValue: cipherService }, + { provide: VaultPopupAutofillService, useValue: autofillSvc }, + + { provide: I18nService, useValue: { t: (k: string) => k } }, + { provide: AccountService, useValue: { activeAccount$: of({ id: "UserId" }) } }, + { provide: OrganizationService, useValue: { hasOrganizations: () => of(false) } }, + { + provide: CipherAuthorizationService, + useValue: { canDeleteCipher$: () => of(true), canCloneCipher$: () => of(true) }, + }, + { provide: CollectionService, useValue: { decryptedCollections$: () => of([]) } }, + { provide: RestrictedItemTypesService, useValue: { restricted$: of([]) } }, + { provide: CipherArchiveService, useValue: { userCanArchive$: () => of(true) } }, + { provide: ToastService, useValue: { showToast: () => {} } }, + { provide: Router, useValue: { navigate: () => Promise.resolve(true) } }, + { provide: PasswordRepromptService, useValue: mock() }, + { + provide: DomainSettingsService, + useValue: domainSettingsService, + }, + { + provide: VaultPopupItemsService, + useValue: vaultPopupItemsService, + }, + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }); + TestBed.overrideProvider(DialogService, { useValue: dialogService }); + await TestBed.compileComponents(); + fixture = TestBed.createComponent(ItemMoreOptionsComponent); + component = fixture.componentInstance; + component.cipher = baseCipher; + })); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + function mockConfirmDialogResult(result: string) { + const openSpy = jest + .spyOn(AutofillConfirmationDialogComponent, "open") + .mockReturnValue({ closed: of(result) } as any); + return openSpy; + } + + it("calls doAutofill without showing the confirmation dialog when the feature flag is disabled or search text is not present", async () => { + autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); + + await component.doAutofill(); + + expect(cipherService.getFullCipherView).toHaveBeenCalled(); + expect(autofillSvc.doAutofill).toHaveBeenCalledTimes(1); + expect(autofillSvc.doAutofill).toHaveBeenCalledWith( + expect.objectContaining({ id: "cipher-1" }), + false, + ); + expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); + expect(dialogService.openSimpleDialog).not.toHaveBeenCalled(); + }); + + it("opens the confirmation dialog with filtered saved URLs when the feature flag is enabled and search text is present", async () => { + featureFlag$.next(true); + hasSearchText$.next(true); + autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com/path" }); + const openSpy = mockConfirmDialogResult(AutofillConfirmationDialogResult.Canceled); + + await component.doAutofill(); + + expect(openSpy).toHaveBeenCalledTimes(1); + const args = openSpy.mock.calls[0][1]; + expect(args.data.currentUrl).toBe("https://page.example.com/path"); + expect(args.data.savedUrls).toEqual(["https://one.example.com", "https://two.example.com/a"]); + }); + + it("does nothing when the user cancels the autofill confirmation dialog", async () => { + featureFlag$.next(true); + autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); + mockConfirmDialogResult(AutofillConfirmationDialogResult.Canceled); + + await component.doAutofill(); + + expect(autofillSvc.doAutofill).not.toHaveBeenCalled(); + expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); + }); + + it("autofills the item without adding the URL when the user selects 'AutofilledOnly'", async () => { + featureFlag$.next(true); + autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); + mockConfirmDialogResult(AutofillConfirmationDialogResult.AutofilledOnly); + + await component.doAutofill(); + + expect(autofillSvc.doAutofill).toHaveBeenCalledTimes(1); + expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); + }); + + it("autofills the item and adds the URL when the user selects 'AutofillAndUrlAdded'", async () => { + featureFlag$.next(true); + autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); + mockConfirmDialogResult(AutofillConfirmationDialogResult.AutofillAndUrlAdded); + + await component.doAutofill(); + + expect(autofillSvc.doAutofillAndSave).toHaveBeenCalledTimes(1); + expect(autofillSvc.doAutofillAndSave.mock.calls[0][1]).toBe(false); + expect(autofillSvc.doAutofill).not.toHaveBeenCalled(); + }); + + it("only shows the exact match dialog when the uri match strategy is Exact and no URIs match", async () => { + featureFlag$.next(true); + uriMatchStrategy$.next(UriMatchStrategy.Exact); + hasSearchText$.next(true); + autofillSvc.currentAutofillTab$.next({ url: "https://no-match.example.com" }); + + await component.doAutofill(); + + expect(dialogService.openSimpleDialog).toHaveBeenCalledTimes(1); + 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("hides the 'Fill and Save' button when showAutofillConfirmation$ is true", async () => { + // Enable both feature flag and search text → makes showAutofillConfirmation$ true + featureFlag$.next(true); + hasSearchText$.next(true); + + fixture.detectChanges(); + await fixture.whenStable(); + + const fillAndSaveButton = fixture.nativeElement.querySelector( + "button[bitMenuItem]:not([disabled])", + ); + + const buttonText = fillAndSaveButton?.textContent?.trim().toLowerCase() ?? ""; + expect(buttonText.includes("fillAndSave".toLowerCase())).toBe(false); + }); +}); 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 94016d2670f..40b6476053b 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 @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { CommonModule } from "@angular/common"; import { booleanAttribute, Component, Input } from "@angular/core"; import { Router, RouterModule } from "@angular/router"; @@ -11,8 +9,12 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { UriMatchStrategy } from "@bitwarden/common/models/domain/domain-service"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { CipherId } from "@bitwarden/common/types/guid"; +import { CipherId, 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 { CipherRepromptType, CipherType } from "@bitwarden/common/vault/enums"; @@ -32,7 +34,12 @@ import { import { PasswordRepromptService } from "@bitwarden/vault"; import { VaultPopupAutofillService } from "../../../services/vault-popup-autofill.service"; +import { VaultPopupItemsService } from "../../../services/vault-popup-items.service"; import { AddEditQueryParams } from "../add-edit/add-edit-v2.component"; +import { + AutofillConfirmationDialogComponent, + AutofillConfirmationDialogResult, +} from "../autofill-confirmation-dialog/autofill-confirmation-dialog.component"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @@ -42,7 +49,7 @@ import { AddEditQueryParams } from "../add-edit/add-edit-v2.component"; imports: [ItemModule, IconButtonModule, MenuModule, CommonModule, JslibModule, RouterModule], }) export class ItemMoreOptionsComponent { - private _cipher$ = new BehaviorSubject(undefined); + private _cipher$ = new BehaviorSubject({} as CipherViewLike); // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-signals @@ -64,7 +71,7 @@ export class ItemMoreOptionsComponent { // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ transform: booleanAttribute }) - showViewOption: boolean; + showViewOption = false; /** * Flag to hide the autofill menu options. Used for items that are @@ -73,10 +80,17 @@ export class ItemMoreOptionsComponent { // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-signals @Input({ transform: booleanAttribute }) - hideAutofillOptions: boolean; + hideAutofillOptions = false; protected autofillAllowed$ = this.vaultPopupAutofillService.autofillAllowed$; + protected showAutofillConfirmation$ = combineLatest([ + this.configService.getFeatureFlag$(FeatureFlag.AutofillConfirmation), + this.vaultPopupItemsService.hasSearchText$, + ]).pipe(map(([isFeatureFlagEnabled, hasSearchText]) => isFeatureFlagEnabled && hasSearchText)); + + protected uriMatchStrategy$ = this.domainSettingsService.resolvedDefaultUriMatchStrategy$; + /** * Observable that emits a boolean value indicating if the user is authorized to clone the cipher. * @protected @@ -146,6 +160,9 @@ export class ItemMoreOptionsComponent { private collectionService: CollectionService, private restrictedItemTypesService: RestrictedItemTypesService, private cipherArchiveService: CipherArchiveService, + private configService: ConfigService, + private vaultPopupItemsService: VaultPopupItemsService, + private domainSettingsService: DomainSettingsService, ) {} get canEdit() { @@ -177,14 +194,63 @@ export class ItemMoreOptionsComponent { return this.cipher.favorite ? "unfavorite" : "favorite"; } - async doAutofill() { - const cipher = await this.cipherService.getFullCipherView(this.cipher); - await this.vaultPopupAutofillService.doAutofill(cipher); - } - async doAutofillAndSave() { const cipher = await this.cipherService.getFullCipherView(this.cipher); - await this.vaultPopupAutofillService.doAutofillAndSave(cipher, false); + await this.vaultPopupAutofillService.doAutofillAndSave(cipher); + } + + async doAutofill() { + const cipher = await this.cipherService.getFullCipherView(this.cipher); + + const showAutofillConfirmation = await firstValueFrom(this.showAutofillConfirmation$); + + if (!showAutofillConfirmation) { + await this.vaultPopupAutofillService.doAutofill(cipher, false); + return; + } + + const uriMatchStrategy = await firstValueFrom(this.uriMatchStrategy$); + if (uriMatchStrategy === UriMatchStrategy.Exact) { + await this.dialogService.openSimpleDialog({ + title: { key: "cannotAutofill" }, + content: { key: "cannotAutofillExactMatch" }, + type: "info", + acceptButtonText: { key: "okay" }, + cancelButtonText: null, + }); + return; + } + + const currentTab = await firstValueFrom(this.vaultPopupAutofillService.currentAutofillTab$); + + if (!currentTab?.url) { + await this.dialogService.openSimpleDialog({ + title: { key: "error" }, + content: { key: "errorGettingAutoFillData" }, + type: "danger", + }); + return; + } + + const ref = AutofillConfirmationDialogComponent.open(this.dialogService, { + data: { + currentUrl: currentTab?.url || "", + savedUrls: cipher.login?.uris?.filter((u) => u.uri).map((u) => u.uri!) ?? [], + }, + }); + + const result = await firstValueFrom(ref.closed); + + switch (result) { + case AutofillConfirmationDialogResult.Canceled: + return; + case AutofillConfirmationDialogResult.AutofilledOnly: + await this.vaultPopupAutofillService.doAutofill(cipher); + return; + case AutofillConfirmationDialogResult.AutofillAndUrlAdded: + await this.vaultPopupAutofillService.doAutofillAndSave(cipher, false); + return; + } } async onView() { @@ -204,15 +270,14 @@ export class ItemMoreOptionsComponent { const cipher = await this.cipherService.getFullCipherView(this.cipher); cipher.favorite = !cipher.favorite; - const activeUserId = await firstValueFrom( + const activeUserId = (await firstValueFrom( this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); + )) as UserId; const encryptedCipher = await this.cipherService.encrypt(cipher, activeUserId); await this.cipherService.updateWithServer(encryptedCipher); this.toastService.showToast({ variant: "success", - title: null, message: this.i18nService.t( this.cipher.favorite ? "itemAddedToFavorites" : "itemRemovedFromFavorites", ), diff --git a/apps/browser/src/vault/popup/services/vault-popup-items.service.ts b/apps/browser/src/vault/popup/services/vault-popup-items.service.ts index a1820a975f1..afe9d61d5af 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-items.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-items.service.ts @@ -261,6 +261,13 @@ export class VaultPopupItemsService { this.remainingCiphers$.pipe(map(() => false)), ).pipe(startWith(true), distinctUntilChanged(), shareReplay({ refCount: false, bufferSize: 1 })); + /** Observable that indicates whether there is search text present. + */ + hasSearchText$: Observable = this._hasSearchText.pipe( + distinctUntilChanged(), + shareReplay({ bufferSize: 1, refCount: true }), + ); + /** * Observable that indicates whether a filter or search text is currently applied to the ciphers. */ diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 085731b034e..bfb40aff106 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -55,6 +55,7 @@ export enum FeatureFlag { PM22134SdkCipherListView = "pm-22134-sdk-cipher-list-view", PM22136_SdkCipherEncryption = "pm-22136-sdk-cipher-encryption", CipherKeyEncryption = "cipher-key-encryption", + AutofillConfirmation = "pm-25083-autofill-confirm-from-search", /* Platform */ IpcChannelFramework = "ipc-channel-framework", @@ -102,6 +103,7 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.PM19941MigrateCipherDomainToSdk]: FALSE, [FeatureFlag.PM22134SdkCipherListView]: FALSE, [FeatureFlag.PM22136_SdkCipherEncryption]: FALSE, + [FeatureFlag.AutofillConfirmation]: FALSE, /* Auth */ [FeatureFlag.PM22110_DisableAlternateLoginMethods]: FALSE, From b8921cb079e3cdc69819033eab9a6b2be8965df4 Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:28:36 -0700 Subject: [PATCH 17/66] fix lint error (#17115) --- .../autofill-confirmation-dialog.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.ts b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.ts index cc2fc546ae6..71c07ad8bfc 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.ts @@ -1,5 +1,5 @@ import { CommonModule } from "@angular/common"; -import { Component, Inject } from "@angular/core"; +import { ChangeDetectionStrategy, Component, Inject } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { Utils } from "@bitwarden/common/platform/misc/utils"; @@ -33,6 +33,7 @@ export type AutofillConfirmationDialogResultType = UnionOfValues< @Component({ templateUrl: "./autofill-confirmation-dialog.component.html", + changeDetection: ChangeDetectionStrategy.OnPush, imports: [ ButtonModule, CalloutComponent, From 6b3c4f87c70543b3f2599f72acf8a10053006a15 Mon Sep 17 00:00:00 2001 From: Vicki League Date: Wed, 29 Oct 2025 16:52:28 -0400 Subject: [PATCH 18/66] [CL-807] Improve aria a11y of nav group (#17078) --- .../src/navigation/nav-group.component.html | 6 ++-- .../src/navigation/nav-group.component.ts | 32 +++++++++++++++++-- .../src/navigation/nav-item.component.html | 11 ++++--- .../src/navigation/nav-item.component.ts | 10 +++++- 4 files changed, 47 insertions(+), 12 deletions(-) diff --git a/libs/components/src/navigation/nav-group.component.html b/libs/components/src/navigation/nav-group.component.html index 195569292f6..bcf6ae2b5b7 100644 --- a/libs/components/src/navigation/nav-group.component.html +++ b/libs/components/src/navigation/nav-group.component.html @@ -8,7 +8,8 @@ [routerLinkActiveOptions]="routerLinkActiveOptions()" (mainContentClicked)="handleMainContentClicked()" [ariaLabel]="ariaLabel()" - [hideActiveStyles]="parentHideActiveStyles" + [hideActiveStyles]="parentHideActiveStyles()" + [ariaCurrentWhenActive]="ariaCurrent()" > diff --git a/bitwarden_license/bit-web/src/app/auth/sso/sso.component.ts b/bitwarden_license/bit-web/src/app/auth/sso/sso-manage.component.ts similarity index 85% rename from bitwarden_license/bit-web/src/app/auth/sso/sso.component.ts rename to bitwarden_license/bit-web/src/app/auth/sso/sso-manage.component.ts index 1c25283ea4f..c7479df7784 100644 --- a/bitwarden_license/bit-web/src/app/auth/sso/sso.component.ts +++ b/bitwarden_license/bit-web/src/app/auth/sso/sso-manage.component.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Component, OnDestroy, OnInit } from "@angular/core"; import { AbstractControl, @@ -55,11 +53,11 @@ const defaultSigningAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha2 // 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-org-manage-sso", - templateUrl: "sso.component.html", + selector: "auth-sso-manage", + templateUrl: "sso-manage.component.html", standalone: false, }) -export class SsoComponent implements OnInit, OnDestroy { +export class SsoManageComponent implements OnInit, OnDestroy { readonly ssoType = SsoType; readonly memberDecryptionType = MemberDecryptionType; @@ -117,31 +115,31 @@ export class SsoComponent implements OnInit, OnDestroy { isInitializing = true; // concerned with UI/UX (i.e. when to show loading spinner vs form) isFormValidatingOrPopulating = true; // tracks when form fields are being validated/populated during load() or submit() - configuredKeyConnectorUrlFromServer: string | null; + configuredKeyConnectorUrlFromServer: string | null = null; memberDecryptionTypeValueChangesSubscription: Subscription | null = null; haveTestedKeyConnector = false; - organizationId: string; - organization: Organization; + organizationId: string | undefined = undefined; + organization: Organization | undefined = undefined; - callbackPath: string; - signedOutCallbackPath: string; - spEntityId: string; - spEntityIdStatic: string; - spMetadataUrl: string; - spAcsUrl: string; + callbackPath: string | undefined = undefined; + signedOutCallbackPath: string | undefined = undefined; + spEntityId: string | undefined = undefined; + spEntityIdStatic: string | undefined = undefined; + spMetadataUrl: string | undefined = undefined; + spAcsUrl: string | undefined = undefined; showClientSecret = false; protected openIdForm = this.formBuilder.group>( { - authority: new FormControl("", Validators.required), - clientId: new FormControl("", Validators.required), - clientSecret: new FormControl("", Validators.required), + authority: new FormControl("", { nonNullable: true, validators: Validators.required }), + clientId: new FormControl("", { nonNullable: true, validators: Validators.required }), + clientSecret: new FormControl("", { nonNullable: true, validators: Validators.required }), metadataAddress: new FormControl(), - redirectBehavior: new FormControl( - OpenIdConnectRedirectBehavior.RedirectGet, - Validators.required, - ), + redirectBehavior: new FormControl(OpenIdConnectRedirectBehavior.RedirectGet, { + nonNullable: true, + validators: Validators.required, + }), getClaimsFromUserInfoEndpoint: new FormControl(), additionalScopes: new FormControl(), additionalUserIdClaimTypes: new FormControl(), @@ -157,22 +155,32 @@ export class SsoComponent implements OnInit, OnDestroy { protected samlForm = this.formBuilder.group>( { - spUniqueEntityId: new FormControl(true, { updateOn: "change" }), - spNameIdFormat: new FormControl(Saml2NameIdFormat.NotConfigured), - spOutboundSigningAlgorithm: new FormControl(defaultSigningAlgorithm), - spSigningBehavior: new FormControl(Saml2SigningBehavior.IfIdpWantAuthnRequestsSigned), - spMinIncomingSigningAlgorithm: new FormControl(defaultSigningAlgorithm), + spUniqueEntityId: new FormControl(true, { nonNullable: true, updateOn: "change" }), + spNameIdFormat: new FormControl(Saml2NameIdFormat.NotConfigured, { nonNullable: true }), + spOutboundSigningAlgorithm: new FormControl(defaultSigningAlgorithm, { nonNullable: true }), + spSigningBehavior: new FormControl(Saml2SigningBehavior.IfIdpWantAuthnRequestsSigned, { + nonNullable: true, + }), + spMinIncomingSigningAlgorithm: new FormControl(defaultSigningAlgorithm, { + nonNullable: true, + }), spWantAssertionsSigned: new FormControl(), spValidateCertificates: new FormControl(), - idpEntityId: new FormControl("", Validators.required), - idpBindingType: new FormControl(Saml2BindingType.HttpRedirect), - idpSingleSignOnServiceUrl: new FormControl("", Validators.required), + idpEntityId: new FormControl("", { nonNullable: true, validators: Validators.required }), + idpBindingType: new FormControl(Saml2BindingType.HttpRedirect, { nonNullable: true }), + idpSingleSignOnServiceUrl: new FormControl("", { + nonNullable: true, + validators: Validators.required, + }), idpSingleLogoutServiceUrl: new FormControl(), - idpX509PublicCert: new FormControl("", Validators.required), - idpOutboundSigningAlgorithm: new FormControl(defaultSigningAlgorithm), + idpX509PublicCert: new FormControl("", { + nonNullable: true, + validators: Validators.required, + }), + idpOutboundSigningAlgorithm: new FormControl(defaultSigningAlgorithm, { nonNullable: true }), idpAllowUnsolicitedAuthnResponse: new FormControl(), - idpAllowOutboundLogoutRequests: new FormControl(true), + idpAllowOutboundLogoutRequests: new FormControl(true, { nonNullable: true }), idpWantAuthnRequestsSigned: new FormControl(), }, { @@ -181,13 +189,16 @@ export class SsoComponent implements OnInit, OnDestroy { ); protected ssoConfigForm = this.formBuilder.group>({ - configType: new FormControl(SsoType.None), - memberDecryptionType: new FormControl(MemberDecryptionType.MasterPassword), - keyConnectorUrl: new FormControl(""), + configType: new FormControl(SsoType.None, { nonNullable: true }), + memberDecryptionType: new FormControl(MemberDecryptionType.MasterPassword, { + nonNullable: true, + }), + keyConnectorUrl: new FormControl("", { nonNullable: true }), openId: this.openIdForm, saml: this.samlForm, - enabled: new FormControl(false), + enabled: new FormControl(false, { nonNullable: true }), ssoIdentifier: new FormControl("", { + nonNullable: true, validators: [Validators.maxLength(50), Validators.required], }), }); @@ -235,7 +246,7 @@ export class SsoComponent implements OnInit, OnDestroy { this.ssoConfigForm .get("configType") - .valueChanges.pipe(takeUntil(this.destroy$)) + ?.valueChanges.pipe(takeUntil(this.destroy$)) .subscribe((newType: SsoType) => { if (newType === SsoType.OpenIdConnect) { this.openIdForm.enable(); @@ -251,8 +262,8 @@ export class SsoComponent implements OnInit, OnDestroy { this.samlForm .get("spSigningBehavior") - .valueChanges.pipe(takeUntil(this.destroy$)) - .subscribe(() => this.samlForm.get("idpX509PublicCert").updateValueAndValidity()); + ?.valueChanges.pipe(takeUntil(this.destroy$)) + .subscribe(() => this.samlForm.get("idpX509PublicCert")?.updateValueAndValidity()); this.route.params .pipe( @@ -286,6 +297,10 @@ export class SsoComponent implements OnInit, OnDestroy { this.memberDecryptionTypeValueChangesSubscription = null; try { + if (!this.organizationId) { + throw new Error("Load: Organization ID is not set"); + } + const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); this.organization = await firstValueFrom( this.organizationService @@ -334,6 +349,11 @@ export class SsoComponent implements OnInit, OnDestroy { this.readOutErrors(); return; } + + if (!this.organizationId) { + throw new Error("Submit: Organization ID is not set"); + } + const request = new OrganizationSsoRequest(); request.enabled = this.enabledCtrl.value; // Return null instead of empty string to avoid duplicate id errors in database @@ -349,7 +369,6 @@ export class SsoComponent implements OnInit, OnDestroy { this.toastService.showToast({ variant: "success", - title: null, message: this.i18nService.t("ssoSettingsSaved"), }); } finally { @@ -407,16 +426,16 @@ export class SsoComponent implements OnInit, OnDestroy { return; } - this.keyConnectorUrl.markAsPending(); + this.keyConnectorUrlFormCtrl.markAsPending(); try { - await this.apiService.getKeyConnectorAlive(this.keyConnectorUrl.value); - this.keyConnectorUrl.updateValueAndValidity(); + await this.apiService.getKeyConnectorAlive(this.keyConnectorUrlFormCtrl.value); + this.keyConnectorUrlFormCtrl.updateValueAndValidity(); } catch { - this.keyConnectorUrl.setErrors({ + this.keyConnectorUrlFormCtrl.setErrors({ invalidUrl: { message: this.i18nService.t("keyConnectorTestFail") }, }); - this.keyConnectorUrl.markAllAsTouched(); + this.keyConnectorUrlFormCtrl.markAllAsTouched(); } this.haveTestedKeyConnector = true; @@ -442,12 +461,12 @@ export class SsoComponent implements OnInit, OnDestroy { get enableTestKeyConnector() { return ( this.ssoConfigForm.value?.memberDecryptionType === MemberDecryptionType.KeyConnector && - !Utils.isNullOrWhitespace(this.keyConnectorUrl?.value) + !Utils.isNullOrWhitespace(this.keyConnectorUrlFormCtrl?.value) ); } - get keyConnectorUrl() { - return this.ssoConfigForm.get("keyConnectorUrl"); + get keyConnectorUrlFormCtrl() { + return this.ssoConfigForm.controls?.keyConnectorUrl as FormControl; } /** @@ -502,6 +521,11 @@ export class SsoComponent implements OnInit, OnDestroy { organizationSsoRequest: OrganizationSsoRequest, ): Promise { const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$)); + + if (!this.organizationId) { + throw new Error("upsertOrganizationWithSsoChanges: Organization ID is not set"); + } + const currentOrganization = await firstValueFrom( this.organizationService .organizations$(userId) From 619354827e9d5f19cbd1aec343749a93f116d3d2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 10:32:01 -0400 Subject: [PATCH 26/66] [deps] UI Foundation: Update ngx-toastr to v19.1.0 (#16631) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Bryan Cunningham --- 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 d1858d4d508..f6e33174737 100644 --- a/package-lock.json +++ b/package-lock.json @@ -56,7 +56,7 @@ "lowdb": "1.0.0", "lunr": "2.3.9", "multer": "2.0.2", - "ngx-toastr": "19.0.0", + "ngx-toastr": "19.1.0", "node-fetch": "2.6.12", "node-forge": "1.3.1", "oidc-client-ts": "2.4.1", @@ -31032,9 +31032,9 @@ } }, "node_modules/ngx-toastr": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-19.0.0.tgz", - "integrity": "sha512-6pTnktwwWD+kx342wuMOWB4+bkyX9221pAgGz3SHOJH0/MI9erLucS8PeeJDFwbUYyh75nQ6AzVtolgHxi52dQ==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-19.1.0.tgz", + "integrity": "sha512-Qa7Kg7QzGKNtp1v04hu3poPKKx8BGBD/Onkhm6CdH5F0vSMdq+BdR/f8DTpZnGFksW891tAFufpiWb9UZX+3vg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" diff --git a/package.json b/package.json index 32056a174b1..12df878b5cf 100644 --- a/package.json +++ b/package.json @@ -193,7 +193,7 @@ "lowdb": "1.0.0", "lunr": "2.3.9", "multer": "2.0.2", - "ngx-toastr": "19.0.0", + "ngx-toastr": "19.1.0", "node-fetch": "2.6.12", "node-forge": "1.3.1", "oidc-client-ts": "2.4.1", From d8e5a524d4cb83cf0b736469dde88ab33761abec Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Thu, 30 Oct 2025 07:41:17 -0700 Subject: [PATCH 27/66] style(sso-login): (Auth) [PM-26535] Make SSO Button Primary if `ssoRequired` (#16757) When SSO is required: - Make the SSO button primary - Add a tooltip to the disabled buttons When SSO is not required: - SSO button remains secondary - No tooltip on the buttons Feature Flags enabled: pm-22110-disable-alternate-login-methods --- apps/browser/src/_locales/en/messages.json | 3 +++ apps/desktop/src/locales/en/messages.json | 3 +++ apps/web/src/locales/en/messages.json | 3 +++ libs/auth/src/angular/login/login.component.html | 12 +++++++++++- libs/auth/src/angular/login/login.component.ts | 2 ++ libs/components/src/index.ts | 1 + 6 files changed, 23 insertions(+), 1 deletion(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 4f230dd9883..a7cee53e08b 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 32545a0c1cd..605fefb03f8 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Submit" }, diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index c1026af4b6e..df67973ee5a 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -1314,6 +1314,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, diff --git a/libs/auth/src/angular/login/login.component.html b/libs/auth/src/angular/login/login.component.html index 26e19f11147..4e1689b1054 100644 --- a/libs/auth/src/angular/login/login.component.html +++ b/libs/auth/src/angular/login/login.component.html @@ -44,6 +44,8 @@ block buttonType="primary" (click)="continuePressed()" + [bitTooltip]="ssoRequired ? ('yourOrganizationRequiresSingleSignOn' | i18n) : ''" + [addTooltipToDescribedby]="ssoRequired" [disabled]="ssoRequired" > {{ "continue" | i18n }} @@ -59,6 +61,8 @@ block buttonType="secondary" (click)="handleLoginWithPasskeyClick()" + [bitTooltip]="ssoRequired ? ('yourOrganizationRequiresSingleSignOn' | i18n) : ''" + [addTooltipToDescribedby]="ssoRequired" [disabled]="ssoRequired" > @@ -67,7 +71,13 @@ - diff --git a/libs/auth/src/angular/login/login.component.ts b/libs/auth/src/angular/login/login.component.ts index 537a42700c8..54a2a3b732b 100644 --- a/libs/auth/src/angular/login/login.component.ts +++ b/libs/auth/src/angular/login/login.component.ts @@ -54,6 +54,7 @@ import { IconButtonModule, LinkModule, ToastService, + TooltipDirective, } from "@bitwarden/components"; import { LoginComponentService, PasswordPolicies } from "./login-component.service"; @@ -82,6 +83,7 @@ export enum LoginUiState { JslibModule, ReactiveFormsModule, RouterModule, + TooltipDirective, ], }) export class LoginComponent implements OnInit, OnDestroy { diff --git a/libs/components/src/index.ts b/libs/components/src/index.ts index f36a3fdddf5..643b5d69da7 100644 --- a/libs/components/src/index.ts +++ b/libs/components/src/index.ts @@ -42,6 +42,7 @@ export * from "./table"; export * from "./tabs"; export * from "./toast"; export * from "./toggle-group"; +export * from "./tooltip"; export * from "./typography"; export * from "./utils"; export * from "./stepper"; From 1e5c0ac41f901bb328c453c78de04dc9cbc0b719 Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Thu, 30 Oct 2025 08:57:48 -0700 Subject: [PATCH 28/66] add reprompt. fix logic (#17122) --- ...utofill-confirmation-dialog.component.html | 10 +- ...fill-confirmation-dialog.component.spec.ts | 70 ++++++- .../autofill-confirmation-dialog.component.ts | 3 + .../item-more-options.component.spec.ts | 185 ++++++++++-------- .../item-more-options.component.ts | 5 + 5 files changed, 182 insertions(+), 91 deletions(-) diff --git a/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html index 77801edc8fe..625c92e38c5 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/autofill-confirmation-dialog/autofill-confirmation-dialog.component.html @@ -48,11 +48,13 @@
- + @if (!viewOnly) { + + } - - +
+

{{ "allApplications" | i18n }}

+
+ + +
+
+ +
- } @else { -
-

{{ "allApplications" | i18n }}

-
- - -
-
- - -
- -
- } + +
} diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/all-applications.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/all-applications.component.ts index 5fbc841778a..279ddc5e6f0 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/all-applications.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/all-applications.component.ts @@ -1,7 +1,7 @@ import { Component, DestroyRef, inject, OnInit } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormControl } from "@angular/forms"; -import { ActivatedRoute } from "@angular/router"; +import { ActivatedRoute, Router } from "@angular/router"; import { debounceTime } from "rxjs"; import { Security } from "@bitwarden/assets/svg"; @@ -61,6 +61,7 @@ export class AllApplicationsComponent implements OnInit { protected activatedRoute: ActivatedRoute, protected toastService: ToastService, protected dataService: RiskInsightsDataService, + private router: Router, // protected allActivitiesService: AllActivitiesService, ) { this.searchControl.valueChanges @@ -78,21 +79,8 @@ export class AllApplicationsComponent implements OnInit { this.dataSource.data = []; }, }); - - // TODO - // this.applicationSummary = this.reportService.generateApplicationsSummary(data); - // this.allActivitiesService.setAllAppsReportSummary(this.applicationSummary); } - goToCreateNewLoginItem = async () => { - // TODO: implement - this.toastService.showToast({ - variant: "warning", - title: "", - message: "Not yet implemented", - }); - }; - isMarkedAsCriticalItem(applicationName: string) { return this.selectedUrls.has(applicationName); } diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.html index cfcdf3a1841..f0cfd069653 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.html @@ -6,26 +6,7 @@ >
{{ "loading" | i18n }}
-
- - -

- {{ "noCriticalApplicationsTitle" | i18n }} -

-
- -

- {{ "noCriticalApplicationsDescription" | i18n }} -

-
- - - -
-
-
+

{{ "criticalApplications" | i18n }}

+
+
+ +
+
+ @if (videoSrc()) { + + } @else if (icon()) { +
+ +
+ } +
+
+ +
+
+ @if (videoSrc()) { + + } @else if (icon()) { +
+ +
+ } +
+
+
diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/empty-state-card.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/empty-state-card.component.ts new file mode 100644 index 00000000000..54d97e984ec --- /dev/null +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/empty-state-card.component.ts @@ -0,0 +1,29 @@ +import { CommonModule } from "@angular/common"; +import { ChangeDetectionStrategy, Component, input, isDevMode, OnInit } from "@angular/core"; + +import { Icon } from "@bitwarden/assets/svg"; +import { ButtonModule, IconModule } from "@bitwarden/components"; + +@Component({ + selector: "empty-state-card", + templateUrl: "./empty-state-card.component.html", + imports: [CommonModule, IconModule, ButtonModule], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class EmptyStateCardComponent implements OnInit { + readonly icon = input(null); + readonly videoSrc = input(null); + readonly title = input(""); + readonly description = input(""); + readonly benefits = input<[string, string][]>([]); + readonly buttonText = input(""); + readonly buttonAction = input<(() => void) | null>(null); + readonly buttonIcon = input(undefined); + + ngOnInit(): void { + if (!this.title() && isDevMode()) { + // eslint-disable-next-line no-console + console.warn("EmptyStateCardComponent: title is required for proper display"); + } + } +} diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.html index 18df046b82c..884501d608e 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.html @@ -1,64 +1,83 @@ -

{{ "riskInsights" | i18n }}

-
- {{ "reviewAtRiskPasswords" | i18n }} +
+
+

{{ "riskInsights" | i18n }}

+
+ {{ "reviewAtRiskPasswords" | i18n }} +
+ @if (dataLastUpdated) { +
+ + {{ + "dataLastUpdated" | i18n: (dataLastUpdated | date: "MMMM d, y 'at' h:mm a") + }} + @let isRunningReport = dataService.isGeneratingReport$ | async; + + + + + + +
+ } +
+ +
+ @if (shouldShowTabs) { + + @if (isRiskInsightsActivityTabFeatureEnabled) { + + + + } + + + + + + + {{ + "criticalApplicationsWithCount" + | i18n: (dataService.criticalReportResults$ | async)?.reportData?.length ?? 0 + }} + + + + + } @else { +
+ +
+ } +
-
- - @if (dataLastUpdated) { - {{ - "dataLastUpdated" | i18n: (dataLastUpdated | date: "MMMM d, y 'at' h:mm a") - }} - } @else { - {{ "noReportRan" | i18n }} - } - @let isRunningReport = dataService.isGeneratingReport$ | async; - - - - - - -
- - @if (isRiskInsightsActivityTabFeatureEnabled) { - - - - } - - - - - - - {{ - "criticalApplicationsWithCount" - | i18n: (dataService.criticalReportResults$ | async)?.reportData?.length ?? 0 - }} - - - - @if (dataService.drawerDetails$ | async; as drawerDetails) { diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts index 8e58ba22454..5bd50967709 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/risk-insights.component.ts @@ -2,7 +2,7 @@ import { CommonModule } from "@angular/common"; import { Component, DestroyRef, OnDestroy, OnInit, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute, Router } from "@angular/router"; -import { EMPTY } from "rxjs"; +import { combineLatest, EMPTY } from "rxjs"; import { map, tap } from "rxjs/operators"; import { JslibModule } from "@bitwarden/angular/jslib.module"; @@ -12,6 +12,7 @@ import { } from "@bitwarden/bit-common/dirt/reports/risk-insights"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { OrganizationId } from "@bitwarden/common/types/guid"; import { AsyncActionsModule, @@ -26,6 +27,7 @@ import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.mod import { AllActivityComponent } from "./activity/all-activity.component"; import { AllApplicationsComponent } from "./all-applications/all-applications.component"; import { CriticalApplicationsComponent } from "./critical-applications/critical-applications.component"; +import { EmptyStateCardComponent } from "./empty-state-card.component"; import { RiskInsightsTabType } from "./models/risk-insights.models"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush @@ -38,6 +40,7 @@ import { RiskInsightsTabType } from "./models/risk-insights.models"; ButtonModule, CommonModule, CriticalApplicationsComponent, + EmptyStateCardComponent, JslibModule, HeaderModule, TabsModule, @@ -61,14 +64,36 @@ export class RiskInsightsComponent implements OnInit, OnDestroy { private organizationId: OrganizationId = "" as OrganizationId; dataLastUpdated: Date | null = null; + refetching: boolean = false; + + // Empty state properties + protected hasReportBeenRun = false; + protected reportHasLoaded = false; + protected hasVaultItems = false; + private organizationName = ""; + + // Empty state computed properties + protected shouldShowImportDataState = false; + protected emptyStateTitle = ""; + protected emptyStateDescription = ""; + protected emptyStateBenefits: [string, string][] = []; + protected emptyStateButtonText = ""; + protected emptyStateButtonIcon = ""; + protected emptyStateButtonAction: (() => void) | null = null; + protected emptyStateVideoSrc: string | null = "/videos/risk-insights-mark-as-critical.mp4"; + + private static readonly IMPORT_ICON = "bwi bwi-download"; + + // TODO: See https://github.com/bitwarden/clients/pull/16832#discussion_r2474523235 constructor( private route: ActivatedRoute, private router: Router, private configService: ConfigService, protected dataService: RiskInsightsDataService, + private i18nService: I18nService, ) { - this.route.queryParams.pipe(takeUntilDestroyed()).subscribe(({ tabIndex }) => { + this.route.queryParams.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(({ tabIndex }) => { this.tabIndex = !isNaN(Number(tabIndex)) ? Number(tabIndex) : RiskInsightsTabType.AllApps; }); @@ -89,7 +114,7 @@ export class RiskInsightsComponent implements OnInit, OnDestroy { tap((orgId) => { if (orgId) { // Initialize Data Service - this.dataService.initializeForOrganization(orgId as OrganizationId); + void this.dataService.initializeForOrganization(orgId as OrganizationId); this.organizationId = orgId as OrganizationId; } else { return EMPTY; @@ -98,12 +123,30 @@ export class RiskInsightsComponent implements OnInit, OnDestroy { ) .subscribe(); - // Subscribe to report result details - this.dataService.enrichedReportData$ + // Combine report data, vault items check, organization details, and generation state + // This declarative pattern ensures proper cleanup and prevents memory leaks + combineLatest([ + this.dataService.enrichedReportData$, + this.dataService.hasVaultItems$, + this.dataService.organizationDetails$, + this.dataService.isGeneratingReport$, + ]) .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((report) => { + .subscribe(([report, hasVaultItems, orgDetails, isGenerating]) => { + // Update report state + this.reportHasLoaded = true; + this.hasReportBeenRun = !!report?.creationDate; this.appsCount = report?.reportData.length ?? 0; this.dataLastUpdated = report?.creationDate ?? null; + + // Update vault items state + this.hasVaultItems = hasVaultItems; + + // Update organization name + this.organizationName = orgDetails?.organizationName ?? ""; + + // Update all empty state properties based on current state + this.updateEmptyStateProperties(isGenerating); }); // Subscribe to drawer state changes @@ -128,6 +171,10 @@ export class RiskInsightsComponent implements OnInit, OnDestroy { } } + get shouldShowTabs(): boolean { + return this.appsCount > 0; + } + async onTabChange(newIndex: number): Promise { await this.router.navigate([], { relativeTo: this.route, @@ -166,4 +213,52 @@ export class RiskInsightsComponent implements OnInit, OnDestroy { } } } + + // Empty state methods + + // TODO: import data button (we have this) OR button for adding new login items + // we want to add this new button as a second option on the empty state card + + goToImportPage = () => { + void this.router.navigate([ + "/organizations", + this.organizationId, + "settings", + "tools", + "import", + ]); + }; + + /** + * Updates all empty state properties based on current state. + * Called whenever the underlying data (hasVaultItems, hasReportBeenRun, reportHasLoaded) changes. + */ + private updateEmptyStateProperties(isGenerating: boolean): void { + // Calculate boolean flags + // Note: We only show empty states when there are NO apps (appsCount === 0) + // The template uses @if(shouldShowTabs) to determine whether to show tabs or empty state + this.shouldShowImportDataState = !this.hasVaultItems && !isGenerating; + + // Update benefits (constant for all states) + this.emptyStateBenefits = [ + [this.i18nService.t("benefit1Title"), this.i18nService.t("benefit1Description")], + [this.i18nService.t("benefit2Title"), this.i18nService.t("benefit2Description")], + [this.i18nService.t("benefit3Title"), this.i18nService.t("benefit3Description")], + ]; + + // Update all state-dependent properties in single if/else + if (this.shouldShowImportDataState) { + this.emptyStateTitle = this.i18nService.t("noApplicationsInOrgTitle", this.organizationName); + this.emptyStateDescription = this.i18nService.t("noApplicationsInOrgDescription"); + this.emptyStateButtonText = this.i18nService.t("importData"); + this.emptyStateButtonIcon = RiskInsightsComponent.IMPORT_ICON; + this.emptyStateButtonAction = this.goToImportPage; + } else { + this.emptyStateTitle = this.i18nService.t("noReportRunTitle"); + this.emptyStateDescription = this.i18nService.t("noReportRunDescription"); + this.emptyStateButtonText = this.i18nService.t("riskInsightsRunReport"); + this.emptyStateButtonIcon = ""; + this.emptyStateButtonAction = this.generateReport.bind(this); + } + } } From 0d543c7059e9267c196a39b786ea69028d9f811c Mon Sep 17 00:00:00 2001 From: Shane Melton Date: Thu, 30 Oct 2025 13:18:32 -0700 Subject: [PATCH 31/66] [PM-27055] Use undefined instead of null for folder/organization filtering (#17120) --- apps/cli/src/commands/list.command.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/cli/src/commands/list.command.ts b/apps/cli/src/commands/list.command.ts index e5174f67913..ff210cf222d 100644 --- a/apps/cli/src/commands/list.command.ts +++ b/apps/cli/src/commands/list.command.ts @@ -102,7 +102,7 @@ export class ListCommand { if (options.folderId === "notnull" && c.folderId != null) { return true; } - const folderId = options.folderId === "null" ? null : options.folderId; + const folderId = options.folderId === "null" ? undefined : options.folderId; if (folderId === c.folderId) { return true; } @@ -112,7 +112,8 @@ export class ListCommand { if (options.organizationId === "notnull" && c.organizationId != null) { return true; } - const organizationId = options.organizationId === "null" ? null : options.organizationId; + const organizationId = + options.organizationId === "null" ? undefined : options.organizationId; if (organizationId === c.organizationId) { return true; } From 4a71503875a8b17bd6043c636bca84230c4d9e76 Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Thu, 30 Oct 2025 13:19:12 -0700 Subject: [PATCH 32/66] [PM-27642] - fix mp reprompt issue (#17131) * fix mp reprompt issue * fix logic. add more specs * update vault popup autofill spec * update jsdoc --- .../item-more-options.component.spec.ts | 219 +++++++++++------- .../item-more-options.component.ts | 26 +-- .../vault-popup-autofill.service.spec.ts | 12 + .../services/vault-popup-autofill.service.ts | 29 ++- 4 files changed, 180 insertions(+), 106 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 d65a876e60f..66bbafb7c31 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 @@ -153,99 +153,14 @@ describe("ItemMoreOptionsComponent", () => { expect(autofillSvc.doAutofill).toHaveBeenCalledTimes(1); expect(autofillSvc.doAutofill).toHaveBeenCalledWith( expect.objectContaining({ id: "cipher-1" }), - false, + true, + true, ); expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); expect(dialogService.openSimpleDialog).not.toHaveBeenCalled(); }); - it("opens the autofill confirmation dialog with filtered saved URLs when the feature flag is enabled and search text is present", async () => { - featureFlag$.next(true); - hasSearchText$.next(true); - autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com/path" }); - const openSpy = mockConfirmDialogResult(AutofillConfirmationDialogResult.Canceled); - - await component.doAutofill(); - - expect(openSpy).toHaveBeenCalledTimes(1); - const args = openSpy.mock.calls[0][1]; - expect(args.data.currentUrl).toBe("https://page.example.com/path"); - expect(args.data.savedUrls).toEqual(["https://one.example.com", "https://two.example.com/a"]); - }); - - it("does nothing when the user cancels the autofill confirmation dialog", async () => { - featureFlag$.next(true); - autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); - mockConfirmDialogResult(AutofillConfirmationDialogResult.Canceled); - - await component.doAutofill(); - - expect(autofillSvc.doAutofill).not.toHaveBeenCalled(); - expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); - }); - - it("calls the autofill service to autofill when the user selects 'AutofilledOnly'", async () => { - featureFlag$.next(true); - autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); - mockConfirmDialogResult(AutofillConfirmationDialogResult.AutofilledOnly); - - await component.doAutofill(); - - expect(autofillSvc.doAutofill).toHaveBeenCalledTimes(1); - expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); - }); - - it("calls the autofill service to doAutofillAndSave when the user selects 'AutofillAndUrlAdded'", async () => { - featureFlag$.next(true); - autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); - mockConfirmDialogResult(AutofillConfirmationDialogResult.AutofillAndUrlAdded); - - await component.doAutofill(); - - expect(autofillSvc.doAutofillAndSave).toHaveBeenCalledTimes(1); - expect(autofillSvc.doAutofillAndSave.mock.calls[0][1]).toBe(false); - expect(autofillSvc.doAutofill).not.toHaveBeenCalled(); - }); - - it("shows the exact match dialog when the uri match strategy is Exact and no URIs match", async () => { - featureFlag$.next(true); - uriMatchStrategy$.next(UriMatchStrategy.Exact); - hasSearchText$.next(true); - autofillSvc.currentAutofillTab$.next({ url: "https://no-match.example.com" }); - - await component.doAutofill(); - - expect(dialogService.openSimpleDialog).toHaveBeenCalledTimes(1); - 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("hides the 'Fill and Save' button when showAutofillConfirmation$ is true", async () => { - // Enable both feature flag and search text → makes showAutofillConfirmation$ true - featureFlag$.next(true); - hasSearchText$.next(true); - - fixture.detectChanges(); - await fixture.whenStable(); - - const fillAndSaveButton = fixture.nativeElement.querySelector( - "button[bitMenuItem]:not([disabled])", - ); - - const buttonText = fillAndSaveButton?.textContent?.trim().toLowerCase() ?? ""; - expect(buttonText.includes("fillAndSave".toLowerCase())).toBe(false); - }); - - it("call the passwordService to passwordRepromptCheck if their cipher has password reprompt enabled", async () => { - baseCipher.reprompt = 2; // Master Password reprompt enabled - featureFlag$.next(true); + it("calls the passwordService to passwordRepromptCheck", async () => { autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); mockConfirmDialogResult(AutofillConfirmationDialogResult.AutofilledOnly); @@ -256,7 +171,6 @@ describe("ItemMoreOptionsComponent", () => { it("does nothing if the user fails master password reprompt", async () => { baseCipher.reprompt = 2; // Master Password reprompt enabled - featureFlag$.next(true); autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); passwordRepromptService.passwordRepromptCheck.mockResolvedValue(false); // Reprompt fails mockConfirmDialogResult(AutofillConfirmationDialogResult.AutofilledOnly); @@ -266,5 +180,132 @@ describe("ItemMoreOptionsComponent", () => { expect(autofillSvc.doAutofill).not.toHaveBeenCalled(); expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); }); + + describe("autofill confirmation dialog", () => { + beforeEach(() => { + featureFlag$.next(true); + hasSearchText$.next(true); + passwordRepromptService.passwordRepromptCheck.mockResolvedValue(true); + }); + + it("opens the autofill confirmation dialog with filtered saved URLs when the feature flag is enabled and search text is present", async () => { + autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com/path" }); + const openSpy = mockConfirmDialogResult(AutofillConfirmationDialogResult.Canceled); + + await component.doAutofill(); + + expect(openSpy).toHaveBeenCalledTimes(1); + const args = openSpy.mock.calls[0][1]; + expect(args.data.currentUrl).toBe("https://page.example.com/path"); + expect(args.data.savedUrls).toEqual([ + "https://one.example.com", + "https://two.example.com/a", + ]); + }); + + it("does nothing when the user cancels the autofill confirmation dialog", async () => { + autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); + mockConfirmDialogResult(AutofillConfirmationDialogResult.Canceled); + + await component.doAutofill(); + + expect(autofillSvc.doAutofill).not.toHaveBeenCalled(); + expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); + }); + + it("calls the autofill service to autofill when the user selects 'AutofilledOnly'", async () => { + autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); + mockConfirmDialogResult(AutofillConfirmationDialogResult.AutofilledOnly); + + await component.doAutofill(); + + expect(autofillSvc.doAutofill).toHaveBeenCalledWith( + expect.objectContaining({ id: "cipher-1" }), + true, + true, + ); + expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); + }); + + it("calls the autofill service to doAutofillAndSave when the user selects 'AutofillAndUrlAdded'", async () => { + autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); + mockConfirmDialogResult(AutofillConfirmationDialogResult.AutofillAndUrlAdded); + + await component.doAutofill(); + + expect(autofillSvc.doAutofillAndSave).toHaveBeenCalledWith( + expect.objectContaining({ id: "cipher-1" }), + false, + true, + ); + expect(autofillSvc.doAutofillAndSave.mock.calls[0][1]).toBe(false); + expect(autofillSvc.doAutofill).not.toHaveBeenCalled(); + }); + + describe("URI match strategy handling", () => { + it("shows the exact match dialog when the uri match strategy is Exact", async () => { + uriMatchStrategy$.next(UriMatchStrategy.Exact); + autofillSvc.currentAutofillTab$.next({ url: "https://no-match.example.com" }); + + await component.doAutofill(); + + expect(dialogService.openSimpleDialog).toHaveBeenCalledTimes(1); + 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("shows the exact match dialog and not the password reprompt dialog when the uri match strategy is Exact and the item has master password reprompt enabled", async () => { + uriMatchStrategy$.next(UriMatchStrategy.Exact); + autofillSvc.currentAutofillTab$.next({ url: "https://no-match.example.com" }); + + await component.doAutofill(); + + expect(dialogService.openSimpleDialog).toHaveBeenCalledTimes(1); + expect(dialogService.openSimpleDialog).toHaveBeenCalledWith( + expect.objectContaining({ + title: expect.objectContaining({ key: "cannotAutofill" }), + content: expect.objectContaining({ key: "cannotAutofillExactMatch" }), + type: "info", + }), + ); + expect(autofillSvc.doAutofill).not.toHaveBeenCalled(); + expect(passwordRepromptService.passwordRepromptCheck).not.toHaveBeenCalled(); + expect(autofillSvc.doAutofillAndSave).not.toHaveBeenCalled(); + }); + }); + + it("hides the 'Fill and Save' button when showAutofillConfirmation$ is true", async () => { + // Enable both feature flag and search text → makes showAutofillConfirmation$ true + + fixture.detectChanges(); + await fixture.whenStable(); + + const fillAndSaveButton = fixture.nativeElement.querySelector( + "button[bitMenuItem]:not([disabled])", + ); + + const buttonText = fillAndSaveButton?.textContent?.trim().toLowerCase() ?? ""; + expect(buttonText.includes("fillAndSave".toLowerCase())).toBe(false); + }); + + it("does nothing if the user fails master password reprompt", async () => { + baseCipher.reprompt = 2; // Master Password reprompt enabled + autofillSvc.currentAutofillTab$.next({ url: "https://page.example.com" }); + passwordRepromptService.passwordRepromptCheck.mockResolvedValue(false); // Reprompt fails + mockConfirmDialogResult(AutofillConfirmationDialogResult.AutofilledOnly); + + await component.doAutofill(); + + expect(autofillSvc.doAutofill).not.toHaveBeenCalled(); + expect(autofillSvc.doAutofillAndSave).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 7c8602808bd..6bc9d3aa207 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 @@ -202,17 +202,6 @@ export class ItemMoreOptionsComponent { async doAutofill() { const cipher = await this.cipherService.getFullCipherView(this.cipher); - if (!(await this.passwordRepromptService.passwordRepromptCheck(this.cipher))) { - return; - } - - const showAutofillConfirmation = await firstValueFrom(this.showAutofillConfirmation$); - - if (!showAutofillConfirmation) { - await this.vaultPopupAutofillService.doAutofill(cipher, false); - return; - } - const uriMatchStrategy = await firstValueFrom(this.uriMatchStrategy$); if (uriMatchStrategy === UriMatchStrategy.Exact) { await this.dialogService.openSimpleDialog({ @@ -225,6 +214,17 @@ export class ItemMoreOptionsComponent { return; } + if (!(await this.passwordRepromptService.passwordRepromptCheck(this.cipher))) { + return; + } + + const showAutofillConfirmation = await firstValueFrom(this.showAutofillConfirmation$); + + if (!showAutofillConfirmation) { + await this.vaultPopupAutofillService.doAutofill(cipher, true, true); + return; + } + const currentTab = await firstValueFrom(this.vaultPopupAutofillService.currentAutofillTab$); if (!currentTab?.url) { @@ -250,10 +250,10 @@ export class ItemMoreOptionsComponent { case AutofillConfirmationDialogResult.Canceled: return; case AutofillConfirmationDialogResult.AutofilledOnly: - await this.vaultPopupAutofillService.doAutofill(cipher); + await this.vaultPopupAutofillService.doAutofill(cipher, true, true); return; case AutofillConfirmationDialogResult.AutofillAndUrlAdded: - await this.vaultPopupAutofillService.doAutofillAndSave(cipher, false); + await this.vaultPopupAutofillService.doAutofillAndSave(cipher, false, true); return; } } diff --git a/apps/browser/src/vault/popup/services/vault-popup-autofill.service.spec.ts b/apps/browser/src/vault/popup/services/vault-popup-autofill.service.spec.ts index 718043b4e85..5818c6e32ff 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-autofill.service.spec.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-autofill.service.spec.ts @@ -262,6 +262,18 @@ describe("VaultPopupAutofillService", () => { ); }); + it("skips password prompt when skipPasswordReprompt is true", async () => { + mockCipher.id = "cipher-with-reprompt"; + mockCipher.reprompt = CipherRepromptType.Password; + mockAutofillService.doAutoFill.mockResolvedValue(null); + + const result = await service.doAutofill(mockCipher, true, true); + + expect(result).toBe(true); + expect(mockPasswordRepromptService.showPasswordPrompt).not.toHaveBeenCalled(); + expect(mockAutofillService.doAutoFill).toHaveBeenCalled(); + }); + describe("closePopup", () => { beforeEach(() => { jest.spyOn(BrowserApi, "closePopup").mockImplementation(); diff --git a/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts b/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts index 3d5b35cded6..6feeec29efc 100644 --- a/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts +++ b/apps/browser/src/vault/popup/services/vault-popup-autofill.service.ts @@ -231,8 +231,10 @@ export class VaultPopupAutofillService { cipher: CipherView, tab: chrome.tabs.Tab, pageDetails: PageDetail[], + skipPasswordReprompt = false, ): Promise { if ( + !skipPasswordReprompt && cipher.reprompt !== CipherRepromptType.None && !(await this.passwordRepromptService.showPasswordPrompt()) ) { @@ -314,12 +316,22 @@ export class VaultPopupAutofillService { * Will copy any TOTP code to the clipboard if available after successful autofill. * @param cipher * @param closePopup If true, will close the popup window after successful autofill. Defaults to true. + * @param skipPasswordReprompt If true, skips the master password reprompt even if the cipher requires it. Defaults to false. */ - async doAutofill(cipher: CipherView, closePopup = true): Promise { + async doAutofill( + cipher: CipherView, + closePopup = true, + skipPasswordReprompt = false, + ): Promise { const tab = await firstValueFrom(this.currentAutofillTab$); const pageDetails = await firstValueFrom(this._currentPageDetails$); - const didAutofill = await this._internalDoAutofill(cipher, tab, pageDetails); + const didAutofill = await this._internalDoAutofill( + cipher, + tab, + pageDetails, + skipPasswordReprompt, + ); if (didAutofill && closePopup) { await this._closePopup(cipher, tab); @@ -350,7 +362,11 @@ export class VaultPopupAutofillService { * @param closePopup If true, will close the popup window after successful autofill. * If false, will show a success toast instead. Defaults to true. */ - async doAutofillAndSave(cipher: CipherView, closePopup = true): Promise { + async doAutofillAndSave( + cipher: CipherView, + closePopup = true, + skipPasswordReprompt = false, + ): Promise { // We can only save URIs for login ciphers if (cipher.type !== CipherType.Login) { return false; @@ -359,7 +375,12 @@ export class VaultPopupAutofillService { const pageDetails = await firstValueFrom(this._currentPageDetails$); const tab = await firstValueFrom(this.currentAutofillTab$); - const didAutofill = await this._internalDoAutofill(cipher, tab, pageDetails); + const didAutofill = await this._internalDoAutofill( + cipher, + tab, + pageDetails, + skipPasswordReprompt, + ); if (!didAutofill) { return false; From 326cd406284a1348ac854059ea7f9da1af313a8a Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:44:49 -0500 Subject: [PATCH 33/66] cipher.id now defaults to an empty string rather than null/undefined - use bang to transform into a boolean (#17137) --- apps/desktop/src/vault/app/vault/vault-v2.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 19c9cffeeb2..bdade04bacd 100644 --- a/apps/desktop/src/vault/app/vault/vault-v2.component.ts +++ b/apps/desktop/src/vault/app/vault/vault-v2.component.ts @@ -792,7 +792,7 @@ export class VaultV2Component async cancelCipher(cipher: CipherView) { this.cipherId = cipher.id; this.cipher = cipher; - this.action = this.cipherId != null ? "view" : null; + this.action = this.cipherId ? "view" : null; await this.go().catch(() => {}); } From 2dd314e992c8b7cb587bb524b6e1835101dc454d Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Thu, 30 Oct 2025 22:10:01 +0100 Subject: [PATCH 34/66] [PM-27083] Prevent collection nesting on import into a MyItems-collection (#16937) * Prevent collection nesting on import into a my items collection My Items collections do not support nested collections. The import source hierarchy needs to be flattened into the My Items collection * Introduce new types for folder and collection relationship Makes it easier to identify which position is for the cipherIndex and which is for the folder-/collection-index * Fix assignment of ciphers to My items collection * Remove unneeded type cast or assertions * Add clarifying comment --------- Co-authored-by: Daniel James Smith --- libs/importer/src/models/import-result.ts | 7 ++- .../src/services/import.service.spec.ts | 43 ++++++++++++++++--- libs/importer/src/services/import.service.ts | 36 +++++++++++----- 3 files changed, 69 insertions(+), 17 deletions(-) diff --git a/libs/importer/src/models/import-result.ts b/libs/importer/src/models/import-result.ts index 9d94b410e7b..b99068ff83f 100644 --- a/libs/importer/src/models/import-result.ts +++ b/libs/importer/src/models/import-result.ts @@ -6,12 +6,15 @@ import { CollectionView } from "@bitwarden/admin-console/common"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; +export type FolderRelationship = [cipherIndex: number, folderIndex: number]; +export type CollectionRelationship = [cipherIndex: number, collectionIndex: number]; + export class ImportResult { success = false; errorMessage: string; ciphers: CipherView[] = []; folders: FolderView[] = []; - folderRelationships: [number, number][] = []; + folderRelationships: FolderRelationship[] = []; collections: CollectionView[] = []; - collectionRelationships: [number, number][] = []; + collectionRelationships: CollectionRelationship[] = []; } diff --git a/libs/importer/src/services/import.service.spec.ts b/libs/importer/src/services/import.service.spec.ts index fd710056e80..b1c028ff063 100644 --- a/libs/importer/src/services/import.service.spec.ts +++ b/libs/importer/src/services/import.service.spec.ts @@ -2,7 +2,11 @@ import { mock, MockProxy } from "jest-mock-extended"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports -import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { + CollectionService, + CollectionTypes, + CollectionView, +} from "@bitwarden/admin-console/common"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction"; @@ -194,7 +198,7 @@ describe("ImportService", () => { ); }); - it("passing importTarget as null on setImportTarget with organizationId throws error", async () => { + it("passing importTarget as undefined on setImportTarget with organizationId throws error", async () => { const setImportTargetMethod = importService["setImportTarget"]( null, organizationId, @@ -204,10 +208,10 @@ describe("ImportService", () => { await expect(setImportTargetMethod).rejects.toThrow(); }); - it("passing importTarget as null on setImportTarget throws error", async () => { + it("passing importTarget as undefined on setImportTarget throws error", async () => { const setImportTargetMethod = importService["setImportTarget"]( null, - "", + undefined, new Object() as CollectionView, ); @@ -239,11 +243,40 @@ describe("ImportService", () => { importResult.ciphers.push(createCipher({ name: "cipher2" })); importResult.folderRelationships.push([0, 0]); - await importService["setImportTarget"](importResult, "", mockImportTargetFolder); + await importService["setImportTarget"](importResult, undefined, mockImportTargetFolder); expect(importResult.folderRelationships.length).toEqual(2); expect(importResult.folderRelationships[0]).toEqual([1, 0]); expect(importResult.folderRelationships[1]).toEqual([0, 1]); }); + + it("If importTarget is of type DefaultUserCollection sets it as new root for all ciphers as nesting is not supported", async () => { + importResult.collections.push(mockCollection1); + importResult.collections.push(mockCollection2); + importResult.ciphers.push(createCipher({ name: "cipher1" })); + importResult.ciphers.push(createCipher({ name: "cipher2" })); + importResult.ciphers.push(createCipher({ name: "cipher3" })); + + importResult.collectionRelationships.push([0, 0]); + importResult.collectionRelationships.push([1, 1]); + importResult.collectionRelationships.push([2, 0]); + + mockImportTargetCollection.type = CollectionTypes.DefaultUserCollection; + await importService["setImportTarget"]( + importResult, + organizationId, + mockImportTargetCollection, + ); + expect(importResult.collections.length).toBe(1); + expect(importResult.collections[0]).toBe(mockImportTargetCollection); + + expect(importResult.collectionRelationships.length).toEqual(3); + expect(importResult.collectionRelationships[0]).toEqual([0, 0]); + expect(importResult.collectionRelationships[1]).toEqual([1, 0]); + expect(importResult.collectionRelationships[2]).toEqual([2, 0]); + + expect(importResult.collectionRelationships.map((r) => r[0])).toEqual([0, 1, 2]); + expect(importResult.collectionRelationships.every((r) => r[1] === 0)).toBe(true); + }); }); }); diff --git a/libs/importer/src/services/import.service.ts b/libs/importer/src/services/import.service.ts index 3efe327e319..f62054f9414 100644 --- a/libs/importer/src/services/import.service.ts +++ b/libs/importer/src/services/import.service.ts @@ -8,6 +8,7 @@ import { CollectionService, CollectionWithIdRequest, CollectionView, + CollectionTypes, } from "@bitwarden/admin-console/common"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; @@ -101,7 +102,7 @@ import { ImportType, regularImportOptions, } from "../models/import-options"; -import { ImportResult } from "../models/import-result"; +import { CollectionRelationship, FolderRelationship, ImportResult } from "../models/import-result"; import { ImportApiServiceAbstraction } from "../services/import-api.service.abstraction"; import { ImportServiceAbstraction } from "../services/import.service.abstraction"; @@ -473,19 +474,20 @@ export class ImportService implements ImportServiceAbstraction { private async setImportTarget( importResult: ImportResult, - organizationId: string, + organizationId: OrganizationId | undefined, importTarget: FolderView | CollectionView, ) { if (!importTarget) { return; } + // Importing into an organization if (organizationId) { if (!(importTarget instanceof CollectionView)) { throw new Error(this.i18nService.t("errorAssigningTargetCollection")); } - const noCollectionRelationShips: [number, number][] = []; + const noCollectionRelationShips: CollectionRelationship[] = []; importResult.ciphers.forEach((c, index) => { if ( !Array.isArray(importResult.collectionRelationships) || @@ -495,15 +497,28 @@ export class ImportService implements ImportServiceAbstraction { } }); - const collections: CollectionView[] = [...importResult.collections]; - importResult.collections = [importTarget as CollectionView]; + // My Items collections do not support collection nesting. + // Flatten all ciphers from nested collections into the import target. + if (importTarget.type === CollectionTypes.DefaultUserCollection) { + importResult.collections = [importTarget]; + + const flattenRelationships: CollectionRelationship[] = []; + importResult.ciphers.forEach((c, index) => { + flattenRelationships.push([index, 0]); + }); + importResult.collectionRelationships = flattenRelationships; + return; + } + + const collections = [...importResult.collections]; + importResult.collections = [importTarget]; collections.map((x) => { const f = new CollectionView(x); f.name = `${importTarget.name}/${x.name}`; importResult.collections.push(f); }); - const relationships: [number, number][] = [...importResult.collectionRelationships]; + const relationships = [...importResult.collectionRelationships]; importResult.collectionRelationships = [...noCollectionRelationShips]; relationships.map((x) => { importResult.collectionRelationships.push([x[0], x[1] + 1]); @@ -512,11 +527,12 @@ export class ImportService implements ImportServiceAbstraction { return; } + // Importing into personal vault if (!(importTarget instanceof FolderView)) { throw new Error(this.i18nService.t("errorAssigningTargetFolder")); } - const noFolderRelationShips: [number, number][] = []; + const noFolderRelationShips: FolderRelationship[] = []; importResult.ciphers.forEach((c, index) => { if (Utils.isNullOrEmpty(c.folderId)) { c.folderId = importTarget.id; @@ -524,8 +540,8 @@ export class ImportService implements ImportServiceAbstraction { } }); - const folders: FolderView[] = [...importResult.folders]; - importResult.folders = [importTarget as FolderView]; + const folders = [...importResult.folders]; + importResult.folders = [importTarget]; folders.map((x) => { const newFolderName = `${importTarget.name}/${x.name}`; const f = new FolderView(); @@ -533,7 +549,7 @@ export class ImportService implements ImportServiceAbstraction { importResult.folders.push(f); }); - const relationships: [number, number][] = [...importResult.folderRelationships]; + const relationships = [...importResult.folderRelationships]; importResult.folderRelationships = [...noFolderRelationShips]; relationships.map((x) => { importResult.folderRelationships.push([x[0], x[1] + 1]); From 48fb8b2bfe53d4713739255b1eb7cb8ec814057a Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 31 Oct 2025 08:12:44 +0100 Subject: [PATCH 35/66] [PM-25250] Prevent configuration and access of self hosted urls over http (#17095) * feat: ban urls not using https * feat: add exception for dev env * feat: block fetching of insecure URLs * feat: add exception for dev env * feat: block notifications from using insecure URL * fix: bug where submission was possible regardless of error * feat: add exception for dev env * fix: missing constructor param --- apps/browser/src/_locales/en/messages.json | 3 ++ .../browser/src/background/main.background.ts | 2 +- apps/desktop/src/locales/en/messages.json | 3 ++ apps/web/src/locales/en/messages.json | 3 ++ .../src/services/jslib-services.module.ts | 4 +- ...self-hosted-env-config-dialog.component.ts | 38 +++++++++++++++---- .../auth/services/anonymous-hub.service.ts | 6 +++ .../internal/signalr-connection.service.ts | 7 ++++ libs/common/src/services/api-errors.ts | 9 +++++ libs/common/src/services/api.service.spec.ts | 36 ++++++++++++++++++ libs/common/src/services/api.service.ts | 6 +++ 11 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 libs/common/src/services/api-errors.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index a7cee53e08b..50c629e87f6 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1641,6 +1641,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Custom environment" }, diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 8170c2a65a0..561ad5e9c9e 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -1215,7 +1215,7 @@ export default class MainBackground { logoutCallback, this.messagingService, this.accountService, - new SignalRConnectionService(this.apiService, this.logService), + new SignalRConnectionService(this.apiService, this.logService, this.platformUtilsService), this.authService, this.webPushConnectionService, this.authRequestAnsweringService, diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 605fefb03f8..70242b6674f 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -1035,6 +1035,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "customEnvironment": { "message": "Custom environment" }, diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 7dd0d99a4eb..2e0fd99bbcc 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -7132,6 +7132,9 @@ "selfHostedEnvFormInvalid": { "message": "You must add either the base Server URL or at least one custom environment." }, + "selfHostedEnvMustUseHttps": { + "message": "URLs must use HTTPS." + }, "apiUrl": { "message": "API server URL" }, diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 94b9f6240a4..853877da810 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -968,7 +968,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: SignalRConnectionService, useClass: SignalRConnectionService, - deps: [ApiServiceAbstraction, LogService], + deps: [ApiServiceAbstraction, LogService, PlatformUtilsServiceAbstraction], }), safeProvider({ provide: WebPushConnectionService, @@ -1223,7 +1223,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: AnonymousHubServiceAbstraction, useClass: AnonymousHubService, - deps: [EnvironmentService, AuthRequestServiceAbstraction], + deps: [EnvironmentService, AuthRequestServiceAbstraction, PlatformUtilsServiceAbstraction], }), safeProvider({ provide: ValidationServiceAbstraction, diff --git a/libs/auth/src/angular/self-hosted-env-config-dialog/self-hosted-env-config-dialog.component.ts b/libs/auth/src/angular/self-hosted-env-config-dialog/self-hosted-env-config-dialog.component.ts index 40fdfb8c17c..6fb40179afa 100644 --- a/libs/auth/src/angular/self-hosted-env-config-dialog/self-hosted-env-config-dialog.component.ts +++ b/libs/auth/src/angular/self-hosted-env-config-dialog/self-hosted-env-config-dialog.component.ts @@ -1,5 +1,5 @@ import { CommonModule } from "@angular/common"; -import { Component, OnDestroy, OnInit } from "@angular/core"; +import { Component, inject, OnDestroy, OnInit } from "@angular/core"; import { AbstractControl, FormBuilder, @@ -16,6 +16,8 @@ import { EnvironmentService, Region, } 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"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports import { @@ -51,6 +53,25 @@ function selfHostedEnvSettingsFormValidator(): ValidatorFn { }; } +function onlyHttpsValidator(): ValidatorFn { + const i18nService = inject(I18nService); + const platformUtilsService = inject(PlatformUtilsService); + + return (control: AbstractControl): ValidationErrors | null => { + const url = control.value as string; + + if (url && !url.startsWith("https://") && !platformUtilsService.isDev()) { + return { + onlyHttpsAllowed: { + message: i18nService.t("selfHostedEnvMustUseHttps"), + }, + }; // invalid + } + + return null; // valid + }; +} + /** * Dialog for configuring self-hosted environment settings. */ @@ -89,12 +110,12 @@ export class SelfHostedEnvConfigDialogComponent implements OnInit, OnDestroy { formGroup = this.formBuilder.group( { - baseUrl: [""], - webVaultUrl: [""], - apiUrl: [""], - identityUrl: [""], - iconsUrl: [""], - notificationsUrl: [""], + baseUrl: ["", [onlyHttpsValidator()]], + webVaultUrl: ["", [onlyHttpsValidator()]], + apiUrl: ["", [onlyHttpsValidator()]], + identityUrl: ["", [onlyHttpsValidator()]], + iconsUrl: ["", [onlyHttpsValidator()]], + notificationsUrl: ["", [onlyHttpsValidator()]], }, { validators: selfHostedEnvSettingsFormValidator() }, ); @@ -162,10 +183,11 @@ export class SelfHostedEnvConfigDialogComponent implements OnInit, OnDestroy { }); } submit = async () => { + this.formGroup.markAllAsTouched(); this.showErrorSummary = false; if (this.formGroup.invalid) { - this.showErrorSummary = true; + this.showErrorSummary = Boolean(this.formGroup.errors?.["atLeastOneUrlIsRequired"]); return; } diff --git a/libs/common/src/auth/services/anonymous-hub.service.ts b/libs/common/src/auth/services/anonymous-hub.service.ts index 3900dd53ee0..561cddb5372 100644 --- a/libs/common/src/auth/services/anonymous-hub.service.ts +++ b/libs/common/src/auth/services/anonymous-hub.service.ts @@ -18,6 +18,8 @@ import { NotificationResponse, } from "../../models/response/notification.response"; import { EnvironmentService } from "../../platform/abstractions/environment.service"; +import { PlatformUtilsService } from "../../platform/abstractions/platform-utils.service"; +import { InsecureUrlNotAllowedError } from "../../services/api-errors"; import { AnonymousHubService as AnonymousHubServiceAbstraction } from "../abstractions/anonymous-hub.service"; export class AnonymousHubService implements AnonymousHubServiceAbstraction { @@ -27,10 +29,14 @@ export class AnonymousHubService implements AnonymousHubServiceAbstraction { constructor( private environmentService: EnvironmentService, private authRequestService: AuthRequestServiceAbstraction, + private platformUtilsService: PlatformUtilsService, ) {} async createHubConnection(token: string) { this.url = (await firstValueFrom(this.environmentService.environment$)).getNotificationsUrl(); + if (!this.url.startsWith("https://") && !this.platformUtilsService.isDev()) { + throw new InsecureUrlNotAllowedError(); + } this.anonHubConnection = new HubConnectionBuilder() .withUrl(this.url + "/anonymous-hub?Token=" + token, { diff --git a/libs/common/src/platform/server-notifications/internal/signalr-connection.service.ts b/libs/common/src/platform/server-notifications/internal/signalr-connection.service.ts index 5998668f138..2a9e7fc7141 100644 --- a/libs/common/src/platform/server-notifications/internal/signalr-connection.service.ts +++ b/libs/common/src/platform/server-notifications/internal/signalr-connection.service.ts @@ -10,8 +10,10 @@ import { Observable, Subscription } from "rxjs"; import { ApiService } from "../../../abstractions/api.service"; import { NotificationResponse } from "../../../models/response/notification.response"; +import { InsecureUrlNotAllowedError } from "../../../services/api-errors"; import { UserId } from "../../../types/guid"; import { LogService } from "../../abstractions/log.service"; +import { PlatformUtilsService } from "../../abstractions/platform-utils.service"; // 2 Minutes const MIN_RECONNECT_TIME = 2 * 60 * 1000; @@ -69,12 +71,17 @@ export class SignalRConnectionService { constructor( private readonly apiService: ApiService, private readonly logService: LogService, + private readonly platformUtilsService: PlatformUtilsService, private readonly hubConnectionBuilderFactory: () => HubConnectionBuilder = () => new HubConnectionBuilder(), private readonly timeoutManager: TimeoutManager = globalThis, ) {} connect$(userId: UserId, notificationsUrl: string) { + if (!notificationsUrl.startsWith("https://") && !this.platformUtilsService.isDev()) { + throw new InsecureUrlNotAllowedError(); + } + return new Observable((subsciber) => { const connection = this.hubConnectionBuilderFactory() .withUrl(notificationsUrl + "/hub", { diff --git a/libs/common/src/services/api-errors.ts b/libs/common/src/services/api-errors.ts new file mode 100644 index 00000000000..6dc9c8fce6d --- /dev/null +++ b/libs/common/src/services/api-errors.ts @@ -0,0 +1,9 @@ +export class InsecureUrlNotAllowedError extends Error { + constructor(url?: string) { + if (url === undefined) { + super("Insecure URL not allowed. All URLs must use HTTPS."); + } else { + super(`Insecure URL not allowed: ${url}. All URLs must use HTTPS.`); + } + } +} diff --git a/libs/common/src/services/api.service.spec.ts b/libs/common/src/services/api.service.spec.ts index 6d6e96de9e3..1fb8f86697f 100644 --- a/libs/common/src/services/api.service.spec.ts +++ b/libs/common/src/services/api.service.spec.ts @@ -20,6 +20,7 @@ import { Environment, EnvironmentService } from "../platform/abstractions/enviro import { LogService } from "../platform/abstractions/log.service"; import { PlatformUtilsService } from "../platform/abstractions/platform-utils.service"; +import { InsecureUrlNotAllowedError } from "./api-errors"; import { ApiService, HttpOperations } from "./api.service"; describe("ApiService", () => { @@ -411,4 +412,39 @@ describe("ApiService", () => { ).rejects.toMatchObject(error); }, ); + + it("throws error when trying to fetch an insecure URL", async () => { + environmentService.getEnvironment$.calledWith(testActiveUser).mockReturnValue( + of({ + getApiUrl: () => "http://example.com", + } satisfies Partial as Environment), + ); + + httpOperations.createRequest.mockImplementation((url, request) => { + return { + url: url, + cache: request.cache, + credentials: request.credentials, + method: request.method, + mode: request.mode, + signal: request.signal ?? undefined, + headers: new Headers(request.headers), + } satisfies Partial as unknown as Request; + }); + + const nativeFetch = jest.fn, [request: Request]>(); + nativeFetch.mockImplementation((request) => { + return Promise.resolve({ + ok: true, + status: 204, + headers: new Headers(), + } satisfies Partial as Response); + }); + sut.nativeFetch = nativeFetch; + + await expect( + async () => await sut.send("GET", "/something", null, true, true, null), + ).rejects.toThrow(InsecureUrlNotAllowedError); + expect(nativeFetch).not.toHaveBeenCalled(); + }); }); diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index b7f5f0ed001..8314e44e75f 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -117,6 +117,8 @@ import { AttachmentResponse } from "../vault/models/response/attachment.response import { CipherResponse } from "../vault/models/response/cipher.response"; import { OptionalCipherResponse } from "../vault/models/response/optional-cipher.response"; +import { InsecureUrlNotAllowedError } from "./api-errors"; + export type HttpOperations = { createRequest: (url: string, request: RequestInit) => Request; }; @@ -1310,6 +1312,10 @@ export class ApiService implements ApiServiceAbstraction { } async fetch(request: Request): Promise { + if (!request.url.startsWith("https://") && !this.platformUtilsService.isDev()) { + throw new InsecureUrlNotAllowedError(); + } + if (request.method === "GET") { request.headers.set("Cache-Control", "no-store"); request.headers.set("Pragma", "no-cache"); From 1264abfb19bc8a5322a03464d3c0b931338e70a9 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 31 Oct 2025 08:13:36 +0100 Subject: [PATCH 36/66] [PM-25569] Expose commercial sdk service to web clients (#16899) * feat: enable commercial SDK usage on web * feat: enable commercial SDK usage in cli * feat: enable commercial SDK usage in browser * feat: add lint to restrict commercial sdk imports --- bitwarden_license/bit-browser/tsconfig.json | 4 +++- bitwarden_license/bit-cli/tsconfig.json | 6 +++++- .../bit-common/src/platform/sdk/sdk-alias.d.ts | 7 +++++++ bitwarden_license/bit-web/tsconfig.build.json | 2 ++ bitwarden_license/bit-web/tsconfig.json | 2 ++ eslint.config.mjs | 11 +++++++++++ 6 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 bitwarden_license/bit-common/src/platform/sdk/sdk-alias.d.ts diff --git a/bitwarden_license/bit-browser/tsconfig.json b/bitwarden_license/bit-browser/tsconfig.json index c3420e280b7..68c52f9d3c6 100644 --- a/bitwarden_license/bit-browser/tsconfig.json +++ b/bitwarden_license/bit-browser/tsconfig.json @@ -15,6 +15,8 @@ "../../apps/browser/src/vault/content/*.ts", "../../libs/common/src/autofill/constants", - "../../libs/common/custom-matchers.d.ts" + "../../libs/common/custom-matchers.d.ts", + + "../../bitwarden_license/bit-common/src/platform/sdk/sdk-alias.d.ts" ] } diff --git a/bitwarden_license/bit-cli/tsconfig.json b/bitwarden_license/bit-cli/tsconfig.json index 6630021232f..68608936abc 100644 --- a/bitwarden_license/bit-cli/tsconfig.json +++ b/bitwarden_license/bit-cli/tsconfig.json @@ -1,3 +1,7 @@ { - "extends": "../../apps/cli/tsconfig" + "extends": "../../apps/cli/tsconfig", + "files": [ + "../../bitwarden_license/bit-common/src/platform/sdk/sdk-alias.d.ts", + "../../bitwarden_license/bit-cli/src/bw.ts" + ] } diff --git a/bitwarden_license/bit-common/src/platform/sdk/sdk-alias.d.ts b/bitwarden_license/bit-common/src/platform/sdk/sdk-alias.d.ts new file mode 100644 index 00000000000..0453c01368c --- /dev/null +++ b/bitwarden_license/bit-common/src/platform/sdk/sdk-alias.d.ts @@ -0,0 +1,7 @@ +declare module "@bitwarden/sdk-internal" { + // In `bit-*` clients the commercial sdk replaces the regular internal sdk, + // this file creates an alias so that typescript understands that. + // The actual replacement is done in the build system via webpack's resolve.alias. + // eslint-disable-next-line no-restricted-imports + export * from "@bitwarden/commercial-sdk-internal"; +} diff --git a/bitwarden_license/bit-web/tsconfig.build.json b/bitwarden_license/bit-web/tsconfig.build.json index 66c475051ed..58acbf09392 100644 --- a/bitwarden_license/bit-web/tsconfig.build.json +++ b/bitwarden_license/bit-web/tsconfig.build.json @@ -1,6 +1,8 @@ { "extends": "./tsconfig", "files": [ + "../../bitwarden_license/bit-common/src/platform/sdk/sdk-alias.d.ts", + "../../apps/web/src/polyfills.ts", "../../apps/web/src/main.ts", "../../apps/web/src/theme.ts", diff --git a/bitwarden_license/bit-web/tsconfig.json b/bitwarden_license/bit-web/tsconfig.json index 0836d3d54ad..8c19f771a26 100644 --- a/bitwarden_license/bit-web/tsconfig.json +++ b/bitwarden_license/bit-web/tsconfig.json @@ -1,6 +1,8 @@ { "extends": "../../apps/web/tsconfig", "files": [ + "../../bitwarden_license/bit-common/src/platform/sdk/sdk-alias.d.ts", + "../../apps/web/src/polyfills.ts", "../../apps/web/src/main.ts", "../../apps/web/src/theme.ts", diff --git a/eslint.config.mjs b/eslint.config.mjs index 656972d2421..6c362a4dc43 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -160,6 +160,11 @@ export default tseslint.config( // allow module index import except: ["**/state/index.ts"], }, + { + target: ["libs/**/*"], + from: ["apps/**/*"], + message: "Libs should not import app-specific code.", + }, ], }, ], @@ -688,6 +693,12 @@ function buildNoRestrictedImports(additionalForbiddenPatterns = [], skipPlatform return [ "error", { + paths: [ + { + name: "@bitwarden/commercial-sdk-internal", + message: "Use @bitwarden/sdk-internal instead.", + }, + ], patterns: [ ...(skipPlatform ? [] : ["**/platform/**/internal", "**/platform/messaging/**"]), "**/src/**/*", // Prevent relative imports across libs. From 8aebfbeace040612f272fb345b016a1274597cf8 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Fri, 31 Oct 2025 08:15:04 +0100 Subject: [PATCH 37/66] [PM-25932] Shorten socket paths (#17093) * feat: shorten socket paths * fix: No such file or directory error * feat: remove tmp folder from path * fix: No such file or directory autofill error --- apps/desktop/desktop_native/core/src/ipc/mod.rs | 10 +++++----- apps/desktop/desktop_native/macos_provider/src/lib.rs | 2 +- apps/desktop/desktop_native/proxy/src/main.rs | 2 +- apps/desktop/src/main/native-messaging.main.ts | 2 +- .../src/platform/main/autofill/native-autofill.main.ts | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/desktop/desktop_native/core/src/ipc/mod.rs b/apps/desktop/desktop_native/core/src/ipc/mod.rs index 41215b3a0ee..5d4cc9e27f7 100644 --- a/apps/desktop/desktop_native/core/src/ipc/mod.rs +++ b/apps/desktop/desktop_native/core/src/ipc/mod.rs @@ -35,7 +35,7 @@ fn internal_ipc_codec(inner: T) -> Framed std::path::PathBuf { #[cfg(target_os = "windows")] { - // Use a unique IPC pipe //./pipe/xxxxxxxxxxxxxxxxx.app.bitwarden per user. + // Use a unique IPC pipe //./pipe/xxxxxxxxxxxxxxxxx.s.bw per user (s for socket). // Hashing prevents problems with reserved characters and file length limitations. use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use sha2::Digest; @@ -43,7 +43,7 @@ pub fn path(name: &str) -> std::path::PathBuf { let hash = sha2::Sha256::digest(home.as_os_str().as_encoded_bytes()); let hash_b64 = URL_SAFE_NO_PAD.encode(hash.as_slice()); - format!(r"\\.\pipe\{hash_b64}.app.{name}").into() + format!(r"\\.\pipe\{hash_b64}.s.{name}").into() } #[cfg(target_os = "macos")] @@ -65,11 +65,11 @@ pub fn path(name: &str) -> std::path::PathBuf { home.pop(); } - let tmp = home.join("Library/Group Containers/LTZ2PFU5D6.com.bitwarden.desktop/tmp"); + let tmp = home.join("Library/Group Containers/LTZ2PFU5D6.com.bitwarden.desktop"); // The tmp directory might not exist, so create it let _ = std::fs::create_dir_all(&tmp); - return tmp.join(format!("app.{name}")); + return tmp.join(format!("s.{name}")); } } @@ -81,6 +81,6 @@ pub fn path(name: &str) -> std::path::PathBuf { // The cache directory might not exist, so create it let _ = std::fs::create_dir_all(&path_dir); - path_dir.join(format!("app.{name}")) + path_dir.join(format!("s.{name}")) } } diff --git a/apps/desktop/desktop_native/macos_provider/src/lib.rs b/apps/desktop/desktop_native/macos_provider/src/lib.rs index 789a56d3048..359fe213996 100644 --- a/apps/desktop/desktop_native/macos_provider/src/lib.rs +++ b/apps/desktop/desktop_native/macos_provider/src/lib.rs @@ -96,7 +96,7 @@ impl MacOSProviderClient { response_callbacks_queue: Arc::new(Mutex::new(HashMap::new())), }; - let path = desktop_core::ipc::path("autofill"); + let path = desktop_core::ipc::path("af"); let queue = client.response_callbacks_queue.clone(); diff --git a/apps/desktop/desktop_native/proxy/src/main.rs b/apps/desktop/desktop_native/proxy/src/main.rs index bad3a9deb54..c2c525b865a 100644 --- a/apps/desktop/desktop_native/proxy/src/main.rs +++ b/apps/desktop/desktop_native/proxy/src/main.rs @@ -68,7 +68,7 @@ async fn main() { #[cfg(target_os = "windows")] let should_foreground = windows::allow_foreground(); - let sock_path = desktop_core::ipc::path("bitwarden"); + let sock_path = desktop_core::ipc::path("bw"); let log_path = { let mut path = sock_path.clone(); diff --git a/apps/desktop/src/main/native-messaging.main.ts b/apps/desktop/src/main/native-messaging.main.ts index 93525164ff5..ba5d8616752 100644 --- a/apps/desktop/src/main/native-messaging.main.ts +++ b/apps/desktop/src/main/native-messaging.main.ts @@ -78,7 +78,7 @@ export class NativeMessagingMain { this.ipcServer.stop(); } - this.ipcServer = await ipc.IpcServer.listen("bitwarden", (error, msg) => { + this.ipcServer = await ipc.IpcServer.listen("bw", (error, msg) => { switch (msg.kind) { case ipc.IpcMessageType.Connected: { this.connected.push(msg.clientId); diff --git a/apps/desktop/src/platform/main/autofill/native-autofill.main.ts b/apps/desktop/src/platform/main/autofill/native-autofill.main.ts index f66eea180cf..71cfcab84ba 100644 --- a/apps/desktop/src/platform/main/autofill/native-autofill.main.ts +++ b/apps/desktop/src/platform/main/autofill/native-autofill.main.ts @@ -35,7 +35,7 @@ export class NativeAutofillMain { ); this.ipcServer = await autofill.IpcServer.listen( - "autofill", + "af", // RegistrationCallback (error, clientId, sequenceNumber, request) => { if (error) { From 9d2b2d18942261c0fe9c6202558f605c90730e4f Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:57:01 +0100 Subject: [PATCH 38/66] Autosync the updated translations (#17144) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/af/messages.json | 9 ++ apps/desktop/src/locales/ar/messages.json | 9 ++ apps/desktop/src/locales/az/messages.json | 9 ++ apps/desktop/src/locales/be/messages.json | 9 ++ apps/desktop/src/locales/bg/messages.json | 9 ++ apps/desktop/src/locales/bn/messages.json | 9 ++ apps/desktop/src/locales/bs/messages.json | 9 ++ apps/desktop/src/locales/ca/messages.json | 9 ++ apps/desktop/src/locales/cs/messages.json | 9 ++ apps/desktop/src/locales/cy/messages.json | 9 ++ apps/desktop/src/locales/da/messages.json | 9 ++ apps/desktop/src/locales/de/messages.json | 9 ++ apps/desktop/src/locales/el/messages.json | 9 ++ apps/desktop/src/locales/en_GB/messages.json | 9 ++ apps/desktop/src/locales/en_IN/messages.json | 9 ++ apps/desktop/src/locales/eo/messages.json | 9 ++ apps/desktop/src/locales/es/messages.json | 9 ++ apps/desktop/src/locales/et/messages.json | 9 ++ apps/desktop/src/locales/eu/messages.json | 9 ++ apps/desktop/src/locales/fa/messages.json | 9 ++ apps/desktop/src/locales/fi/messages.json | 9 ++ apps/desktop/src/locales/fil/messages.json | 9 ++ apps/desktop/src/locales/fr/messages.json | 31 ++++--- apps/desktop/src/locales/gl/messages.json | 9 ++ apps/desktop/src/locales/he/messages.json | 9 ++ apps/desktop/src/locales/hi/messages.json | 9 ++ apps/desktop/src/locales/hr/messages.json | 9 ++ apps/desktop/src/locales/hu/messages.json | 9 ++ apps/desktop/src/locales/id/messages.json | 9 ++ apps/desktop/src/locales/it/messages.json | 9 ++ apps/desktop/src/locales/ja/messages.json | 9 ++ apps/desktop/src/locales/ka/messages.json | 9 ++ apps/desktop/src/locales/km/messages.json | 9 ++ apps/desktop/src/locales/kn/messages.json | 9 ++ apps/desktop/src/locales/ko/messages.json | 9 ++ apps/desktop/src/locales/lt/messages.json | 9 ++ apps/desktop/src/locales/lv/messages.json | 17 +++- apps/desktop/src/locales/me/messages.json | 9 ++ apps/desktop/src/locales/ml/messages.json | 9 ++ apps/desktop/src/locales/mr/messages.json | 9 ++ apps/desktop/src/locales/my/messages.json | 9 ++ apps/desktop/src/locales/nb/messages.json | 9 ++ apps/desktop/src/locales/ne/messages.json | 9 ++ apps/desktop/src/locales/nl/messages.json | 9 ++ apps/desktop/src/locales/nn/messages.json | 9 ++ apps/desktop/src/locales/or/messages.json | 9 ++ apps/desktop/src/locales/pl/messages.json | 15 +++- apps/desktop/src/locales/pt_BR/messages.json | 9 ++ apps/desktop/src/locales/pt_PT/messages.json | 11 ++- apps/desktop/src/locales/ro/messages.json | 9 ++ apps/desktop/src/locales/ru/messages.json | 21 +++-- apps/desktop/src/locales/si/messages.json | 9 ++ apps/desktop/src/locales/sk/messages.json | 89 +++++++++++--------- apps/desktop/src/locales/sl/messages.json | 9 ++ apps/desktop/src/locales/sr/messages.json | 9 ++ apps/desktop/src/locales/sv/messages.json | 9 ++ apps/desktop/src/locales/ta/messages.json | 9 ++ apps/desktop/src/locales/te/messages.json | 9 ++ apps/desktop/src/locales/th/messages.json | 9 ++ apps/desktop/src/locales/tr/messages.json | 9 ++ apps/desktop/src/locales/uk/messages.json | 9 ++ apps/desktop/src/locales/vi/messages.json | 17 +++- apps/desktop/src/locales/zh_CN/messages.json | 31 ++++--- apps/desktop/src/locales/zh_TW/messages.json | 9 ++ 64 files changed, 656 insertions(+), 80 deletions(-) diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index 0701eb833da..0a3cbd229cd 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Dien In" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index 7e7f9faf5fe..7636c30576b 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "استخدام تسجيل دخول واحد" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "إرسال" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index 4289d577aac..37761036c3e 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Vahid daxil olma üsulunu istifadə et" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Göndər" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Arxivlənmiş elementlər ümumi axtarış nəticələrindən və avto-doldurma təkliflərindən xaric ediləcək. Bu elementi arxivləmək istədiyinizə əminsiniz?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Poçt kodu" + }, + "cardNumberLabel": { + "message": "Kart nömrəsi" } } diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index 1f2ac683790..2e5a58e0e24 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Адправіць" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index 995d992db3e..03b6c4d5090 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Използване на еднократна идентификация" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Подаване" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Архивираните елементи са изключени от общите резултати при търсене и от предложенията за автоматично попълване. Наистина ли искате да архивирате този елемент?" + }, + "zipPostalCodeLabel": { + "message": "Пощенски код" + }, + "cardNumberLabel": { + "message": "Номер на картата" } } diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index 7a64fec30da..e47df9a26cb 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "জমা দিন" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index 150b579b09d..5b453c176dc 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Potvrdi" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index dec07e3efe0..bb3dc27d957 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Usa inici de sessió únic" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Envia" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index 50b9cdf8844..f20911eceb7 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Použít jednotné přihlášení" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Odeslat" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archivované položky jsou vyloučeny z obecných výsledků vyhledávání a z návrhů automatického vyplňování. Jste si jisti, že chcete tuto položku archivovat?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / PSČ" + }, + "cardNumberLabel": { + "message": "Číslo karty" } } diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index 03a29097352..af0a7029865 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Submit" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index a5a45db979a..b1e1ad6d201 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Brug Single Sign-On" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Indsend" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index 002ef104b96..580c9f42313 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Single Sign-on verwenden" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Absenden" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archivierte Einträge werden von allgemeinen Suchergebnissen und Auto-Ausfüllen-Vorschlägen ausgeschlossen. Bist du sicher, dass du diesen Eintrag archivieren möchtest?" + }, + "zipPostalCodeLabel": { + "message": "PLZ / Postleitzahl" + }, + "cardNumberLabel": { + "message": "Kartennummer" } } diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index 6d381b8fa66..232c8448d98 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Χρήση single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Υποβολή" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index 6594b2812e3..a2c96c63f51 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Submit" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index 20745ccfaf1..f746504d8e7 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Submit" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "PIN" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index 14972f29f79..44fdd715cbd 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Submit" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index 2850044205f..0e9b137c2b1 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Usar inicio de sesión único" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Enviar" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index 75395b451b6..3bf585f0351 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Kinnita" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index 0f5ebaca284..a79964b304b 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Bidali" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index f097a21b7b7..24e45b6cac0 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "استفاده از ورود تک مرحله‌ای" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "ثبت" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index 725f1ebb7f2..c95934a1f36 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Käytä kertakirjautumista" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Jatka" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index a23e6913c06..317f8808af7 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Isumite" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index 10885ea46f4..ddab6285e01 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Utiliser l'authentification unique" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Soumettre" }, @@ -1826,7 +1829,7 @@ "message": "Code PIN invalide." }, "tooManyInvalidPinEntryAttemptsLoggingOut": { - "message": "Trop de tentatives de saisie du code PIN incorrectes. Déconnexion." + "message": "Trop de tentatives de saisie du code NIP incorrectes. Déconnexion." }, "unlockWithWindowsHello": { "message": "Déverrouiller avec Windows Hello" @@ -1853,10 +1856,10 @@ "message": "Verrouiller avec le mot de passe principal au redémarrage" }, "requireMasterPasswordOrPinOnAppRestart": { - "message": "Require master password or PIN on app restart" + "message": "Exiger un mot de passe principal ou un code NIP au redémarrage de l'application" }, "requireMasterPasswordOnAppRestart": { - "message": "Require master password on app restart" + "message": "Exiger un mot de passe principal redémarrage de l'application" }, "deleteAccount": { "message": "Supprimer le compte" @@ -2446,10 +2449,10 @@ "message": "Essayez de nouveau" }, "verificationRequiredForActionSetPinToContinue": { - "message": "Vérification requise pour cette action. Définissez un code PIN pour continuer." + "message": "Vérification requise pour cette action. Définissez un code NIP pour continuer." }, "setPin": { - "message": "Définir un code PIN" + "message": "Définir un code NIP" }, "verifyWithBiometrics": { "message": "Vérifier par biométrie" @@ -2467,7 +2470,7 @@ "message": "Utiliser le mot de passe principal" }, "usePin": { - "message": "Utiliser le code PIN" + "message": "Utiliser le code NIP" }, "useBiometrics": { "message": "Utiliser la biométrie" @@ -2556,7 +2559,7 @@ } }, "vaultCustomTimeoutMinimum": { - "message": "Minimum custom timeout is 1 minute." + "message": "Le délai d'expiration personnalisé minimum est de 1 minute." }, "inviteAccepted": { "message": "Invitation acceptée" @@ -3583,7 +3586,7 @@ "message": "Code incorrect" }, "incorrectPin": { - "message": "Code PIN incorrect" + "message": "Code NIP incorrect" }, "multifactorAuthenticationFailed": { "message": "Authentification multifacteur échouée" @@ -4159,7 +4162,7 @@ "description": "Verb" }, "unArchive": { - "message": "Unarchive" + "message": "Désarchiver" }, "itemsInArchive": { "message": "Éléments dans l'archive" @@ -4171,15 +4174,21 @@ "message": "Les éléments archivés apparaîtront ici et seront exclus des résultats de recherche généraux et des suggestions de remplissage automatique." }, "itemWasSentToArchive": { - "message": "Item was sent to archive" + "message": "L'élément a été envoyé à l'archive" }, "itemWasUnarchived": { - "message": "Item was unarchived" + "message": "L'élément a été désarchivé" }, "archiveItem": { "message": "Archiver l'élément" }, "archiveItemConfirmDesc": { "message": "Les éléments archivés sont exclus des résultats de recherche généraux et des suggestions de remplissage automatique. Êtes-vous sûr de vouloir archiver cet élément ?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Code postal" + }, + "cardNumberLabel": { + "message": "Numéro de carte" } } diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index 3073fef032a..c6856f3375a 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Submit" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index fcbd038adf3..dc41911950b 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "השתמש בכניסה יחידה" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "שלח" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index ca2b4cbced1..5a4895e20a1 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Submit" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index 129dd27b09a..fd2cc31685e 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Jedinstvena prijava (SSO)" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Pošalji" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Arhivirane stavke biti će izuzete iz rezultata općih pretraga i preporuka auto-ispune. Sigurno želiš arhivirati?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index 8e06affda49..c0918cfba4b 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Egyszeri bejelentkezés használata" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Beküldés" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Az archivált elemek ki vannak zárva az általános keresési eredményekből és az automatikus kitöltési javaslatokból. Biztosan archiválni szeretnénk ezt az elemet?" + }, + "zipPostalCodeLabel": { + "message": "Irányítószám" + }, + "cardNumberLabel": { + "message": "Kártya szám" } } diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index 2aea4e5f1ab..f41c13de593 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Kirim" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index c851dc2b298..39501439da9 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Usa il Single Sign-On" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Invia" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index 1b61929ac38..543ac4027f7 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "シングルサインオンを使用する" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "送信" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index 769cc602815..fec326e1158 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "გადაცემა" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index 3073fef032a..c6856f3375a 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Submit" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index e987d3d811b..c33d2ca3d4e 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "ಒಪ್ಪಿಸು" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index 55da2761122..2c84d22640b 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Single sign-on(SSO) 사용" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "보내기" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/lt/messages.json b/apps/desktop/src/locales/lt/messages.json index 38971c8c675..f053f0806ca 100644 --- a/apps/desktop/src/locales/lt/messages.json +++ b/apps/desktop/src/locales/lt/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Išsaugoti" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index 487991ddfa4..6fb441b1f7b 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Izmantot vienoto pieteikšanos" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Iesniegt" }, @@ -1378,7 +1381,7 @@ "message": "Valoda" }, "languageDesc": { - "message": "Mainīt lietotnes valodu. Ir nepieciešama pārsāknēšana." + "message": "Mainīt lietotnes valodu. Ir nepieciešama atkārtota palaišana." }, "theme": { "message": "Izskats" @@ -1414,7 +1417,7 @@ "message": "Pārsāknēt, lai atjauninātu" }, "restartToUpdateDesc": { - "message": "Versija $VERSION_NUM$ ir gatava uzstādīšanai. Ir jāpārsāknē lietotne, lai pabeigtu uzstādīšanu. Vai pārsāknēt un atjaunināt tagad?", + "message": "Versija $VERSION_NUM$ ir gatava uzstādīšanai. Lietotne ir jāpalaiž no jauna, lai pabeigtu uzstādīšanu. Vai palaist no jauna un atjaunināt tagad?", "placeholders": { "version_num": { "content": "$1", @@ -1853,10 +1856,10 @@ "message": "Aizslēgt ar galveno paroli pēc pārsāknēšanas" }, "requireMasterPasswordOrPinOnAppRestart": { - "message": "Pieprasīt galveno paroli vai PIN pēc lietotnes pārsāknēšanas" + "message": "Pieprasīt galveno paroli vai PIN pēc lietotnes atkārtotas palaišanas" }, "requireMasterPasswordOnAppRestart": { - "message": "Pieprasīt galveno paroli pēc lietotnes pārsāknēšanas" + "message": "Pieprasīt galveno paroli pēc lietotnes atkārtotas palaišanas" }, "deleteAccount": { "message": "Izdzēst kontu" @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Arhivētie vienumi netiek iekļauti vispārējās meklēšanas iznākumos un automātiskās aizpildes ieteikumos. Vai tiešām ahrivēt šo vienumu?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Pasta indekss" + }, + "cardNumberLabel": { + "message": "Kartes numurs" } } diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index 298d13ce2dd..4d1779b5cd0 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Podnesi" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index 456ade5aec7..14b215bc346 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "സമർപ്പിക്കുക" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index 3073fef032a..c6856f3375a 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Submit" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index 7d754800060..7bd62d93bac 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Submit" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index 7c70d751245..0047f866933 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Bruk singulær pålogging" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Send inn" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index bf78d49ff23..1f7b8ab49e1 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Submit" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index fd568c9bbb4..28aaa851d42 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Single sign-on gebruiken" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Opslaan" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Gearchiveerde items worden uitgesloten van algemene zoekresultaten en automatische invulsuggesties. Weet je zeker dat je dit item wilt archiveren?" + }, + "zipPostalCodeLabel": { + "message": "Postcode" + }, + "cardNumberLabel": { + "message": "Kaartnummer" } } diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index bee0f8ed4fc..899ce7cc927 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Send inn" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index 32c05bd53ff..1878cbc6a8b 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Submit" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index 08585674532..ef35183ccce 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Użyj logowania jednokrotnego" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Wyślij" }, @@ -1853,10 +1856,10 @@ "message": "Zablokuj hasłem głównym po uruchomieniu ponownym" }, "requireMasterPasswordOrPinOnAppRestart": { - "message": "Require master password or PIN on app restart" + "message": "Wymagaj hasła głównego lub PIN po uruchomieniu aplikacji" }, "requireMasterPasswordOnAppRestart": { - "message": "Require master password on app restart" + "message": "Wymagaj hasła głównego po uruchomieniu aplikacji" }, "deleteAccount": { "message": "Usuń konto" @@ -3810,7 +3813,7 @@ "message": "Potwierdź użycie klucza SSH" }, "agentForwardingWarningTitle": { - "message": "Warning: Agent Forwarding" + "message": "Ostrzeżenie: Przekazywanie agenta" }, "agentForwardingWarningText": { "message": "Żądanie pochodzi ze zdalnego zalogowanego urządzenia" @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Zarchiwizowane elementy są wykluczone z wyników wyszukiwania i sugestii autouzupełniania. Czy na pewno chcesz archiwizować element?" + }, + "zipPostalCodeLabel": { + "message": "Kod pocztowy" + }, + "cardNumberLabel": { + "message": "Numer karty" } } diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index 4e80920cbf9..5113e99cacf 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Usar autenticação única" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Enviar" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Itens arquivados são excluídos dos resultados da pesquisa geral e das sugestões de preenchimento automático. Tem certeza de que deseja arquivar este item?" + }, + "zipPostalCodeLabel": { + "message": "CEP / Código postal" + }, + "cardNumberLabel": { + "message": "Número do cartão" } } diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index 0bb2142ba5a..5fdf0e85cdc 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Utilizar início de sessão único" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "A sua organização exige o início de sessão único." + }, "submit": { "message": "Submeter" }, @@ -1292,7 +1295,7 @@ "message": "Na suspensão do sistema" }, "onLocked": { - "message": "No bloqueio do sistema" + "message": "Ao bloquear o sistema" }, "onRestart": { "message": "Ao reiniciar" @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Os itens arquivados são excluídos dos resultados gerais da pesquisa e das sugestões de preenchimento automático. Tem a certeza de que pretende arquivar este item?" + }, + "zipPostalCodeLabel": { + "message": "Código postal" + }, + "cardNumberLabel": { + "message": "Número do cartão" } } diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index dab5ed8112e..5a6f4b0276e 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Trimitere" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index 684adf61875..63837c98e5a 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Использовать единый вход" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Отправить" }, @@ -3870,7 +3873,7 @@ "message": "Изменить пароль, подверженный риску" }, "changeAtRiskPasswordAndAddWebsite": { - "message": "Этот логин находится под угрозой и у него отсутствует веб-сайт. Добавьте веб-сайт и смените пароль для большей безопасности." + "message": "Этот логин подвержен риску и у него отсутствует веб-сайт. Добавьте веб-сайт и смените пароль для большей безопасности." }, "missingWebsite": { "message": "Отсутствует сайт" @@ -4120,16 +4123,16 @@ "message": "Bitwarden не проверяет местоположение ввода, поэтому, прежде чем использовать ярлык, убедитесь, что вы находитесь в нужном окне и поле." }, "typeShortcut": { - "message": "Type shortcut" + "message": "Введите сочетание клавиш" }, "editAutotypeShortcutDescription": { - "message": "Include one or two of the following modifiers: Ctrl, Alt, Win, or Shift, and a letter." + "message": "Включите один или два из следующих модификаторов: Ctrl, Alt, Win или Shift и букву." }, "invalidShortcut": { - "message": "Invalid shortcut" + "message": "Недопустимое сочетание клавиш" }, "moreBreadcrumbs": { - "message": "Больше хлебных крошек", + "message": "Дополнительная навигация", "description": "This is used in the context of a breadcrumb navigation, indicating that there are more items in the breadcrumb trail that are not currently displayed." }, "next": { @@ -4142,7 +4145,7 @@ "message": "Подтвердить" }, "enableAutotypeShortcutPreview": { - "message": "Enable autotype shortcut (Feature Preview)" + "message": "Включить сочетание клавиш ввода (предварительная версия функции)" }, "enableAutotypeShortcutDescription": { "message": "Прежде чем использовать ярлык, убедитесь, что вы поставили курсор в нужное поле, чтобы избежать ввода данных в неправильное место." @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Архивированные элементы исключены из общих результатов поиска и предложений автозаполнения. Вы уверены, что хотите архивировать этот элемент?" + }, + "zipPostalCodeLabel": { + "message": "Почтовый индекс" + }, + "cardNumberLabel": { + "message": "Номер карты" } } diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index 397bbbe23c7..7f191bf2161 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Submit" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index 66a42c52182..aaf45ac65b2 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -131,7 +131,7 @@ "message": "Spustiť" }, "copyValue": { - "message": "Skopírovať hodnotu", + "message": "Kopírovať hodnotu", "description": "Copy value to clipboard" }, "minimizeOnCopyToClipboard": { @@ -144,7 +144,7 @@ "message": "Prepnúť viditeľnosť" }, "toggleCollapse": { - "message": "Prepnúť zloženie", + "message": "Prepnúť zbalenie", "description": "Toggling an expand/collapse state." }, "cardholderName": { @@ -364,7 +364,7 @@ "message": "Krstné meno" }, "middleName": { - "message": "Druhé meno" + "message": "Stredné meno" }, "lastName": { "message": "Priezvisko" @@ -409,7 +409,7 @@ "message": "Upraviť" }, "authenticatorKeyTotp": { - "message": "Kľúč overovateľa (TOTP)" + "message": "Overovací kľúč (TOTP)" }, "authenticatorKey": { "message": "Overovací kľúč" @@ -514,10 +514,10 @@ "message": "Meno je povinné." }, "addedItem": { - "message": "Položka pridaná" + "message": "Položka bola pridaná" }, "editedItem": { - "message": "Položka upravená" + "message": "Položka bola uložená" }, "deleteItem": { "message": "Odstrániť položku" @@ -557,7 +557,7 @@ "message": "Vygenerovať nové heslo" }, "copyPassword": { - "message": "Skopírovať heslo" + "message": "Kopírovať heslo" }, "regenerateSshKey": { "message": "Generovať nový kľúč SSH" @@ -659,10 +659,10 @@ "message": "Zavrieť" }, "minNumbers": { - "message": "Minimálny počet číslic" + "message": "Minimum číslic" }, "minSpecial": { - "message": "Minimum špeciálnych", + "message": "Minimum špeciálnych znakov", "description": "Minimum Special Characters" }, "ambiguous": { @@ -687,20 +687,20 @@ "message": "Hľadať v obľúbených" }, "searchType": { - "message": "Search type", + "message": "Typ vyhľadávania", "description": "Search item type" }, "newAttachment": { "message": "Pridať novú prílohu" }, "deletedAttachment": { - "message": "Príloha odstránená" + "message": "Príloha bola odstránená" }, "deleteAttachmentConfirmation": { "message": "Naozaj chcete odstrániť prílohu?" }, "attachmentSaved": { - "message": "Príloha bola uložená." + "message": "Príloha bola uložená" }, "addAttachment": { "message": "Priložiť prílohu" @@ -712,7 +712,7 @@ "message": "Súbor" }, "selectFile": { - "message": "Vybrať súbor." + "message": "Vyberte súbor" }, "maxFileSize": { "message": "Maximálna veľkosť súboru je 500 MB." @@ -721,16 +721,16 @@ "message": "Staršie šifrovanie už nie je podporované. Ak chcete obnoviť svoj účet, obráťte sa na podporu." }, "editedFolder": { - "message": "Priečinok upravený" + "message": "Priečinok bol upravený" }, "addedFolder": { - "message": "Priečinok pridaný" + "message": "Priečinok bol pridaný" }, "deleteFolderConfirmation": { "message": "Naozaj chcete odstrániť tento priečinok?" }, "deletedFolder": { - "message": "Priečinok odstránený" + "message": "Priečinok bol odstránený" }, "loginOrCreateNewAccount": { "message": "Prihláste sa alebo si vytvorte nový účet, aby ste mohli pristupovať k vášmu bezpečnému trezoru." @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Použiť jednotné prihlásenie" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Potvrdiť" }, @@ -955,7 +958,7 @@ "description": "Select another two-step login method" }, "useYourRecoveryCode": { - "message": "Použiť obnovovací kód" + "message": "Použiť kód na obnovenie" }, "insertU2f": { "message": "Vložte váš bezpečnostný kľúč do USB portu počítača. Ak má tlačidlo, stlačte ho." @@ -981,7 +984,7 @@ "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { - "message": "Overiť sa prostredníctvom Duo Security vašej organizácie použitím Duo Mobile aplikácie, SMS, telefonátu alebo U2F bezpečnostným kľúčom.", + "message": "Overenie pomocou Duo Security pre vašu organizáciu pomocou aplikácie Duo Mobile, SMS, telefonického hovoru alebo bezpečnostného kľúča U2F.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "verifyYourIdentity": { @@ -997,7 +1000,7 @@ "message": "FIDO2 WebAuthn" }, "webAuthnDesc": { - "message": "Použiť akýkoľvek WebAuthn bezpečnostný kľúč pre prístup k vášmu účtu." + "message": "Použite akýkoľvek kompatibilný bezpečnostný kľúč WebAuthn na prístup k svojmu účtu." }, "emailTitle": { "message": "E-mail" @@ -1006,7 +1009,7 @@ "message": "Zadajte kód zaslaný na váš e-mail." }, "loginUnavailable": { - "message": "Prihlásenie nedostupné" + "message": "Prihlásenie nie je dostupné" }, "noTwoStepProviders": { "message": "Tento účet má povolené dvojstupňové prihlásenie, ale žiadny z nakonfigurovaných poskytovateľov nie je podporovaný na tomto zariadení." @@ -1021,7 +1024,7 @@ "message": "Vyberte metódu dvojstupňového prihlásenia" }, "selfHostedEnvironment": { - "message": "Prevádzkované vo vlastnom prostredí" + "message": "Prostredie s vlastným hostingom" }, "selfHostedBaseUrlHint": { "message": "Zadajte základnú URL adresu lokálne hosťovanej inštalácie Bitwarden. Napríklad: https://bitwarden.company.com" @@ -1058,13 +1061,13 @@ "message": "URL servera identít" }, "notificationsUrl": { - "message": "URL adresa servera pre oznámenia" + "message": "URL servera pre upozornenia" }, "iconsUrl": { "message": "URL servera ikon" }, "environmentSaved": { - "message": "URL prostredia boli uložené." + "message": "URL adresy prostredia boli uložené" }, "ok": { "message": "Ok" @@ -1115,10 +1118,10 @@ "message": "Odhlásiť sa" }, "addNewLogin": { - "message": "Pridať nové prihlasovacie údaje" + "message": "Nové prihlasovacie údaje" }, "addNewItem": { - "message": "Pridať novú položku" + "message": "Nová položka" }, "view": { "message": "Zobraziť" @@ -1154,7 +1157,7 @@ "message": "Sledujte nás" }, "syncVault": { - "message": "Synchronizovať trezor teraz" + "message": "Synchronizovať trezor" }, "changeMasterPass": { "message": "Zmeniť hlavné heslo" @@ -1166,7 +1169,7 @@ "message": "Hlavné heslo si môžete zmeniť vo webovej aplikácii Bitwarden." }, "fingerprintPhrase": { - "message": "Fráza odtlačku", + "message": "Jedinečný identifikátor", "description": "A 'fingerprint phrase' is a unique word phrase (similar to a passphrase) that a user can use to authenticate their public key with another user, for the purposes of sharing." }, "yourAccountsFingerprint": { @@ -1177,13 +1180,13 @@ "message": "Prejsť do webového trezora" }, "getMobileApp": { - "message": "Získajte mobilnú aplikáciu" + "message": "Získať mobilnú aplikáciu" }, "getBrowserExtension": { "message": "Získať rozšírenie pre prehliadač" }, "syncingComplete": { - "message": "Synchronizácia dokončená" + "message": "Synchronizácia bola dokončená" }, "syncingFailed": { "message": "Synchronizácia zlyhala" @@ -1232,7 +1235,7 @@ } }, "twoStepLoginConfirmation": { - "message": "Dvojstupňové prihlasovanie robí váš účet bezpečnejším vďaka vyžadovaniu bezpečnostného kódu z overovacej aplikácie vždy, keď sa prihlásite. Dvojstupňové prihlasovanie môžete povoliť vo webovom trezore bitwarden.com. Chcete teraz navštíviť túto stránku?" + "message": "Dvojstupňové prihlásenie zvyšuje bezpečnosť vášho účtu tým, že vyžaduje overenie prihlásenia pomocou iného zariadenia, napríklad bezpečnostného kľúča, overovacej aplikácie, SMS, telefonického hovoru alebo e-mailu. Dvojstupňové prihlásenie môžete nastaviť na bitwarden.com. Chcete stránku navštíviť teraz?" }, "twoStepLogin": { "message": "Dvojstupňové prihlásenie" @@ -1339,7 +1342,7 @@ "message": "Namiesto zatvorenia okna zobraziť ikonu na paneli úloh." }, "enableTray": { - "message": "Povoliť ikonu na systémovej lište" + "message": "Povoliť ikonu na paneli úloh" }, "enableTrayDesc": { "message": "Vždy zobraziť ikonu na systémovej lište." @@ -1411,7 +1414,7 @@ } }, "restartToUpdate": { - "message": "Reštartovať pre dokončenie aktualizácie" + "message": "Reštartovať na dokončenie aktualizácie" }, "restartToUpdateDesc": { "message": "Verzia $VERSION_NUM$ je pripravená na inštaláciu. Je nutné reštartovať aplikáciu, aby sa inštalácia mohla dokončiť. Chcete ju reštartovať a aktualizovať teraz?", @@ -1476,7 +1479,7 @@ "message": "Momentálne nie ste prémiovým členom." }, "premiumSignUpAndGet": { - "message": "Zaregistrujte sa pre prémiové členstvo a získajte:" + "message": "Zaregistrujte sa na prémiové členstvo a získajte:" }, "premiumSignUpStorage": { "message": "1 GB šifrovaného úložiska." @@ -1518,7 +1521,7 @@ } }, "refreshComplete": { - "message": "Obnova kompletná" + "message": "Obnova bola dokončená" }, "passwordHistory": { "message": "História hesla" @@ -1563,7 +1566,7 @@ "description": "Paste from clipboard" }, "selectAll": { - "message": "Označiť všetko" + "message": "Vybrať Všetko" }, "zoomIn": { "message": "Priblížiť" @@ -1572,7 +1575,7 @@ "message": "Oddialiť" }, "resetZoom": { - "message": "Obnoviť pôvodné zobrazenie" + "message": "Obnoviť priblíženie" }, "toggleFullScreen": { "message": "Prepnúť na celú obrazovku" @@ -1581,7 +1584,7 @@ "message": "Znovu načítať" }, "toggleDevTools": { - "message": "Prepnúť vývojárske nástroje" + "message": "Prepnúť nástroje pre vývojárov" }, "minimize": { "message": "Minimalizovať", @@ -1591,7 +1594,7 @@ "message": "Priblíženie" }, "bringAllToFront": { - "message": "Preniesť všetko dopredu", + "message": "Presunúť všetko dopredu", "description": "Bring all windows to front (foreground)" }, "aboutBitwarden": { @@ -3819,10 +3822,10 @@ "message": "žiada o prístup k" }, "sshkeyApprovalMessageSuffix": { - "message": "in order to" + "message": "aby bolo možné" }, "sshActionLogin": { - "message": "authenticate to a server" + "message": "overenie na serveri" }, "sshActionSign": { "message": "podpísať správu" @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archivované položky sú vylúčené zo všeobecného vyhľadávania a z návrhov automatického vypĺňania. Naozaj chcete archivovať túto položku?" + }, + "zipPostalCodeLabel": { + "message": "PSČ" + }, + "cardNumberLabel": { + "message": "Číslo karty" } } diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index 597cb62b4ea..c2380687634 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Potrdi" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index 20e55677171..1e94787f824 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Употребити једнократну пријаву" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Пошаљи" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Архивиране ставке су искључене из општих резултата претраге и предлога за ауто попуњавање. Јесте ли сигурни да желите да архивирате ову ставку?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index 575e5755441..6811a6b8583 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Använd Single Sign-On" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Skicka" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Arkiverade objekt är uteslutna från allmänna sökresultat och förslag för autofyll. Är du säker på att du vill arkivera detta objekt?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/ta/messages.json b/apps/desktop/src/locales/ta/messages.json index 0ee270c981b..5e6bccf2a6e 100644 --- a/apps/desktop/src/locales/ta/messages.json +++ b/apps/desktop/src/locales/ta/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "ஒற்றை உள்நுழையைப் பயன்படுத்து" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "சமர்ப்பி" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index 3073fef032a..c6856f3375a 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Submit" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index a2637894dc4..e2d4509ddf4 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "ส่งข้อมูล" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index 0d93d84fa2a..88ee8f4e635 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Çoklu oturum açma kullan" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Kuruluşunuz çoklu oturum açma gerektiriyor." + }, "submit": { "message": "Gönder" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / posta kodu" + }, + "cardNumberLabel": { + "message": "Kart numarası" } } diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index 577ce4a5d78..5c4583aa9b6 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Використати єдиний вхід" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Відправити" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "Archived items are excluded from general search results and autofill suggestions. Are you sure you want to archive this item?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index f1341734453..dd7747dab9f 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "Dùng đăng nhập một lần" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "Gửi" }, @@ -1244,7 +1247,7 @@ "message": "Đóng kho sau" }, "vaultTimeout1": { - "message": "Quá hạn" + "message": "Thời gian chờ" }, "vaultTimeoutAction1": { "message": "Hành động sau khi đóng kho" @@ -1853,10 +1856,10 @@ "message": "Khóa bằng mật khẩu chính khi khởi động lại" }, "requireMasterPasswordOrPinOnAppRestart": { - "message": "Require master password or PIN on app restart" + "message": "Yêu cầu mật khẩu chính hoặc mã PIN khi khởi động lại ứng dụng" }, "requireMasterPasswordOnAppRestart": { - "message": "Require master password on app restart" + "message": "Yêu cầu mật khẩu chính khi khởi động lại ứng dụng" }, "deleteAccount": { "message": "Xóa tài khoản" @@ -4174,12 +4177,18 @@ "message": "Mục đã được chuyển vào kho lưu trữ" }, "itemWasUnarchived": { - "message": "Item was unarchived" + "message": "Mục đã được bỏ lưu trữ" }, "archiveItem": { "message": "Lưu trữ mục" }, "archiveItemConfirmDesc": { "message": "Các mục đã lưu trữ sẽ bị loại khỏi kết quả tìm kiếm chung và gợi ý tự động điền. Bạn có chắc chắn muốn lưu trữ mục này không?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index 9a8aa724778..7a4e9f7bc7b 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "使用单点登录" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "提交" }, @@ -1238,13 +1241,13 @@ "message": "两步登录" }, "vaultTimeoutHeader": { - "message": "密码库超时时间" + "message": "密码库超时" }, "vaultTimeout": { - "message": "密码库超时时间" + "message": "密码库超时" }, "vaultTimeout1": { - "message": "超时" + "message": "超时时间" }, "vaultTimeoutAction1": { "message": "超时动作" @@ -1964,7 +1967,7 @@ "message": "永久删除" }, "vaultTimeoutLogOutConfirmation": { - "message": "超时后注销将解除对密码库的所有访问权限,并需要进行在线身份验证。确定使用此设置吗?" + "message": "超时后注销账户将解除对密码库的所有访问权限,并需要进行在线身份验证。确定要使用此设置吗?" }, "vaultTimeoutLogOutConfirmationTitle": { "message": "超时动作确认" @@ -2431,7 +2434,7 @@ "message": "您的主密码不符合某一项或多项组织策略要求。要访问密码库,必须立即更新您的主密码。继续操作将使您退出当前会话,并要求您重新登录。其他设备上的活动会话可能会继续保持活动状态长达一小时。" }, "changePasswordWarning": { - "message": "更改密码后,您需要使用新密码登录。 在其他设备上的活动会话将在一小时内注销。" + "message": "更改密码后,您需要使用新密码登录。在其他设备上的活动会话将在一小时内注销。" }, "accountRecoveryUpdateMasterPasswordSubtitle": { "message": "更改您的主密码以完成账户恢复。" @@ -2498,7 +2501,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "您的组织策略正在影响您的密码库超时时间。最大允许的密码库超时时间是 $HOURS$ 小时 $MINUTES$ 分钟。您的密码库超时动作被设置为 $ACTION$。", + "message": "您的组织策略正在影响您的密码库超时。最大允许的密码库超时为 $HOURS$ 小时 $MINUTES$ 分钟。您的密码库超时动作被设置为 $ACTION$。", "placeholders": { "hours": { "content": "$1", @@ -2515,7 +2518,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "您的组织策略已将您的密码库超时动作设置为 $ACTION$。", + "message": "您的组织策略已将您的密码库超时动作设置为「$ACTION$」。", "placeholders": { "action": { "content": "$1", @@ -2524,13 +2527,13 @@ } }, "vaultTimeoutTooLarge": { - "message": "您的密码库超时时间超出了组织设置的限制。" + "message": "您的密码库超时超出了您组织设置的限制。" }, "vaultTimeoutPolicyAffectingOptions": { "message": "企业策略要求已应用到您的超时选项中" }, "vaultTimeoutPolicyInEffect": { - "message": "您的组织策略已将您最大允许的密码库超时时间设置为 $HOURS$ 小时 $MINUTES$ 分钟。", + "message": "您的组织策略已将您最大允许的密码库超时设置为 $HOURS$ 小时 $MINUTES$ 分钟。", "placeholders": { "hours": { "content": "$1", @@ -2543,7 +2546,7 @@ } }, "vaultTimeoutPolicyMaximumError": { - "message": "超时时间超出了您组织设置的限制:最多 $HOURS$ 小时 $MINUTES$ 分钟", + "message": "超时超出了您组织设置的限制:最多 $HOURS$ 小时 $MINUTES$ 分钟", "placeholders": { "hours": { "content": "$1", @@ -2556,7 +2559,7 @@ } }, "vaultCustomTimeoutMinimum": { - "message": "自定义超时时间最小为 1 分钟。" + "message": "自定义超时最少为 1 分钟。" }, "inviteAccepted": { "message": "邀请已接受" @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "已归档的项目将被排除在一般搜索结果和自动填充建议之外。确定要归档此项目吗?" + }, + "zipPostalCodeLabel": { + "message": "ZIP / 邮政编码" + }, + "cardNumberLabel": { + "message": "卡号" } } diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index 9b29eb12a2d..78a4f950f40 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -771,6 +771,9 @@ "useSingleSignOn": { "message": "使用單一登入" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "submit": { "message": "送出" }, @@ -4181,5 +4184,11 @@ }, "archiveItemConfirmDesc": { "message": "封存的項目將不會出現在一般搜尋結果或自動填入建議中。確定要封存此項目嗎?" + }, + "zipPostalCodeLabel": { + "message": "郵編 / 郵政代碼" + }, + "cardNumberLabel": { + "message": "支付卡號碼" } } From 4c1eba2086fca1ddecfed74a3cfb9d217dcadb89 Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Fri, 31 Oct 2025 10:10:22 +0100 Subject: [PATCH 39/66] [PM-27583] Add icon to chromium import helper (#17126) Adds an icon to the windows binary. --------- Co-authored-by: Daniel James Smith --- .github/CODEOWNERS | 3 +- apps/desktop/desktop_native/Cargo.lock | 122 ++++++++++++++++-- apps/desktop/desktop_native/Cargo.toml | 7 +- .../Cargo.toml | 40 ++++++ .../bitwarden_chromium_import_helper/build.rs | 9 ++ .../resources.rc | 1 + .../src/main.rs | 13 ++ .../src/windows.rs} | 10 +- apps/desktop/desktop_native/build.js | 2 +- .../chromium_importer/Cargo.toml | 12 -- .../chromium_importer/README.md | 2 - 11 files changed, 188 insertions(+), 33 deletions(-) create mode 100644 apps/desktop/desktop_native/bitwarden_chromium_import_helper/Cargo.toml create mode 100644 apps/desktop/desktop_native/bitwarden_chromium_import_helper/build.rs create mode 100644 apps/desktop/desktop_native/bitwarden_chromium_import_helper/resources.rc create mode 100644 apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/main.rs rename apps/desktop/desktop_native/{chromium_importer/src/bin/bitwarden_chromium_import_helper.rs => bitwarden_chromium_import_helper/src/windows.rs} (98%) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 676c4b4657b..39e968d941b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -29,8 +29,9 @@ libs/common/src/auth @bitwarden/team-auth-dev ## Tools team files ## apps/browser/src/tools @bitwarden/team-tools-dev apps/cli/src/tools @bitwarden/team-tools-dev -apps/desktop/src/app/tools @bitwarden/team-tools-dev +apps/desktop/desktop_native/bitwarden_chromium_import_helper @bitwarden/team-tools-dev apps/desktop/desktop_native/chromium_importer @bitwarden/team-tools-dev +apps/desktop/src/app/tools @bitwarden/team-tools-dev apps/web/src/app/tools @bitwarden/team-tools-dev libs/angular/src/tools @bitwarden/team-tools-dev libs/common/src/models/export @bitwarden/team-tools-dev diff --git a/apps/desktop/desktop_native/Cargo.lock b/apps/desktop/desktop_native/Cargo.lock index c4fcbe86a78..cf635f39df3 100644 --- a/apps/desktop/desktop_native/Cargo.lock +++ b/apps/desktop/desktop_native/Cargo.lock @@ -440,6 +440,24 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "bitwarden_chromium_import_helper" +version = "0.0.0" +dependencies = [ + "anyhow", + "base64", + "chromium_importer", + "clap", + "embed-resource", + "scopeguard", + "sysinfo", + "tokio", + "tracing", + "tracing-subscriber", + "verifysign", + "windows 0.61.1", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -590,23 +608,19 @@ dependencies = [ "base64", "cbc", "chacha20poly1305", - "clap", "dirs", "hex", "oo7", "pbkdf2", "rand 0.9.1", "rusqlite", - "scopeguard", "security-framework", "serde", "serde_json", "sha1", - "sysinfo", "tokio", "tracing", "tracing-subscriber", - "verifysign", "windows 0.61.1", ] @@ -1046,7 +1060,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1163,6 +1177,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embed-resource" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55a075fc573c64510038d7ee9abc7990635863992f83ebc52c8b433b8411a02e" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 0.9.5", + "vswhom", + "winreg", +] + [[package]] name = "embed_plist" version = "1.2.2" @@ -3109,6 +3137,15 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_spanned" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +dependencies = [ + "serde", +] + [[package]] name = "serial_test" version = "3.2.0" @@ -3510,12 +3547,36 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime 0.7.0", + "toml_parser", + "toml_writer", + "winnow", +] + [[package]] name = "toml_datetime" version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +[[package]] +name = "toml_datetime" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +dependencies = [ + "serde", +] + [[package]] name = "toml_edit" version = "0.22.26" @@ -3523,10 +3584,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", - "toml_datetime", + "toml_datetime 0.6.9", "winnow", ] +[[package]] +name = "toml_parser" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" + [[package]] name = "tracing" version = "0.1.41" @@ -3688,7 +3764,7 @@ dependencies = [ "paste", "serde", "textwrap", - "toml", + "toml 0.5.11", "uniffi_meta", "uniffi_udl", ] @@ -3742,7 +3818,7 @@ dependencies = [ "quote", "serde", "syn", - "toml", + "toml 0.5.11", "uniffi_meta", ] @@ -3848,6 +3924,26 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -4474,6 +4570,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/apps/desktop/desktop_native/Cargo.toml b/apps/desktop/desktop_native/Cargo.toml index 6a366316328..7a835b81c1b 100644 --- a/apps/desktop/desktop_native/Cargo.toml +++ b/apps/desktop/desktop_native/Cargo.toml @@ -2,6 +2,7 @@ resolver = "2" members = [ "autotype", + "bitwarden_chromium_import_helper", "chromium_importer", "core", "macos_provider", @@ -68,7 +69,11 @@ tokio = "=1.45.0" tokio-stream = "=0.1.15" tokio-util = "=0.7.13" tracing = "=0.1.41" -tracing-subscriber = { version = "=0.3.20", features = ["fmt", "env-filter", "tracing-log"] } +tracing-subscriber = { version = "=0.3.20", features = [ + "fmt", + "env-filter", + "tracing-log", +] } typenum = "=1.18.0" uniffi = "=0.28.3" widestring = "=1.2.0" diff --git a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/Cargo.toml b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/Cargo.toml new file mode 100644 index 00000000000..d1e4ac899bd --- /dev/null +++ b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "bitwarden_chromium_import_helper" +version.workspace = true +license.workspace = true +edition.workspace = true +publish.workspace = true + +[dependencies] + +[target.'cfg(target_os = "windows")'.dependencies] +chromium_importer = { path = "../chromium_importer" } +clap = { version = "=4.5.40", features = ["derive"] } +scopeguard = { workspace = true } +sysinfo = { workspace = true } +verifysign = "=0.2.4" +windows = { workspace = true, features = [ + "Wdk_System_SystemServices", + "Win32_Security_Cryptography", + "Win32_Security", + "Win32_Storage_FileSystem", + "Win32_System_IO", + "Win32_System_Memory", + "Win32_System_Pipes", + "Win32_System_ProcessStatus", + "Win32_System_Services", + "Win32_System_Threading", + "Win32_UI_Shell", + "Win32_UI_WindowsAndMessaging", +] } +anyhow = { workspace = true } +base64 = { workspace = true } +tokio = { workspace = true, features = ["full"] } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } + +[build-dependencies] +embed-resource = "=3.0.6" + +[lints] +workspace = true diff --git a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/build.rs b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/build.rs new file mode 100644 index 00000000000..326929ec7c8 --- /dev/null +++ b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/build.rs @@ -0,0 +1,9 @@ +fn main() { + if std::env::var("CARGO_CFG_TARGET_OS").expect("to be set by cargo") == "windows" { + println!("cargo:rerun-if-changed=resources.rc"); + + embed_resource::compile("resources.rc", embed_resource::NONE) + .manifest_optional() + .expect("to compile resources"); + } +} diff --git a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/resources.rc b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/resources.rc new file mode 100644 index 00000000000..c300cc5d77f --- /dev/null +++ b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/resources.rc @@ -0,0 +1 @@ +1 ICON "../../resources/icon.ico" diff --git a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/main.rs b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/main.rs new file mode 100644 index 00000000000..036e04de16b --- /dev/null +++ b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/main.rs @@ -0,0 +1,13 @@ +#[cfg(target_os = "windows")] +mod windows; + +#[cfg(target_os = "windows")] +#[tokio::main] +async fn main() { + windows::main().await; +} + +#[cfg(not(target_os = "windows"))] +fn main() { + // Empty +} diff --git a/apps/desktop/desktop_native/chromium_importer/src/bin/bitwarden_chromium_import_helper.rs b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows.rs similarity index 98% rename from apps/desktop/desktop_native/chromium_importer/src/bin/bitwarden_chromium_import_helper.rs rename to apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows.rs index 5041028ed4e..4902d1f1d64 100644 --- a/apps/desktop/desktop_native/chromium_importer/src/bin/bitwarden_chromium_import_helper.rs +++ b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows.rs @@ -1,5 +1,3 @@ -// Hide everything inside a platform specific module to avoid clippy errors on other platforms -#[cfg(target_os = "windows")] mod windows_binary { use anyhow::{anyhow, Result}; use base64::{engine::general_purpose, Engine as _}; @@ -479,7 +477,7 @@ mod windows_binary { } } - pub async fn main() { + pub(crate) async fn main() { if ENABLE_DEVELOPER_LOGGING { init_logging(LOG_FILENAME.as_ref(), LevelFilter::DEBUG); } @@ -508,8 +506,4 @@ mod windows_binary { } } -#[tokio::main] -async fn main() { - #[cfg(target_os = "windows")] - windows_binary::main().await; -} +pub(crate) use windows_binary::*; diff --git a/apps/desktop/desktop_native/build.js b/apps/desktop/desktop_native/build.js index 9af3f832723..a7ed89a9c17 100644 --- a/apps/desktop/desktop_native/build.js +++ b/apps/desktop/desktop_native/build.js @@ -54,7 +54,7 @@ function buildImporterBinaries(target, release = true) { const bin = "bitwarden_chromium_import_helper"; const targetArg = target ? `--target ${target}` : ""; const releaseArg = release ? "--release" : ""; - child_process.execSync(`cargo build --bin ${bin} ${releaseArg} ${targetArg} --features windows-binary`, {stdio: 'inherit', cwd: path.join(__dirname, "chromium_importer")}); + child_process.execSync(`cargo build --bin ${bin} ${releaseArg} ${targetArg}`); if (target) { // Copy the resulting binary to the dist folder diff --git a/apps/desktop/desktop_native/chromium_importer/Cargo.toml b/apps/desktop/desktop_native/chromium_importer/Cargo.toml index 739382e2815..99246af3f90 100644 --- a/apps/desktop/desktop_native/chromium_importer/Cargo.toml +++ b/apps/desktop/desktop_native/chromium_importer/Cargo.toml @@ -29,10 +29,6 @@ security-framework = { workspace = true } [target.'cfg(target_os = "windows")'.dependencies] chacha20poly1305 = { workspace = true } -clap = { version = "=4.5.40", features = ["derive"] } -scopeguard = { workspace = true } -sysinfo = { workspace = true, optional = true } -verifysign = "=0.2.4" windows = { workspace = true, features = [ "Wdk_System_SystemServices", "Win32_Security_Cryptography", @@ -53,11 +49,3 @@ oo7 = { workspace = true } [lints] workspace = true - -[features] -windows-binary = ["dep:sysinfo"] - -[[bin]] -name = "bitwarden_chromium_import_helper" -path = "src/bin/bitwarden_chromium_import_helper.rs" -required-features = ["windows-binary"] diff --git a/apps/desktop/desktop_native/chromium_importer/README.md b/apps/desktop/desktop_native/chromium_importer/README.md index 9bacea4822a..cec477c34a3 100644 --- a/apps/desktop/desktop_native/chromium_importer/README.md +++ b/apps/desktop/desktop_native/chromium_importer/README.md @@ -14,8 +14,6 @@ The Windows **Application Bound Encryption (ABE)** subsystem consists of two mai - **client library** — a library that is part of the desktop client application - **bitwarden_chromium_import_helper.exe** — a password decryptor running as **ADMINISTRATOR** and later as **SYSTEM** -_(The name of the binary will be changed in the released product.)_ - See the last section for a concise summary of the entire process. ### Goal From 98849a5a65604039ea9bd3dffe66fe9be288a0c2 Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Fri, 31 Oct 2025 08:52:26 -0400 Subject: [PATCH 40/66] [PM-27214] Update SDK to version 365 (#17130) * update sdk version --- package-lock.json | 16 ++++++++-------- package.json | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index f6e33174737..da21f9ca730 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,8 +23,8 @@ "@angular/platform-browser": "19.2.14", "@angular/platform-browser-dynamic": "19.2.14", "@angular/router": "19.2.14", - "@bitwarden/commercial-sdk-internal": "0.2.0-main.357", - "@bitwarden/sdk-internal": "0.2.0-main.357", + "@bitwarden/commercial-sdk-internal": "0.2.0-main.365", + "@bitwarden/sdk-internal": "0.2.0-main.365", "@electron/fuses": "1.8.0", "@emotion/css": "11.13.5", "@koa/multer": "4.0.0", @@ -4607,9 +4607,9 @@ "link": true }, "node_modules/@bitwarden/commercial-sdk-internal": { - "version": "0.2.0-main.357", - "resolved": "https://registry.npmjs.org/@bitwarden/commercial-sdk-internal/-/commercial-sdk-internal-0.2.0-main.357.tgz", - "integrity": "sha512-eIArJelJKwG+aEGbtdhc5dKRBFopmyGJl+ClUQGJUFHzfrPGDcaSI04a/sSUK0NtbaxQOsf8qSvk+iKvISkKmw==", + "version": "0.2.0-main.365", + "resolved": "https://registry.npmjs.org/@bitwarden/commercial-sdk-internal/-/commercial-sdk-internal-0.2.0-main.365.tgz", + "integrity": "sha512-yRc2k29rKMxss6qH2TP91VcE6tNR6/A2ASZMj+Om2MEaanV82zcx89dkShh6RP0jXICM+c/m6BgGkmu+1Pcp8w==", "license": "BITWARDEN SOFTWARE DEVELOPMENT KIT LICENSE AGREEMENT", "dependencies": { "type-fest": "^4.41.0" @@ -4712,9 +4712,9 @@ "link": true }, "node_modules/@bitwarden/sdk-internal": { - "version": "0.2.0-main.357", - "resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.357.tgz", - "integrity": "sha512-qo8kCzrWNJP69HeI6WRyJMCFXYUJqLbaQCFoDgQkQa3ICrwpw5g9gW5y4P9FOa/DHdj8BgVbFGAX+YylbUb0/A==", + "version": "0.2.0-main.365", + "resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.365.tgz", + "integrity": "sha512-x0sqAuyknFOGf5ZfbuFTxL0olMiGyyLbJ10tXCYHnrkjdspdNm2BGZc64NQgXz5h+PH1Uwtow/01o/a4F0YTHw==", "license": "GPL-3.0", "dependencies": { "type-fest": "^4.41.0" diff --git a/package.json b/package.json index 12df878b5cf..31d7f424569 100644 --- a/package.json +++ b/package.json @@ -160,8 +160,8 @@ "@angular/platform-browser": "19.2.14", "@angular/platform-browser-dynamic": "19.2.14", "@angular/router": "19.2.14", - "@bitwarden/sdk-internal": "0.2.0-main.357", - "@bitwarden/commercial-sdk-internal": "0.2.0-main.357", + "@bitwarden/sdk-internal": "0.2.0-main.365", + "@bitwarden/commercial-sdk-internal": "0.2.0-main.365", "@electron/fuses": "1.8.0", "@emotion/css": "11.13.5", "@koa/multer": "4.0.0", From 6024e1d05fae003f491d5c08361d23c1d532ebe5 Mon Sep 17 00:00:00 2001 From: Leslie Tilton <23057410+Banrion@users.noreply.github.com> Date: Fri, 31 Oct 2025 08:46:37 -0500 Subject: [PATCH 41/66] [PM-26968] Save risk insights summary and metrics (#17132) * Update type guards * Add metric data types. Update places saving a risk insights report summary to save metrics * Fix types and test error * Fix critical report members * Update test case for null username in type-guard * Fix report application mapped data check --- .../helpers/type-guards/basic-type-guards.ts | 162 +++++++ .../risk-insights-type-guards.spec.ts | 87 +--- .../type-guards/risk-insights-type-guards.ts | 200 +++++++++ .../risk-insights/models/api-models.types.ts | 8 + .../models/api/risk-insights-metrics.api.ts | 42 ++ .../models/data/risk-insights-metrics.data.ts | 34 ++ .../models/domain/risk-insights-metrics.ts | 55 +++ .../risk-insights/models/mocks/mock-data.ts | 4 +- .../models/report-data-service.types.ts | 3 - .../risk-insights/models/report-models.ts | 2 +- .../models/view/risk-insights-metrics.view.ts | 52 +++ .../api/risk-insights-api.service.spec.ts | 32 +- .../services/api/risk-insights-api.service.ts | 8 +- .../risk-insights-encryption.service.ts | 11 +- ...risk-insights-orchestrator.service.spec.ts | 16 + .../risk-insights-orchestrator.service.ts | 307 ++++++++++---- .../risk-insights-report.service.spec.ts | 15 +- .../domain/risk-insights-report.service.ts | 91 ++-- .../domain/risk-insights-type-guards.ts | 397 ------------------ .../app-table-row-scrollable.component.html | 6 +- 20 files changed, 924 insertions(+), 608 deletions(-) create mode 100644 bitwarden_license/bit-common/src/dirt/reports/risk-insights/helpers/type-guards/basic-type-guards.ts rename bitwarden_license/bit-common/src/dirt/reports/risk-insights/{services/domain => helpers/type-guards}/risk-insights-type-guards.spec.ts (85%) create mode 100644 bitwarden_license/bit-common/src/dirt/reports/risk-insights/helpers/type-guards/risk-insights-type-guards.ts create mode 100644 bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/api/risk-insights-metrics.api.ts create mode 100644 bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/data/risk-insights-metrics.data.ts create mode 100644 bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/domain/risk-insights-metrics.ts create mode 100644 bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/view/risk-insights-metrics.view.ts delete mode 100644 bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-type-guards.ts diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/helpers/type-guards/basic-type-guards.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/helpers/type-guards/basic-type-guards.ts new file mode 100644 index 00000000000..47d638b41e5 --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/helpers/type-guards/basic-type-guards.ts @@ -0,0 +1,162 @@ +// I'm leaving this here as an example of further improvements we can make to check types +// We can define a nominal type for PositiveSafeNumber to enhance type safety +// const POSITIVE_SAFE_NUMBER_SYMBOL: unique symbol = Symbol("POSITIVE_SAFE_NUMBER"); + +// This file sets up basic types and guards for values we expect from decrypted data + +// Basic types +export type BoundedString = string; +export type BoundedStringOrNull = BoundedString | null; +export type PositiveSafeNumber = number; +export type BoundedArray = T[]; +export type DateOrNull = Date | null; +export type DateString = string; +export type DateStringOrNull = DateString | null; + +// Constants +/** + * Security limits for validation (prevent DoS attacks and ensure reasonable data sizes) + */ +export const BOUNDED_STRING_MAX_LENGTH = 1000; // Reasonable limit for names, emails, GUIDs +export const BOUNDED_ARRAY_MAX_LENGTH = 50000; // Reasonable limit for report arrays +export const BOUNDED_NUMBER_MAX_COUNT = 10000000; // 10 million - reasonable upper bound for count fields + +// Type guard methods +export function isBoolean(value: unknown): value is boolean { + return typeof value === "boolean"; +} + +export function isBoundedPositiveNumber(value: unknown): value is PositiveSafeNumber { + return ( + typeof value === "number" && + Number.isFinite(value) && + Number.isSafeInteger(value) && + value >= 0 && + value <= BOUNDED_NUMBER_MAX_COUNT + ); +} + +export function isBoundedString(value: unknown): value is BoundedString { + return typeof value === "string" && value.length > 0 && value.length <= BOUNDED_STRING_MAX_LENGTH; +} + +export function isBoundedStringOrNull(value: unknown): value is BoundedStringOrNull { + return value == null || isBoundedString(value); +} + +export const isBoundedStringArray = createBoundedArrayGuard(isBoundedString); + +export function isBoundedArray(arr: unknown): arr is BoundedArray { + return Array.isArray(arr) && arr.length < BOUNDED_ARRAY_MAX_LENGTH; +} + +/** + * A type guard to check if a value is a valid Date object + * @param value The value to check + * @returns True if the value is a valid Date object, false otherwise + */ +export function isDate(value: unknown): value is Date { + return value instanceof Date && !isNaN(value.getTime()); +} + +/** + * A type guard to check if a value is a valid Date object or null + * @param value The value to check + * @returns True if the value is a valid Date object, false otherwise + */ +export function isDateOrNull(value: unknown): value is DateOrNull { + return value === null || isDate(value); +} + +/** + * A type guard to check if a value is a valid date string + * This also checks that the string value can be correctly parsed into a valid Date object + * @param value The value to check + * @returns True if the value is a valid date string, false otherwise + */ +export function isDateString(value: unknown): value is DateString { + if (typeof value !== "string") { + return false; + } + + // Attempt to create a Date object from the string. + const date = new Date(value); + + // Return true only if the string produced a valid date. + // We use `getTime()` to check for validity, as `new Date('invalid')` returns `NaN` for its time value. + return !isNaN(date.getTime()); +} + +/** + * A type guard to check if a value is a valid date string or null + * This also checks that the string value can be correctly parsed into a valid Date object + * @param value The value to check + * @returns True if the value is a valid date string or null, false otherwise + */ +export function isDateStringOrNull(value: unknown): value is DateStringOrNull { + return value === null || isDateString(value); +} + +/** + * A higher-order function that takes a type guard for T and returns a + * new type guard for an array of T. + */ +export function createBoundedArrayGuard(isType: (item: unknown) => item is T) { + return function (arr: unknown): arr is T[] { + return isBoundedArray(arr) && arr.every(isType); + }; +} + +type TempObject = Record; + +/** + * + * @param validators + * @returns + */ +export function createValidator(validators: { + [K in keyof T]: (value: unknown) => value is T[K]; +}): (obj: unknown) => obj is T { + const keys = Object.keys(validators) as (keyof T)[]; + + return function (obj: unknown): obj is T { + if (typeof obj !== "object" || obj === null) { + return false; + } + + if (Object.getPrototypeOf(obj) !== Object.prototype) { + return false; + } + + // Prevent dangerous properties that could be used for prototype pollution + // Check for __proto__, constructor, and prototype as own properties + const dangerousKeys = ["__proto__", "constructor", "prototype"]; + for (const key of dangerousKeys) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + return false; + } + } + + // Type cast to TempObject for key checks + const tempObj = obj as TempObject; + + // Commenting out for compatibility of removed keys from data + // Leaving the code commented for now for further discussion + // Check for unexpected properties + // const actualKeys = Object.keys(tempObj); + // const expectedKeys = new Set(keys as string[]); + // if (actualKeys.some((key) => !expectedKeys.has(key))) { + // return false; + // } + + // Check for each property's existence and type + return keys.every((key) => { + // Use 'in' to check for property existence before accessing it + if (!(key in tempObj)) { + return false; + } + // Pass the value to its specific validator + return validators[key](tempObj[key]); + }); + }; +} diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-type-guards.spec.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/helpers/type-guards/risk-insights-type-guards.spec.ts similarity index 85% rename from bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-type-guards.spec.ts rename to bitwarden_license/bit-common/src/dirt/reports/risk-insights/helpers/type-guards/risk-insights-type-guards.spec.ts index 3e32937d9e2..f48437ab006 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-type-guards.spec.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/helpers/type-guards/risk-insights-type-guards.spec.ts @@ -164,33 +164,6 @@ describe("Risk Insights Type Guards", () => { expect(validateOrganizationReportSummary(validData)).toEqual(validData); }); - it("should throw error for missing totalMemberCount", () => { - const invalidData = { - totalApplicationCount: 5, - totalAtRiskMemberCount: 2, - totalAtRiskApplicationCount: 1, - totalCriticalApplicationCount: 3, - totalCriticalMemberCount: 4, - totalCriticalAtRiskMemberCount: 1, - totalCriticalAtRiskApplicationCount: 1, - }; - - expect(() => validateOrganizationReportSummary(invalidData)).toThrow( - /Invalid OrganizationReportSummary: missing or invalid fields: totalMemberCount \(number\)/, - ); - }); - - it("should throw error for multiple missing fields", () => { - const invalidData = { - totalMemberCount: 10, - // missing multiple fields - }; - - expect(() => validateOrganizationReportSummary(invalidData)).toThrow( - /Invalid OrganizationReportSummary: missing or invalid fields:.*totalApplicationCount/, - ); - }); - it("should throw error for invalid field types", () => { const invalidData = { totalMemberCount: "10", // should be number @@ -204,7 +177,7 @@ describe("Risk Insights Type Guards", () => { }; expect(() => validateOrganizationReportSummary(invalidData)).toThrow( - /Invalid OrganizationReportSummary/, + /Invalid report summary/, ); }); }); @@ -254,7 +227,7 @@ describe("Risk Insights Type Guards", () => { ]; expect(() => validateOrganizationReportApplicationArray(invalidData)).toThrow( - "Invalid date string: invalid-date", + "Invalid application data: array contains 1 invalid OrganizationReportApplication element(s) at indices: 0", ); }); @@ -339,10 +312,10 @@ describe("Risk Insights Type Guards", () => { expect(isMemberDetails(invalidData)).toBe(false); }); - it("should return true for undefined userName", () => { + it("should return true for null userName", () => { const validData = { userGuid: "user-1", - userName: undefined as string | undefined, + userName: null as string | null, email: "john@example.com", cipherId: "cipher-1", }; @@ -369,17 +342,6 @@ describe("Risk Insights Type Guards", () => { expect(isMemberDetails(invalidData)).toBe(false); }); - it("should return false for objects with unexpected properties", () => { - const invalidData = { - userGuid: "user-1", - userName: "John Doe", - email: "john@example.com", - cipherId: "cipher-1", - unexpectedProperty: "should fail", - }; - expect(isMemberDetails(invalidData)).toBe(false); - }); - it("should return false for prototype pollution attempts", () => { const invalidData = { userGuid: "user-1", @@ -482,22 +444,6 @@ describe("Risk Insights Type Guards", () => { }; expect(isApplicationHealthReportDetail(invalidData)).toBe(false); }); - - it("should return false for objects with unexpected properties", () => { - const invalidData = { - applicationName: "Test App", - passwordCount: 10, - atRiskPasswordCount: 2, - atRiskCipherIds: ["cipher-1"], - memberCount: 5, - atRiskMemberCount: 1, - memberDetails: [] as MemberDetails[], - atRiskMemberDetails: [] as MemberDetails[], - cipherIds: ["cipher-1"], - injectedProperty: "malicious", - }; - expect(isApplicationHealthReportDetail(invalidData)).toBe(false); - }); }); describe("isOrganizationReportSummary", () => { @@ -556,21 +502,6 @@ describe("Risk Insights Type Guards", () => { }; expect(isOrganizationReportSummary(invalidData)).toBe(false); }); - - it("should return false for objects with unexpected properties", () => { - const invalidData = { - totalMemberCount: 10, - totalApplicationCount: 5, - totalAtRiskMemberCount: 2, - totalAtRiskApplicationCount: 1, - totalCriticalApplicationCount: 3, - totalCriticalMemberCount: 4, - totalCriticalAtRiskMemberCount: 1, - totalCriticalAtRiskApplicationCount: 1, - extraField: "should be rejected", - }; - expect(isOrganizationReportSummary(invalidData)).toBe(false); - }); }); describe("isOrganizationReportApplication", () => { @@ -610,16 +541,6 @@ describe("Risk Insights Type Guards", () => { expect(isOrganizationReportApplication(validData)).toBe(true); }); - it("should return false for objects with unexpected properties", () => { - const invalidData = { - applicationName: "Test App", - isCritical: true, - reviewedDate: null as Date | null, - injectedProperty: "malicious", - }; - expect(isOrganizationReportApplication(invalidData)).toBe(false); - }); - it("should return false for prototype pollution attempts via __proto__", () => { const invalidData = { applicationName: "Test App", diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/helpers/type-guards/risk-insights-type-guards.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/helpers/type-guards/risk-insights-type-guards.ts new file mode 100644 index 00000000000..68a1594ff5c --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/helpers/type-guards/risk-insights-type-guards.ts @@ -0,0 +1,200 @@ +import { + ApplicationHealthReportDetail, + MemberDetails, + OrganizationReportApplication, + OrganizationReportSummary, +} from "../../models"; + +import { + createBoundedArrayGuard, + createValidator, + isBoolean, + isBoundedString, + isBoundedStringArray, + isBoundedStringOrNull, + isBoundedPositiveNumber, + BOUNDED_ARRAY_MAX_LENGTH, + isDate, + isDateString, +} from "./basic-type-guards"; + +// Risk Insights specific type guards + +/** + * Type guard to validate MemberDetails structure + * Exported for testability + * Strict validation: rejects objects with unexpected properties and prototype pollution + */ +export const isMemberDetails = createValidator({ + userGuid: isBoundedString, + userName: isBoundedStringOrNull, + email: isBoundedString, + cipherId: isBoundedString, +}); +export const isMemberDetailsArray = createBoundedArrayGuard(isMemberDetails); + +/** + * Type guard to validate ApplicationHealthReportDetail structure + * Exported for testability + * Strict validation: rejects objects with unexpected properties and prototype pollution + */ +export const isApplicationHealthReportDetail = createValidator({ + applicationName: isBoundedString, + atRiskCipherIds: isBoundedStringArray, + atRiskMemberCount: isBoundedPositiveNumber, + atRiskMemberDetails: isMemberDetailsArray, + atRiskPasswordCount: isBoundedPositiveNumber, + cipherIds: isBoundedStringArray, + memberCount: isBoundedPositiveNumber, + memberDetails: isMemberDetailsArray, + passwordCount: isBoundedPositiveNumber, +}); +export const isApplicationHealthReportDetailArray = createBoundedArrayGuard( + isApplicationHealthReportDetail, +); + +/** + * Type guard to validate OrganizationReportSummary structure + * Exported for testability + * Strict validation: rejects objects with unexpected properties and prototype pollution + */ +export const isOrganizationReportSummary = createValidator({ + totalMemberCount: isBoundedPositiveNumber, + totalApplicationCount: isBoundedPositiveNumber, + totalAtRiskMemberCount: isBoundedPositiveNumber, + totalAtRiskApplicationCount: isBoundedPositiveNumber, + totalCriticalApplicationCount: isBoundedPositiveNumber, + totalCriticalMemberCount: isBoundedPositiveNumber, + totalCriticalAtRiskMemberCount: isBoundedPositiveNumber, + totalCriticalAtRiskApplicationCount: isBoundedPositiveNumber, +}); + +// Adding to support reviewedDate casting for mapping until the date is saved as a string +function isValidDateOrNull(value: unknown): value is Date | null { + return value == null || isDate(value) || isDateString(value); +} + +/** + * Type guard to validate OrganizationReportApplication structure + * Exported for testability + * Strict validation: rejects objects with unexpected properties and prototype pollution + */ +export const isOrganizationReportApplication = createValidator({ + applicationName: isBoundedString, + isCritical: isBoolean, + // ReviewedDate is currently being saved to the database as a Date type + // We can improve this when OrganizationReportApplication is updated + // to use the Domain, Api, and View model pattern to convert the type to a string + // for storage instead of Date + // Should eventually be changed to isDateStringOrNull + reviewedDate: isValidDateOrNull, +}); +export const isOrganizationReportApplicationArray = createBoundedArrayGuard( + isOrganizationReportApplication, +); + +/** + * Validates and returns an array of ApplicationHealthReportDetail + * @throws Error if validation fails + */ +export function validateApplicationHealthReportDetailArray( + data: unknown, +): ApplicationHealthReportDetail[] { + if (!Array.isArray(data)) { + throw new Error( + "Invalid report data: expected array of ApplicationHealthReportDetail, received non-array", + ); + } + + if (data.length > BOUNDED_ARRAY_MAX_LENGTH) { + throw new Error( + `Invalid report data: array length ${data.length} exceeds maximum allowed length ${BOUNDED_ARRAY_MAX_LENGTH}`, + ); + } + + const invalidItems = data + .map((item, index) => ({ item, index })) + .filter(({ item }) => !isApplicationHealthReportDetail(item)); + + if (invalidItems.length > 0) { + const invalidIndices = invalidItems.map(({ index }) => index).join(", "); + throw new Error( + `Invalid report data: array contains ${invalidItems.length} invalid ApplicationHealthReportDetail element(s) at indices: ${invalidIndices}`, + ); + } + + if (!isApplicationHealthReportDetailArray(data)) { + // Throw for type casting return + // Should never get here + throw new Error("Invalid report data"); + } + + return data; +} + +/** + * Validates and returns OrganizationReportSummary + * @throws Error if validation fails + */ +export function validateOrganizationReportSummary(data: unknown): OrganizationReportSummary { + if (!isOrganizationReportSummary(data)) { + throw new Error("Invalid report summary"); + } + + return data; +} + +/** + * Validates and returns an array of OrganizationReportApplication + * @throws Error if validation fails + */ +export function validateOrganizationReportApplicationArray( + data: unknown, +): OrganizationReportApplication[] { + if (!Array.isArray(data)) { + throw new Error( + "Invalid application data: expected array of OrganizationReportApplication, received non-array", + ); + } + + if (data.length > BOUNDED_ARRAY_MAX_LENGTH) { + throw new Error( + `Invalid application data: array length ${data.length} exceeds maximum allowed length ${BOUNDED_ARRAY_MAX_LENGTH}`, + ); + } + + const invalidItems = data + .map((item, index) => ({ item, index })) + .filter(({ item }) => !isOrganizationReportApplication(item)); + + if (invalidItems.length > 0) { + const invalidIndices = invalidItems.map(({ index }) => index).join(", "); + throw new Error( + `Invalid application data: array contains ${invalidItems.length} invalid OrganizationReportApplication element(s) at indices: ${invalidIndices}`, + ); + } + + const mappedData = data.map((item) => ({ + ...item, + reviewedDate: item.reviewedDate + ? item.reviewedDate instanceof Date + ? item.reviewedDate + : (() => { + const date = new Date(item.reviewedDate); + if (!isDate(date)) { + throw new Error(`Invalid date string: ${item.reviewedDate}`); + } + return date; + })() + : null, + })); + + if (!isOrganizationReportApplicationArray(mappedData)) { + // Throw for type casting return + // Should never get here + throw new Error("Invalid application data"); + } + + // Convert string dates to Date objects for reviewedDate + return mappedData; +} diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/api-models.types.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/api-models.types.ts index 529789ebb8d..a5b8e86dcce 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/api-models.types.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/api-models.types.ts @@ -4,6 +4,7 @@ import { OrganizationId, OrganizationReportId } from "@bitwarden/common/types/gu import { createNewSummaryData } from "../helpers"; +import { RiskInsightsMetricsData } from "./data/risk-insights-metrics.data"; import { OrganizationReportSummary, PasswordHealthReportApplicationId } from "./report-models"; // -------------------- Password Health Report Models -------------------- @@ -41,6 +42,7 @@ export interface SaveRiskInsightsReportRequest { reportData: string; summaryData: string; applicationData: string; + metrics: RiskInsightsMetricsData; contentEncryptionKey: string; }; } @@ -136,6 +138,12 @@ export interface UpdateRiskInsightsApplicationDataRequest { applicationData: string; }; } +export interface UpdateRiskInsightsSummaryDataRequest { + data: { + summaryData: string; + metrics: RiskInsightsMetricsData; + }; +} export class UpdateRiskInsightsApplicationDataResponse extends BaseResponse { constructor(response: any) { super(response); diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/api/risk-insights-metrics.api.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/api/risk-insights-metrics.api.ts new file mode 100644 index 00000000000..681df6cdcd5 --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/api/risk-insights-metrics.api.ts @@ -0,0 +1,42 @@ +import { BaseResponse } from "@bitwarden/common/models/response/base.response"; + +export class RiskInsightsMetricsApi extends BaseResponse { + totalApplicationCount: number = 0; + totalAtRiskApplicationCount: number = 0; + totalCriticalApplicationCount: number = 0; + totalCriticalAtRiskApplicationCount: number = 0; + totalMemberCount: number = 0; + totalAtRiskMemberCount: number = 0; + totalCriticalMemberCount: number = 0; + totalCriticalAtRiskMemberCount: number = 0; + totalPasswordCount: number = 0; + totalAtRiskPasswordCount: number = 0; + totalCriticalPasswordCount: number = 0; + totalCriticalAtRiskPasswordCount: number = 0; + + constructor(data: any) { + super(data); + if (data == null) { + return; + } + + this.totalApplicationCount = this.getResponseProperty("totalApplicationCount"); + this.totalAtRiskApplicationCount = this.getResponseProperty("totalAtRiskApplicationCount"); + this.totalCriticalApplicationCount = this.getResponseProperty("totalCriticalApplicationCount"); + this.totalCriticalAtRiskApplicationCount = this.getResponseProperty( + "totalCriticalAtRiskApplicationCount", + ); + this.totalMemberCount = this.getResponseProperty("totalMemberCount"); + this.totalAtRiskMemberCount = this.getResponseProperty("totalAtRiskMemberCount"); + this.totalCriticalMemberCount = this.getResponseProperty("totalCriticalMemberCount"); + this.totalCriticalAtRiskMemberCount = this.getResponseProperty( + "totalCriticalAtRiskMemberCount", + ); + this.totalPasswordCount = this.getResponseProperty("totalPasswordCount"); + this.totalAtRiskPasswordCount = this.getResponseProperty("totalAtRiskPasswordCount"); + this.totalCriticalPasswordCount = this.getResponseProperty("totalCriticalPasswordCount"); + this.totalCriticalAtRiskPasswordCount = this.getResponseProperty( + "totalCriticalAtRiskPasswordCount", + ); + } +} diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/data/risk-insights-metrics.data.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/data/risk-insights-metrics.data.ts new file mode 100644 index 00000000000..382004e02a2 --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/data/risk-insights-metrics.data.ts @@ -0,0 +1,34 @@ +import { RiskInsightsMetricsApi } from "../api/risk-insights-metrics.api"; + +export class RiskInsightsMetricsData { + totalApplicationCount: number = 0; + totalAtRiskApplicationCount: number = 0; + totalCriticalApplicationCount: number = 0; + totalCriticalAtRiskApplicationCount: number = 0; + totalMemberCount: number = 0; + totalAtRiskMemberCount: number = 0; + totalCriticalMemberCount: number = 0; + totalCriticalAtRiskMemberCount: number = 0; + totalPasswordCount: number = 0; + totalAtRiskPasswordCount: number = 0; + totalCriticalPasswordCount: number = 0; + totalCriticalAtRiskPasswordCount: number = 0; + + constructor(data?: RiskInsightsMetricsApi) { + if (data == null) { + return; + } + this.totalApplicationCount = data.totalApplicationCount; + this.totalAtRiskApplicationCount = data.totalAtRiskApplicationCount; + this.totalCriticalApplicationCount = data.totalCriticalApplicationCount; + this.totalCriticalAtRiskApplicationCount = data.totalCriticalAtRiskApplicationCount; + this.totalMemberCount = data.totalMemberCount; + this.totalAtRiskMemberCount = data.totalAtRiskMemberCount; + this.totalCriticalMemberCount = data.totalCriticalMemberCount; + this.totalCriticalAtRiskMemberCount = data.totalCriticalAtRiskMemberCount; + this.totalPasswordCount = data.totalPasswordCount; + this.totalAtRiskPasswordCount = data.totalAtRiskPasswordCount; + this.totalCriticalPasswordCount = data.totalCriticalPasswordCount; + this.totalCriticalAtRiskPasswordCount = data.totalCriticalAtRiskPasswordCount; + } +} diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/domain/risk-insights-metrics.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/domain/risk-insights-metrics.ts new file mode 100644 index 00000000000..309e2aac2ea --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/domain/risk-insights-metrics.ts @@ -0,0 +1,55 @@ +import Domain from "@bitwarden/common/platform/models/domain/domain-base"; + +import { RiskInsightsMetricsData } from "../data/risk-insights-metrics.data"; + +export class RiskInsightsMetrics extends Domain { + totalApplicationCount: number = 0; + totalAtRiskApplicationCount: number = 0; + totalCriticalApplicationCount: number = 0; + totalCriticalAtRiskApplicationCount: number = 0; + totalMemberCount: number = 0; + totalAtRiskMemberCount: number = 0; + totalCriticalMemberCount: number = 0; + totalCriticalAtRiskMemberCount: number = 0; + totalPasswordCount: number = 0; + totalAtRiskPasswordCount: number = 0; + totalCriticalPasswordCount: number = 0; + totalCriticalAtRiskPasswordCount: number = 0; + + constructor(data?: RiskInsightsMetricsData) { + super(); + if (data == null) { + return; + } + this.totalApplicationCount = data.totalApplicationCount; + this.totalAtRiskApplicationCount = data.totalAtRiskApplicationCount; + this.totalCriticalApplicationCount = data.totalCriticalApplicationCount; + this.totalCriticalAtRiskApplicationCount = data.totalCriticalAtRiskApplicationCount; + this.totalMemberCount = data.totalMemberCount; + this.totalAtRiskMemberCount = data.totalAtRiskMemberCount; + this.totalCriticalMemberCount = data.totalCriticalMemberCount; + this.totalCriticalAtRiskMemberCount = data.totalCriticalAtRiskMemberCount; + this.totalPasswordCount = data.totalPasswordCount; + this.totalAtRiskPasswordCount = data.totalAtRiskPasswordCount; + this.totalCriticalPasswordCount = data.totalCriticalPasswordCount; + this.totalCriticalAtRiskPasswordCount = data.totalCriticalAtRiskPasswordCount; + } + + toRiskInsightsMetricsData(): RiskInsightsMetricsData { + const m = new RiskInsightsMetricsData(); + m.totalApplicationCount = this.totalApplicationCount; + m.totalAtRiskApplicationCount = this.totalAtRiskApplicationCount; + m.totalCriticalApplicationCount = this.totalCriticalApplicationCount; + m.totalCriticalAtRiskApplicationCount = this.totalCriticalAtRiskApplicationCount; + m.totalMemberCount = this.totalMemberCount; + m.totalAtRiskMemberCount = this.totalAtRiskMemberCount; + m.totalCriticalMemberCount = this.totalCriticalMemberCount; + m.totalCriticalAtRiskMemberCount = this.totalCriticalAtRiskMemberCount; + m.totalPasswordCount = this.totalPasswordCount; + m.totalAtRiskPasswordCount = this.totalAtRiskPasswordCount; + m.totalCriticalPasswordCount = this.totalCriticalPasswordCount; + m.totalCriticalAtRiskPasswordCount = this.totalCriticalAtRiskPasswordCount; + + return m; + } +} diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/mocks/mock-data.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/mocks/mock-data.ts index 022b0aa42fb..33dd8676223 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/mocks/mock-data.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/mocks/mock-data.ts @@ -92,8 +92,8 @@ export const mockApplicationData: OrganizationReportApplication[] = [ ]; export const mockEnrichedReportData: ApplicationHealthReportDetailEnriched[] = [ - { ...mockApplication1, isMarkedAsCritical: true, ciphers: [] }, - { ...mockApplication2, isMarkedAsCritical: false, ciphers: [] }, + { ...mockApplication1, isMarkedAsCritical: true }, + { ...mockApplication2, isMarkedAsCritical: false }, ]; export const mockCipherViews: CipherView[] = [ diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-data-service.types.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-data-service.types.ts index 6196c788ecd..77a3cea419b 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-data-service.types.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-data-service.types.ts @@ -1,5 +1,3 @@ -import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; - import { ApplicationHealthReportDetail, OrganizationReportApplication, @@ -8,7 +6,6 @@ import { export type ApplicationHealthReportDetailEnriched = ApplicationHealthReportDetail & { isMarkedAsCritical: boolean; - ciphers: CipherView[]; }; export interface RiskInsightsEnrichedData { reportData: ApplicationHealthReportDetailEnriched[]; diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-models.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-models.ts index 497c10dbcad..cada0acb694 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-models.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-models.ts @@ -15,7 +15,7 @@ import { ExposedPasswordDetail, WeakPasswordDetail } from "./password-health"; */ export type MemberDetails = { userGuid: string; - userName?: string; + userName: string | null; email: string; cipherId: string; }; diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/view/risk-insights-metrics.view.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/view/risk-insights-metrics.view.ts new file mode 100644 index 00000000000..87b92c32fe4 --- /dev/null +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/view/risk-insights-metrics.view.ts @@ -0,0 +1,52 @@ +import { Jsonify } from "type-fest"; + +import { View } from "@bitwarden/common/models/view/view"; + +import { RiskInsightsMetrics } from "../domain/risk-insights-metrics"; + +export class RiskInsightsMetricsView implements View { + totalApplicationCount: number = 0; + totalAtRiskApplicationCount: number = 0; + totalCriticalApplicationCount: number = 0; + totalCriticalAtRiskApplicationCount: number = 0; + totalMemberCount: number = 0; + totalAtRiskMemberCount: number = 0; + totalCriticalMemberCount: number = 0; + totalCriticalAtRiskMemberCount: number = 0; + totalPasswordCount: number = 0; + totalAtRiskPasswordCount: number = 0; + totalCriticalPasswordCount: number = 0; + totalCriticalAtRiskPasswordCount: number = 0; + + constructor(data?: RiskInsightsMetrics) { + if (data == null) { + return; + } + this.totalApplicationCount = data.totalApplicationCount; + this.totalAtRiskApplicationCount = data.totalAtRiskApplicationCount; + this.totalCriticalApplicationCount = data.totalCriticalApplicationCount; + this.totalCriticalAtRiskApplicationCount = data.totalCriticalAtRiskApplicationCount; + this.totalMemberCount = data.totalMemberCount; + this.totalAtRiskMemberCount = data.totalAtRiskMemberCount; + this.totalCriticalMemberCount = data.totalCriticalMemberCount; + this.totalCriticalAtRiskMemberCount = data.totalCriticalAtRiskMemberCount; + this.totalPasswordCount = data.totalPasswordCount; + this.totalAtRiskPasswordCount = data.totalAtRiskPasswordCount; + this.totalCriticalPasswordCount = data.totalCriticalPasswordCount; + this.totalCriticalAtRiskPasswordCount = data.totalCriticalAtRiskPasswordCount; + } + + toJSON() { + return this; + } + + static fromJSON( + obj: Partial>, + ): RiskInsightsMetricsView | undefined { + return Object.assign(new RiskInsightsMetricsView(), obj); + } + + // toSdkRiskInsightsMetricsView(): SdkRiskInsightsMetricsView {} + + // static fromRiskInsightsMetricsView(obj?: SdkRiskInsightsMetricsView): RiskInsightsMetricsView | undefined {} +} diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.spec.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.spec.ts index 879563af526..59764bc38a3 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.spec.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.spec.ts @@ -15,6 +15,7 @@ import { SaveRiskInsightsReportRequest, SaveRiskInsightsReportResponse, } from "../../models/api-models.types"; +import { RiskInsightsMetrics } from "../../models/domain/risk-insights-metrics"; import { mockApplicationData, mockReportData, mockSummaryData } from "../../models/mocks/mock-data"; import { RiskInsightsApiService } from "../api/risk-insights-api.service"; @@ -33,6 +34,20 @@ describe("RiskInsightsApiService", () => { const mockSummaryEnc = makeEncString(JSON.stringify(mockSummaryData)); const mockApplicationsEnc = makeEncString(JSON.stringify(mockApplicationData)); + const mockMetrics: RiskInsightsMetrics = new RiskInsightsMetrics(); + mockMetrics.totalApplicationCount = 3; + mockMetrics.totalAtRiskApplicationCount = 1; + mockMetrics.totalAtRiskMemberCount = 2; + mockMetrics.totalAtRiskPasswordCount = 1; + mockMetrics.totalCriticalApplicationCount = 1; + mockMetrics.totalCriticalAtRiskApplicationCount = 1; + mockMetrics.totalCriticalMemberCount = 1; + mockMetrics.totalCriticalAtRiskMemberCount = 1; + mockMetrics.totalCriticalPasswordCount = 0; + mockMetrics.totalCriticalAtRiskPasswordCount = 0; + mockMetrics.totalMemberCount = 5; + mockMetrics.totalPasswordCount = 2; + const mockSaveRiskInsightsReportRequest: SaveRiskInsightsReportRequest = { data: { organizationId: orgId, @@ -41,6 +56,7 @@ describe("RiskInsightsApiService", () => { summaryData: mockReportEnc.decryptedValue ?? "", applicationData: mockReportEnc.decryptedValue ?? "", contentEncryptionKey: mockReportKey.decryptedValue ?? "", + metrics: mockMetrics.toRiskInsightsMetricsData(), }, }; @@ -191,12 +207,24 @@ describe("RiskInsightsApiService", () => { mockApiService.send.mockResolvedValueOnce(undefined); - const result = await firstValueFrom(service.updateRiskInsightsSummary$(data, orgId, reportId)); + const result = await firstValueFrom( + service.updateRiskInsightsSummary$(reportId, orgId, { + data: { + summaryData: data.encryptedSummaryData.encryptedString!, + metrics: mockMetrics.toRiskInsightsMetricsData(), + }, + }), + ); expect(mockApiService.send).toHaveBeenCalledWith( "PATCH", `/reports/organizations/${orgId.toString()}/data/summary/${reportId.toString()}`, - data, + { + summaryData: data.encryptedSummaryData.encryptedString!, + metrics: mockMetrics.toRiskInsightsMetricsData(), + reportId, + organizationId: orgId, + }, true, true, ); diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.ts index d1896f487b2..5b094e8dec0 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.ts @@ -5,9 +5,9 @@ import { ErrorResponse } from "@bitwarden/common/models/response/error.response" import { OrganizationId, OrganizationReportId } from "@bitwarden/common/types/guid"; import { - EncryptedDataWithKey, UpdateRiskInsightsApplicationDataRequest, UpdateRiskInsightsApplicationDataResponse, + UpdateRiskInsightsSummaryDataRequest, } from "../../models"; import { GetRiskInsightsApplicationDataResponse, @@ -73,14 +73,14 @@ export class RiskInsightsApiService { } updateRiskInsightsSummary$( - summaryData: EncryptedDataWithKey, - organizationId: OrganizationId, reportId: OrganizationReportId, + organizationId: OrganizationId, + request: UpdateRiskInsightsSummaryDataRequest, ): Observable { const dbResponse = this.apiService.send( "PATCH", `/reports/organizations/${organizationId.toString()}/data/summary/${reportId.toString()}`, - summaryData, + { ...request.data, reportId: reportId, organizationId }, true, true, ); diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-encryption.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-encryption.service.ts index abeae1fdb29..99ac2276a3e 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-encryption.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-encryption.service.ts @@ -9,6 +9,11 @@ import { KeyService } from "@bitwarden/key-management"; import { LogService } from "@bitwarden/logging"; import { createNewSummaryData } from "../../helpers"; +import { + validateApplicationHealthReportDetailArray, + validateOrganizationReportApplicationArray, + validateOrganizationReportSummary, +} from "../../helpers/type-guards/risk-insights-type-guards"; import { ApplicationHealthReportDetail, DecryptedReportData, @@ -18,12 +23,6 @@ import { OrganizationReportSummary, } from "../../models"; -import { - validateApplicationHealthReportDetailArray, - validateOrganizationReportApplicationArray, - validateOrganizationReportSummary, -} from "./risk-insights-type-guards"; - export class RiskInsightsEncryptionService { constructor( private keyService: KeyService, diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.spec.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.spec.ts index 7606e3af7f3..9894c6522a8 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.spec.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.spec.ts @@ -11,6 +11,7 @@ import { LogService } from "@bitwarden/logging"; import { createNewSummaryData } from "../../helpers"; import { RiskInsightsData, SaveRiskInsightsReportResponse } from "../../models"; +import { RiskInsightsMetrics } from "../../models/domain/risk-insights-metrics"; import { mockMemberCipherDetailsResponse } from "../../models/mocks/member-cipher-details-response.mock"; import { mockApplicationData, @@ -182,6 +183,20 @@ describe("RiskInsightsOrchestratorService", () => { // Act service.generateReport(); + const metricsData = new RiskInsightsMetrics(); + metricsData.totalApplicationCount = 3; + metricsData.totalAtRiskApplicationCount = 1; + metricsData.totalAtRiskMemberCount = 2; + metricsData.totalAtRiskPasswordCount = 1; + metricsData.totalCriticalApplicationCount = 1; + metricsData.totalCriticalAtRiskApplicationCount = 1; + metricsData.totalCriticalMemberCount = 1; + metricsData.totalCriticalAtRiskMemberCount = 1; + metricsData.totalCriticalPasswordCount = 0; + metricsData.totalCriticalAtRiskPasswordCount = 0; + metricsData.totalMemberCount = 5; + metricsData.totalPasswordCount = 2; + // Assert service.rawReportData$.subscribe((state) => { if (!state.loading && state.data) { @@ -193,6 +208,7 @@ describe("RiskInsightsOrchestratorService", () => { mockEnrichedReportData, mockSummaryData, mockApplicationData, + metricsData, { organizationId: mockOrgId, userId: mockUserId }, ); expect(state.data.reportData).toEqual(mockEnrichedReportData); diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts index 79d90f9c8fd..472aa7f5f68 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts @@ -47,11 +47,13 @@ import { ApplicationHealthReportDetailEnriched, PasswordHealthReportApplicationsResponse, } from "../../models"; +import { RiskInsightsMetrics } from "../../models/domain/risk-insights-metrics"; import { RiskInsightsEnrichedData } from "../../models/report-data-service.types"; import { CipherHealthReport, MemberDetails, OrganizationReportApplication, + OrganizationReportSummary, ReportState, } from "../../models/report-models"; import { MemberCipherDetailsApiService } from "../api/member-cipher-details-api.service"; @@ -188,6 +190,12 @@ export class RiskInsightsOrchestratorService { this.fetchReport(); } + /** + * Removes a critical application from a report. + * + * @param criticalApplication Application name of the critical application to remove + * @returns + */ removeCriticalApplication$(criticalApplication: string): Observable { this.logService.info( "[RiskInsightsOrchestratorService] Removing critical applications from report", @@ -200,24 +208,53 @@ export class RiskInsightsOrchestratorService { this._userId$.pipe(filter((userId) => !!userId)), ), map(([reportState, organizationDetails, userId]) => { + const report = reportState?.data; + if (!report) { + throwError(() => Error("Tried to update critical applications without a report")); + } + // Create a set for quick lookup of the new critical apps - const existingApplicationData = reportState?.data?.applicationData || []; + const existingApplicationData = report!.applicationData || []; const updatedApplicationData = this._removeCriticalApplication( existingApplicationData, criticalApplication, ); + // Updated summary data after changing critical apps + const updatedSummaryData = this.reportService.getApplicationsSummary( + report!.reportData, + updatedApplicationData, + ); + + // Used for creating metrics with updated application data + const manualEnrichedApplications = report!.reportData.map( + (application): ApplicationHealthReportDetailEnriched => ({ + ...application, + isMarkedAsCritical: this.reportService.isCriticalApplication( + application, + updatedApplicationData, + ), + }), + ); + + // For now, merge the report with the critical marking flag to make the enriched type + // We don't care about the individual ciphers in this instance + // After the report and enriched report types are consolidated, this mapping can be removed + // and the class will expose getCriticalApplications + const metrics = this._getReportMetrics(manualEnrichedApplications, updatedSummaryData); + const updatedState = { ...reportState, data: { ...reportState.data, + summaryData: updatedSummaryData, applicationData: updatedApplicationData, }, } as ReportState; - return { reportState, organizationDetails, updatedState, userId }; + return { reportState, organizationDetails, updatedState, userId, metrics }; }), - switchMap(({ reportState, organizationDetails, updatedState, userId }) => { + switchMap(({ reportState, organizationDetails, updatedState, userId, metrics }) => { return from( this.riskInsightsEncryptionService.encryptRiskInsightsReport( { @@ -237,39 +274,53 @@ export class RiskInsightsOrchestratorService { organizationDetails, updatedState, encryptedData, + metrics, })), ); }), - switchMap(({ reportState, organizationDetails, updatedState, encryptedData }) => { + switchMap(({ reportState, organizationDetails, updatedState, encryptedData, metrics }) => { this.logService.debug( `[RiskInsightsOrchestratorService] Saving applicationData with toggled critical flag for report with id: ${reportState?.data?.id} and org id: ${organizationDetails?.organizationId}`, ); if (!reportState?.data?.id || !organizationDetails?.organizationId) { return of({ ...reportState }); } - return this.reportApiService - .updateRiskInsightsApplicationData$( - reportState.data.id, - organizationDetails.organizationId, - { - data: { - applicationData: encryptedData.encryptedApplicationData.toSdk(), - }, + + // Update applications data with critical marking + const updateApplicationsCall = this.reportApiService.updateRiskInsightsApplicationData$( + reportState.data.id, + organizationDetails.organizationId, + { + data: { + applicationData: encryptedData.encryptedApplicationData.toSdk(), }, - ) - .pipe( - map(() => updatedState), - tap((finalState) => { - this._markUnmarkUpdatesSubject.next({ - ...finalState, - organizationId: reportState.organizationId, - }); - }), - catchError((error: unknown) => { - this.logService.error("Failed to save updated applicationData", error); - return of({ ...reportState, error: "Failed to remove a critical application" }); - }), - ); + }, + ); + // Update summary after recomputing + const updateSummaryCall = this.reportApiService.updateRiskInsightsSummary$( + reportState.data.id, + organizationDetails.organizationId, + { + data: { + summaryData: encryptedData.encryptedSummaryData.toSdk(), + metrics: metrics.toRiskInsightsMetricsData(), + }, + }, + ); + + return forkJoin([updateApplicationsCall, updateSummaryCall]).pipe( + map(() => updatedState), + tap((finalState) => { + this._markUnmarkUpdatesSubject.next({ + ...finalState, + organizationId: reportState.organizationId, + }); + }), + catchError((error: unknown) => { + this.logService.error("Failed to save remove critical application", error); + return of({ ...reportState, error: "Failed to remove a critical application" }); + }), + ); }), ); } @@ -286,25 +337,54 @@ export class RiskInsightsOrchestratorService { this._userId$.pipe(filter((userId) => !!userId)), ), map(([reportState, organizationDetails, userId]) => { + const report = reportState?.data; + if (!report) { + throwError(() => Error("Tried to update critical applications without a report")); + } + // Create a set for quick lookup of the new critical apps const newCriticalAppNamesSet = new Set(criticalApplications); - const existingApplicationData = reportState?.data?.applicationData || []; + const existingApplicationData = report!.applicationData || []; const updatedApplicationData = this._mergeApplicationData( existingApplicationData, newCriticalAppNamesSet, ); + // Updated summary data after changing critical apps + const updatedSummaryData = this.reportService.getApplicationsSummary( + report!.reportData, + updatedApplicationData, + ); + + // Used for creating metrics with updated application data + const manualEnrichedApplications = report!.reportData.map( + (application): ApplicationHealthReportDetailEnriched => ({ + ...application, + isMarkedAsCritical: this.reportService.isCriticalApplication( + application, + updatedApplicationData, + ), + }), + ); + + // For now, merge the report with the critical marking flag to make the enriched type + // We don't care about the individual ciphers in this instance + // After the report and enriched report types are consolidated, this mapping can be removed + // and the class will expose getCriticalApplications + const metrics = this._getReportMetrics(manualEnrichedApplications, updatedSummaryData); + const updatedState = { ...reportState, data: { ...reportState.data, + summaryData: updatedSummaryData, applicationData: updatedApplicationData, }, } as ReportState; - return { reportState, organizationDetails, updatedState, userId }; + return { reportState, organizationDetails, updatedState, userId, metrics }; }), - switchMap(({ reportState, organizationDetails, updatedState, userId }) => { + switchMap(({ reportState, organizationDetails, updatedState, userId, metrics }) => { return from( this.riskInsightsEncryptionService.encryptRiskInsightsReport( { @@ -324,39 +404,52 @@ export class RiskInsightsOrchestratorService { organizationDetails, updatedState, encryptedData, + metrics, })), ); }), - switchMap(({ reportState, organizationDetails, updatedState, encryptedData }) => { + switchMap(({ reportState, organizationDetails, updatedState, encryptedData, metrics }) => { this.logService.debug( `[RiskInsightsOrchestratorService] Saving critical applications on applicationData with report id: ${reportState?.data?.id} and org id: ${organizationDetails?.organizationId}`, ); if (!reportState?.data?.id || !organizationDetails?.organizationId) { return of({ ...reportState }); } - return this.reportApiService - .updateRiskInsightsApplicationData$( - reportState.data.id, - organizationDetails.organizationId, - { - data: { - applicationData: encryptedData.encryptedApplicationData.toSdk(), - }, + // Update applications data with critical marking + const updateApplicationsCall = this.reportApiService.updateRiskInsightsApplicationData$( + reportState.data.id, + organizationDetails.organizationId, + { + data: { + applicationData: encryptedData.encryptedApplicationData.toSdk(), }, - ) - .pipe( - map(() => updatedState), - tap((finalState) => { - this._markUnmarkUpdatesSubject.next({ - ...finalState, - organizationId: reportState.organizationId, - }); - }), - catchError((error: unknown) => { - this.logService.error("Failed to save updated applicationData", error); - return of({ ...reportState, error: "Failed to save critical applications" }); - }), - ); + }, + ); + + // Update summary after recomputing + const updateSummaryCall = this.reportApiService.updateRiskInsightsSummary$( + reportState.data.id, + organizationDetails.organizationId, + { + data: { + summaryData: encryptedData.encryptedSummaryData.toSdk(), + metrics: metrics.toRiskInsightsMetricsData(), + }, + }, + ); + return forkJoin([updateApplicationsCall, updateSummaryCall]).pipe( + map(() => updatedState), + tap((finalState) => { + this._markUnmarkUpdatesSubject.next({ + ...finalState, + organizationId: reportState.organizationId, + }); + }), + catchError((error: unknown) => { + this.logService.error("Failed to save critical applications", error); + return of({ ...reportState, error: "Failed to save critical applications" }); + }), + ); }), ); } @@ -503,18 +596,43 @@ export class RiskInsightsOrchestratorService { this.reportService.generateApplicationsReport(cipherHealthReports), ), withLatestFrom(this.rawReportData$), - map(([report, previousReport]) => ({ - report: report, - summary: this.reportService.getApplicationsSummary(report), - applications: this.reportService.getOrganizationApplications( + map(([report, previousReport]) => { + // Update the application data + const updatedApplicationData = this.reportService.getOrganizationApplications( report, previousReport?.data?.applicationData ?? [], - ), - })), - switchMap(({ report, summary, applications }) => { - // Save the report after enrichment + ); + + const manualEnrichedApplications = report.map( + (application): ApplicationHealthReportDetailEnriched => ({ + ...application, + isMarkedAsCritical: this.reportService.isCriticalApplication( + application, + updatedApplicationData, + ), + }), + ); + + const updatedSummary = this.reportService.getApplicationsSummary( + report, + updatedApplicationData, + ); + // For now, merge the report with the critical marking flag to make the enriched type + // We don't care about the individual ciphers in this instance + // After the report and enriched report types are consolidated, this mapping can be removed + // and the class will expose getCriticalApplications + const metrics = this._getReportMetrics(manualEnrichedApplications, updatedSummary); + + return { + report, + summary: updatedSummary, + applications: updatedApplicationData, + metrics, + }; + }), + switchMap(({ report, summary, applications, metrics }) => { return this.reportService - .saveRiskInsightsReport$(report, summary, applications, { + .saveRiskInsightsReport$(report, summary, applications, metrics, { organizationId, userId, }) @@ -557,6 +675,50 @@ export class RiskInsightsOrchestratorService { ); } + // Calculates the metrics for a report + // This function will be moved to the RiskInsightsReportService after the + // ApplicationHealthReportDetail and ApplicationHealthReportDetailEnriched types + // are consolidated into one + _getReportMetrics( + reports: ApplicationHealthReportDetailEnriched[], + summary: OrganizationReportSummary, + ): RiskInsightsMetrics { + const metrics = new RiskInsightsMetrics(); + const s = summary; + + // Copy summary information + metrics.totalApplicationCount = s.totalApplicationCount; + metrics.totalAtRiskApplicationCount = s.totalAtRiskApplicationCount; + metrics.totalCriticalApplicationCount = s.totalCriticalApplicationCount; + metrics.totalCriticalAtRiskApplicationCount = s.totalCriticalAtRiskApplicationCount; + metrics.totalMemberCount = s.totalMemberCount; + metrics.totalAtRiskMemberCount = s.totalAtRiskMemberCount; + metrics.totalCriticalMemberCount = s.totalCriticalMemberCount; + metrics.totalCriticalAtRiskMemberCount = s.totalCriticalAtRiskMemberCount; + + // Calculate additional metrics + let totalPasswordCount = 0; + let totalAtRiskPasswordCount = 0; + let totalCriticalPasswordCount = 0; + let totalCriticalAtRiskPasswordCount = 0; + + reports.forEach((report) => { + totalPasswordCount += report.cipherIds.length; + totalAtRiskPasswordCount += report.atRiskCipherIds.length; + + if (report.isMarkedAsCritical) { + totalCriticalPasswordCount += report.cipherIds.length; + totalCriticalAtRiskPasswordCount += report.atRiskCipherIds.length; + } + }); + + metrics.totalPasswordCount = totalPasswordCount; + metrics.totalAtRiskPasswordCount = totalAtRiskPasswordCount; + metrics.totalCriticalPasswordCount = totalCriticalPasswordCount; + metrics.totalCriticalAtRiskPasswordCount = totalCriticalAtRiskPasswordCount; + + return metrics; + } /** * Associates the members with the ciphers they have access to. Calculates the password health. * Finds the trimmed uris. @@ -597,12 +759,14 @@ export class RiskInsightsOrchestratorService { ); } + // Updates the existing application data to include critical applications + // Does not remove critical applications not in the set private _mergeApplicationData( existingApplications: OrganizationReportApplication[], criticalApplications: Set, ): OrganizationReportApplication[] { const setToMerge = new Set(criticalApplications); - // First, iterate through the existing apps and update their isCritical flag + const updatedApps = existingApplications.map((app) => { const foundCritical = setToMerge.has(app.applicationName); @@ -770,7 +934,10 @@ export class RiskInsightsOrchestratorService { (app) => app.isMarkedAsCritical, ); // Generate a new summary based on just the critical applications - const summary = this.reportService.getApplicationsSummary(criticalApplications); + const summary = this.reportService.getApplicationsSummary( + criticalApplications, + enrichedReports.applicationData, + ); return { ...enrichedReports, summaryData: summary, @@ -789,24 +956,18 @@ export class RiskInsightsOrchestratorService { */ private _setupEnrichedReportData() { // Setup the enriched report data pipeline - const enrichmentSubscription = combineLatest([ - this.rawReportData$, - this._ciphers$.pipe(filter((data) => !!data)), - ]).pipe( - switchMap(([rawReportData, ciphers]) => { + const enrichmentSubscription = combineLatest([this.rawReportData$]).pipe( + switchMap(([rawReportData]) => { this.logService.debug( "[RiskInsightsOrchestratorService] Enriching report data with ciphers and critical app status", ); - const criticalApps = + const criticalAppsData = rawReportData?.data?.applicationData.filter((app) => app.isCritical) ?? []; - const criticalApplicationNames = new Set(criticalApps.map((ca) => ca.applicationName)); const rawReports = rawReportData.data?.reportData || []; - const cipherMap = this.reportService.getApplicationCipherMap(ciphers, rawReports); const enrichedReports: ApplicationHealthReportDetailEnriched[] = rawReports.map((app) => ({ ...app, - ciphers: cipherMap.get(app.applicationName) || [], - isMarkedAsCritical: criticalApplicationNames.has(app.applicationName), + isMarkedAsCritical: this.reportService.isCriticalApplication(app, criticalAppsData), })); const enrichedData = { diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.spec.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.spec.ts index 3061ea8cded..16f9710fc70 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.spec.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.spec.ts @@ -11,6 +11,7 @@ import { GetRiskInsightsReportResponse, SaveRiskInsightsReportResponse, } from "../../models/api-models.types"; +import { RiskInsightsMetrics } from "../../models/domain/risk-insights-metrics"; import { mockCiphers } from "../../models/mocks/ciphers.mock"; import { mockMemberCipherDetailsResponse } from "../../models/mocks/member-cipher-details-response.mock"; import { @@ -127,10 +128,16 @@ describe("RiskInsightsReportService", () => { mockRiskInsightsApiService.saveRiskInsightsReport$.mockReturnValue(of(saveResponse)); service - .saveRiskInsightsReport$(mockReportData, mockSummaryData, mockApplicationData, { - organizationId: mockOrganizationId, - userId: mockUserId, - }) + .saveRiskInsightsReport$( + mockReportData, + mockSummaryData, + mockApplicationData, + new RiskInsightsMetrics(), + { + organizationId: mockOrganizationId, + userId: mockUserId, + }, + ) .subscribe({ next: (response) => { done.fail("Expected error due to invalid response"); diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.ts index 47745b7e577..5ea66a71221 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.ts @@ -9,6 +9,7 @@ import { isSaveRiskInsightsReportResponse, SaveRiskInsightsReportResponse, } from "../../models/api-models.types"; +import { RiskInsightsMetrics } from "../../models/domain/risk-insights-metrics"; import { ApplicationHealthReportDetail, OrganizationReportSummary, @@ -27,6 +28,13 @@ export class RiskInsightsReportService { private riskInsightsEncryptionService: RiskInsightsEncryptionService, ) {} + filterApplicationsByCritical( + report: ApplicationHealthReportDetail[], + applicationData: OrganizationReportApplication[], + ): ApplicationHealthReportDetail[] { + return report.filter((application) => this.isCriticalApplication(application, applicationData)); + } + /** * Report data for the aggregation of uris to like uris and getting password/member counts, * members, and at risk statuses. @@ -43,36 +51,62 @@ export class RiskInsightsReportService { ); } + /** + * + * @param applications The list of application health report details to map ciphers to + * @param organizationId + * @returns + */ + getApplicationCipherMap( + ciphers: CipherView[], + applications: ApplicationHealthReportDetail[], + ): Map { + const cipherMap = new Map(); + applications.forEach((app) => { + const filteredCiphers = ciphers.filter((c) => app.cipherIds.includes(c.id)); + cipherMap.set(app.applicationName, filteredCiphers); + }); + return cipherMap; + } + /** * Gets the summary from the application health report. Returns total members and applications as well * as the total at risk members and at risk applications * @param reports The previously calculated application health report data * @returns A summary object containing report totals */ - getApplicationsSummary(reports: ApplicationHealthReportDetail[]): OrganizationReportSummary { - const totalMembers = reports.flatMap((x) => x.memberDetails); - const uniqueMembers = getUniqueMembers(totalMembers); + getApplicationsSummary( + reports: ApplicationHealthReportDetail[], + applicationData: OrganizationReportApplication[], + ): OrganizationReportSummary { + const totalUniqueMembers = getUniqueMembers(reports.flatMap((x) => x.memberDetails)); + const atRiskUniqueMembers = getUniqueMembers(reports.flatMap((x) => x.atRiskMemberDetails)); - const atRiskMembers = reports.flatMap((x) => x.atRiskMemberDetails); - const uniqueAtRiskMembers = getUniqueMembers(atRiskMembers); + const criticalReports = this.filterApplicationsByCritical(reports, applicationData); + const criticalUniqueMembers = getUniqueMembers(criticalReports.flatMap((x) => x.memberDetails)); + const criticalAtRiskUniqueMembers = getUniqueMembers( + criticalReports.flatMap((x) => x.atRiskMemberDetails), + ); return { - totalMemberCount: uniqueMembers.length, - totalAtRiskMemberCount: uniqueAtRiskMembers.length, + totalMemberCount: totalUniqueMembers.length, + totalAtRiskMemberCount: atRiskUniqueMembers.length, totalApplicationCount: reports.length, totalAtRiskApplicationCount: reports.filter((app) => app.atRiskPasswordCount > 0).length, - totalCriticalMemberCount: 0, - totalCriticalAtRiskMemberCount: 0, - totalCriticalApplicationCount: 0, - totalCriticalAtRiskApplicationCount: 0, + totalCriticalMemberCount: criticalUniqueMembers.length, + totalCriticalAtRiskMemberCount: criticalAtRiskUniqueMembers.length, + totalCriticalApplicationCount: criticalReports.length, + totalCriticalAtRiskApplicationCount: criticalReports.filter( + (app) => app.atRiskPasswordCount > 0, + ).length, }; } /** - * Generate a snapshot of applications and related data associated to this report + * Get information associated to the report applications that can be modified * * @param reports - * @returns A list of applications with a critical marking flag + * @returns A list of applications with a critical marking flag and review date */ getOrganizationApplications( reports: ApplicationHealthReportDetail[], @@ -92,7 +126,7 @@ export class RiskInsightsReportService { }); } - // No previous applications, return all as non-critical with current date + // No previous applications, return all as non-critical with no review date return reports.map( (report): OrganizationReportApplication => ({ applicationName: report.applicationName, @@ -168,6 +202,15 @@ export class RiskInsightsReportService { ); } + isCriticalApplication( + application: ApplicationHealthReportDetail, + applicationData: OrganizationReportApplication[], + ): boolean { + return applicationData.some( + (a) => a.applicationName == application.applicationName && a.isCritical, + ); + } + /** * Encrypts the risk insights report data for a specific organization. * @param organizationId The ID of the organization. @@ -179,6 +222,7 @@ export class RiskInsightsReportService { report: ApplicationHealthReportDetail[], summary: OrganizationReportSummary, applications: OrganizationReportApplication[], + metrics: RiskInsightsMetrics, encryptionParameters: { organizationId: OrganizationId; userId: UserId; @@ -212,6 +256,7 @@ export class RiskInsightsReportService { summaryData: encryptedSummaryData.toSdk(), applicationData: encryptedApplicationData.toSdk(), contentEncryptionKey: contentEncryptionKey.toSdk(), + metrics: metrics.toRiskInsightsMetricsData(), }, }, // Keep the original EncString alongside the SDK payload so downstream can return the EncString type. @@ -256,24 +301,6 @@ export class RiskInsightsReportService { return applicationMap; } - /** - * - * @param applications The list of application health report details to map ciphers to - * @param organizationId - * @returns - */ - getApplicationCipherMap( - ciphers: CipherView[], - applications: ApplicationHealthReportDetail[], - ): Map { - const cipherMap = new Map(); - applications.forEach((app) => { - const filteredCiphers = ciphers.filter((c) => app.cipherIds.includes(c.id)); - cipherMap.set(app.applicationName, filteredCiphers); - }); - return cipherMap; - } - // --------------------------- Aggregation methods --------------------------- /** * Loop through the flattened cipher to uri data. If the item exists it's values need to be updated with the new item. diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-type-guards.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-type-guards.ts deleted file mode 100644 index c225586d6f7..00000000000 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-type-guards.ts +++ /dev/null @@ -1,397 +0,0 @@ -import { - ApplicationHealthReportDetail, - MemberDetails, - OrganizationReportApplication, - OrganizationReportSummary, -} from "../../models"; - -/** - * Security limits for validation (prevent DoS attacks and ensure reasonable data sizes) - */ -const MAX_STRING_LENGTH = 1000; // Reasonable limit for names, emails, GUIDs -const MAX_ARRAY_LENGTH = 50000; // Reasonable limit for report arrays -const MAX_COUNT = 10000000; // 10 million - reasonable upper bound for count fields - -/** - * Type guard to validate MemberDetails structure - * Exported for testability - * Strict validation: rejects objects with unexpected properties and prototype pollution - */ -export function isMemberDetails(obj: any): obj is MemberDetails { - if (typeof obj !== "object" || obj === null) { - return false; - } - - // Prevent prototype pollution - check prototype is Object.prototype - if (Object.getPrototypeOf(obj) !== Object.prototype) { - return false; - } - - // Prevent dangerous properties that could be used for prototype pollution - // Check for __proto__, constructor, and prototype as own properties - const dangerousKeys = ["__proto__", "constructor", "prototype"]; - for (const key of dangerousKeys) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - return false; - } - } - - // Strict property validation - reject unexpected properties - const allowedKeys = ["userGuid", "userName", "email", "cipherId"]; - const actualKeys = Object.keys(obj); - const hasUnexpectedProps = actualKeys.some((key) => !allowedKeys.includes(key)); - if (hasUnexpectedProps) { - return false; - } - - return ( - typeof obj.userGuid === "string" && - obj.userGuid.length > 0 && - obj.userGuid.length <= MAX_STRING_LENGTH && - (obj.userName === null || - obj.userName === undefined || - (typeof obj.userName === "string" && - obj.userName.length > 0 && - obj.userName.length <= MAX_STRING_LENGTH)) && - typeof obj.email === "string" && - obj.email.length > 0 && - obj.email.length <= MAX_STRING_LENGTH && - typeof obj.cipherId === "string" && - obj.cipherId.length > 0 && - obj.cipherId.length <= MAX_STRING_LENGTH - ); -} - -/** - * Type guard to validate ApplicationHealthReportDetail structure - * Exported for testability - * Strict validation: rejects objects with unexpected properties and prototype pollution - */ -export function isApplicationHealthReportDetail(obj: any): obj is ApplicationHealthReportDetail { - if (typeof obj !== "object" || obj === null) { - return false; - } - - // Prevent prototype pollution - check prototype is Object.prototype - if (Object.getPrototypeOf(obj) !== Object.prototype) { - return false; - } - - // Prevent dangerous properties that could be used for prototype pollution - // Check for __proto__, constructor, and prototype as own properties - const dangerousKeys = ["__proto__", "constructor", "prototype"]; - for (const key of dangerousKeys) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - return false; - } - } - - // Strict property validation - reject unexpected properties - const allowedKeys = [ - "applicationName", - "passwordCount", - "atRiskPasswordCount", - "atRiskCipherIds", - "memberCount", - "atRiskMemberCount", - "memberDetails", - "atRiskMemberDetails", - "cipherIds", - ]; - const actualKeys = Object.keys(obj); - const hasUnexpectedProps = actualKeys.some((key) => !allowedKeys.includes(key)); - if (hasUnexpectedProps) { - return false; - } - - return ( - typeof obj.applicationName === "string" && - obj.applicationName.length > 0 && - obj.applicationName.length <= MAX_STRING_LENGTH && - typeof obj.passwordCount === "number" && - Number.isFinite(obj.passwordCount) && - Number.isSafeInteger(obj.passwordCount) && - obj.passwordCount >= 0 && - obj.passwordCount <= MAX_COUNT && - typeof obj.atRiskPasswordCount === "number" && - Number.isFinite(obj.atRiskPasswordCount) && - Number.isSafeInteger(obj.atRiskPasswordCount) && - obj.atRiskPasswordCount >= 0 && - obj.atRiskPasswordCount <= MAX_COUNT && - Array.isArray(obj.atRiskCipherIds) && - obj.atRiskCipherIds.length <= MAX_ARRAY_LENGTH && - obj.atRiskCipherIds.every( - (id: any) => typeof id === "string" && id.length > 0 && id.length <= MAX_STRING_LENGTH, - ) && - typeof obj.memberCount === "number" && - Number.isFinite(obj.memberCount) && - Number.isSafeInteger(obj.memberCount) && - obj.memberCount >= 0 && - obj.memberCount <= MAX_COUNT && - typeof obj.atRiskMemberCount === "number" && - Number.isFinite(obj.atRiskMemberCount) && - Number.isSafeInteger(obj.atRiskMemberCount) && - obj.atRiskMemberCount >= 0 && - obj.atRiskMemberCount <= MAX_COUNT && - Array.isArray(obj.memberDetails) && - obj.memberDetails.length <= MAX_ARRAY_LENGTH && - obj.memberDetails.every(isMemberDetails) && - Array.isArray(obj.atRiskMemberDetails) && - obj.atRiskMemberDetails.length <= MAX_ARRAY_LENGTH && - obj.atRiskMemberDetails.every(isMemberDetails) && - Array.isArray(obj.cipherIds) && - obj.cipherIds.length <= MAX_ARRAY_LENGTH && - obj.cipherIds.every( - (id: any) => typeof id === "string" && id.length > 0 && id.length <= MAX_STRING_LENGTH, - ) - ); -} - -/** - * Type guard to validate OrganizationReportSummary structure - * Exported for testability - * Strict validation: rejects objects with unexpected properties and prototype pollution - */ -export function isOrganizationReportSummary(obj: any): obj is OrganizationReportSummary { - if (typeof obj !== "object" || obj === null) { - return false; - } - - // Prevent prototype pollution - check prototype is Object.prototype - if (Object.getPrototypeOf(obj) !== Object.prototype) { - return false; - } - - // Prevent dangerous properties that could be used for prototype pollution - // Check for __proto__, constructor, and prototype as own properties - const dangerousKeys = ["__proto__", "constructor", "prototype"]; - for (const key of dangerousKeys) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - return false; - } - } - - // Strict property validation - reject unexpected properties - const allowedKeys = [ - "totalMemberCount", - "totalApplicationCount", - "totalAtRiskMemberCount", - "totalAtRiskApplicationCount", - "totalCriticalApplicationCount", - "totalCriticalMemberCount", - "totalCriticalAtRiskMemberCount", - "totalCriticalAtRiskApplicationCount", - ]; - const actualKeys = Object.keys(obj); - const hasUnexpectedProps = actualKeys.some((key) => !allowedKeys.includes(key)); - if (hasUnexpectedProps) { - return false; - } - - return ( - typeof obj.totalMemberCount === "number" && - Number.isFinite(obj.totalMemberCount) && - Number.isSafeInteger(obj.totalMemberCount) && - obj.totalMemberCount >= 0 && - obj.totalMemberCount <= MAX_COUNT && - typeof obj.totalApplicationCount === "number" && - Number.isFinite(obj.totalApplicationCount) && - Number.isSafeInteger(obj.totalApplicationCount) && - obj.totalApplicationCount >= 0 && - obj.totalApplicationCount <= MAX_COUNT && - typeof obj.totalAtRiskMemberCount === "number" && - Number.isFinite(obj.totalAtRiskMemberCount) && - Number.isSafeInteger(obj.totalAtRiskMemberCount) && - obj.totalAtRiskMemberCount >= 0 && - obj.totalAtRiskMemberCount <= MAX_COUNT && - typeof obj.totalAtRiskApplicationCount === "number" && - Number.isFinite(obj.totalAtRiskApplicationCount) && - Number.isSafeInteger(obj.totalAtRiskApplicationCount) && - obj.totalAtRiskApplicationCount >= 0 && - obj.totalAtRiskApplicationCount <= MAX_COUNT && - typeof obj.totalCriticalApplicationCount === "number" && - Number.isFinite(obj.totalCriticalApplicationCount) && - Number.isSafeInteger(obj.totalCriticalApplicationCount) && - obj.totalCriticalApplicationCount >= 0 && - obj.totalCriticalApplicationCount <= MAX_COUNT && - typeof obj.totalCriticalMemberCount === "number" && - Number.isFinite(obj.totalCriticalMemberCount) && - Number.isSafeInteger(obj.totalCriticalMemberCount) && - obj.totalCriticalMemberCount >= 0 && - obj.totalCriticalMemberCount <= MAX_COUNT && - typeof obj.totalCriticalAtRiskMemberCount === "number" && - Number.isFinite(obj.totalCriticalAtRiskMemberCount) && - Number.isSafeInteger(obj.totalCriticalAtRiskMemberCount) && - obj.totalCriticalAtRiskMemberCount >= 0 && - obj.totalCriticalAtRiskMemberCount <= MAX_COUNT && - typeof obj.totalCriticalAtRiskApplicationCount === "number" && - Number.isFinite(obj.totalCriticalAtRiskApplicationCount) && - Number.isSafeInteger(obj.totalCriticalAtRiskApplicationCount) && - obj.totalCriticalAtRiskApplicationCount >= 0 && - obj.totalCriticalAtRiskApplicationCount <= MAX_COUNT - ); -} - -/** - * Type guard to validate OrganizationReportApplication structure - * Exported for testability - * Strict validation: rejects objects with unexpected properties and prototype pollution - */ -export function isOrganizationReportApplication(obj: any): obj is OrganizationReportApplication { - if (typeof obj !== "object" || obj === null) { - return false; - } - - // Prevent prototype pollution - check prototype is Object.prototype - if (Object.getPrototypeOf(obj) !== Object.prototype) { - return false; - } - - // Prevent dangerous properties that could be used for prototype pollution - // Check for __proto__, constructor, and prototype as own properties - const dangerousKeys = ["__proto__", "constructor", "prototype"]; - for (const key of dangerousKeys) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - return false; - } - } - - // Strict property validation - reject unexpected properties - const allowedKeys = ["applicationName", "isCritical", "reviewedDate"]; - const actualKeys = Object.keys(obj); - const hasUnexpectedProps = actualKeys.some((key) => !allowedKeys.includes(key)); - if (hasUnexpectedProps) { - return false; - } - - return ( - typeof obj.applicationName === "string" && - obj.applicationName.length > 0 && - obj.applicationName.length <= MAX_STRING_LENGTH && - typeof obj.isCritical === "boolean" && - (obj.reviewedDate === null || - obj.reviewedDate instanceof Date || - typeof obj.reviewedDate === "string") - ); -} - -/** - * Validates and returns an array of ApplicationHealthReportDetail - * @throws Error if validation fails - */ -export function validateApplicationHealthReportDetailArray( - data: any, -): ApplicationHealthReportDetail[] { - if (!Array.isArray(data)) { - throw new Error( - "Invalid report data: expected array of ApplicationHealthReportDetail, received non-array", - ); - } - - if (data.length > MAX_ARRAY_LENGTH) { - throw new Error( - `Invalid report data: array length ${data.length} exceeds maximum allowed length ${MAX_ARRAY_LENGTH}`, - ); - } - - const invalidItems = data - .map((item, index) => ({ item, index })) - .filter(({ item }) => !isApplicationHealthReportDetail(item)); - - if (invalidItems.length > 0) { - const invalidIndices = invalidItems.map(({ index }) => index).join(", "); - throw new Error( - `Invalid report data: array contains ${invalidItems.length} invalid ApplicationHealthReportDetail element(s) at indices: ${invalidIndices}`, - ); - } - - return data as ApplicationHealthReportDetail[]; -} - -/** - * Validates and returns OrganizationReportSummary - * @throws Error if validation fails - */ -export function validateOrganizationReportSummary(data: any): OrganizationReportSummary { - if (!isOrganizationReportSummary(data)) { - const missingFields: string[] = []; - - if (typeof data?.totalMemberCount !== "number") { - missingFields.push("totalMemberCount (number)"); - } - if (typeof data?.totalApplicationCount !== "number") { - missingFields.push("totalApplicationCount (number)"); - } - if (typeof data?.totalAtRiskMemberCount !== "number") { - missingFields.push("totalAtRiskMemberCount (number)"); - } - if (typeof data?.totalAtRiskApplicationCount !== "number") { - missingFields.push("totalAtRiskApplicationCount (number)"); - } - if (typeof data?.totalCriticalApplicationCount !== "number") { - missingFields.push("totalCriticalApplicationCount (number)"); - } - if (typeof data?.totalCriticalMemberCount !== "number") { - missingFields.push("totalCriticalMemberCount (number)"); - } - if (typeof data?.totalCriticalAtRiskMemberCount !== "number") { - missingFields.push("totalCriticalAtRiskMemberCount (number)"); - } - if (typeof data?.totalCriticalAtRiskApplicationCount !== "number") { - missingFields.push("totalCriticalAtRiskApplicationCount (number)"); - } - - throw new Error( - `Invalid OrganizationReportSummary: ${missingFields.length > 0 ? `missing or invalid fields: ${missingFields.join(", ")}` : "structure validation failed"}`, - ); - } - - return data as OrganizationReportSummary; -} - -/** - * Validates and returns an array of OrganizationReportApplication - * @throws Error if validation fails - */ -export function validateOrganizationReportApplicationArray( - data: any, -): OrganizationReportApplication[] { - if (!Array.isArray(data)) { - throw new Error( - "Invalid application data: expected array of OrganizationReportApplication, received non-array", - ); - } - - if (data.length > MAX_ARRAY_LENGTH) { - throw new Error( - `Invalid application data: array length ${data.length} exceeds maximum allowed length ${MAX_ARRAY_LENGTH}`, - ); - } - - const invalidItems = data - .map((item, index) => ({ item, index })) - .filter(({ item }) => !isOrganizationReportApplication(item)); - - if (invalidItems.length > 0) { - const invalidIndices = invalidItems.map(({ index }) => index).join(", "); - throw new Error( - `Invalid application data: array contains ${invalidItems.length} invalid OrganizationReportApplication element(s) at indices: ${invalidIndices}`, - ); - } - - // Convert string dates to Date objects for reviewedDate - return data.map((item) => ({ - ...item, - reviewedDate: item.reviewedDate - ? item.reviewedDate instanceof Date - ? item.reviewedDate - : (() => { - const date = new Date(item.reviewedDate); - if (isNaN(date.getTime())) { - throw new Error(`Invalid date string: ${item.reviewedDate}`); - } - return date; - })() - : null, - })) as OrganizationReportApplication[]; -} diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/app-table-row-scrollable.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/app-table-row-scrollable.component.html index ea41dd0aff3..79af3869d99 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/app-table-row-scrollable.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/app-table-row-scrollable.component.html @@ -45,7 +45,11 @@ tabindex="0" [attr.aria-label]="'viewItem' | i18n" > - + + Date: Fri, 31 Oct 2025 16:13:41 +0100 Subject: [PATCH 42/66] [PM-27641] Enable signature validation (#17150) Enables the currently disabled signature validation. This is a blocker for release. --- .../bitwarden_chromium_import_helper/src/windows.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows.rs b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows.rs index 4902d1f1d64..7ab80ff0f35 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows.rs +++ b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows.rs @@ -38,7 +38,7 @@ mod windows_binary { Pipes::GetNamedPipeServerProcessId, Threading::{ OpenProcess, OpenProcessToken, QueryFullProcessImageNameW, PROCESS_NAME_WIN32, - PROCESS_QUERY_INFORMATION, PROCESS_VM_READ, + PROCESS_QUERY_LIMITED_INFORMATION, }, }, UI::Shell::IsUserAnAdmin, @@ -65,7 +65,7 @@ mod windows_binary { const LOG_FILENAME: &str = "c:\\path\\to\\log.txt"; // This is an example filename, replace it with you own // This should be enabled for production - const ENABLE_SERVER_SIGNATURE_VALIDATION: bool = false; + const ENABLE_SERVER_SIGNATURE_VALIDATION: bool = true; const EXPECTED_SERVER_SIGNATURE_SHA256_THUMBPRINT: &str = "9f6680c4720dbf66d1cb8ed6e328f58e42523badc60d138c7a04e63af14ea40d"; @@ -138,8 +138,7 @@ mod windows_binary { dbg_log!("Resolving process executable path for PID {}", pid); // Open the process handle - let hprocess = - unsafe { OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid) }?; + let hprocess = unsafe { OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid) }?; dbg_log!("Opened process handle for PID {}", pid); // Close when no longer needed @@ -332,8 +331,7 @@ mod windows_binary { } fn get_process_handle(pid: u32) -> Result { - let hprocess = - unsafe { OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid) }?; + let hprocess = unsafe { OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid) }?; Ok(hprocess) } From 8c185c9d2b71ce032843afca6c8c4b36f139037d Mon Sep 17 00:00:00 2001 From: Oscar Hinton Date: Fri, 31 Oct 2025 16:50:13 +0100 Subject: [PATCH 43/66] [PM-27645] Check signature of helper exe (#17155) --- .../Cargo.toml | 1 - .../src/windows.rs | 29 ++----------------- .../chromium_importer/Cargo.toml | 1 + .../chromium_importer/src/chromium/mod.rs | 4 ++- .../src/chromium/platform/windows/mod.rs | 10 +++++++ .../chromium/platform/windows/signature.rs | 28 ++++++++++++++++++ 6 files changed, 44 insertions(+), 29 deletions(-) create mode 100644 apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/signature.rs diff --git a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/Cargo.toml b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/Cargo.toml index d1e4ac899bd..dc5358b0c73 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/Cargo.toml +++ b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/Cargo.toml @@ -12,7 +12,6 @@ chromium_importer = { path = "../chromium_importer" } clap = { version = "=4.5.40", features = ["derive"] } scopeguard = { workspace = true } sysinfo = { workspace = true } -verifysign = "=0.2.4" windows = { workspace = true, features = [ "Wdk_System_SystemServices", "Win32_Security_Cryptography", diff --git a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows.rs b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows.rs index 7ab80ff0f35..9abc8c95a1f 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows.rs +++ b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows.rs @@ -20,7 +20,6 @@ mod windows_binary { use tracing_subscriber::{ fmt, layer::SubscriberExt as _, util::SubscriberInitExt as _, EnvFilter, Layer as _, }; - use verifysign::CodeSignVerifier; use windows::{ core::BOOL, Wdk::System::SystemServices::SE_DEBUG_PRIVILEGE, @@ -45,7 +44,7 @@ mod windows_binary { }, }; - use chromium_importer::chromium::ADMIN_TO_USER_PIPE_NAME; + use chromium_importer::chromium::{verify_signature, ADMIN_TO_USER_PIPE_NAME}; #[derive(Parser)] #[command(name = "bitwarden_chromium_import_helper")] @@ -66,8 +65,6 @@ mod windows_binary { // This should be enabled for production const ENABLE_SERVER_SIGNATURE_VALIDATION: bool = true; - const EXPECTED_SERVER_SIGNATURE_SHA256_THUMBPRINT: &str = - "9f6680c4720dbf66d1cb8ed6e328f58e42523badc60d138c7a04e63af14ea40d"; // List of SYSTEM process names to try to impersonate const SYSTEM_PROCESS_NAMES: [&str; 2] = ["services.exe", "winlogon.exe"]; @@ -383,29 +380,7 @@ mod windows_binary { dbg_log!("Pipe server executable path: {}", exe_path.display()); - let verifier = CodeSignVerifier::for_file(exe_path.as_path()).map_err(|e| { - anyhow!("verifysign init failed for {}: {:?}", exe_path.display(), e) - })?; - - let signature = verifier.verify().map_err(|e| { - anyhow!( - "verifysign verify failed for {}: {:?}", - exe_path.display(), - e - ) - })?; - - dbg_log!("Pipe server executable path: {}", exe_path.display()); - - // Dump signature fields for debugging/inspection - dbg_log!("Signature fields:"); - dbg_log!(" Subject Name: {:?}", signature.subject_name()); - dbg_log!(" Issuer Name: {:?}", signature.issuer_name()); - dbg_log!(" SHA1 Thumbprint: {:?}", signature.sha1_thumbprint()); - dbg_log!(" SHA256 Thumbprint: {:?}", signature.sha256_thumbprint()); - dbg_log!(" Serial Number: {:?}", signature.serial()); - - if signature.sha256_thumbprint() != EXPECTED_SERVER_SIGNATURE_SHA256_THUMBPRINT { + if !verify_signature(&exe_path)? { return Err(anyhow!("Pipe server signature is not valid")); } diff --git a/apps/desktop/desktop_native/chromium_importer/Cargo.toml b/apps/desktop/desktop_native/chromium_importer/Cargo.toml index 99246af3f90..51ad450a6fc 100644 --- a/apps/desktop/desktop_native/chromium_importer/Cargo.toml +++ b/apps/desktop/desktop_native/chromium_importer/Cargo.toml @@ -43,6 +43,7 @@ windows = { workspace = true, features = [ "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging", ] } +verifysign = "=0.2.4" [target.'cfg(target_os = "linux")'.dependencies] oo7 = { workspace = true } diff --git a/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs index 0873c66d053..471e35da23e 100644 --- a/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs @@ -10,7 +10,9 @@ use rusqlite::{params, Connection}; mod platform; #[cfg(target_os = "windows")] -pub use platform::ADMIN_TO_USER_PIPE_NAME; +pub use platform::{ + verify_signature, ADMIN_TO_USER_PIPE_NAME, EXPECTED_SIGNATURE_SHA256_THUMBPRINT, +}; pub(crate) use platform::SUPPORTED_BROWSERS as PLATFORM_SUPPORTED_BROWSERS; diff --git a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/mod.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/mod.rs index e47e9bf08d2..a8045cf1182 100644 --- a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/mod.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/mod.rs @@ -13,8 +13,10 @@ use crate::chromium::{BrowserConfig, CryptoService, LocalState}; use crate::util; mod abe; mod abe_config; +mod signature; pub use abe_config::ADMIN_TO_USER_PIPE_NAME; +pub use signature::*; // // Public API @@ -60,6 +62,9 @@ pub(crate) fn get_crypto_service( const ADMIN_EXE_FILENAME: &str = "bitwarden_chromium_import_helper.exe"; +// This should be enabled for production +const ENABLE_SIGNATURE_VALIDATION: bool = true; + // // CryptoService // @@ -179,6 +184,11 @@ impl WindowsCryptoService { } let admin_exe_path = get_admin_exe_path()?; + + if ENABLE_SIGNATURE_VALIDATION && !verify_signature(&admin_exe_path)? { + return Err(anyhow!("Helper executable signature is not valid")); + } + let admin_exe_str = admin_exe_path .to_str() .ok_or_else(|| anyhow!("Failed to convert {} path to string", ADMIN_EXE_FILENAME))?; diff --git a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/signature.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/signature.rs new file mode 100644 index 00000000000..a30b396db28 --- /dev/null +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/signature.rs @@ -0,0 +1,28 @@ +use anyhow::{anyhow, Result}; +use std::path::Path; +use tracing::{debug, info}; +use verifysign::CodeSignVerifier; + +pub const EXPECTED_SIGNATURE_SHA256_THUMBPRINT: &str = + "9f6680c4720dbf66d1cb8ed6e328f58e42523badc60d138c7a04e63af14ea40d"; + +pub fn verify_signature(path: &Path) -> Result { + info!("verifying signature of: {}", path.display()); + + let verifier = CodeSignVerifier::for_file(path) + .map_err(|e| anyhow!("verifysign init failed for {}: {:?}", path.display(), e))?; + + let signature = verifier + .verify() + .map_err(|e| anyhow!("verifysign verify failed for {}: {:?}", path.display(), e))?; + + // Dump signature fields for debugging/inspection + debug!("Signature fields:"); + debug!(" Subject Name: {:?}", signature.subject_name()); + debug!(" Issuer Name: {:?}", signature.issuer_name()); + debug!(" SHA1 Thumbprint: {:?}", signature.sha1_thumbprint()); + debug!(" SHA256 Thumbprint: {:?}", signature.sha256_thumbprint()); + debug!(" Serial Number: {:?}", signature.serial()); + + Ok(signature.sha256_thumbprint() == EXPECTED_SIGNATURE_SHA256_THUMBPRINT) +} From 1476dce396ba644dc19b73ad17e9bc76a09f612e Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 17:57:55 +0100 Subject: [PATCH 44/66] Autosync the updated translations (#17146) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/af/messages.json | 115 ++++++++-- apps/web/src/locales/ar/messages.json | 115 ++++++++-- apps/web/src/locales/az/messages.json | 115 ++++++++-- apps/web/src/locales/be/messages.json | 115 ++++++++-- apps/web/src/locales/bg/messages.json | 115 ++++++++-- apps/web/src/locales/bn/messages.json | 115 ++++++++-- apps/web/src/locales/bs/messages.json | 115 ++++++++-- apps/web/src/locales/ca/messages.json | 115 ++++++++-- apps/web/src/locales/cs/messages.json | 115 ++++++++-- apps/web/src/locales/cy/messages.json | 115 ++++++++-- apps/web/src/locales/da/messages.json | 115 ++++++++-- apps/web/src/locales/de/messages.json | 169 ++++++++++---- apps/web/src/locales/el/messages.json | 115 ++++++++-- apps/web/src/locales/en_GB/messages.json | 115 ++++++++-- apps/web/src/locales/en_IN/messages.json | 115 ++++++++-- apps/web/src/locales/eo/messages.json | 115 ++++++++-- apps/web/src/locales/es/messages.json | 115 ++++++++-- apps/web/src/locales/et/messages.json | 115 ++++++++-- apps/web/src/locales/eu/messages.json | 115 ++++++++-- apps/web/src/locales/fa/messages.json | 115 ++++++++-- apps/web/src/locales/fi/messages.json | 115 ++++++++-- apps/web/src/locales/fil/messages.json | 115 ++++++++-- apps/web/src/locales/fr/messages.json | 169 ++++++++++---- apps/web/src/locales/gl/messages.json | 115 ++++++++-- apps/web/src/locales/he/messages.json | 115 ++++++++-- apps/web/src/locales/hi/messages.json | 115 ++++++++-- apps/web/src/locales/hr/messages.json | 115 ++++++++-- apps/web/src/locales/hu/messages.json | 115 ++++++++-- apps/web/src/locales/id/messages.json | 207 ++++++++++++----- apps/web/src/locales/it/messages.json | 115 ++++++++-- apps/web/src/locales/ja/messages.json | 115 ++++++++-- apps/web/src/locales/ka/messages.json | 115 ++++++++-- apps/web/src/locales/km/messages.json | 115 ++++++++-- apps/web/src/locales/kn/messages.json | 115 ++++++++-- apps/web/src/locales/ko/messages.json | 115 ++++++++-- apps/web/src/locales/lv/messages.json | 115 ++++++++-- apps/web/src/locales/ml/messages.json | 115 ++++++++-- apps/web/src/locales/mr/messages.json | 115 ++++++++-- apps/web/src/locales/my/messages.json | 115 ++++++++-- apps/web/src/locales/nb/messages.json | 115 ++++++++-- apps/web/src/locales/ne/messages.json | 115 ++++++++-- apps/web/src/locales/nl/messages.json | 145 +++++++++--- apps/web/src/locales/nn/messages.json | 115 ++++++++-- apps/web/src/locales/or/messages.json | 115 ++++++++-- apps/web/src/locales/pl/messages.json | 115 ++++++++-- apps/web/src/locales/pt_BR/messages.json | 115 ++++++++-- apps/web/src/locales/pt_PT/messages.json | 151 ++++++++++--- apps/web/src/locales/ro/messages.json | 115 ++++++++-- apps/web/src/locales/ru/messages.json | 269 +++++++++++++++-------- apps/web/src/locales/si/messages.json | 115 ++++++++-- apps/web/src/locales/sk/messages.json | 157 ++++++++++--- apps/web/src/locales/sl/messages.json | 115 ++++++++-- apps/web/src/locales/sr_CS/messages.json | 115 ++++++++-- apps/web/src/locales/sr_CY/messages.json | 115 ++++++++-- apps/web/src/locales/sv/messages.json | 163 ++++++++++---- apps/web/src/locales/ta/messages.json | 115 ++++++++-- apps/web/src/locales/te/messages.json | 115 ++++++++-- apps/web/src/locales/th/messages.json | 115 ++++++++-- apps/web/src/locales/tr/messages.json | 115 ++++++++-- apps/web/src/locales/uk/messages.json | 115 ++++++++-- apps/web/src/locales/vi/messages.json | 183 +++++++++++---- apps/web/src/locales/zh_CN/messages.json | 201 ++++++++++++----- apps/web/src/locales/zh_TW/messages.json | 115 ++++++++-- 63 files changed, 6695 insertions(+), 1214 deletions(-) diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index 065d91f727b..0b1ea74fc6c 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Uitnodiging is Aanvaar" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Kluis-uittel" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Uur" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minute" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "U organisasiebeleide beïnvloed u kluisuitelling. Maksimum toegelate kluisuittelling is $HOURS$ uur en $MINUTES$ minuut(e)", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index fa45c8ad898..fa84a235bff 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "استخدام تسجيل الدخول الأحادي" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "مرحباً بعودتك" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "ساعات" @@ -6510,6 +6573,21 @@ "minutes": { "message": "دقائق" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index 0d4205ca5ab..64a5dc373f6 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "$ORGNAME$ təşkilatında heç bir tətbiq tapılmadı", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "İstifadəçilər giriş məlumatlarını saxladıqda, tətbiqlər burada görünür və riskli parolları göstərir. Kritik tətbiqləri işarələyin və parollarını güncəlləməsi üçün istifadəçiləri məlumatlandırın." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Heç bir tətbiqi kritik olaraq işarələməmisiniz" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Kritik olaraq işarələmə funksionallığı, gələcək güncəlləmədə tətbiq olunacaq" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Kritik olaraq işarələməni götür" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Vahid daxil olma üsulunu istifadə et" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Yenidən xoş gəlmisiniz" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Dəvət qəbul edildi" }, - "inviteAcceptedDesc": { - "message": "Bir inzibatçı üzvlüyünüzü təsdiqlədikdən sonra bu təşkilata erişə bilərsiniz. Üzvlüyünüz təsdiqləndikdə sizə bir e-poçt göndərəcəyik." + "invitationAcceptedDesc": { + "message": "Dəvətiniz uğurla qəbul edildi." }, "inviteInitAcceptedDesc": { "message": "Artıq bu təşkilata erişə bilərsiniz." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Təşkilatınız, şifrə açma seçimlərinizi güncəllədi. Seyfinizə erişmək üçün lütfən bir ana parol təyin edin." }, - "maximumVaultTimeout": { - "message": "Seyf vaxtının bitməsi" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Üzvlər üçün maksimum seyf bitmə vaxtını ayarlayın." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maksimum seyf bitmə vaxtı" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Yararsız maksimum seyf bitmə vaxtı." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Saat" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Dəqiqə" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Təşkilatınızın siyasətləri, icazə verilən maksimum seyf bitmə vaxtını $HOURS$ saat $MINUTES$ dəqiqə olaraq ayarladı.", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id, müasir hücumlara qarşı daha güclü qoruma təklif edir. Güclü cihazlara sahib qabaqcıl istifadəçilər üçün idealdır." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Poçt kodu" + }, + "cardNumberLabel": { + "message": "Kart nömrəsi" + }, + "startFreeFamiliesTrial": { + "message": "Ödənişsiz Ailələr sınağını başlat" } } diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index 9bc30d40c03..21713eb5af8 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Праграмы ў $ORG NAME$ не знойдзены", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Тут будуць адлюстроўвацца праграмы па меры таго, як карыстальнікі будуць захоўваць запісы ўваходу, якія знаходзяцца ў зоне рызыкі. Пазначце крытычныя праграмы і апавяшчайце карыстальнікаў аб неабходнасці абнавіць паролі." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Запрашэнне прынята" }, - "inviteAcceptedDesc": { - "message": "Вы можаце атрымаць доступ да гэтай арганізацыі адразу пасля таго, як будзе пацверджаны ваш удзел. Мы апавясцім вас, калі гэта адбудзецца." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "Цяпер вы можаце атрымаць доступ да гэтай арганізацыі." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Час чакання сховішча" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Прызначыць час чакання сховішча для ўдзельнікаў." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Максімальны час чакання сховішча" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Памылковы максімальны час чакання сховішча." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Гадзіны" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Хвіліны" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Палітыка вашай арганізацыі ўплывае на час чакання сховішча. Максімальны дазволены час чакання сховішча складае $HOURS$ гадз. і $MINUTES$ хв.", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index dbb9103e7b3..f093f825ac2 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Няма намерени приложения в $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Когато потребителите запазват данните си за вписване, тук ще се появяват приложения, а също и ще се показват паролите в риск. Можете да отбелязвате важните приложения и да напомняте на потребителите да сменят паролите си." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Не сте отбелязали нито едно приложение като важно" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Функционалността за отбелязване на нещо като важно ще бъде добавена в бъдеща версия" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Премахване от важните" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Използване на еднократна идентификация" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Добре дошли отново" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Поканата е приета" }, - "inviteAcceptedDesc": { - "message": "Ще имате достъп до организацията, след като администраторът ѝ потвърди членството ви. Ще ви изпратим електронно писмо, когато това се случи." + "invitationAcceptedDesc": { + "message": "Поканата е приета успешно." }, "inviteInitAcceptedDesc": { "message": "Вече имате достъп до тази организация." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Вашата организация е променила настройките за шифроване. Задайте главна парола, за да получите достъп до трезора си." }, - "maximumVaultTimeout": { - "message": "Време за достъп" + "sessionTimeoutPolicyTitle": { + "message": "Време на давност за сесията" }, - "maximumVaultTimeoutDesc": { - "message": "Настройте максимално време за достъп до трезора, което ще важи за всички потребители." + "sessionTimeoutPolicyDescription": { + "message": "Задайте максимално време на давност на сесиите за достъп за всички членове, без собствениците." }, - "maximumVaultTimeoutLabel": { - "message": "Максимално време за достъп до трезора" + "maximumAllowedTimeout": { + "message": "Максимално допустимо време на достъп" }, - "invalidMaximumVaultTimeout": { - "message": "Неправилно максимално време за достъп." + "maximumAllowedTimeoutRequired": { + "message": "Максималното допустимо време е задължително." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Времето е зададено грешно. Променете поне една стойност." + }, + "sessionTimeoutAction": { + "message": "Действие при изтичане на времето за сесията" + }, + "immediately": { + "message": "Незабавно" + }, + "onSystemLock": { + "message": "При заключване на системата" + }, + "onAppRestart": { + "message": "При рестартиране на приложението" }, "hours": { "message": "Часа" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Минути" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Наистина ли искате да разрешите максималното време за достъп да бъде „Безкрайно“ за всички членове?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "Така ключовете за шифроване на членовете ще бъдат запазени на техните устройства. Ако изберете това, уверете се, че устройствата им са защитени адекватно." + }, + "learnMoreAboutDeviceProtection": { + "message": "Научете повече относно защитата на устройствата" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "„Заключването на системата“ ще се прилага само за браузъра и приложението за компютър" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "Мобилното приложение и това по уеб ще ползват настройката „При рестартиране на приложението€ като максимално разрешено време за достъп, тъй като другата не се поддържа." + }, "vaultTimeoutPolicyInEffect": { "message": "Настройките на организацията Ви влияят върху времето за достъп до трезора Ви. Максималното разрешено време за достъп е $HOURS$ час(а) и $MINUTES$ минути", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id предлага по-добра защита срещу съвременните атаки. Подходящ за напреднали потребители с по-мощни устройства." + }, + "zipPostalCodeLabel": { + "message": "Пощенски код" + }, + "cardNumberLabel": { + "message": "Номер на картата" + }, + "startFreeFamiliesTrial": { + "message": "Започнете безплатния пробен период на Семейния план" } } diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index 19ba18f741f..acb63d2eb01 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index eeed875c587..cd2cc6bd85a 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index 977b173ff0c..8db6c9e6749 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Usa inici de sessió únic" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Benvingut/da de nou" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitació acceptada" }, - "inviteAcceptedDesc": { - "message": "Podeu accedir a aquesta organització una vegada que un administrador confirme la vostra pertinença. Rebreu un correu electrònic quan això passe." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "Ara podeu accedir a aquesta organització." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Temps d'espera de la caixa forta" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Configureu un temps d'espera màxima a la caixa forta per a tots els usuaris." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Temps d'espera màxima a la caixa forta" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Temps d'espera màxima a la caixa forta no vàlid." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hores" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minuts" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Les polítiques de l'organització afecten el temps d'espera de la caixa forta. El temps d'espera màxim permès d'aquesta és de $HOURS$ hores i $MINUTES$ minuts.", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index 5a9e6c1a6d2..433b820b7a0 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "V $ORG NAME$ nebyly nalezeny žádné aplikace", + "noApplicationsInOrgTitle": { + "message": "Pro $ORG NAME$ nebyly nalezeny žádné aplikace", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Jakmile uživatelé uloží přihlašovací údaje, zobrazí se zde aplikace, které zobrazují všechna ohrožená hesla. Označte kritické aplikace a upozorněte uživatele, aby hesla aktualizovali." + "noApplicationsInOrgDescription": { + "message": "Importujte přihlašovací údaje Vaší organizace a začněte sledovat bezpečnostní rizika spojená s přihlašovacími údaji. Po importu získáte přístup k:" + }, + "benefit1Title": { + "message": "Upřednostnit rizika" + }, + "benefit1Description": { + "message": "Zaměřit se na aplikace, na které záleží nejvíce" + }, + "benefit2Title": { + "message": "Průvodce nápravou" + }, + "benefit2Description": { + "message": "Přiřadit ohroženým členům řízené úkoly, aby střídali používání ohrožených přihlašovacích údajů" + }, + "benefit3Title": { + "message": "Sledovat průběh" + }, + "benefit3Description": { + "message": "Sleduje změny v průběhu času pro zobrazení zlepšení zabezpečení." + }, + "noReportRunTitle": { + "message": "Spustit první hlášení pro zobrazení aplikací" + }, + "noReportRunDescription": { + "message": "Vygenerujte zprávu o rizicích, abyste mohli analyzovat aplikace Vaší organizace a identifikovat riziková hesla, která vyžadují pozornost. Spuštěním první zprávy:" }, "noCriticalApplicationsTitle": { "message": "Neoznačili jste žádné aplikace jako kritické" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Funkce označení jako kritické bude implementována v budoucí aktualizaci" }, + "applicationReviewSaved": { + "message": "Kontrola aplikace uložena" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ aplikací označeno jako kritické", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "Nové aplikace zkontrolovány" + }, + "errorSavingReviewStatus": { + "message": "Chyba při ukládání stavu kontroly" + }, + "pleaseTryAgain": { + "message": "Zkuste to znovu" + }, "unmarkAsCritical": { "message": "Zrušit označení jako kritické" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Použít jednotné přihlášení" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Vaše organizace vyžaduje jednotné přihlášení." + }, "welcomeBack": { "message": "Vítejte zpět" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Pozvánka byla přijata" }, - "inviteAcceptedDesc": { - "message": "K této organizaci můžete přistupovat, jakmile správce potvrdí Vaše členství. Až se tak stane, pošleme Vám e-mail." + "invitationAcceptedDesc": { + "message": "Vaše pozvánka byla úspěšně přijata." }, "inviteInitAcceptedDesc": { "message": "Nyní můžete přistupovat k této organizaci." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Vaše organizace aktualizovala možnosti dešifrování. Nastavte hlavní heslo pro přístup k Vašemu trezoru." }, - "maximumVaultTimeout": { - "message": "Časový limit trezoru" + "sessionTimeoutPolicyTitle": { + "message": "Časový limit relace" }, - "maximumVaultTimeoutDesc": { - "message": "Nastaví maximální časový limit trezoru pro všechny uživatele." + "sessionTimeoutPolicyDescription": { + "message": "Nastavte maximální časový limit relace pro všechny členy kromě vlastníků." }, - "maximumVaultTimeoutLabel": { - "message": "Maximální časový limit trezoru" + "maximumAllowedTimeout": { + "message": "Maximální povolený časový limit" }, - "invalidMaximumVaultTimeout": { - "message": "Neplatný maximální časový limit trezoru." + "maximumAllowedTimeoutRequired": { + "message": "Je vyžadován maximální povolený časový limit." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Čas je neplatný. Změňte alespoň jednu hodnotu." + }, + "sessionTimeoutAction": { + "message": "Akce časového limitu relace" + }, + "immediately": { + "message": "Okamžitě" + }, + "onSystemLock": { + "message": "Při uzamknutí systému" + }, + "onAppRestart": { + "message": "Při restartu aplikace" }, "hours": { "message": "Hodiny" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minuty" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Jste si jisti, že chcete povolit maximální časový limit \"Nikdy\" pro všechny členy?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "Tato volba uloží šifrovací klíče Vašich členů do jejich zařízení. Pokud zvolíte tuto volbu, ujistěte se, že jsou jejich zařízení náležitě chráněna." + }, + "learnMoreAboutDeviceProtection": { + "message": "Další informace o ochraně zařízení" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "Volba \"Při uzamknutí systému\" se bude vztahovat jen na prohlížeč a desktopovou aplikaci" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "Mobilní a webová aplikace bude používat \"Při restartu aplikace\" jako maximální povolený časový limit, protože tato volba není podporována." + }, "vaultTimeoutPolicyInEffect": { "message": "Zásady Vaší organizace nastavily maximální povolený časový limit trezoru na $HOURS$ hodin a $MINUTES$ minut.", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id nabízí silnější ochranu proti moderním útokům. Nejlepší pro pokročilé uživatele s výkonnými zařízeními." + }, + "zipPostalCodeLabel": { + "message": "ZIP / PSČ" + }, + "cardNumberLabel": { + "message": "Číslo karty" + }, + "startFreeFamiliesTrial": { + "message": "Zahájit bezplatnou zkušební verzi pro rodiny" } } diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index 794f717d9b8..49f2f0ffcc3 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index 5f3c0658b03..aff7bfc0168 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Ingen apps fundet i $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Når brugerne gemmer logins, vil apps med udsatte adgangskoder fremgå her. Markér kritiske apps og giv brugerne besked om at opdatere adgangskoder." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Brug Single Sign-On" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Velkommen tilbage" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepteret" }, - "inviteAcceptedDesc": { - "message": "Du kan få adgang til denne organisation, når en administrator bekræfter dit medlemskab. Vi sender dig en e-mail, når dette sker." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "Denne organisation kan nu tilgås." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Organisationen har opdateret brugernes dekrypteringsmuligheder. Opsæt en hovedadgangskode for at tilgå boksen." }, - "maximumVaultTimeout": { - "message": "Boks-timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Opsæt en maksimal boks-timeout for medlemmer." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maksimal boks-timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Ugyldig maksimal boks-timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Timer" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutter" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Organisationspolitikker har sat din maks. tilladte boks-timeout. til $HOURS$ time(r) og $MINUTES$ minut(ter).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index 515581c7769..8f72a5d2f01 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Keine Anwendungen in $ORG NAME$ gefunden", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Während Benutzer Zugangsdaten speichern, werden hier Anwendungen angezeigt, die alle gefährdeten Passwörter anzeigen. Markiere kritische Anwendungen und benachrichtige Benutzer, um Passwörter zu ändern." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Du hast keine Anwendung als kritisch markiert" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Die Funktion \"Als kritisch markieren\" wird in einer zukünftigen Aktualisierung implementiert" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Markierung als kritisch aufheben" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Single Sign-on verwenden" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Willkommen zurück" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Einladung angenommen" }, - "inviteAcceptedDesc": { - "message": "Du kannst der Organisation beitreten, sobald ein Administrator deine Mitgliedschaft bestätigt hat. Wir werden dich dann per E-Mail benachrichtigen." + "invitationAcceptedDesc": { + "message": "Deine Einladung wurde erfolgreich angenommen." }, "inviteInitAcceptedDesc": { "message": "Du kannst nun auf diese Organisation zugreifen." @@ -5714,63 +5762,63 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Learn more about the credential lifecycle.'" }, "availableNow": { - "message": "Available now" + "message": "Jetzt verfügbar" }, "autoConfirm": { - "message": "Automatic confirmation of new users" + "message": "Automatische Bestätigung neuer Benutzer" }, "autoConfirmDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked.", + "message": "Neue Benutzer, die in die Organisation eingeladen werden, werden automatisch bestätigt, wenn das Gerät eines Administrators entsperrt wird.", "description": "This is the description of the policy as it appears in the 'Policies' page" }, "howToTurnOnAutoConfirm": { - "message": "How to turn on automatic user confirmation" + "message": "So aktivierst du die automatische Benutzerbestätigung" }, "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "message": "Öffne deine Bitwarden-Erweiterung." }, "autoConfirmStep2a": { - "message": "Select", + "message": "Wähle", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmStep2b": { - "message": " Turn on.", + "message": " Aktivieren.", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmExtensionOpened": { - "message": "Successfully opened the Bitwarden browser extension. You can now activate the automatic user confirmation setting." + "message": "Die Bitwarden Browser-Erweiterung wurde erfolgreich geöffnet. Du kannst nun die automatische Benutzerbestätigung aktivieren." }, "autoConfirmPolicyEditDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked. Before turning on this policy, please review and agree to the following: ", + "message": "Neue Benutzer, die zur Organisation eingeladen werden, werden automatisch bestätigt, wenn das Gerät eines Administrators entsperrt wird. Bevor du diese Richtlinie aktivierst, lies bitte Folgendes durch und stimme zu: ", "description": "This is the description of the policy as it appears inside the policy edit dialog" }, "autoConfirmAcceptSecurityRiskTitle": { - "message": "Potential security risk. " + "message": "Potenzielles Sicherheitsrisiko. " }, "autoConfirmAcceptSecurityRiskDescription": { - "message": "Automatic user confirmation could pose a security risk to your organization’s data." + "message": "Eine automatische Benutzerbestätigung könnte ein Sicherheitsrisiko für die Daten deines Unternehmens darstellen." }, "autoConfirmAcceptSecurityRiskLearnMore": { - "message": "Learn about the risks", + "message": "Erfahre mehr über die Risiken", "description": "The is the link copy for the first check box option in the edit policy dialog" }, "autoConfirmSingleOrgRequired": { - "message": "Single organization policy required. " + "message": "Richtlinie einer einzelnen Organisation erforderlich. " }, "autoConfirmSingleOrgRequiredDescription": { - "message": "Anyone part of more than one organization will have their access revoked until they leave the other organizations." + "message": "Jeder, der mehr als einer Organisation angehört, verliert seinen Zugriff, bis er aus den anderen Organisationen austritt." }, "autoConfirmSingleOrgExemption": { - "message": "Single organization policy will extend to all roles. " + "message": "Die Richtlinie für einzelne Organisationen wird für alle Rollen gelten. " }, "autoConfirmNoEmergencyAccess": { - "message": "No emergency access. " + "message": "Kein Notfallzugriff. " }, "autoConfirmNoEmergencyAccessDescription": { - "message": "Emergency Access will be removed." + "message": "Der Notfallzugriff wird entfernt." }, "autoConfirmCheckBoxLabel": { - "message": "I accept these risks and policy updates" + "message": "Ich akzeptiere diese Risiken und Richtlinien-Aktualisierungen" }, "personalOwnership": { "message": "Persönlichen Tresor entfernen" @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Deine Organisation hat deine Entschlüsselungsoptionen aktualisiert. Bitte lege ein Master-Passwort fest, um auf deinen Tresor zuzugreifen." }, - "maximumVaultTimeout": { - "message": "Tresor-Timeout" + "sessionTimeoutPolicyTitle": { + "message": "Sitzungs-Timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Konfiguriere ein maximales Tresor-Timeout für alle Benutzer." + "sessionTimeoutPolicyDescription": { + "message": "Lege ein maximales Sitzungs-Timeout für alle Mitglieder außer für Eigentümer fest." }, - "maximumVaultTimeoutLabel": { - "message": "Maximales Tresor-Timeout" + "maximumAllowedTimeout": { + "message": "Maximal zulässiges Timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Ungültiger maximaler Tresor-Timeout." + "maximumAllowedTimeoutRequired": { + "message": "Ein maximal zulässiges Timeout ist erforderlich." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Die Zeit ist ungültig. Ändere mindestens einen Wert." + }, + "sessionTimeoutAction": { + "message": "Aktion bei Sitzungs-Timeout" + }, + "immediately": { + "message": "Sofort" + }, + "onSystemLock": { + "message": "Wenn System gesperrt" + }, + "onAppRestart": { + "message": "Beim Neustart der App" }, "hours": { "message": "Stunden" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minuten" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Bist du sicher, dass du für alle Mitglieder ein maximales Timeout von \"Nie\" zulassen möchtest?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "Mit dieser Option werden die Verschlüsselungsschlüssel deiner Mitglieder auf deren Geräten gespeichert. Wenn du diese Option wählst, stelle sicher, dass deren Geräte ausreichend geschützt sind." + }, + "learnMoreAboutDeviceProtection": { + "message": "Erfahre mehr über den Geräteschutz" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"Wenn System gesperrt\" gilt nur für die Browser-Erweiterung und die Desktop-App" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "Die Smartphone und Web-App verwenden \"Beim Neustart der App\" als maximal zulässiges Timeout, da die Option nicht unterstützt wird." + }, "vaultTimeoutPolicyInEffect": { "message": "Deine Unternehmensrichtlinien haben das maximal zulässige Tresor-Timeout auf $HOURS$ Stunde(n) und $MINUTES$ Minute(n) festgelegt.", "placeholders": { @@ -11911,30 +11989,39 @@ "message": "Unternehmens-Tarife anzeigen" }, "updateEncryptionSettings": { - "message": "Update encryption settings" + "message": "Verschlüsselungseinstellungen aktualisieren" }, "updateYourEncryptionSettings": { - "message": "Update your encryption settings" + "message": "Aktualisiere deine Verschlüsselungseinstellungen" }, "updateSettings": { - "message": "Update settings" + "message": "Einstellungen aktualisieren" }, "algorithm": { - "message": "Algorithm" + "message": "Algorithmus" }, "encryptionKeySettingsHowShouldWeEncryptYourData": { - "message": "Choose how Bitwarden should encrypt your vault data. All options are secure, but stronger methods offer better protection - especially against brute-force attacks. Bitwarden recommends the default setting for most users." + "message": "Wähle aus, wie Bitwarden deine Tresordaten verschlüsseln soll. Alle Optionen sind sicher, aber stärkere Methoden bieten besseren Schutz - vor allem gegen Brute-Force-Angriffe. Bitwarden empfiehlt die Standardeinstellung für die meisten Benutzer." }, "encryptionKeySettingsIncreaseImproveSecurity": { - "message": "Increasing the values above the default will improve security, but your vault may take longer to unlock as a result." + "message": "Die Erhöhung der Werte über den Standard hinaus wird die Sicherheit verbessern, aber es kann dann länger dauern, bis dein Tresor entsperrt wird." }, "encryptionKeySettingsAlgorithmPopoverTitle": { - "message": "About encryption algorithms" + "message": "Über Verschlüsselungsalgorithmen" }, "encryptionKeySettingsAlgorithmPopoverPBKDF2": { - "message": "PBKDF2-SHA256 is a well-tested encryption method that balances security and performance. Good for all users." + "message": "PBKDF2-SHA256 ist eine bewährte Verschlüsselungsmethode, die Sicherheit und Leistung in einem ausgewogenen Verhältnis vereint. Gut für alle Benutzer." }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { - "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + "message": "Argon2id bietet einen stärkeren Schutz gegen moderne Attacken. Am besten geeignet für fortgeschrittene Benutzer mit leistungsstarken Geräten." + }, + "zipPostalCodeLabel": { + "message": "PLZ / Postleitzahl" + }, + "cardNumberLabel": { + "message": "Kartennummer" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index 8bd0ecaee1b..0e1dd5e6399 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Χρήση καθολικής σύνδεσης" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Καλώς ορίσατε και πάλι" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Η Πρόσκληση έγινε αποδεκτή" }, - "inviteAcceptedDesc": { - "message": "Μπορείτε να αποκτήσετε πρόσβαση σε αυτόν τον οργανισμό μόλις ο διαχειριστής σας επιβεβαιώσει την ιδιότητα μέλους. Θα σας στείλουμε ένα email όταν αυτό συμβεί." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Χρόνος Λήξης Vault" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Ρύθμιση μέγιστου χρονικού ορίου vault για όλους τους χρήστες." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Μέγιστο Χρονικό Όριο Vault" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Μη Έγκυρο Μέγιστο Χρονικό Όριο Vault." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Ώρες" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Λεπτά" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Οι πολιτικές του οργανισμού σας επηρεάζουν το χρονικό όριο vault σας. Το μέγιστο επιτρεπόμενο Χρονικό όριο Vault είναι $HOURS$ ώρα(ες) και $MINUTES$ λεπτό(ά)", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index 09d507efd08..cfda4b26853 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organisation's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritise risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyse your organisation's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organisation requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organisation once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organisation." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organisation has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organisation policies are affecting your vault timeout. Maximum allowed vault timeout is $HOURS$ hour(s) and $MINUTES$ minute(s)", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index a265546c82c..ee4b318f47f 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organisation's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritise risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyse your organisation's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organisation requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organisation once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organisation." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organisation has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organisation policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "PIN" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index 88add866744..5a97cab4b86 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Bonrevenon" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invito Akceptita" }, - "inviteAcceptedDesc": { - "message": "Vi povas aliri ĉi tiun organizon post kiam administranto konfirmos vian membrecon. Ni sendos al vi retpoŝton kiam tio okazos." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Tempolimo de trezorejo" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Horoj" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutoj" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index db3dd22cc5c..dd95446fd0c 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No se encontraron aplicaciones en $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Usar inicio de sesión único" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Bienvenido de nuevo" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitación Aceptada" }, - "inviteAcceptedDesc": { - "message": "Puede acceder a esta organización una vez que un administrador confirme su membresía. Te enviaremos un correo electrónico cuando eso suceda." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "Ahora puede acceder a esta organización." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Tiempo de espera de la caja fuerte excedido" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Configurar un tiempo de espera máximo para la caja fuerte de todos los usuarios." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Tiempo de espera máximo de la caja fuerte" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Tiempo de espera máximo de la caja fuerte inválido." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Horas" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutos" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Las políticas de tu organización están afectando al tiempo de espera de tu caja fuerte. El máximo permitido de espera es de $HOURS$ hora(s) y de $MINUTES$ minuto(s)", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index 306dc5aaab7..9142554d701 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Tere tulemast tagasi" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Kutse on vastu võetud" }, - "inviteAcceptedDesc": { - "message": "Pääsed organisatsiooni kirjetele ligi niipea, kui administraator sinu liikmelisuse kinnitab. Teavitame sind sellest e-posti teel." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "Sul on nüüd ligipääs sellele organisatsioonile." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Hoidla ajalõpp" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Tundi" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutit" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Organisatsiooni poliitikad mõjutavad sinu hoidla ajalõppu. Maksimaalne lubatud hoidla ajalõpp on $HOURS$ tund(i) ja $MINUTES$ minut(it)", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index 1339a2b8cec..dbfcd903a5d 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Gonbidapena onartua" }, - "inviteAcceptedDesc": { - "message": "Erakundeko kudeatzaile batek berresten zaituenean sartu ahal izango zara. Email bat bidaliko dizugu hori gertatzen denean." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Kutxa gotorraren itxaronaldia" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Ezarri kutxa gotorraren gehienezko itxaronaldia kideentzat." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Kutxa gotorreko gehienezko itxaronaldia" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Kutxa gotorreko itxaronaldia baliogabea." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Ordu" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutu" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Zure erakundearen politikek zure itxaronaldiari eragiten diote. Itxaronaldiak gehienez ere $HOURS$ ordu eta $MINUTES$ minutu izango ditu", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index 121dfa49fca..1c0fbbde367 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "هیچ برنامه‌ای در $ORG NAME$ یافت نشد", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "با ذخیره‌ی اطلاعات ورود توسط کاربران، برنامه‌ها در اینجا نمایش داده می‌شوند و کلمات عبور در معرض خطر مشخص می‌گردند. برنامه‌های حیاتی را علامت‌گذاری کرده و کاربران را برای به‌روزرسانی کلمات عبور مطلع کنید." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "استفاده از ورود تک مرحله‌ای" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "خوش آمدید" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "دعوتنامه پذیرفته شد" }, - "inviteAcceptedDesc": { - "message": "زمانی که یک سرپرست عضویت شما را تأیید کرد، می‌توانید به این سازمان دسترسی داشته باشید. زمانی که این اتفاق بیفتد، یک ایمیل برای شما ارسال خواهیم کرد." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "اکنون می‌توانید به این سازمان دسترسی داشته باشید." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "سازمان شما گزینه‌های رمزگشایی را به‌روزرسانی کرده است. لطفاً برای دسترسی به گاوصندوق خود، یک کلمه عبور اصلی تنظیم کنید." }, - "maximumVaultTimeout": { - "message": "متوقف شدن گاو‌صندوق" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "حداکثر بازه زمانی گاوصندوق را برای اعضا تنظیم کنید." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "حداکثر مهلت گاوصندوق" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "پایان زمان گاوصندوق نامعتبر است." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "ساعت" @@ -6510,6 +6573,21 @@ "minutes": { "message": "دقیقه" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "خط مشی های سازمانتان بر مهلت زمانی گاوصندوق شما تأثیر می‌گذارد. حداکثر زمان مجاز گاوصندوق $HOURS$ ساعت و $MINUTES$ دقیقه است", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index ffa7582b0a0..d39b002e185 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Sovelluksia ei löytynyt organisaatiosta $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Käytä kertakirjautumista" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Tervetuloa takaisin" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Kutsu hyväksyttiin" }, - "inviteAcceptedDesc": { - "message": "Saat organisaation käyttöoikeudet ylläpitäjän vahvistettua jäsentyytesi. Saat vahvistuksesta ilmoituksen sähköpostitse." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "Sinulla on nyt organisaation käyttöoikeus." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Organisaatiosi on muuttanut salauksen purun asetuksia. Avaa holvisi asettamalla pääsalasana." }, - "maximumVaultTimeout": { - "message": "Holvin aikakatkaisu" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Aseta jäsenille holvin aikakatkaisun enimmäisaika." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Holvin aikakatkaisun enimmäisaika" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Virheellinen holvin aikakatkaisun enimmäisaika." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Tuntia" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minuuttia" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Organisaatiokäytännöt ovat määrittäneet holvisi aikakatkaisun enimmäisajaksi $HOURS$ tuntia $MINUTES$ minuuttia.", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index 6342f27966c..c38d950bc1d 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Tinanggap ang paanyaya" }, - "inviteAcceptedDesc": { - "message": "Maaari mong ma access ang organisasyong ito sa sandaling kumpirmahin ng isang administrator ang iyong pagiging miyembro. Ipapadala namin sa iyo ang isang email kapag nangyari iyon." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Magtakda ng maximum vault timeout para sa mga miyembro." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum na timeout sa vault" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Imbalidong maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Oras" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minuto" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Nakakaapekto ang mga patakaran ng iyong organisasyon sa iyong vault timeout. Ang pinakamataas na pinapayagang Vault Timeout ay $HOURS$ oras at $MINUTES$ minuto", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index ff9f26dd353..dd8a8c21ba1 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Aucune application trouvée dans $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "Aucune application trouvée pour $ORG NOM$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Au fur et à mesure que les utilisateurs enregistrent des identifiants, les applications apparaissent ici, montrant les mots de passe à risque. Marquez les applications critiques et avertissez les utilisateurs de mettre à jour les mots de passe." + "noApplicationsInOrgDescription": { + "message": "Importez les données de connexion de votre organisation pour commencer à surveiller les risques de sécurité des identifiants. Une fois importé, vous pouvez :" + }, + "benefit1Title": { + "message": "Prioriser les risques" + }, + "benefit1Description": { + "message": "Vous concentrer sur les applications qui comptent le plus" + }, + "benefit2Title": { + "message": "Guider la remédiation" + }, + "benefit2Description": { + "message": "Assignez aux membres à risque des tâches guidées pour faire pivoter les identifiants à risque." + }, + "benefit3Title": { + "message": "Suivre la progression" + }, + "benefit3Description": { + "message": "Suivre les changements au fil du temps pour afficher les améliorations de sécurité" + }, + "noReportRunTitle": { + "message": "Exécutez votre premier rapport pour voir les applications" + }, + "noReportRunDescription": { + "message": "Générer un rapport de connaissance des risques pour analyser les applications de votre organisation et identifier les mots de passe à risque qui nécessitent une attention. Exécuter votre premier rapport fera :" }, "noCriticalApplicationsTitle": { "message": "Vous n'avez marqué aucune application comme critique" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Marquer comme fonctionnalité critique sera implémentée dans une mise à jour future" }, + "applicationReviewSaved": { + "message": "Révision de l'application enregistrée" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marquées comme critiques", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "Nouvelles demandes examinées" + }, + "errorSavingReviewStatus": { + "message": "Erreur lors de l'enregistrement du statut de la révision" + }, + "pleaseTryAgain": { + "message": "Veuillez réessayer" + }, "unmarkAsCritical": { "message": "Retirer la cote critique" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Utiliser l'authentification unique" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Votre organisation nécessite une authentification unique." + }, "welcomeBack": { "message": "Content de vous revoir" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation acceptée" }, - "inviteAcceptedDesc": { - "message": "Vous pourrez accéder à cette organisation dès qu'un administrateur aura confirmé votre adhésion. Nous vous enverrons un courriel lorsque cela se produira." + "invitationAcceptedDesc": { + "message": "Votre invitation a été acceptée avec succès." }, "inviteInitAcceptedDesc": { "message": "Vous pouvez maintenant accéder à cette organisation." @@ -5714,63 +5762,63 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Learn more about the credential lifecycle.'" }, "availableNow": { - "message": "Available now" + "message": "Disponible maintenant" }, "autoConfirm": { - "message": "Automatic confirmation of new users" + "message": "Confirmation automatique des nouveaux utilisateurs" }, "autoConfirmDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked.", + "message": "Les nouveaux utilisateurs invités à rejoindre l'organisation seront automatiquement confirmés lorsque l'appareil d'un administrateur est déverrouillé.", "description": "This is the description of the policy as it appears in the 'Policies' page" }, "howToTurnOnAutoConfirm": { - "message": "How to turn on automatic user confirmation" + "message": "Comment activer la confirmation automatique de l'utilisateur" }, "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "message": "Ouvrez votre extension Bitwarden." }, "autoConfirmStep2a": { - "message": "Select", + "message": "Sélectionner", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmStep2b": { - "message": " Turn on.", + "message": " Activer.", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmExtensionOpened": { - "message": "Successfully opened the Bitwarden browser extension. You can now activate the automatic user confirmation setting." + "message": "Ouverture réussie de l'extension du navigateur Bitwarden. Vous pouvez maintenant activer le paramètre de confirmation automatique de l'utilisateur." }, "autoConfirmPolicyEditDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked. Before turning on this policy, please review and agree to the following: ", + "message": "Les nouveaux utilisateurs invités à rejoindre l'organisation seront automatiquement confirmés lorsque l'appareil d'un administrateur est déverrouillé. Avant d'activer cette politique de sécurité, veuillez revoir et accepter les éléments suivants : ", "description": "This is the description of the policy as it appears inside the policy edit dialog" }, "autoConfirmAcceptSecurityRiskTitle": { - "message": "Potential security risk. " + "message": "Risque de sécurité potentiel. " }, "autoConfirmAcceptSecurityRiskDescription": { - "message": "Automatic user confirmation could pose a security risk to your organization’s data." + "message": "La confirmation automatique de l'utilisateur peut présenter un risque pour la sécurité des données de votre organisation." }, "autoConfirmAcceptSecurityRiskLearnMore": { - "message": "Learn about the risks", + "message": "En savoir plus sur les risques", "description": "The is the link copy for the first check box option in the edit policy dialog" }, "autoConfirmSingleOrgRequired": { - "message": "Single organization policy required. " + "message": "Une politique d'organisation unique est requise. " }, "autoConfirmSingleOrgRequiredDescription": { - "message": "Anyone part of more than one organization will have their access revoked until they leave the other organizations." + "message": "Toute personne faisant partie de plus d'une organisation sera révoquée jusqu'à ce qu'elle quitte les autres organisations." }, "autoConfirmSingleOrgExemption": { - "message": "Single organization policy will extend to all roles. " + "message": "La politique d'organisation unique s'étendra à tous les rôles. " }, "autoConfirmNoEmergencyAccess": { - "message": "No emergency access. " + "message": "Pas d'accès d'urgence. " }, "autoConfirmNoEmergencyAccessDescription": { - "message": "Emergency Access will be removed." + "message": "L'accès d'urgence sera retiré." }, "autoConfirmCheckBoxLabel": { - "message": "I accept these risks and policy updates" + "message": "J'accepte ces risques et mises à jour de la politique de sécurité" }, "personalOwnership": { "message": "Supprimer le coffre individuel" @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Votre organisation a mis à jour vos options de déchiffrement. Veuillez définir un mot de passe principal pour accéder à votre coffre." }, - "maximumVaultTimeout": { - "message": "Délai d'expiration du coffre" + "sessionTimeoutPolicyTitle": { + "message": "Délai d'expiration de la session" }, - "maximumVaultTimeoutDesc": { - "message": "Configurer un délai maximum d'expiration du coffre pour tous les utilisateurs." + "sessionTimeoutPolicyDescription": { + "message": "Définir un délai d'expiration maximum de session pour tous les membres sauf les propriétaires." }, - "maximumVaultTimeoutLabel": { - "message": "Délai maximum d'expiration du coffre" + "maximumAllowedTimeout": { + "message": "Délai d'expiration maximum autorisé" }, - "invalidMaximumVaultTimeout": { - "message": "Délai maximum d'expiration du coffre invalide." + "maximumAllowedTimeoutRequired": { + "message": "Le délai d'expiration maximum autorisé est requis." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Le temps n'est pas valide. Changez au moins une valeur." + }, + "sessionTimeoutAction": { + "message": "Action après délai d'expiration de la session" + }, + "immediately": { + "message": "Immédiatement" + }, + "onSystemLock": { + "message": "Au verrouillage du système" + }, + "onAppRestart": { + "message": "Au redémarrage de l'application" }, "hours": { "message": "Heures" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Êtes-vous certain de vouloir autoriser un délai d'expiration maximum de \"Jamais\" pour tous les membres ?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "Cette option enregistrera les clés de chiffrement de vos membres sur leurs appareils. Si vous choisissez cette option, assurez-vous que leurs appareils sont correctement protégés." + }, + "learnMoreAboutDeviceProtection": { + "message": "En savoir plus sur la protection de l'appareil" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "Le \"Verrouillage du système\" ne s'appliquera qu'au navigateur et à l'application bureau" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "Les applications mobile et web utiliseront \"au redémarrage de l'application\" comme délai d'expiration maximum autorisé, puisque l'option n'est pas prise en charge." + }, "vaultTimeoutPolicyInEffect": { "message": "Les politiques de sécurité de votre organisation ont défini le délai d'expiration de votre coffre à $HOURS$ heure(s) et $MINUTES$ minute(s) maximum.", "placeholders": { @@ -11911,30 +11989,39 @@ "message": "Voir les forfaits d'affaires" }, "updateEncryptionSettings": { - "message": "Update encryption settings" + "message": "Mettre à jour les paramètres de chiffrement" }, "updateYourEncryptionSettings": { - "message": "Update your encryption settings" + "message": "Mettre à jour vos paramètres de chiffrement" }, "updateSettings": { - "message": "Update settings" + "message": "Mettre à jour les paramètres" }, "algorithm": { - "message": "Algorithm" + "message": "Algorithme" }, "encryptionKeySettingsHowShouldWeEncryptYourData": { - "message": "Choose how Bitwarden should encrypt your vault data. All options are secure, but stronger methods offer better protection - especially against brute-force attacks. Bitwarden recommends the default setting for most users." + "message": "Choisissez comment Bitwarden devrait chiffrer les données de votre coffre. Toutes les options sont sécures, mais les méthodes plus robustes offrent une meilleure protection - en particulier contre les attaques par force brute. Bitwarden recommande le réglage par défaut pour la plupart des utilisateurs." }, "encryptionKeySettingsIncreaseImproveSecurity": { - "message": "Increasing the values above the default will improve security, but your vault may take longer to unlock as a result." + "message": "Augmenter les valeurs au-dessus des valeurs par défaut améliorera la sécurité, mais votre coffre peut prendre plus de temps à déverrouiller dû à cela." }, "encryptionKeySettingsAlgorithmPopoverTitle": { - "message": "About encryption algorithms" + "message": "À propos des algorithmes de chiffrement" }, "encryptionKeySettingsAlgorithmPopoverPBKDF2": { - "message": "PBKDF2-SHA256 is a well-tested encryption method that balances security and performance. Good for all users." + "message": "PBKDF2-SHA256 est une méthode de cryptage bien testée qui offre un équilibre entre la sécurité et les performances. Il est adéquat pour tous les utilisateurs." }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { - "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + "message": "Argon2id offre une protection renforcée contre les attaques modernes. Il est le meilleur pour les utilisateurs avancés utilisant des appareils puissants." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Code postal" + }, + "cardNumberLabel": { + "message": "Numéro de carte" + }, + "startFreeFamiliesTrial": { + "message": "Commencez l'essai gratuit au forfait Familles" } } diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index 18d9cd9cd65..fc561fdc46a 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index dbc95083710..f28f566fd3d 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "לא נמצאו יישומים אצל $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "ככל שמשתמשים שומרים כניסות, יישומים מופיעים כאן, ומוצגות כל הסיסמאות בסיכון. סמן יישומים קריטיים והודע למשתמשים לעדכן סיסמאות." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "לא סימנת אף יישום כקריטי" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "בטל סימון כקריטי" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "השתמש בכניסה יחידה" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "ברוך שובך" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "ההזמנה התקבלה" }, - "inviteAcceptedDesc": { - "message": "תוכל לקבל גישה לארגון זה כשאחד המנהלים יאשר את החברות שלך. נשלח לך מייל כשזה יקרה." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "אתה יכול עכשיו לגשת אל ארגון זה." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "הארגון שלך עדכן את אפשרויות הפענוח שלך. נא להגדיר סיסמה ראשית כדי לגשת לכספת שלך." }, - "maximumVaultTimeout": { - "message": "פסק זמן לכספת" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "הגדר פסק זמן מרבי לכספת עבור חברים." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "פסק זמן מרבי לכספת" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "פסק זמן מרבי לכספת לא חוקי." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "שעות" @@ -6510,6 +6573,21 @@ "minutes": { "message": "דקות" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "פוליסות הארגון שלך הגדירו את פסק הזמן לכספת המרבי שלך ל־$HOURS$ שעות ו־$MINUTES$ דקות.", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index 42b7a424840..4744fe42487 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index f057a77ab5b..c95eb0a5075 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Nisu nađene aplikacije u $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Kako korisnici spremaju podatke za prijavu, ovdje se pojavljuju aplikacije koje prikazuju sve rizične lozinke. Označi kritične aplikacije i obavijesti korisnike da ažuriraju lozinke." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Niti jedna aplikacija nije označena kao kritična" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Funkcionalnost „Označi kao kritično” bit će implementirana u budućem ažuriranju" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Odznači kao kritično" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Jedinstvena prijava (SSO)" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Dobro došli natrag" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Poziv prihvaćen" }, - "inviteAcceptedDesc": { - "message": "Pristup organizaciji biti će ti omogućen čim administrator potvrdi tvoje članstvo, o čemu ćemo te obavijestiti e-poštom." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "Sada imaš pristup organizaciji." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Tvoja je organizacija ažurirala tvoje opcije dešifriranja. Postavi glavnu lozinku za pristup svom trezoru." }, - "maximumVaultTimeout": { - "message": "Istek trezora" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Najveći dozvoljeni istek trezora za sve korisnike." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Najveći istek trezora" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Najveći istek trezora nije ispravan." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "sat(i)" @@ -6510,6 +6573,21 @@ "minutes": { "message": "min." }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Pravilo tvoje organizacije utječe na istek trezora. Najveće dozvoljeno vrijeme isteka je $HOURS$:$MINUTES$ h.", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id nudi jaču zaštitu od modernih napada. Najbolje za napredne korisnike s moćnim uređajima." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index d299514da4c..0753f1d77c7 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Nem találhatók alkalmazások: $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Amint a felhasználók elmentik a bejelentkezéseket, itt megjelennek az alkalmazások és megjelenítik a veszélyeztetett jelszavakat. Jelöljük meg a kritikus alkalmazásokat és értesítsük a felhasználókat a jelszavak frissítéséről." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Egyetlen alkalmazás sem lett megjelölve kritikusként." @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "A kritikus funkcióként megjelölés egy jövőbeli frissítésben lesz megvalósítva." }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Ktritkus jelölés eltávolítása" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Egyszeri bejelentkezés használata" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Üdvözlet újra" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "A meghívás elfogadásra került." }, - "inviteAcceptedDesc": { - "message": "A szervezet elérhető, amikor az adminisztrátor jóváhagyja a tagságot. Amint ez megtörténik, értesítő email érkezik." + "invitationAcceptedDesc": { + "message": "A meghívó sikeresen elfogadásra került." }, "inviteInitAcceptedDesc": { "message": "Mostantól elérhető ez a szervezet." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "A szervezete frissítette a visszafejtési beállításokat. Állítsunk be egy mestarjelszót a széf eléréséhez." }, - "maximumVaultTimeout": { - "message": "Széf időkifutás" + "sessionTimeoutPolicyTitle": { + "message": "Munkamenet időkifutás" }, - "maximumVaultTimeoutDesc": { - "message": "Állítsunk be maximális széf időtúllépést minden felhasználó számára." + "sessionTimeoutPolicyDescription": { + "message": "Állítsunk be egy maximális munkamenet időtúllépést minden tag számára a tulajdonosokat kivéve." }, - "maximumVaultTimeoutLabel": { - "message": "Maximális széf időkifutás" + "maximumAllowedTimeout": { + "message": "Maximálisan engedélyezett időkifutás" }, - "invalidMaximumVaultTimeout": { - "message": "A maximális széf időkifutás érvénytelen." + "maximumAllowedTimeoutRequired": { + "message": "A maximális megengedett időkifutás megadása szükséges." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Az idő érvénytelen. Legalább egy értéket módosításunk." + }, + "sessionTimeoutAction": { + "message": "Munkamenet időkifutási művelet" + }, + "immediately": { + "message": "Azonnal" + }, + "onSystemLock": { + "message": "Rendszer zárolásakor" + }, + "onAppRestart": { + "message": "Alkalmazás újraindításakor" }, "hours": { "message": "Óra" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Perc" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Biztosan szeretnénk minden tag számára engedélyezni a \"Soha\" maximálisi időkifutást?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "Ez az opció elmenti tagjai titkosítási kulcsait az eszközeikre. Ha ezt a lehetőséget választjuk, gondoskodjunk arról, hogy eszközeik megfelelő védelemben részesüljenek." + }, + "learnMoreAboutDeviceProtection": { + "message": "További információ az eszköz védelemről" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "A \"rendszerzár\" csak a böngészőre és az asztali alkalmazásra vonatkozik." + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "A mobil- és webalkalmazás az \"alkalmazás újraindításakor“ kifejezést használja a maximális megengedett időkifutásként, mivel az opció nem támogatott." + }, "vaultTimeoutPolicyInEffect": { "message": "A szervezeti házirendek hatással vannak a széf időkorlátjára. A széf időkorlátja legfeljebb $HOURS$ óra és $MINUTES$ perc lehet.", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Az Argon2id erősebb védelmet nyújt a modern támadásokkal szemben. A legjobb a nagy teljesítményű eszközökkel rendelkező haladó felhasználók számára." + }, + "zipPostalCodeLabel": { + "message": "Irányítószám" + }, + "cardNumberLabel": { + "message": "Kártya szám" + }, + "startFreeFamiliesTrial": { + "message": "Az ingyenes Családok próbaverzió elindítása" } } diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index 352b88ce2d1..2dfdfceec11 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -155,7 +155,7 @@ } }, "newPasswordsAtRisk": { - "message": "$COUNT$ new passwords at-risk", + "message": "$COUNT$ sandi baru berisiko", "placeholders": { "count": { "content": "$1", @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Tidak menemukan aplikasi di $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Setelah pengguna menyimpan login, aplikasi akan muncul di sini menampilkan sandi yang berisiko. Tandai aplikasi penting dan peringatkan pengguna untuk memperbarui sandi." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Anda belum menandai aplikasi apa pun sebagai penting" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Fungsi tandai sebagai penting akan diterapkan pada pembaruan mendatang" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Batalkan sebagai penting" }, @@ -911,7 +956,7 @@ "description": "Header for new text send" }, "newItemHeaderFileSend": { - "message": "File Kirim baru", + "message": "Berkas Kirim baru", "description": "Header for new file send" }, "editItemHeaderLogin": { @@ -1107,7 +1152,7 @@ "message": "Brankas Saya" }, "allVaults": { - "message": "Semua Vault" + "message": "Semua Brankas" }, "vault": { "message": "Brankas" @@ -1116,7 +1161,7 @@ "message": "Brankas" }, "vaultItems": { - "message": "Item Brankas" + "message": "Item brankas" }, "filter": { "message": "Filter" @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Gunakan masuk tunggal" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Selamat datang kembali" }, @@ -1354,7 +1402,7 @@ "message": "Gunakan untuk enkripsi brankas" }, "useForVaultEncryptionInfo": { - "message": "Masuk dan buka kunci di perangkat yang didukung tanpa sandi utama Anda. Ikuti petunjuk dari peramban Anda untuk menyelesaikan pengaturan." + "message": "Masuk dan buka kunci di perangkat yang didukung tanpa sandi utama Anda. Ikuti petunjuk dari peramban Anda untuk menyelesaikan setelan." }, "useForVaultEncryptionErrorReadingPasskey": { "message": "Terjadi kesalahan saat membaca kunci sandi. Coba lagi atau hapus centang opsi ini." @@ -1492,7 +1540,7 @@ "message": "Petunjuk sandi utama" }, "masterPassHintText": { - "message": "If you forget your password, the password hint can be sent to your email. $CURRENT$/$MAXIMUM$ character maximum.", + "message": "Jika Anda lupa sandi, petunjuk sandi dapat dikirim ke email Anda. Maksimum karakter $CURRENT$/$MAXIMUM$.", "placeholders": { "current": { "content": "$1", @@ -2220,7 +2268,7 @@ "message": "Impor Data" }, "onboardingImportDataDetailsPartOne": { - "message": "If you don't have any data to import, you can create a ", + "message": "Jika Anda tidak memiliki data untuk diimpor, Anda dapat membuat ", "description": "This will be part of a larger sentence, that will read like this: If you don't have any data to import, you can create a new item instead. (Optional second half: You may need to wait until your administrator confirms your organization membership.)" }, "onboardingImportDataDetailsLink": { @@ -2232,11 +2280,11 @@ "description": "This will be part of a larger sentence, that will read like this: If you don't have any data to import, you can create a new login instead. (Optional second half: You may need to wait until your administrator confirms your organization membership.)" }, "onboardingImportDataDetailsPartTwoNoOrgs": { - "message": " instead.", + "message": " saja.", "description": "This will be part of a larger sentence, that will read like this: If you don't have any data to import, you can create a new item instead." }, "onboardingImportDataDetailsPartTwoWithOrgs": { - "message": " instead. You may need to wait until your administrator confirms your organization membership.", + "message": " saja. Anda mungkin perlu menunggu hingga administrator mengonfirmasi keanggotaan organisasi Anda.", "description": "This will be part of a larger sentence, that will read like this: If you don't have any data to import, you can create a new item instead. You may need to wait until your administrator confirms your organization membership." }, "importError": { @@ -2249,7 +2297,7 @@ "message": "Data telah berhasil diimpor ke lemari besi Anda." }, "importSuccessNumberOfItems": { - "message": "A total of $AMOUNT$ items were imported.", + "message": "Sebanyak $AMOUNT$ item diimpor.", "placeholders": { "amount": { "content": "$1", @@ -2258,7 +2306,7 @@ } }, "dataExportSuccess": { - "message": "Data successfully exported" + "message": "Data berhasil diekspor" }, "importWarning": { "message": "Anda mengimpor data ke $ORGANIZATION$. Data Anda dapat dibagikan dengan anggota organisasi ini. Apakah Anda ingin melanjutkan?", @@ -2279,10 +2327,10 @@ "message": "Tidak dapat mendekripsi file yang diekspor. Kunci enkripsi Anda tidak cocok dengan kunci enkripsi yang digunakan untuk menekspor data terkait." }, "destination": { - "message": "Destination" + "message": "Tujuan" }, "learnAboutImportOptions": { - "message": "Learn about your import options" + "message": "Pelajari tentang pilihan impor Anda" }, "selectImportFolder": { "message": "Pilih folder" @@ -2291,13 +2339,13 @@ "message": "Pilih koleksi" }, "importTargetHintCollection": { - "message": "Select this option if you want the imported file contents moved to a collection" + "message": "Pilih opsi ini jika Anda ingin konten file yang diimpor dipindahkan ke koleksi" }, "importTargetHintFolder": { - "message": "Select this option if you want the imported file contents moved to a folder" + "message": "Pilih opsi ini jika Anda ingin konten file yang diimpor dipindahkan ke folder" }, "importUnassignedItemsError": { - "message": "File contains unassigned items." + "message": "Berkas berisi item yang belum ditetapkan." }, "selectFormat": { "message": "Pilih format file impor" @@ -2343,7 +2391,7 @@ "message": "Ubah bahasa yang digunakan oleh brankas web." }, "showIconsChangePasswordUrls": { - "message": "Show website icons and retrieve change password URLs" + "message": "Tampilkan ikon situs web dan ambil URL perubahan sandi" }, "default": { "message": "Bawaan" @@ -2391,23 +2439,23 @@ "message": "Login dua-langkah" }, "twoStepLoginEnforcement": { - "message": "Two-step Login Enforcement" + "message": "Penegakan Login Dua Langkah" }, "twoStepLoginDesc": { "message": "Amankan akun Anda dengan meminta langkah tambahan saat masuk." }, "twoStepLoginTeamsDesc": { - "message": "Enable two-step login for your organization." + "message": "Aktifkan login dua langkah untuk organisasi Anda." }, "twoStepLoginEnterpriseDescStart": { - "message": "Enforce Bitwarden Two-step Login options for members by using the ", + "message": "Terapkan opsi Login Dua Langkah Bitwarden untuk anggota dengan menggunakan ", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Enforce Bitwarden Two-step Login options for members by using the Two-step Login Policy.'" }, "twoStepLoginPolicy": { - "message": "Two-step Login Policy" + "message": "Kebijakan Login Dua Langkah" }, "twoStepLoginOrganizationDuoDesc": { - "message": "To enforce Two-step Login through Duo, use the options below." + "message": "Untuk menerapkan Login Dua Langkah melalui Duo, gunakan opsi di bawah ini." }, "twoStepLoginOrganizationSsoDesc": { "message": "If you have setup SSO or plan to, Two-step Login may already be enforced through your Identity Provider." @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Undangan Diterima" }, - "inviteAcceptedDesc": { - "message": "Anda dapat mengakses organisasi ini setelah administrator mengonfirmasi keanggotaan Anda. Kami akan mengirimi Anda email jika itu terjadi." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "Anda sekarang dapat mengakses organisasi ini." @@ -5375,14 +5423,14 @@ "message": "Pemilik dan Administrator Organisasi dibebaskan dari penegakan kebijakan ini." }, "limitSendViews": { - "message": "Limit views" + "message": "Batasi tampilan" }, "limitSendViewsHint": { - "message": "No one can view this Send after the limit is reached.", + "message": "Tidak seorang pun dapat melihat Kirim ini setelah batas tercapai.", "description": "Displayed under the limit views field on Send" }, "limitSendViewsCount": { - "message": "$ACCESSCOUNT$ views left", + "message": "$ACCESSCOUNT$ tampilan tersisa", "description": "Displayed under the limit views field on Send", "placeholders": { "accessCount": { @@ -5392,11 +5440,11 @@ } }, "sendDetails": { - "message": "Send details", + "message": "Detail Kirim", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTypeTextToShare": { - "message": "Text to share" + "message": "Teks untuk dibagikan" }, "sendTypeFile": { "message": "Berkas" @@ -5405,7 +5453,7 @@ "message": "Teks" }, "sendPasswordDescV3": { - "message": "Add an optional password for recipients to access this Send.", + "message": "Tambahkan sandi opsional bagi penerima untuk mengakses Kirim ini.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createSend": { @@ -5515,11 +5563,11 @@ "message": "Download attachments" }, "sendAccessPasswordTitle": { - "message": "Enter the password to view this Send", + "message": "Masukkan sandi untuk melihat Kirim ini", "description": "Title of the Send password authentication screen." }, "sendAccessContentTitle": { - "message": "View Send", + "message": "Lihat Kirim", "description": "Title of the Send view content screen." }, "sendAccessUnavailable": { @@ -5730,47 +5778,47 @@ "message": "Open your Bitwarden extension." }, "autoConfirmStep2a": { - "message": "Select", + "message": "Pilih", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmStep2b": { - "message": " Turn on.", + "message": " Aktifkan.", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmExtensionOpened": { - "message": "Successfully opened the Bitwarden browser extension. You can now activate the automatic user confirmation setting." + "message": "Ekstensi peramban Bitwarden berhasil dibuka. Anda sekarang dapat mengaktifkan pengaturan konfirmasi pengguna otomatis." }, "autoConfirmPolicyEditDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked. Before turning on this policy, please review and agree to the following: ", + "message": "Pengguna baru yang diundang ke organisasi akan otomatis dikonfirmasi saat perangkat admin dibuka kuncinya. Sebelum mengaktifkan kebijakan ini, harap tinjau dan setujui hal-hal berikut: ", "description": "This is the description of the policy as it appears inside the policy edit dialog" }, "autoConfirmAcceptSecurityRiskTitle": { - "message": "Potential security risk. " + "message": "Potensi risiko keamanan. " }, "autoConfirmAcceptSecurityRiskDescription": { - "message": "Automatic user confirmation could pose a security risk to your organization’s data." + "message": "Konfirmasi pengguna otomatis dapat menimbulkan risiko keamanan terhadap data organisasi Anda." }, "autoConfirmAcceptSecurityRiskLearnMore": { - "message": "Learn about the risks", + "message": "Pelajari tentang risikonya", "description": "The is the link copy for the first check box option in the edit policy dialog" }, "autoConfirmSingleOrgRequired": { - "message": "Single organization policy required. " + "message": "Diperlukan kebijakan organisasi tunggal. " }, "autoConfirmSingleOrgRequiredDescription": { - "message": "Anyone part of more than one organization will have their access revoked until they leave the other organizations." + "message": "Siapa pun yang menjadi bagian dari lebih dari satu organisasi akan dicabut aksesnya hingga mereka meninggalkan organisasi lain." }, "autoConfirmSingleOrgExemption": { - "message": "Single organization policy will extend to all roles. " + "message": "Kebijakan organisasi tunggal akan diperluas ke semua peran. " }, "autoConfirmNoEmergencyAccess": { - "message": "No emergency access. " + "message": "Tidak ada akses darurat. " }, "autoConfirmNoEmergencyAccessDescription": { - "message": "Emergency Access will be removed." + "message": "Akses Darurat akan dihapus." }, "autoConfirmCheckBoxLabel": { - "message": "I accept these risks and policy updates" + "message": "Saya menerima risiko dan pembaruan kebijakan ini" }, "personalOwnership": { "message": "Kepemilikan Pribadi" @@ -6049,7 +6097,7 @@ } }, "viewSend": { - "message": "View Send", + "message": "Lihat Kirim", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "viewSendHiddenEmailWarning": { @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Batas Waktu Brankas" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Batas waktu Maksimal Brankas" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Batas waktu Maksimal Brankas tidak valid." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Jam" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Menit" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -10612,10 +10690,10 @@ "message": "Learn more about Bitwarden's API" }, "fileSends": { - "message": "File Sends" + "message": "Berkas Kirim" }, "textSends": { - "message": "Text Sends" + "message": "Teks Kirim" }, "includesXMembers": { "message": "for $COUNT$ member", @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index beb8e30f443..75492cb79cc 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Nessuna applicazione trovata in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Quando gli utenti salvano i login, i relativi dati e le password a rischio sono mostrati qui. Contrassegna le applicazioni critiche e notifica agli utenti di aggiornare le password." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Non hai contrassegnato nessuna applicazione come critica" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "La funzionalità per contrassegnare gli elementi critici sarà implementata in un aggiornamento futuro" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Contrassegna l'elemento come non critico" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Usa login unificato (SSO)" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Bentornato/a" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invito accettato" }, - "inviteAcceptedDesc": { - "message": "Puoi accedere a questa organizzazione se l'amministratore conferma la tua iscrizione. Ti invieremo un'email quando sarà confermata." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "Ora puoi accedere a questa organizzazione." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "La tua organizzazione ha aggiornato le tue opzioni di decrittazione. Per favore, imposta una master password per accedere alla tua cassaforte." }, - "maximumVaultTimeout": { - "message": "Timeout cassaforte" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Imposta un timeout massimo della cassaforte per i membri." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Timeout massimo della cassaforte" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Timeout massimo della cassaforte non valido." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Ore" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minuti" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Le politiche della tua organizzazione hanno impostato il timeout massimo consentito della tua cassaforte su $HOURS$ ore e $MINUTES$ minuti.", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index 09c3c0c5d5a..91bbf61dca7 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "$ORG NAME$ にアプリが見つかりませんでした", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "ユーザーがログイン情報を保存すると、アプリがここに表示され、リスクの高いパスワードがあれば表示されます。特に重要なアプリはマークして、パスワードを更新するようにユーザーに通知しましょう。" + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "シングルサインオンを使用する" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "ようこそ" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "招待が承諾されました。" }, - "inviteAcceptedDesc": { - "message": "組織の管理者が承認すると組織へアクセスできるようになります。アクセスできるようになったらメールでお知らせします。" + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "この組織にアクセスできるようになりました。" @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "組織が復号オプションを更新しました。保管庫にアクセスするにはマスターパスワードを設定してください。" }, - "maximumVaultTimeout": { - "message": "保管庫のタイムアウト" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "メンバーの保管庫の最大タイムアウトを設定します。" + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "保管庫の最大タイムアウト" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "保管庫の最大タイムアウト時間が不正です。" + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "時間" @@ -6510,6 +6573,21 @@ "minutes": { "message": "分" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "保管庫のタイムアウトは組織のポリシーの影響下にあります。保管庫の最大タイムアウト時間は $HOURS$ 時間 $MINUTES$ 分です。", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index c856c9b1b1d..2bdaea7f75d 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index fc5efa82d50..338f5c10d8a 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index ecb0ca92704..1680aaa057b 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "ಆಮಂತ್ರಣವನ್ನು ಸ್ವೀಕರಿಸಲಾಗಿದೆ" }, - "inviteAcceptedDesc": { - "message": "ನಿರ್ವಾಹಕರು ನಿಮ್ಮ ಸದಸ್ಯತ್ವವನ್ನು ಖಚಿತಪಡಿಸಿದ ನಂತರ ನೀವು ಈ ಸಂಸ್ಥೆಯನ್ನು ಪ್ರವೇಶಿಸಬಹುದು. ಅದು ಸಂಭವಿಸಿದಾಗ ನಾವು ನಿಮಗೆ ಇಮೇಲ್ ಕಳುಹಿಸುತ್ತೇವೆ." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index 92b63d85c68..3faf6ce4f69 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "초대 수락됨" }, - "inviteAcceptedDesc": { - "message": "관리자의 확인을 받으면 조직에 액세스할 수 있습니다. 승인이 이뤄지면 이메일을 보내드리겠습니다." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "보관함 시간 제한" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "최대 보관함 시간 제한" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "시" @@ -6510,6 +6573,21 @@ "minutes": { "message": "분" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index 80fb4a98175..ee7c89f73ca 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "$ORG NAME$ netika atrasta neviena lietotne", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Tiklīdz lietotāji saglabā pieteikšanās vienumus, šeit parādās lietotnes, norādot uz jebkurām riskam pakļautām parolēm. Atzīmēt kritiskās lietotnēs un apziņot lietotājus, lai atjaunina paroles." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Neviena lietotne nav atzīmēta kā kritiska" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Iespēja atzīmēt kā būtisku tiks ieviesta nākotnes atjauninājumā" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Noņemt kritiskuma atzīmi" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Izmantot vienoto pieteikšanos" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Laipni lūdzam atpakaļ" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Uzaicinājums pieņemts" }, - "inviteAcceptedDesc": { - "message": "Piekļūt apvienībai varēs, kad tās pārvaldnieks apstiprinās dalību. Tiks nosūtīts e-pasta ziņojums, kad tas notiks." + "invitationAcceptedDesc": { + "message": "Sekmīgi pieņēma Tavu uzaicinājumu." }, "inviteInitAcceptedDesc": { "message": "Tagad vari piekļūt šai apvienībai." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Tava apvienība ir atjauninājusi tavas atšifrēšanas iespējas. Lūgums iestatīt galveno paroli, lai piekļūtu savai glabātavai." }, - "maximumVaultTimeout": { - "message": "Glabātavas noildze" + "sessionTimeoutPolicyTitle": { + "message": "Sesijas noildze" }, - "maximumVaultTimeoutDesc": { - "message": "Uzstādīt lielāko iespējamo glabātavas noildzi visiem lietotājiem." + "sessionTimeoutPolicyDescription": { + "message": "Lielākās pieļaujamās sesijas noildzes iestatīšana visiem dalībniekiem, izņemot īpašniekus." }, - "maximumVaultTimeoutLabel": { - "message": "Lielākā iespējamā glabātavas noildze" + "maximumAllowedTimeout": { + "message": "Lielakā atļautā noildze" }, - "invalidMaximumVaultTimeout": { - "message": "Nederīga lielākās iespējamās glabātavas noildzes vērtība." + "maximumAllowedTimeoutRequired": { + "message": "Lielākā atļautā noildze ir nepieciešama." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Laiks ir nederīgs. Jānomaina vismaz viena vērtība." + }, + "sessionTimeoutAction": { + "message": "Sesijas noildzes darbība" + }, + "immediately": { + "message": "Nekavējoties" + }, + "onSystemLock": { + "message": "Pēc sistēmas aizslēgšanas" + }, + "onAppRestart": { + "message": "Pēc lietotnes atkārtotas palaišanas" }, "hours": { "message": "Stundas" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minūtes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Vai tiešām ļaut lielāko pieļaujamo noildzi “Nekad” visiem dalībniekiem?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "Šī iespēja saglabās dalībnieku šifrēšanas atslēgas viņu ierīcēs. Ja izvēlas šo iespēju, jāpārliecinās, ka dalībnieku ierīces ir pienācīgi aizsargātas." + }, + "learnMoreAboutDeviceProtection": { + "message": "Uzzināt vairāk par ierīču aizsardzību" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"Sistēmas aizslēgšana\" attieksies tikai uz pārlūka un darbvirsmas lietotni" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "Viedierīču un tīmekļa lietotne izmantos “Pēc lietotnes atkārtotas palaišanas” kā lielāko atļauto noildzi, jo šī iespēja netiek nodrošināta." + }, "vaultTimeoutPolicyInEffect": { "message": "Apvienības nosacījumi ietekmē glabātavas noildzi. Lielākā atļautā glabātavas noildze ir $HOURS$ stunda(s) un $MINUTES$ minūte(s)", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id sniedz spēcīgāku aizsardzību pret mūsdienu uzbrukumiem. Vislabāk piemērots lietpratējiem ar jaudīgām ierīcēm." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Pasta indekss" + }, + "cardNumberLabel": { + "message": "Kartes numurs" + }, + "startFreeFamiliesTrial": { + "message": "Uzsākt ģimenes plāna bezmaksas izmēģinājumu" } } diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index 4f77abe98ba..b087d181c52 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "ക്ഷണം സ്വീകരിച്ചു" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index a9639023b9d..294713bbd17 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index fc5efa82d50..338f5c10d8a 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index 348ef01c2a8..14ae5e59a88 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Bruk singulær pålogging" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Velkommen tilbake" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitasjonen ble godkjent" }, - "inviteAcceptedDesc": { - "message": "Du kan få tilgang til denne organisasjonen så fort en administrator godkjenner ditt medlemskap. Vi vil sende deg E-post når det skjer." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Tidsavbrudd i hvelvet" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Konfigurere en maks tidsavbrudd for hvelvet for alle brukere." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maksimal hvelv tidsavbrudd" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Ugyldig maksimal hvelv tidsavbrudd." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Åpningstider" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutter" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Organisasjonens retningslinjer påvirker tidsavbruddet for hvelvet ditt. Maksimalt tillatt tidsavbrudd for hvelv er $HOURS$ time(r) og $MINUTES$ minutt(er)", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index d069e4a6e9c..9ac3d615dfb 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index 93334ff9e3c..051e9b616aa 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Nieuwe applicaties gevonden in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Als gebruikers inloggegevens opslaan, verschijnen hier applicaties met wachtwoorden die risico lopen. Markeer belangrijke applicaties en attendeer gebruikers op het bijwerken van wachtwoorden." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Je hebt nog geen applicaties als belangrijk aangewezen" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Markeren als kritieke functionaliteit wordt geïmplementeerd in een toekomstige update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Markeren als belangrijk ongedaan maken" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Single sign-on gebruiken" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welkom terug" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Uitnodiging geaccepteerd" }, - "inviteAcceptedDesc": { - "message": "Je krijgt toegang tot deze organisatie zodra een beheerder je lidmaatschap bevestigt. We sturen je een e-mail zodra dat gebeurt." + "invitationAcceptedDesc": { + "message": "Je uitnodiging is geaccepteerd." }, "inviteInitAcceptedDesc": { "message": "Je hebt nu toegang tot deze organisatie." @@ -5717,20 +5765,20 @@ "message": "Nu beschikbaar" }, "autoConfirm": { - "message": "Automatic confirmation of new users" + "message": "Automatische bevestiging van nieuwe gebruikers" }, "autoConfirmDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked.", + "message": "Nieuwe uitgenodigde gebruikers in de organisatie worden automatisch bevestigd wanneer het apparaat van een beheerder is ontgrendeld.", "description": "This is the description of the policy as it appears in the 'Policies' page" }, "howToTurnOnAutoConfirm": { - "message": "How to turn on automatic user confirmation" + "message": "Automatische bevestiging van gebruikers inschakelen" }, "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "message": "De Bitwarden-extensie openen." }, "autoConfirmStep2a": { - "message": "Select", + "message": "Selecteer", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmStep2b": { @@ -5738,39 +5786,39 @@ "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmExtensionOpened": { - "message": "Successfully opened the Bitwarden browser extension. You can now activate the automatic user confirmation setting." + "message": "De Bitwarden-browserextensie is succesvol geopend. Je kunt nu de automatische bevestiging van de gebruiker activeren." }, "autoConfirmPolicyEditDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked. Before turning on this policy, please review and agree to the following: ", + "message": "Nieuwe uitgenodigde gebruikers in de organisatie worden automatisch bevestigd wanneer het apparaat van een beheerder is ontgrendeld. Voordat je dit beleid gaat inschakelt, graag het volgende lezen en instemmen: ", "description": "This is the description of the policy as it appears inside the policy edit dialog" }, "autoConfirmAcceptSecurityRiskTitle": { "message": "Mogelijk beveiligingsrisico. " }, "autoConfirmAcceptSecurityRiskDescription": { - "message": "Automatic user confirmation could pose a security risk to your organization’s data." + "message": "Automatische gebruikersbevestiging kan een beveiligingsrisico vormen voor de gegevens van je organisatie." }, "autoConfirmAcceptSecurityRiskLearnMore": { - "message": "Learn about the risks", + "message": "Meer informatie over de risico's", "description": "The is the link copy for the first check box option in the edit policy dialog" }, "autoConfirmSingleOrgRequired": { - "message": "Single organization policy required. " + "message": "Enkele Organisatie-beleid vereist " }, "autoConfirmSingleOrgRequiredDescription": { - "message": "Anyone part of more than one organization will have their access revoked until they leave the other organizations." + "message": "Iedereen die deel uitmaakt van meer dan één organisatie krijgt zijn toegang ingetrokken totdat deze de andere organisaties verlaat." }, "autoConfirmSingleOrgExemption": { - "message": "Single organization policy will extend to all roles. " + "message": "Het beleid inzake één organisatie strekt zich uit tot alle rollen. " }, "autoConfirmNoEmergencyAccess": { - "message": "No emergency access. " + "message": "Geen noodtoegang. " }, "autoConfirmNoEmergencyAccessDescription": { - "message": "Emergency Access will be removed." + "message": "Noodtoegang zal worden verwijderd." }, "autoConfirmCheckBoxLabel": { - "message": "I accept these risks and policy updates" + "message": "Ik accepteer deze risico's en beleidsupdates" }, "personalOwnership": { "message": "Persoonlijk eigendom" @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Je organisatie heeft je versleutelingsopties gewijzigd. Stel een hoofdwachtwoord in om toegang te krijgen tot je kluis." }, - "maximumVaultTimeout": { - "message": "Time-out van de kluis" + "sessionTimeoutPolicyTitle": { + "message": "Sessie time-out" }, - "maximumVaultTimeoutDesc": { - "message": "Stel een maximale time-out van de kluis in voor alle gebruikers." + "sessionTimeoutPolicyDescription": { + "message": "Stel een maximale sessie time-out in voor alle leden behalve eigenaren." }, - "maximumVaultTimeoutLabel": { - "message": "Maximale time-out van de kluis" + "maximumAllowedTimeout": { + "message": "Maximaal toegestane time-out" }, - "invalidMaximumVaultTimeout": { - "message": "Ongeldige maximale time-out van de kluis." + "maximumAllowedTimeoutRequired": { + "message": "Maximaal toegestane time-out is vereist." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Tijd is ongeldig. Wijzig ten minste één waarde." + }, + "sessionTimeoutAction": { + "message": "Actie voor sessie time-out" + }, + "immediately": { + "message": "Onmiddellijk" + }, + "onSystemLock": { + "message": "Bij systeemvergrendeling" + }, + "onAppRestart": { + "message": "Na opnieuw starten" }, "hours": { "message": "Uren" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minuten" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Weet je zeker dat je een maximale time-out van \"Nooit\" wilt toestaan voor alle leden?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "Deze optie slaat de encryptiesleutels van je leden op hun apparaten op. Als je deze optie kiest, zorg er dan voor dat hun apparaten voldoende worden beschermd." + }, + "learnMoreAboutDeviceProtection": { + "message": "Lees meer over apparaatbescherming" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"Systeem vergrendeling\" zal alleen van toepassing zijn op de browser en desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "De mobiele en web-app gebruikt \"bij herstarten van de app\" als maximaal toegestane time-out, aangezien de optie niet wordt ondersteund." + }, "vaultTimeoutPolicyInEffect": { "message": "Het beleid van je organisatie heeft invloed op de time-out van je kluis. De maximaal toegestane time-out voor je kluis is $HOURS$ uur en $MINUTES$ minuten.", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id biedt een sterkere bescherming tegen moderne aanvallen. Het beste voor geavanceerde gebruikers met krachtige apparaten." + }, + "zipPostalCodeLabel": { + "message": "Postcode" + }, + "cardNumberLabel": { + "message": "Kaartnummer" + }, + "startFreeFamiliesTrial": { + "message": "Start gratis Families-proefperiode" } } diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index fdc9eb8127f..f0b0a82da9c 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index fc5efa82d50..338f5c10d8a 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index 82bb4a85cbb..d81146ad284 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Nie znaleziono aplikacji w $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Jako użytkownicy zapisują logowania, pojawiają się tutaj aplikacje, pokazujące wszelkie hasła zagrożone. Zaznacz kluczowe aplikacje i powiadamiaj użytkowników o potrzebie aktualizacji haseł." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Użyj jednokrotnego logowania" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Witaj ponownie" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Zaproszenie zostało zaakceptowane" }, - "inviteAcceptedDesc": { - "message": "Dostęp do tej organizacji otrzymasz po potwierdzeniu członkostwa przez administratora. Dostaniesz wtedy wiadomość e-mail." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "Możesz teraz uzyskać dostęp do tej organizacji." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Twoja organizacja zaktualizowała opcje szyfrowania. Ustaw hasło główne, aby uzyskać dostęp do sejfu." }, - "maximumVaultTimeout": { - "message": "Blokowanie sejfu" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Ustaw maksymalny czas blokowania sejfu dla wszystkich użytkowników." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maksymalny czas blokowania sejfu" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Maksymalny czas blokowania sejfu jest nieprawidłowy." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Godziny" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minuty" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Zasady organizacji mają wpływ na czas blokowania sejfu. Maksymalny dozwolony czas wynosi $HOURS$ godz. i $MINUTES$ min.", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index 61296561fe6..fdcbc0e27c9 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Nenhum aplicativo encontrado em $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Quando usuários salvam credenciais, aplicativos aparecem aqui, mostrando quaisquer senhas em risco. Marque apps como críticos e notifique usuários para que atualizem as senhas." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Você não marcou nenhum aplicativo como crítico" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "A funcionalidade de marcar como crítico será implementada em uma atualização futura" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Desmarcar como crítico" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Usar login único" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Boas-vindas de volta" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Convite aceito" }, - "inviteAcceptedDesc": { - "message": "Você pode acessar essa organização quando um administrador confirmar sua associação. Enviaremos um e-mail quando isso acontecer." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "Você agora pode acessar esta organização." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Sua organização atualizou suas opções de descriptografia. Por favor, defina uma senha mestra para acessar seu cofre." }, - "maximumVaultTimeout": { - "message": "Tempo Limite do Cofre" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Configure o tempo máximo para o cofre para todos os usuários." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Tempo Limite Máximo do Cofre" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Tempo Máximo de Cofre Inválido." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Horas" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutos" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "As políticas da sua organização estão afetando o tempo limite do seu cofre. O Tempo Limite Máximo permitido do Cofre é $HOURS$ hora(s) e $MINUTES$ minuto(s)", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index 321beaf907d..972fad28bd5 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Não foram encontradas aplicações em $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "Não foram encontradas aplicações para $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "À medida que os utilizadores guardam as credenciais, as aplicações aparecem aqui, mostrando quaisquer palavras-passe em risco. Marque as aplicações críticas e notifique os utilizadores para atualizarem as palavras-passe." + "noApplicationsInOrgDescription": { + "message": "Importe as credenciais da sua organização para começar a monitorizar os riscos de segurança das credenciais. Após a importação, poderá:" + }, + "benefit1Title": { + "message": "Priorizar os riscos" + }, + "benefit1Description": { + "message": "Com foco nas aplicações mais importantes" + }, + "benefit2Title": { + "message": "Remediação de guias" + }, + "benefit2Description": { + "message": "Atribuir tarefas orientadas aos membros em risco para alterar as credenciais em risco" + }, + "benefit3Title": { + "message": "Monitorizar o progresso" + }, + "benefit3Description": { + "message": "Acompanhe as alterações ao longo do tempo para mostrar as melhorias na segurança" + }, + "noReportRunTitle": { + "message": "Execute o seu primeiro relatório para ver as aplicações" + }, + "noReportRunDescription": { + "message": "Gere um relatório de insights de risco para analisar as aplicações da sua organização e identificar palavras-passe em risco que precisam de atenção. A execução do seu primeiro relatório irá:" }, "noCriticalApplicationsTitle": { "message": "Não marcou nenhuma aplicação como crítica" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "A funcionalidade marcada como crítica será implementada numa atualização futura" }, + "applicationReviewSaved": { + "message": "Revisão da aplicação guardada" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ marcadas como críticas", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "Novas aplicações revistas" + }, + "errorSavingReviewStatus": { + "message": "Erro ao guardar o estado da revisão" + }, + "pleaseTryAgain": { + "message": "Por favor, tente novamente" + }, "unmarkAsCritical": { "message": "Desmarcar como crítica" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Utilizar início de sessão único" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "A sua organização exige o início de sessão único." + }, "welcomeBack": { "message": "Bem-vindo de volta" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Convite aceite" }, - "inviteAcceptedDesc": { - "message": "Pode aceder a esta organização quando um administrador confirmar a sua inscrição. Enviar-lhe-emos um e-mail quando tal acontecer." + "invitationAcceptedDesc": { + "message": "O seu convite foi aceite com sucesso." }, "inviteInitAcceptedDesc": { "message": "Já pode aceder a esta organização." @@ -5714,63 +5762,63 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Learn more about the credential lifecycle.'" }, "availableNow": { - "message": "Available now" + "message": "Já disponível" }, "autoConfirm": { - "message": "Automatic confirmation of new users" + "message": "Confirmação automática de novos utilizadores" }, "autoConfirmDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked.", + "message": "Os novos utilizadores convidados para a organização serão automaticamente confirmados quando o dispositivo de um administrador for desbloqueado.", "description": "This is the description of the policy as it appears in the 'Policies' page" }, "howToTurnOnAutoConfirm": { - "message": "How to turn on automatic user confirmation" + "message": "Como ativar a confirmação automática do utilizador" }, "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "message": "Abra a sua extensão Bitwarden." }, "autoConfirmStep2a": { - "message": "Select", + "message": "Selecione", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmStep2b": { - "message": " Turn on.", + "message": " Ativar.", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmExtensionOpened": { - "message": "Successfully opened the Bitwarden browser extension. You can now activate the automatic user confirmation setting." + "message": "A extensão do navegador Bitwarden foi aberta com sucesso. Agora pode ativar a configuração de confirmação automática do utilizador." }, "autoConfirmPolicyEditDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked. Before turning on this policy, please review and agree to the following: ", + "message": "Os novos utilizadores convidados para a organização serão automaticamente confirmados quando o dispositivo de um administrador for desbloqueado. Antes de ativar esta política, reveja e concorde com o seguinte: ", "description": "This is the description of the policy as it appears inside the policy edit dialog" }, "autoConfirmAcceptSecurityRiskTitle": { - "message": "Potential security risk. " + "message": "Potencial risco de segurança. " }, "autoConfirmAcceptSecurityRiskDescription": { - "message": "Automatic user confirmation could pose a security risk to your organization’s data." + "message": "A confirmação automática do utilizador pode representar um risco de segurança para os dados da sua organização." }, "autoConfirmAcceptSecurityRiskLearnMore": { - "message": "Learn about the risks", + "message": "Saiba mais sobre os riscos", "description": "The is the link copy for the first check box option in the edit policy dialog" }, "autoConfirmSingleOrgRequired": { - "message": "Single organization policy required. " + "message": "É necessária uma política de organização única. " }, "autoConfirmSingleOrgRequiredDescription": { - "message": "Anyone part of more than one organization will have their access revoked until they leave the other organizations." + "message": "Qualquer pessoa que faça parte de mais de uma organização terá o seu acesso revogado até que saia das outras organizações." }, "autoConfirmSingleOrgExemption": { - "message": "Single organization policy will extend to all roles. " + "message": "A política de organização única será estendida a todos os papéis. " }, "autoConfirmNoEmergencyAccess": { - "message": "No emergency access. " + "message": "Sem acesso de emergência. " }, "autoConfirmNoEmergencyAccessDescription": { - "message": "Emergency Access will be removed." + "message": "O acesso de emergência será removido." }, "autoConfirmCheckBoxLabel": { - "message": "I accept these risks and policy updates" + "message": "Aceito estes riscos e atualizações da política" }, "personalOwnership": { "message": "Remover cofre pessoal" @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "A sua organização atualizou as opções de desencriptação. Por favor, defina uma palavra-passe mestra para aceder ao seu cofre." }, - "maximumVaultTimeout": { - "message": "Tempo limite do cofre" + "sessionTimeoutPolicyTitle": { + "message": "Tempo limite da sessão" }, - "maximumVaultTimeoutDesc": { - "message": "Definir um tempo limite máximo do cofre para os membros." + "sessionTimeoutPolicyDescription": { + "message": "Defina um tempo limite máximo de sessão para todos os membros, exceto proprietários." }, - "maximumVaultTimeoutLabel": { - "message": "Tempo limite máximo do cofre" + "maximumAllowedTimeout": { + "message": "Tempo limite máximo permitido" }, - "invalidMaximumVaultTimeout": { - "message": "Tempo limite máximo do cofre inválido." + "maximumAllowedTimeoutRequired": { + "message": "É necessário o tempo limite máximo permitido." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "O tempo é inválido. Altere pelo menos um valor." + }, + "sessionTimeoutAction": { + "message": "Ação de tempo limite da sessão" + }, + "immediately": { + "message": "Imediatamente" + }, + "onSystemLock": { + "message": "Ao bloquear o sistema" + }, + "onAppRestart": { + "message": "Ao reiniciar a app" }, "hours": { "message": "Horas" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutos" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Tem a certeza de que deseja permitir um tempo limite máximo de \"Nunca\" para todos os membros?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "Esta opção guardará as chaves de encriptação dos seus membros nos seus dispositivos. Se escolher esta opção, certifique-se de que os dispositivos deles estão adequadamente protegidos." + }, + "learnMoreAboutDeviceProtection": { + "message": "Saiba mais sobre a proteção de dispositivos" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "O \"bloqueio do sistema\" só se aplicará ao navegador e à app para computador" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "A app móvel e Web utilizará \"ao reiniciar a app\" como o tempo limite máximo permitido, uma vez que a opção não é suportada." + }, "vaultTimeoutPolicyInEffect": { "message": "As políticas da sua organização definiram o tempo limite máximo permitido do cofre de $HOURS$ hora(s) e $MINUTES$ minuto(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "O Argon2id oferece uma proteção mais forte contra ataques modernos. Ideal para utilizadores avançados com dispositivos potentes." + }, + "zipPostalCodeLabel": { + "message": "Código postal" + }, + "cardNumberLabel": { + "message": "Número do cartão" + }, + "startFreeFamiliesTrial": { + "message": "Comece o teste gratuito do plano Familiar" } } diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index f60f94498f1..8793e1dc632 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Autentificare unică" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Bine ați revenit!" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitație acceptată" }, - "inviteAcceptedDesc": { - "message": "Puteți accesa această organizație după ce un administrator vă confirmă abonamentul. Vă vom trimite un e-mail când se întâmplă acest lucru." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Timp de expirare seif" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Setează un timp de expirare maxim a seifului pentru membri." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Timpul maxim de expirare a seifului" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Timpul maxim de expirare a seifului invalid." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Ore" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minute" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Politicile organizației afectează timpul de expirare a seifului. Timpul maxim de expirare a seifului este de $HOURS$ oră(e) și $MINUTES$ minut(e)", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index fe47aff8f39..08f36143fc7 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -12,7 +12,7 @@ "message": "Критичные приложения" }, "noCriticalAppsAtRisk": { - "message": "Никакие критически важные приложения не подвергаются риску" + "message": "Никакие критичные приложения не подвергаются риску" }, "accessIntelligence": { "message": "Access Intelligence" @@ -24,7 +24,7 @@ "message": "Риск пароля" }, "reviewAtRiskPasswords": { - "message": "Проанализируйте пароли, подверженные риску (слабые, скомпрометированные или повторно используемые), во всех приложениях. Выберите наиболее важные приложения, чтобы определить приоритетные меры безопасности для пользователей, направленные на устранение подверженных риску паролей." + "message": "Проанализируйте пароли, подверженные риску (слабые, скомпрометированные или повторно используемые), во всех приложениях. Выберите наиболее критичные приложения, чтобы определить приоритетные меры безопасности для пользователей, направленные на устранение подверженных риску паролей." }, "dataLastUpdated": { "message": "Последнее обновление: $DATE$", @@ -88,25 +88,25 @@ "message": "Прогресс смены пароля" }, "assignMembersTasksToMonitorProgress": { - "message": "Assign members tasks to monitor progress" + "message": "Назначайте членам команды задачи по мониторингу прогресса" }, "onceYouReviewApps": { - "message": "Once you review applications and mark them as critical, you can assign tasks to members to resolve at-risk items and monitor progress here" + "message": "Как только вы оцените приложения и пометите их как критичные, вы можете назначить задачи участникам касающиеся элементов, подверженных риску, и следить за прогрессом здесь" }, "sendReminders": { - "message": "Send reminders" + "message": "Отправить напоминания" }, "onceYouMarkApplicationsCriticalTheyWillDisplayHere": { - "message": "Once you mark applications critical, they will display here." + "message": "Как только вы пометите приложения как критичные, они отобразятся здесь." }, "viewAtRiskMembers": { - "message": "View at-risk members" + "message": "Просмотр участников подверженных риску" }, "viewAtRiskApplications": { - "message": "View at-risk applications" + "message": "Просмотр приложений, подверженных риску" }, "criticalApplicationsAreAtRisk": { - "message": "$COUNT$ out of $TOTAL$ critical applications are at-risk due to at-risk passwords", + "message": "$COUNT$ из $TOTAL$ критичных приложений подвержены риску из-за уязвимых паролей", "placeholders": { "count": { "content": "$1", @@ -128,7 +128,7 @@ } }, "countOfCriticalApplications": { - "message": "$COUNT$ critical applications", + "message": "$COUNT$ критичных приложений", "placeholders": { "count": { "content": "$1", @@ -137,7 +137,7 @@ } }, "countOfApplicationsAtRisk": { - "message": "$COUNT$ applications at-risk", + "message": "$COUNT$ приложений, подверженных риску", "placeholders": { "count": { "content": "$1", @@ -146,7 +146,7 @@ } }, "countOfAtRiskPasswords": { - "message": "$COUNT$ passwords at-risk", + "message": "$COUNT$ паролей, подверженных риску", "placeholders": { "count": { "content": "$1", @@ -155,7 +155,7 @@ } }, "newPasswordsAtRisk": { - "message": "$COUNT$ new passwords at-risk", + "message": "$COUNT$ новых паролей, подверженных риску", "placeholders": { "count": { "content": "$1", @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Не найдено приложений в $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "Не найдено приложений для $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "По мере сохранения пользователями логинов, здесь отображаются приложения, с подверженными риску паролями. Отметьте критичные приложения и уведомите пользователей об необходимости обновления пароля." + "noApplicationsInOrgDescription": { + "message": "Импортируйте логины вашей организации, чтобы начать мониторинг угроз безопасности учетных данных. После импорта вы получите доступ к:" + }, + "benefit1Title": { + "message": "Приоритет рисков" + }, + "benefit1Description": { + "message": "Сосредоточьтесь на наиболее важных приложениях" + }, + "benefit2Title": { + "message": "Руководство по восстановлению" + }, + "benefit2Description": { + "message": "Назначайте участникам, подверженным риску, управляемые задания для ротации учетных данных" + }, + "benefit3Title": { + "message": "Отслеживать прогресс" + }, + "benefit3Description": { + "message": "Отслеживать изменения с течением времени, чтобы показать улучшения безопасности" + }, + "noReportRunTitle": { + "message": "Запустите ваш первый отчет для просмотра приложений" + }, + "noReportRunDescription": { + "message": "Создайте аналитический отчет о рисках для анализа приложений вашей организации и выявления паролей, подверженных риску, которые требуют внимания. Запуск первого отчета позволит:" }, "noCriticalApplicationsTitle": { "message": "Вы не отметили ни одного приложения в качестве критичного" @@ -194,25 +218,25 @@ "message": "Выбрать критичные приложения" }, "markAppAsCritical": { - "message": "Пометить приложение как критическое" + "message": "Пометить приложение как критичное" }, "markAsCritical": { - "message": "Mark as critical" + "message": "Пометить как критичное" }, "applicationsSelected": { - "message": "applications selected" + "message": "приложений выбрано" }, "selectApplication": { - "message": "Select application" + "message": "Выбрать приложение" }, "unselectApplication": { - "message": "Unselect application" + "message": "Снять выбор с приложения" }, "applicationsMarkedAsCriticalSuccess": { - "message": "Приложения помечены как критические" + "message": "Приложения помечены как критичные" }, "applicationsMarkedAsCriticalFail": { - "message": "Failed to mark applications as critical" + "message": "Не удалось пометить приложения как критичные" }, "application": { "message": "Приложение" @@ -230,13 +254,13 @@ "message": "Поиск приложений" }, "atRiskMembers": { - "message": "Участники группы риска" + "message": "Участники, подверженные риску" }, "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "message": "Участники, имеющие доступ к элементам, подверженным риску, для критичных приложений" }, "membersAtRiskCount": { - "message": "$COUNT$ members at-risk", + "message": "$COUNT$ участников, подверженных риску", "placeholders": { "count": { "content": "$1", @@ -245,7 +269,7 @@ } }, "atRiskMembersWithCount": { - "message": "Пользователи повышенного риска ($COUNT$)", + "message": "Участники, подверженные риску ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -254,7 +278,7 @@ } }, "atRiskApplicationsWithCount": { - "message": "Приложения с повышенным риском ($COUNT$)", + "message": "Приложения, подверженные риску ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -296,16 +320,16 @@ "message": "Всего участников" }, "atRiskApplications": { - "message": "Приложения повышенного риска" + "message": "Приложения, подверженные риску" }, "totalApplications": { "message": "Всего приложений" }, "applicationsNeedingReview": { - "message": "Applications needing review" + "message": "Приложения, требующие проверки" }, "newApplicationsWithCount": { - "message": "$COUNT$ new applications", + "message": "$COUNT$ новых приложений", "placeholders": { "count": { "content": "$1", @@ -314,22 +338,43 @@ } }, "newApplicationsDescription": { - "message": "Review new applications to mark as critical and keep your organization secure" + "message": "Проверяйте новые приложения, чтобы пометить их как критичные и обеспечить защиту вашей организации" }, "reviewNow": { - "message": "Review now" + "message": "Проверить сейчас" }, "prioritizeCriticalApplications": { - "message": "Prioritize critical applications" + "message": "Приоритет критичных приложений" }, "atRiskItems": { - "message": "At-risk items" + "message": "Элементы, подверженные риску" }, "markAsCriticalPlaceholder": { - "message": "Mark as critical functionality will be implemented in a future update" + "message": "Функционал отметки как критического будет реализован в будущем обновлении" + }, + "applicationReviewSaved": { + "message": "Обзор приложения сохранен" + }, + "applicationsMarkedAsCritical": { + "message": "Помечены как критичные приложений: $COUNT$", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "Рассмотрены новые заявки" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Попробуйте еще раз" }, "unmarkAsCritical": { - "message": "Снять отметку критического" + "message": "Снять отметку критичного" }, "criticalApplicationUnmarkedSuccessfully": { "message": "Приложение больше не является критичным" @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Использовать единый вход" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Ваша организация требует единого входа." + }, "welcomeBack": { "message": "С возвращением" }, @@ -3058,10 +3106,10 @@ } }, "showPricingSummary": { - "message": "Show pricing summary" + "message": "Показать сведения о ценах" }, "hidePricingSummary": { - "message": "Hide pricing summary" + "message": "Скрыть сведения о ценах" }, "summary": { "message": "Итого" @@ -3364,7 +3412,7 @@ } }, "planNameFamilies": { - "message": "Семья" + "message": "Families" }, "planDescFamilies": { "message": "Для личного использования, чтобы поделиться с семьей и друзьями." @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Приглашение принято" }, - "inviteAcceptedDesc": { - "message": "Вы получите доступ к этой организации, как только администратор подтвердит ваше членство. Когда это произойдет, мы отправим вам email." + "invitationAcceptedDesc": { + "message": "Ваше приглашение успешно принято." }, "inviteInitAcceptedDesc": { "message": "Теперь вы можете получить доступ к этой организации." @@ -5704,7 +5752,7 @@ "message": "не будет автоматически выбираться при создании новых элементов" }, "organizationDataOwnershipWarning3": { - "message": "невозможно управлять из консоли администратора до тех пор, пока пользователь не будет удален" + "message": "невозможно управлять из консоли администратора пока пользователь не будет удален" }, "organizationDataOwnershipWarningContentTop": { "message": "При отключении этой политики коллекция по умолчанию: " @@ -5785,10 +5833,10 @@ "message": "В соответствии с корпоративной политикой вам запрещено сохранять элементы в личном хранилище. Измените владельца на организацию и выберите из доступных Коллекций." }, "desktopAutotypePolicy": { - "message": "Desktop Autotype Default Setting" + "message": "Настройки по умолчанию для автозаполнения на компьютере" }, "desktopAutotypePolicyDesc": { - "message": "Turn Desktop Autotype ON by default for members. Members can turn Autotype off manually in the Desktop client.", + "message": "Включите автозаполнение на компьютере по умолчанию для участников. Участники могут отключить автозаполнение вручную в клиентском приложении для компьютера.", "description": "This policy will enable Desktop Autotype by default for members on Unlock." }, "disableSend": { @@ -5955,7 +6003,7 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Learn more, see how it works, **or** try it now.'" }, "developmentDevOpsAndITTeamsChooseBWSecret": { - "message": "Команды разработчиков, DevOps и ИТ-специалистов выбирают Bitwarden Secrets Manager для защищенного управления и развертывания своих инфраструктурных и машинных секретов." + "message": "Команды разработчиков, DevOps и ИТ-специалистов выбирают Bitwarden Secrets Manager для защищенного управления и развертывания своих инфраструктурных и компьютерных секретов." }, "centralizeSecretsManagement": { "message": "Централизуйте управление секретами." @@ -5979,7 +6027,7 @@ "message": "Укрепление безопасности бизнеса." }, "strengthenBusinessSecurityDescription": { - "message": "Обеспечение жесткого контроля над доступом машин и людей к секретам с помощью интеграции SSO, журналов событий и ротации доступа." + "message": "Обеспечение жесткого контроля над доступом компьютеров и людей к секретам с помощью интеграции SSO, журналов событий и ротации доступа." }, "tryItNow": { "message": "Попробуйте сейчас" @@ -6006,7 +6054,7 @@ "message": "Запрос доступа к Менеджеру секретов отправлен администраторам по email." }, "requestAccessSMDefaultEmailContent": { - "message": "Привет,\n\nЯ запрашиваю подписку на Менеджер секретов Bitwarden для нашей команды. Ваша поддержка будет иметь большое значение!\n\nМенеджер секретов Bitwarden — это комплексное решение для управления секретами с шифрованием, предназначенное для защищенного хранения, совместного использования и развертывания машинных учетных данных, таких как ключи API, пароли баз данных и сертификаты аутентификации.\n\nМенеджер секретов поможет нам:\n\n- улучшить защищенность\n- отимизировать операции\n- предотвратить дорогостоящие утечки секретной информации\n\nЧтобы запросить бесплатную пробную версию для нашей команды, обратитесь в Bitwarden.\n\nСпасибо за помощь!" + "message": "Привет,\n\nЯ запрашиваю подписку на Менеджер секретов Bitwarden для нашей команды. Ваша поддержка будет иметь большое значение!\n\nМенеджер секретов Bitwarden — это комплексное решение для управления секретами с шифрованием, предназначенное для защищенного хранения, совместного использования и развертывания аккакунтов компьютеров таких как ключи API, пароли баз данных и сертификаты аутентификации.\n\nМенеджер секретов поможет нам:\n\n- улучшить защищенность\n- отимизировать операции\n- предотвратить дорогостоящие утечки секретной информации\n\nЧтобы запросить бесплатную пробную версию для нашей команды, обратитесь в Bitwarden.\n\nСпасибо за помощь!" }, "giveMembersAccess": { "message": "Предоставить участникам доступ:" @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Ваша организация обновила параметры расшифровки. Пожалуйста, установите мастер-пароль для доступа к вашему хранилищу." }, - "maximumVaultTimeout": { - "message": "Тайм-аут хранилища" + "sessionTimeoutPolicyTitle": { + "message": "Тайм-аут сеанса" }, - "maximumVaultTimeoutDesc": { - "message": "Установить максимальный тайм-аут хранилища для пользователей." + "sessionTimeoutPolicyDescription": { + "message": "Установите максимальный тайм-аут сеанса для всех участников, кроме владельцев." }, - "maximumVaultTimeoutLabel": { - "message": "Максимальный тайм-аут хранилища" + "maximumAllowedTimeout": { + "message": "Максимально допустимый тайм-аут" }, - "invalidMaximumVaultTimeout": { - "message": "Недопустимый максимальный тайм-аут хранилища." + "maximumAllowedTimeoutRequired": { + "message": "Требуется максимально допустимый тайм-аут." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Время указано неверно. Измените хотя бы одно значение." + }, + "sessionTimeoutAction": { + "message": "Действие при тайм-ауте сеанса" + }, + "immediately": { + "message": "Немедленно" + }, + "onSystemLock": { + "message": "Вместе с компьютером" + }, + "onAppRestart": { + "message": "При перезапуске приложения" }, "hours": { "message": "Час." @@ -6510,6 +6573,21 @@ "minutes": { "message": "Мин." }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "Этот параметр сохранит ключи шифрования ваших пользователей на их устройствах. Если вы выберете этот параметр, убедитесь, что их устройства должным образом защищены." + }, + "learnMoreAboutDeviceProtection": { + "message": "Узнайте больше о защите устройств" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"Блокировка системы\" будет применяться только к браузеру и приложению для компьютера" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "Мобильное и веб-приложение будут использовать \"при перезапуске приложения\" в качестве максимально допустимого тайм-аута, поскольку эта опция не поддерживается." + }, "vaultTimeoutPolicyInEffect": { "message": "В соответствии с политиками вашей организации максимально допустимый тайм-аут хранилища составляет $HOURS$ час. и $MINUTES$ мин.", "placeholders": { @@ -7375,7 +7453,7 @@ "message": "Неизвестный секрет. Вам может потребоваться запросить разрешение на доступ к этому секрету." }, "unknownServiceAccount": { - "message": "Unknown machine account, you may need to request permission to access this machine account." + "message": "Неизвестный аккаунт компьютера, возможно, вам потребуется запросить разрешение на доступ к этому аккаунту компьютера." }, "unknownProject": { "message": "Неизвестный проект. Вам может потребоваться запросить разрешение на доступ к этому проекту." @@ -8728,7 +8806,7 @@ } }, "accessedProjectWithIdentifier": { - "message": "Accessed a project with identifier: $PROJECT_ID$.", + "message": "Получен доступ к проекту с идентификатором: $PROJECT_ID$.", "placeholders": { "project_id": { "content": "$1", @@ -8755,7 +8833,7 @@ } }, "nameUnavailableServiceAccountDeleted": { - "message": "Deleted machine account Id: $SERVICE_ACCOUNT_ID$", + "message": "Удален аккаунт компьютера Id: $SERVICE_ACCOUNT_ID$", "placeholders": { "service_account_id": { "content": "$1", @@ -8773,7 +8851,7 @@ } }, "addedUserToServiceAccountWithId": { - "message": "Added user: $USER_ID$ to machine account with identifier: $SERVICE_ACCOUNT_ID$", + "message": "Добавлен пользователь: $USER_ID$ в аккаунт компьютера с идентификатором: $SERVICE_ACCOUNT_ID$", "placeholders": { "user_id": { "content": "$1", @@ -8786,7 +8864,7 @@ } }, "removedUserToServiceAccountWithId": { - "message": "Removed user: $USER_ID$ from machine account with identifier: $SERVICE_ACCOUNT_ID$", + "message": "Удален пользователь: $USER_ID$ из аккаунта компьютера с идентификатором: $SERVICE_ACCOUNT_ID$", "placeholders": { "user_id": { "content": "$1", @@ -8799,7 +8877,7 @@ } }, "removedGroupFromServiceAccountWithId": { - "message": "Removed group: $GROUP_ID$ from machine account with identifier: $SERVICE_ACCOUNT_ID$", + "message": "Удалена группа: $GROUP_ID$ из аккаунта компьютера с идентификатором: $SERVICE_ACCOUNT_ID$", "placeholders": { "group_id": { "content": "$1", @@ -8812,7 +8890,7 @@ } }, "serviceAccountCreatedWithId": { - "message": "Created machine account with identifier: $SERVICE_ACCOUNT_ID$", + "message": "Создан аккаунт компьютера с идентификатором: $SERVICE_ACCOUNT_ID$", "placeholders": { "service_account_id": { "content": "$1", @@ -8821,7 +8899,7 @@ } }, "addedGroupToServiceAccountId": { - "message": "Added group: $GROUP_ID$ to machine account with identifier: $SERVICE_ACCOUNT_ID$", + "message": "Добавлена группа: $GROUP_ID$ в аккаунт компьютера с идентификатором: $SERVICE_ACCOUNT_ID$", "placeholders": { "group_id": { "content": "$1", @@ -8834,7 +8912,7 @@ } }, "serviceAccountDeletedWithId": { - "message": "Deleted machine account with identifier: $SERVICE_ACCOUNT_ID$", + "message": "Удален аккаунт компьютера с идентификатором: $SERVICE_ACCOUNT_ID$", "placeholders": { "service_account_id": { "content": "$1", @@ -9679,7 +9757,7 @@ "message": "Назначить" }, "assignTasks": { - "message": "Assign tasks" + "message": "Назначить задачи" }, "assignToCollections": { "message": "Назначить коллекциям" @@ -10182,10 +10260,10 @@ "message": "URL" }, "bearerToken": { - "message": "Токен на предъявителя" + "message": "Bearer Token" }, "repositoryNameHint": { - "message": "Name of the repository to ingest into" + "message": "Название репозитория для импорта" }, "index": { "message": "Индекс" @@ -10564,7 +10642,7 @@ "message": "ID проекта" }, "projectsAccessedByMachineAccount": { - "message": "С помощью этого машинного аккаунта можно получить доступ к следующим проектам." + "message": "С помощью этого аккаунта компьютера можно получить доступ к следующим проектам." }, "config": { "message": "Конфигурация" @@ -10771,10 +10849,10 @@ "message": "Неограниченное количество пользователей" }, "UpTo50MachineAccounts": { - "message": "До 50 машинных аккаунтов" + "message": "До 50 аккаунтов компьютеров" }, "UpTo20MachineAccounts": { - "message": "До 20 машинных аккаунтов" + "message": "До 20 аккаунтов компьютеров" }, "current": { "message": "Текущий" @@ -11310,10 +11388,10 @@ "message": "Изменить пароль, подверженный риску" }, "changeAtRiskPasswordAndAddWebsite": { - "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." + "message": "Этот логин подвержен риску и у него отсутствует веб-сайт. Добавьте веб-сайт и смените пароль для большей безопасности." }, "missingWebsite": { - "message": "Missing website" + "message": "Отсутствует сайт" }, "removeUnlockWithPinPolicyTitle": { "message": "Отключить разблокировку PIN-кодом" @@ -11672,7 +11750,7 @@ "message": "Необходимые действия: В платежных реквизитах не указан идентификационный номер налогоплательщика. Если идентификационный номер налогоплательщика не указан, в ваших счетах могут быть указаны дополнительные налоги." }, "moreBreadcrumbs": { - "message": "More breadcrumbs", + "message": "Дополнительная навигация", "description": "This is used in the context of a breadcrumb navigation, indicating that there are more items in the breadcrumb trail that are not currently displayed." }, "addTaxId": { @@ -11863,52 +11941,52 @@ "message": "Families" }, "upgradeToFamilies": { - "message": "Upgrade to Families" + "message": "Обновить до Families" }, "upgradeToPremium": { - "message": "Upgrade to Premium" + "message": "Обновить до Премиум" }, "familiesUpdated": { - "message": "You've upgraded to Families!" + "message": "Вы обновились до Families!" }, "taxCalculationError": { - "message": "There was an error calculating tax for your location. Please try again." + "message": "Произошла ошибка при расчете налога для вашего местоположения. Повторите попытку." }, "individualUpgradeWelcomeMessage": { - "message": "Welcome to Bitwarden" + "message": "Добро пожаловать в Bitwarden" }, "individualUpgradeDescriptionMessage": { - "message": "Unlock more security features with Premium, or start sharing items with Families" + "message": "Разблокируйте дополнительные функции безопасности с Премиум или начните делиться элементами с семьей" }, "individualUpgradeTaxInformationMessage": { - "message": "Prices exclude tax and are billed annually." + "message": "Цены указаны без учета налогов и оплачиваются ежегодно." }, "organizationNameDescription": { - "message": "Your organization name will appear in invitations you send to members." + "message": "Название вашей организации будет отображаться в приглашениях, которые вы отправляете участникам." }, "continueWithoutUpgrading": { - "message": "Continue without upgrading" + "message": "Продолжить без обновления" }, "upgradeYourPlan": { - "message": "Upgrade your plan" + "message": "Изменить план" }, "upgradeNow": { - "message": "Upgrade now" + "message": "Изменить сейчас" }, "formWillCreateNewFamiliesOrgMessage": { - "message": "Completing this form will create a new Families organization. You can upgrade your Free organization from the Admin Console." + "message": "Заполнение этой формы приведет к созданию новой организации Families. Вы можете обновить свою бесплатную организацию в консоли администратора." }, "upgradeErrorMessage": { - "message": "We encountered an error while processing your upgrade. Please try again." + "message": "При обработке вашего изменения произошла ошибка. Повторите попытку." }, "bitwardenFreeplanMessage": { - "message": "You have the Bitwarden Free plan" + "message": "У вас есть план Bitwarden Free" }, "upgradeCompleteSecurity": { - "message": "Upgrade for complete security" + "message": "Перейти для полной защищенности" }, "viewbusinessplans": { - "message": "View business plans" + "message": "Посмотреть бизнес-планы" }, "updateEncryptionSettings": { "message": "Обновить настройки шифрования" @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id обеспечивает более надежную защиту от современных атак. Лучше всего подходит для опытных пользователей с мощными устройствами." + }, + "zipPostalCodeLabel": { + "message": "Почтовый индекс" + }, + "cardNumberLabel": { + "message": "Номер карты" + }, + "startFreeFamiliesTrial": { + "message": "Начать пробную версию Families" } } diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index 1a6dff74e32..3941850c2f2 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index 1a5e3b3c020..5cfc3500eb8 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Neboli nájdené žiadne aplikácie v $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Keď používatelia uložia prihlasovacie údaje, tu sa zobrazia aplikácie a ohrozené heslá. Označte kritické aplikácie a upozornite používateľov, aby si aktualizovali heslá." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Neoznačili ste žiadne aplikácie ako kritické" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Funkcia označovania za kritické bude implementovaná v budúcej aktualizácii" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Zrušiť označenie za kritické" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Použiť jednotné prihlásenie" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Vitajte späť" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Pozvánka prijatá" }, - "inviteAcceptedDesc": { - "message": "Prístup k organizácii získate keď administrátor potvrdí vaše členstvo. Keď sa tak stane, pošleme vám email." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -5714,63 +5762,63 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Learn more about the credential lifecycle.'" }, "availableNow": { - "message": "Available now" + "message": "Dostupné teraz" }, "autoConfirm": { - "message": "Automatic confirmation of new users" + "message": "Automatické potvrdzovanie nových členov" }, "autoConfirmDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked.", + "message": "Noví používatelia pozvaní do organizácie budú automaticky potvrdení ak je zariadene správcu odomknuté.", "description": "This is the description of the policy as it appears in the 'Policies' page" }, "howToTurnOnAutoConfirm": { - "message": "How to turn on automatic user confirmation" + "message": "Ako zapnúť automatické potvrdzovanie používateľov" }, "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "message": "Otvoriť rozšírenie Bitwarden." }, "autoConfirmStep2a": { - "message": "Select", + "message": "Vybrať", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmStep2b": { - "message": " Turn on.", + "message": " Zapnúť.", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmExtensionOpened": { - "message": "Successfully opened the Bitwarden browser extension. You can now activate the automatic user confirmation setting." + "message": "Rozšírenie Bitwarden pre prehliadače úspešne otvorene. Teraz môžete v nastaveniach zapnúť automatické potvrdzovanie používateľov." }, "autoConfirmPolicyEditDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked. Before turning on this policy, please review and agree to the following: ", + "message": "Noví používatelia pozvaní do organizácie budú automaticky potvrdení ak je zariadene správcu odomknuté. Pred zapnutím tohto pravidla skontrolujte a odsúhlaste následujúce: ", "description": "This is the description of the policy as it appears inside the policy edit dialog" }, "autoConfirmAcceptSecurityRiskTitle": { - "message": "Potential security risk. " + "message": "Potencionálne bezpečnostné riziko. " }, "autoConfirmAcceptSecurityRiskDescription": { - "message": "Automatic user confirmation could pose a security risk to your organization’s data." + "message": "Automatické potvrdzovanie používateľov môže predstavovať bezpečnostné ohrozenie dát vašej organizácie." }, "autoConfirmAcceptSecurityRiskLearnMore": { - "message": "Learn about the risks", + "message": "Dozvedieť sa viac o rizikách", "description": "The is the link copy for the first check box option in the edit policy dialog" }, "autoConfirmSingleOrgRequired": { - "message": "Single organization policy required. " + "message": "Vyžaduje sa pravidlo jednej organizácie. " }, "autoConfirmSingleOrgRequiredDescription": { - "message": "Anyone part of more than one organization will have their access revoked until they leave the other organizations." + "message": "Ktokoľvek je členom viacerých organizácií bude mat znemožnený prístup dokiaľ neopustí ostatné organizácie." }, "autoConfirmSingleOrgExemption": { - "message": "Single organization policy will extend to all roles. " + "message": "Pravidlo jednej organizácie sa rozšíri na všetky roly. " }, "autoConfirmNoEmergencyAccess": { - "message": "No emergency access. " + "message": "Žiadny núdzový prístup. " }, "autoConfirmNoEmergencyAccessDescription": { - "message": "Emergency Access will be removed." + "message": "Núdzový prístup bude odstránený." }, "autoConfirmCheckBoxLabel": { - "message": "I accept these risks and policy updates" + "message": "Akceptujem tieto riziká a aktualizácie pravidiel" }, "personalOwnership": { "message": "Zakázať osobný trezor" @@ -5932,7 +5980,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendAccessTaglineLearnMore": { - "message": "Viac informácií o", + "message": "Zistite viac o", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read '**Learn more about** Bitwarden Send or sign up to try it today.'" }, "sendVaultCardProductDesc": { @@ -6032,11 +6080,11 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Learn more about Bitwarden Send **or** sign up to try it today.'" }, "sendAccessTaglineSignUp": { - "message": "zaregistrujte", + "message": "sa zaregistrujte a", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Learn more about Bitwarden Send or **sign up** to try it today.'" }, "sendAccessTaglineTryToday": { - "message": "skúste to ešte dnes.", + "message": "vyskúšajte ho ešte dnes.", "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Learn more about Bitwarden Send or sign up to **try it today.**'" }, "sendAccessCreatorIdentifier": { @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Vaša organizácia aktualizovala možnosti dešifrovania. Na prístup k trezoru nastavte hlavné heslo." }, - "maximumVaultTimeout": { - "message": "Časový limit pre trezor" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Nastaviť maximálny časový limit trezora pre všetkých používateľov." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximálny časový limit pre trezor" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Neplatný maximálny časový limit pre trezor." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hodiny" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minúty" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Zásady vašej organizácie ovplyvňujú časový limit trezoru. Maximálny povolený časový limit trezoru je $HOURS$ h a $MINUTES$ m", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index 3b29b6eba50..785a43a769c 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Časovna omejite trezorja" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index f11b0954525..5385ec2ea19 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Nije pronađena nijedna aplikacija u $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Dok korisnici čuvaju prijave, aplikacije se pojavljuju ovde, prikazujući sve rizične lozinke. Označite kritične aplikacije i obavestite korisnike da ažuriraju lozinke." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/sr_CY/messages.json b/apps/web/src/locales/sr_CY/messages.json index fe028e4b06b..e05675cd49b 100644 --- a/apps/web/src/locales/sr_CY/messages.json +++ b/apps/web/src/locales/sr_CY/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Није пронађена ниједна апликација у $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Док корисници чувају пријаве, апликације се појављују овде, приказујући све ризичне лозинке. Означите критичне апликације и обавестите кориснике да ажурирају лозинке." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Нисте означили ниједну апликацију као критичну" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Означи као критичну функционалност ће бити имплементирана у будућем ажурирању" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Уклони као критично" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Употребити једнократну пријаву" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Добродошли назад" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Позив прихваћен" }, - "inviteAcceptedDesc": { - "message": "Овој организацији можете приступити када администратор потврди ваше чланство. Послаћемо вам имејл када се то догоди." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "Сада можете приступити овој организацији." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Ваша организација је ажурирала опције дешифровања. Поставите главну лозинку за приступ вашем сефу." }, - "maximumVaultTimeout": { - "message": "Тајмаут сефа" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Конфигуришите максимално време сефа за све кориснике." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Максимални тајмаут сефа" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Погрешан максимални тајмаут сефа." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Сати/а" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Минути/а" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Полиса ваше организације утиче на време истека сефа. Максимално дозвољено време истека је $HOURS$ сат(и) и $MINUTES$ minut(а)", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index c9a6131ae10..d3861b133b6 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Inga applikationer hittades i $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "När användare sparar inloggningar visas applikationer här och visar eventuella riskfyllda lösenord. Markera kritiska appar och meddela användarna att de ska uppdatera lösenorden." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Du har inte markerat några applikationer som kritiska" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Markera som kritisk funktionalitet kommer att implementeras i en framtida uppdatering" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Avmarkera som kritisk" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Använd Single Sign-On" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Välkommen tillbaka" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Inbjudan accepterades" }, - "inviteAcceptedDesc": { - "message": "Du kan komma åt denna organisation när en administratör har bekräftat ditt medlemskap. Vi skickar ett e-postmeddelande till dig när det sker." + "invitationAcceptedDesc": { + "message": "Accepterade din inbjudan." }, "inviteInitAcceptedDesc": { "message": "Du har nu tillgång till denna organisation." @@ -5720,11 +5768,11 @@ "message": "Automatisk bekräftelse av nya användare" }, "autoConfirmDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked.", + "message": "Nya användare som bjuds in till organisationen kommer automatiskt att bekräftas när en administratörs enhet är upplåst.", "description": "This is the description of the policy as it appears in the 'Policies' page" }, "howToTurnOnAutoConfirm": { - "message": "How to turn on automatic user confirmation" + "message": "Så här aktiverar du automatisk användarbekräftelse" }, "autoConfirmStep1": { "message": "Öppna ditt Bitwarden-tillägg." @@ -5734,14 +5782,14 @@ "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmStep2b": { - "message": " Turn on.", + "message": " Slå på.", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmExtensionOpened": { "message": "Successfully opened the Bitwarden browser extension. You can now activate the automatic user confirmation setting." }, "autoConfirmPolicyEditDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked. Before turning on this policy, please review and agree to the following: ", + "message": "Nya användare som bjuds in till organisationen kommer automatiskt att bekräftas när en administratörs enhet är upplåst. Innan du aktiverar denna policy, granska och godkänn följande: ", "description": "This is the description of the policy as it appears inside the policy edit dialog" }, "autoConfirmAcceptSecurityRiskTitle": { @@ -5755,7 +5803,7 @@ "description": "The is the link copy for the first check box option in the edit policy dialog" }, "autoConfirmSingleOrgRequired": { - "message": "Single organization policy required. " + "message": "En organisationspolicy krävs. " }, "autoConfirmSingleOrgRequiredDescription": { "message": "Anyone part of more than one organization will have their access revoked until they leave the other organizations." @@ -5764,10 +5812,10 @@ "message": "Single organization policy will extend to all roles. " }, "autoConfirmNoEmergencyAccess": { - "message": "No emergency access. " + "message": "Ingen nödåtkomst. " }, "autoConfirmNoEmergencyAccessDescription": { - "message": "Emergency Access will be removed." + "message": "Nödåtkomst kommer att tas bort." }, "autoConfirmCheckBoxLabel": { "message": "Jag accepterar dessa risker och policyuppdateringar" @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Din organisation har uppdaterat dina dekrypteringsalternativ. Ange ett huvudlösenord för att komma åt ditt valv." }, - "maximumVaultTimeout": { - "message": "Timeout för valv" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Ange en maximal tidsgräns för valv för medlemmar." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximal tidsgräns för valv" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Ogiltig maximal tidsgräns för valv." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Timmar" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minuter" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "I organisationens policyer har den maximalt tillåtna tidsgränsen för valvet angetts till $HOURS$ timme(n) och $MINUTES$ minut(er).", "placeholders": { @@ -8812,7 +8890,7 @@ } }, "serviceAccountCreatedWithId": { - "message": "Created machine account with identifier: $SERVICE_ACCOUNT_ID$", + "message": "Skapade maskinkonto med identifierare: $SERVICE_ACCOUNT_ID$", "placeholders": { "service_account_id": { "content": "$1", @@ -8821,7 +8899,7 @@ } }, "addedGroupToServiceAccountId": { - "message": "Added group: $GROUP_ID$ to machine account with identifier: $SERVICE_ACCOUNT_ID$", + "message": "Lade till grupp: $GROUP_ID$ till maskinkonto med identifierare: $SERVICE_ACCOUNT_ID$", "placeholders": { "group_id": { "content": "$1", @@ -8834,7 +8912,7 @@ } }, "serviceAccountDeletedWithId": { - "message": "Deleted machine account with identifier: $SERVICE_ACCOUNT_ID$", + "message": "Tog bort maskinkonto med identifierare: $SERVICE_ACCOUNT_ID$", "placeholders": { "service_account_id": { "content": "$1", @@ -10077,7 +10155,7 @@ "message": "Skicka händelsedata till din Logscale-instans" }, "datadogEventIntegrationDesc": { - "message": "Send vault event data to your Datadog instance" + "message": "Skicka data om valvhändelser till din Datadog-instans" }, "failedToSaveIntegration": { "message": "Misslyckades med att spara integration. Försök igen senare." @@ -11499,7 +11577,7 @@ "message": "Öppna Bitwarden-tillägget" }, "bitwardenExtensionInstalledOpenExtension": { - "message": "The Bitwarden extension is installed! Open the extension to log in and start autofilling." + "message": "Bitwarden-tillägget är installerat! Öppna tillägget för att logga in och starta autofyllning." }, "openExtensionToAutofill": { "message": "Öppna tillägget för att logga in och starta autofyllning." @@ -11884,7 +11962,7 @@ "message": "Priser exklusive moms och faktureras årligen." }, "organizationNameDescription": { - "message": "Your organization name will appear in invitations you send to members." + "message": "Ditt organisationsnamn visas i inbjudningar som du skickar till medlemmar." }, "continueWithoutUpgrading": { "message": "Fortsätt utan att uppgradera" @@ -11896,13 +11974,13 @@ "message": "Uppgradera nu" }, "formWillCreateNewFamiliesOrgMessage": { - "message": "Completing this form will create a new Families organization. You can upgrade your Free organization from the Admin Console." + "message": "Genom att fylla i detta formulär skapas en ny Familjer-organisation. Du kan uppgradera din kostnadsfria organisation från administratörskonsolen." }, "upgradeErrorMessage": { "message": "Vi stötte på ett fel vid bearbetningen av din uppgradering. Försök igen." }, "bitwardenFreeplanMessage": { - "message": "You have the Bitwarden Free plan" + "message": "Du har Bitwarden Free-planen" }, "upgradeCompleteSecurity": { "message": "Uppgradera för fullständig säkerhet" @@ -11911,30 +11989,39 @@ "message": "Visa prismodeller" }, "updateEncryptionSettings": { - "message": "Update encryption settings" + "message": "Uppdatera krypteringsinställningar" }, "updateYourEncryptionSettings": { - "message": "Update your encryption settings" + "message": "Uppdatera dina krypteringsinställningar" }, "updateSettings": { - "message": "Update settings" + "message": "Uppdatera inställningar" }, "algorithm": { - "message": "Algorithm" + "message": "Algoritm" }, "encryptionKeySettingsHowShouldWeEncryptYourData": { - "message": "Choose how Bitwarden should encrypt your vault data. All options are secure, but stronger methods offer better protection - especially against brute-force attacks. Bitwarden recommends the default setting for most users." + "message": "Välj hur Bitwarden ska kryptera dina valvdata. Alla alternativ är säkra, men starkare metoder ger bättre skydd - särskilt mot uttömmande attacker. Bitwarden rekommenderar standardinställningen för de flesta användare." }, "encryptionKeySettingsIncreaseImproveSecurity": { - "message": "Increasing the values above the default will improve security, but your vault may take longer to unlock as a result." + "message": "Att öka värdena ovanför standard kommer att förbättra säkerheten, men ditt valv kan ta längre tid att låsa upp som ett resultat." }, "encryptionKeySettingsAlgorithmPopoverTitle": { - "message": "About encryption algorithms" + "message": "Om krypteringsalgoritmer" }, "encryptionKeySettingsAlgorithmPopoverPBKDF2": { - "message": "PBKDF2-SHA256 is a well-tested encryption method that balances security and performance. Good for all users." + "message": "PBKDF2-SHA256 är en vältestad krypteringsmetod som balanserar säkerhet och prestanda. Bra för alla användare." }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { - "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + "message": "Argon2id erbjuder ett starkare skydd mot moderna attacker. Bäst för avancerade användare med kraftfulla enheter." + }, + "zipPostalCodeLabel": { + "message": "Postnummer" + }, + "cardNumberLabel": { + "message": "Kortnummer" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/ta/messages.json b/apps/web/src/locales/ta/messages.json index a8fe0c062b4..86d94a3c2c1 100644 --- a/apps/web/src/locales/ta/messages.json +++ b/apps/web/src/locales/ta/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "$ORG NAME$ இல் எந்தப் பயன்பாடுகளும் கண்டறியப்படவில்லை", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "பயனர்கள் உள்நுழைவுகளை சேமிக்கும்போது, ​​பயன்பாடுகள் இங்கே தோன்றி, எந்தவொரு ஆபத்தான கடவுச்சொற்களையும் காண்பிக்கும். முக்கியமான பயன்பாடுகளைக் குறியிட்டு, கடவுச்சொற்களைப் புதுப்பிக்க பயனர்களுக்குத் தெரிவிக்கவும்." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "முக்கியமானதாக இருந்து குறியீட்டை நீக்கு" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "ஒற்றை உள்நுழைவைப் பயன்படுத்து" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "மீண்டும் வருக" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "அழைப்பு ஏற்கப்பட்டது" }, - "inviteAcceptedDesc": { - "message": "ஒரு நிர்வாகி உங்கள் உறுப்பினரை உறுதிப்படுத்தியவுடன் இந்த நிறுவனத்தை நீங்கள் அணுகலாம். அது நடக்கும்போது நாங்கள் உங்களுக்கு ஒரு மின்னஞ்சல் அனுப்புவோம்." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "நீங்கள் இப்போது இந்த நிறுவனத்தை அணுகலாம்." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "உங்கள் நிறுவனம் உங்கள் குறியாக்க நீக்குதல் விருப்பங்களை புதுப்பித்துள்ளது. உங்கள் பெட்டகத்தை அணுக ஒரு மாஸ்டர் கடவுச்சொல்லை அமைக்கவும்." }, - "maximumVaultTimeout": { - "message": "பெட்டக காலக்கெடு" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "உறுப்பினர்களுக்கான அதிகபட்ச பெட்டக காலக்கெடுவை அமைக்கவும்." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "அதிகபட்ச பெட்டக காலக்கெடு" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "செல்லாத அதிகபட்ச பெட்டக காலக்கெடு." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "மணிநேரம்" @@ -6510,6 +6573,21 @@ "minutes": { "message": "நிமிடங்கள்" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "உங்கள் நிறுவன கொள்கைகள் உங்கள் அதிகபட்ச அனுமதிக்கப்பட்ட பெட்டக காலக்கெடுவை $HOURS$ மணிநேரங்கள் மற்றும் $MINUTES$ நிமிடங்களுக்கு அமைத்துள்ளன.", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index fc5efa82d50..338f5c10d8a 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index 2b61c420c78..ddf7d93fa23 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "No applications found in $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "As users save logins, applications appear here, showing any at-risk passwords. Mark critical apps and notify users to update passwords." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Invitation accepted" }, - "inviteAcceptedDesc": { - "message": "You can access this organization once an administrator confirms your membership. We'll send you an email when that happens." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "You can now access this organization." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Your organization has updated your decryption options. Please set a master password to access your vault." }, - "maximumVaultTimeout": { - "message": "Vault timeout" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Set a maximum vault timeout for members." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maximum vault timeout" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Invalid maximum vault timeout." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Hours" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Minutes" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Your organization policies have set your maximum allowed vault timeout to $HOURS$ hour(s) and $MINUTES$ minute(s).", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index b9f55c329f9..e019e106dc2 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "$ORG NAME$ kuruluşunda hiçbir uygulama bulunmadı", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Kullanıcılar hesaplarını kaydettikçe uygulamalar burada görünür ve risk altındaki parolaları gösterir. Kritik uygulamaları işaretleyerek kullanıcıları parolalarını güncellemeleri için bilgilendirebilirsiniz." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Hiçbir uygulamayı kritik olarak işaretlemediniz" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Kritik olarak işaretlemeyi kaldır" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Çoklu oturum açma kullan" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Tekrar hoş geldiniz" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Davet kabul edildi" }, - "inviteAcceptedDesc": { - "message": "Yöneticiler üyeliğinizi onayladıktan sonra kuruluşa erişebilirsiniz. Üyeliğiniz onaylandığında size e-posta göndereceğiz." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "Artık bu kuruluşa erişebilirsiniz." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Kuruluşunuz deşifre seçeneklerinizi güncelledi. Kasanıza erişmek için lütfen bir ana şifre belirleyin." }, - "maximumVaultTimeout": { - "message": "Kasa zaman aşımı" + "sessionTimeoutPolicyTitle": { + "message": "Oturum zaman aşımı" }, - "maximumVaultTimeoutDesc": { - "message": "Üyeler için maksimum kasa zaman aşımı ayarlayın." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Maksimum kasa zaman aşımı" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Geçersiz maksimum kasa zaman aşımı." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Oturum zaman aşımı eylemi" + }, + "immediately": { + "message": "Hemen" + }, + "onSystemLock": { + "message": "Sistem kilitlenince" + }, + "onAppRestart": { + "message": "Uygulama yeniden başlatılınca" }, "hours": { "message": "Saat" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Dakika" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Kuruluş ilkeleriniz izin verilen maksimum kasa zaman aşımını $HOURS$ saat $MINUTES$ dakika olarak belirlemiş.", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index f87be8e87da..d763cfb3203 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Не знайдено програм в $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Коли користувачі зберігатимуть записи, тут з'являтимуться програми з ризикованими паролями. Позначте критичні програми та сповіщайте користувачів про необхідність зміни паролів." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Ви не відмітили жодного додатку в якості критичного" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Mark as critical functionality will be implemented in a future update" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Unmark as critical" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Використати єдиний вхід" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "З поверненням" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Запрошення прийнято" }, - "inviteAcceptedDesc": { - "message": "Ви можете отримати доступ до цієї організації одразу після підтвердження адміністратором. Ми надішлемо вам електронне повідомлення, коли це станеться." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "Тепер ви маєте доступ до цієї організації." @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Ваша організація оновила параметри дешифрування. Встановіть головний пароль для доступу до сховища." }, - "maximumVaultTimeout": { - "message": "Час очікування сховища" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Встановити максимальний час очікування сховища для учасників." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Максимальний час очікування сховища" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Недійсний максимальний час очікування сховища." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Годин" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Хвилин" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Політикою вашої організації встановлено максимальний дозволений час очікування сховища $HOURS$ годин, $MINUTES$ хвилин.", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index c6ce44709ea..4715652dbf6 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -155,7 +155,7 @@ } }, "newPasswordsAtRisk": { - "message": "$COUNT$ new passwords at-risk", + "message": "$COUNT$ mật khẩu mới có rủi ro", "placeholders": { "count": { "content": "$1", @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "Không tìm thấy ứng dụng nào trong $ORG NAME$", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "Khi người dùng lưu thông tin đăng nhập, các ứng dụng sẽ hiển thị tại đây, bao gồm cả các mật khẩu có nguy cơ bị lộ. Đánh dấu các ứng dụng quan trọng và thông báo cho người dùng cập nhật mật khẩu." + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "Bạn chưa đánh dấu ứng dụng nào là quan trọng" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "Chức năng đánh dấu là quan trọng sẽ được triển khai trong bản cập nhật sau" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "Bỏ đánh dấu là nghiêm trọng" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "Dùng đăng nhập một lần" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Chào mừng bạn trở lại" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "Đã chấp nhận lời mời" }, - "inviteAcceptedDesc": { - "message": "Bạn có thể truy cập tổ chức này sau khi quản trị viên xác nhận tư cách thành viên của bạn. Chúng tôi sẽ gửi email cho bạn khi được xác nhận." + "invitationAcceptedDesc": { + "message": "Successfully accepted your invitation." }, "inviteInitAcceptedDesc": { "message": "Bạn hiện có thể truy cập tổ chức này." @@ -5714,63 +5762,63 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Learn more about the credential lifecycle.'" }, "availableNow": { - "message": "Available now" + "message": "Khả dụng bây giờ" }, "autoConfirm": { - "message": "Automatic confirmation of new users" + "message": "Tự động xác nhận người dùng mới" }, "autoConfirmDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked.", + "message": "Người dùng mới được mời vào tổ chức sẽ tự động được xác nhận khi thiết bị của quản trị viên đang mở khóa.", "description": "This is the description of the policy as it appears in the 'Policies' page" }, "howToTurnOnAutoConfirm": { - "message": "How to turn on automatic user confirmation" + "message": "Cách bật xác nhận người dùng tự động" }, "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "message": "Mở tiện ích mở rộng Bitwarden của bạn." }, "autoConfirmStep2a": { - "message": "Select", + "message": "Chọn", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmStep2b": { - "message": " Turn on.", + "message": " Bật.", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmExtensionOpened": { - "message": "Successfully opened the Bitwarden browser extension. You can now activate the automatic user confirmation setting." + "message": "Đã mở tiện ích mở rộng Bitwarden trong trình duyệt. Giờ bạn có thể bật tùy chọn xác nhận người dùng tự động." }, "autoConfirmPolicyEditDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked. Before turning on this policy, please review and agree to the following: ", + "message": "Người dùng mới được mời vào tổ chức sẽ tự động được xác nhận khi thiết bị của quản trị viên đang mở khóa. Trước khi bật chính sách này, hãy đọc kỹ và đồng ý với các điều sau: ", "description": "This is the description of the policy as it appears inside the policy edit dialog" }, "autoConfirmAcceptSecurityRiskTitle": { - "message": "Potential security risk. " + "message": "Nguy cơ bảo mật tiềm ẩn. " }, "autoConfirmAcceptSecurityRiskDescription": { - "message": "Automatic user confirmation could pose a security risk to your organization’s data." + "message": "Tính năng xác nhận người dùng tự động có thể làm tăng rủi ro bảo mật cho dữ liệu của tổ chức bạn." }, "autoConfirmAcceptSecurityRiskLearnMore": { - "message": "Learn about the risks", + "message": "Tìm hiểu về các rủi ro", "description": "The is the link copy for the first check box option in the edit policy dialog" }, "autoConfirmSingleOrgRequired": { - "message": "Single organization policy required. " + "message": "Chính sách một tổ chức là bắt buộc. " }, "autoConfirmSingleOrgRequiredDescription": { - "message": "Anyone part of more than one organization will have their access revoked until they leave the other organizations." + "message": "Bất kỳ ai thuộc nhiều hơn một tổ chức sẽ bị thu hồi quyền truy cập cho đến khi họ rời khỏi các tổ chức khác." }, "autoConfirmSingleOrgExemption": { - "message": "Single organization policy will extend to all roles. " + "message": "Chính sách một tổ chức sẽ áp dụng cho tất cả các vai trò. " }, "autoConfirmNoEmergencyAccess": { - "message": "No emergency access. " + "message": "Không có quyền truy cập khẩn cấp. " }, "autoConfirmNoEmergencyAccessDescription": { - "message": "Emergency Access will be removed." + "message": "Quyền truy cập khẩn cấp sẽ bị xóa." }, "autoConfirmCheckBoxLabel": { - "message": "I accept these risks and policy updates" + "message": "Tôi chấp nhận những rủi ro và cập nhật chính sách này" }, "personalOwnership": { "message": "Xóa kho lưu trữ riêng lẻ" @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "Tổ chức của bạn đã cập nhật tùy chọn giải mã. Vui lòng đặt mật khẩu chính để truy cập vào kho lưu trữ của bạn." }, - "maximumVaultTimeout": { - "message": "Thời gian chờ của kho" + "sessionTimeoutPolicyTitle": { + "message": "Session timeout" }, - "maximumVaultTimeoutDesc": { - "message": "Đặt thời gian chờ tối đa cho kho dành cho các thành viên." + "sessionTimeoutPolicyDescription": { + "message": "Set a maximum session timeout for all members except owners." }, - "maximumVaultTimeoutLabel": { - "message": "Thời gian chờ tối đa của kho" + "maximumAllowedTimeout": { + "message": "Maximum allowed timeout" }, - "invalidMaximumVaultTimeout": { - "message": "Thời gian chờ tối đa của kho không hợp lệ." + "maximumAllowedTimeoutRequired": { + "message": "Maximum allowed timeout is required." + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "Time is invalid. Change at least one value." + }, + "sessionTimeoutAction": { + "message": "Session timeout action" + }, + "immediately": { + "message": "Immediately" + }, + "onSystemLock": { + "message": "On system lock" + }, + "onAppRestart": { + "message": "On app restart" }, "hours": { "message": "Giờ" @@ -6510,6 +6573,21 @@ "minutes": { "message": "Phút" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "This option will save your members' encryption keys on their devices. If you choose this option, ensure that their devices are adequately protected." + }, + "learnMoreAboutDeviceProtection": { + "message": "Learn more about device protection" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "\"System lock\" will only apply to the browser and desktop app" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + }, "vaultTimeoutPolicyInEffect": { "message": "Chính sách của tổ chức bạn đã đặt thời gian chờ tối đa của kho là $HOURS$ giờ và $MINUTES$ phút.", "placeholders": { @@ -11069,13 +11147,13 @@ "message": "Đã xác nhận tên miền" }, "itemAddedToFavorites": { - "message": "Item added to favorites" + "message": "Đã thêm mục vào yêu thích" }, "itemRemovedFromFavorites": { - "message": "Item removed from favorites" + "message": "Đã xóa mục khỏi yêu thích" }, "copyNote": { - "message": "Copy note" + "message": "Sao chép ghi chú" }, "organizationNameMaxLength": { "message": "Tên tổ chức không được vượt quá 50 ký tự." @@ -11902,39 +11980,48 @@ "message": "Chúng tôi gặp lỗi khi xử lý nâng cấp của bạn. Vui lòng thử lại." }, "bitwardenFreeplanMessage": { - "message": "You have the Bitwarden Free plan" + "message": "Bạn đang sử dụng gói Bitwarden Miễn phí" }, "upgradeCompleteSecurity": { - "message": "Upgrade for complete security" + "message": "Nâng cấp để tăng cường bảo mật toàn diện" }, "viewbusinessplans": { - "message": "View business plans" + "message": "Xem các gói doanh nghiệp" }, "updateEncryptionSettings": { - "message": "Update encryption settings" + "message": "Cập nhật cài đặt mã hóa" }, "updateYourEncryptionSettings": { - "message": "Update your encryption settings" + "message": "Cập nhật cài đặt mã hóa của bạn" }, "updateSettings": { - "message": "Update settings" + "message": "Cập nhật cài đặt" }, "algorithm": { - "message": "Algorithm" + "message": "Thuật toán" }, "encryptionKeySettingsHowShouldWeEncryptYourData": { - "message": "Choose how Bitwarden should encrypt your vault data. All options are secure, but stronger methods offer better protection - especially against brute-force attacks. Bitwarden recommends the default setting for most users." + "message": "Chọn cách Bitwarden mã hóa dữ liệu trong kho của bạn. Mọi tùy chọn đều an toàn, nhưng những phương pháp mạnh hơn sẽ giúp bảo vệ tốt hơn - đặc biệt là trước các cuộc tấn công brute-force. Bitwarden khuyến nghị giữ cài đặt mặc định cho hầu hết người dùng." }, "encryptionKeySettingsIncreaseImproveSecurity": { - "message": "Increasing the values above the default will improve security, but your vault may take longer to unlock as a result." + "message": "Tăng các giá trị cao hơn mức mặc định sẽ giúp tăng cường bảo mật, nhưng đồng thời kho của bạn có thể mất nhiều thời gian hơn để mở khóa." }, "encryptionKeySettingsAlgorithmPopoverTitle": { - "message": "About encryption algorithms" + "message": "Về các thuật toán mã hóa" }, "encryptionKeySettingsAlgorithmPopoverPBKDF2": { - "message": "PBKDF2-SHA256 is a well-tested encryption method that balances security and performance. Good for all users." + "message": "PBKDF2-SHA256 là phương pháp mã hóa đã được kiểm chứng, cân bằng tốt giữa tính bảo mật và hiệu suất. Phù hợp cho mọi người dùng." }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { - "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + "message": "Argon2id mang đến khả năng bảo vệ mạnh hơn trước các hình thức tấn công hiện đại, phù hợp nhất cho người dùng nâng cao với thiết bị mạnh mẽ." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Số thẻ" + }, + "startFreeFamiliesTrial": { + "message": "Start free Families trial" } } diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index aec6d62d9ba..80833c17225 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "在 $ORG NAME$ 中未发现任何应用程序", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "当用户保存登录信息时,应用程序会出现在这里,显示所有存在风险的密码。标记关键应用程序并通知用户更新密码。" + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "您还没有将任何应用程序标记为关键" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "标记为关键功能将在未来更新中实现" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "取消标记为关键" }, @@ -657,7 +702,7 @@ "message": "通过在父文件夹名后面添加「/」来嵌套文件夹。示例:Social/Forums" }, "deleteFolderPermanently": { - "message": "确定要永久删除这个文件夹吗?" + "message": "确定要永久删除此文件夹吗?" }, "baseDomain": { "message": "基础域名", @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "使用单点登录" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "欢迎回来" }, @@ -2054,7 +2102,7 @@ "message": "继续操作将使您退出当前会话,并要求您重新登录。其他设备上的活动会话可能会继续保持活动状态长达一小时。" }, "changePasswordWarning": { - "message": "更改密码后,您需要使用新密码登录。其他设备上的活动会话将在一小时内注销。" + "message": "更改密码后,您需要使用新密码登录。在其他设备上的活动会话将在一小时内注销。" }, "emailChanged": { "message": "电子邮箱已保存" @@ -3508,7 +3556,7 @@ } }, "trialConfirmationEmail": { - "message": "我们已经发送了一封确认邮件到您团队的计费电子邮箱 " + "message": "我们已经发送了一封确认电子邮件到您团队的计费电子邮箱 " }, "monthly": { "message": "每月" @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "邀请已接受" }, - "inviteAcceptedDesc": { - "message": "管理员确认您的成员资格后,您就可以访问此组织了。届时我们会向您发送一封电子邮件。" + "invitationAcceptedDesc": { + "message": "成功接受了您的邀请。" }, "inviteInitAcceptedDesc": { "message": "您现在可以访问这个组织了。" @@ -4886,10 +4934,10 @@ "message": "筛选" }, "vaultTimeout": { - "message": "密码库超时时间" + "message": "密码库超时" }, "vaultTimeout1": { - "message": "超时" + "message": "超时时间" }, "vaultTimeoutDesc": { "message": "选择您的密码库何时执行密码库超时动作。" @@ -5433,7 +5481,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendPermanentConfirmation": { - "message": "您确定要永久删除这个 Send 吗?", + "message": "确定要永久删除此 Send 吗?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deletionDate": { @@ -5695,7 +5743,7 @@ "description": "This will be used as a hyperlink" }, "organizationDataOwnershipWarningTitle": { - "message": "您确定要继续吗?" + "message": "确定要继续吗?" }, "organizationDataOwnershipWarning1": { "message": "成员仍可以访问" @@ -5714,63 +5762,63 @@ "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Learn more about the credential lifecycle.'" }, "availableNow": { - "message": "Available now" + "message": "目前可用" }, "autoConfirm": { - "message": "Automatic confirmation of new users" + "message": "自动确认新用户" }, "autoConfirmDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked.", + "message": "当管理员的设备解锁时,邀请加入组织的新用户将被自动确认。", "description": "This is the description of the policy as it appears in the 'Policies' page" }, "howToTurnOnAutoConfirm": { - "message": "How to turn on automatic user confirmation" + "message": "如何启用自动用户确认" }, "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "message": "打开您的 Bitwarden 扩展。" }, "autoConfirmStep2a": { - "message": "Select", + "message": "选择", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmStep2b": { - "message": " Turn on.", + "message": "启用。", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" }, "autoConfirmExtensionOpened": { - "message": "Successfully opened the Bitwarden browser extension. You can now activate the automatic user confirmation setting." + "message": "成功打开 Bitwarden 浏览器扩展。您现在可以激活自动用户确认设置。" }, "autoConfirmPolicyEditDescription": { - "message": "New users invited to the organization will be automatically confirmed when an admin’s device is unlocked. Before turning on this policy, please review and agree to the following: ", + "message": "当管理员的设备解锁时,邀请加入组织的新用户将被自动确认。启用此策略前,请审查并同意:", "description": "This is the description of the policy as it appears inside the policy edit dialog" }, "autoConfirmAcceptSecurityRiskTitle": { - "message": "Potential security risk. " + "message": "潜在的安全风险。" }, "autoConfirmAcceptSecurityRiskDescription": { - "message": "Automatic user confirmation could pose a security risk to your organization’s data." + "message": "自动用户确认可能对您的组织数据带来安全风险。" }, "autoConfirmAcceptSecurityRiskLearnMore": { - "message": "Learn about the risks", + "message": "进一步了解此风险", "description": "The is the link copy for the first check box option in the edit policy dialog" }, "autoConfirmSingleOrgRequired": { - "message": "Single organization policy required. " + "message": "要求单一组织策略。" }, "autoConfirmSingleOrgRequiredDescription": { - "message": "Anyone part of more than one organization will have their access revoked until they leave the other organizations." + "message": "属于多个组织的任何人,他们的访问权限将被撤销,直到他们离开其他组织。" }, "autoConfirmSingleOrgExemption": { - "message": "Single organization policy will extend to all roles. " + "message": "单一组织策略将扩展到所有角色。" }, "autoConfirmNoEmergencyAccess": { - "message": "No emergency access. " + "message": "没有紧急访问权限。" }, "autoConfirmNoEmergencyAccessDescription": { - "message": "Emergency Access will be removed." + "message": "紧急访问权限将被移除。" }, "autoConfirmCheckBoxLabel": { - "message": "I accept these risks and policy updates" + "message": "我接受这些风险和策略更新" }, "personalOwnership": { "message": "禁用个人密码库" @@ -5982,7 +6030,7 @@ "message": "通过 SSO 集成、事件日志和访问轮换,保持机器和人类对机密访问权限的严格控制。" }, "tryItNow": { - "message": "立即试用" + "message": "立即体验" }, "sendRequest": { "message": "发送请求" @@ -6141,7 +6189,7 @@ } }, "eventResetSsoLink": { - "message": "重置用户 $ID$ 的 SSO 链接", + "message": "重置了用户 $ID$ 的 SSO 链接", "placeholders": { "id": { "content": "$1", @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "您的组织更新了您的解密选项。要访问您的密码库,请设置一个主密码。" }, - "maximumVaultTimeout": { - "message": "密码库超时时间" + "sessionTimeoutPolicyTitle": { + "message": "会话超时" }, - "maximumVaultTimeoutDesc": { - "message": "为成员配置最大密码库超时时间。" + "sessionTimeoutPolicyDescription": { + "message": "为除所有者以外的所有成员设置最大会话超时。" }, - "maximumVaultTimeoutLabel": { - "message": "最大密码库超时时间" + "maximumAllowedTimeout": { + "message": "最大允许的超时" }, - "invalidMaximumVaultTimeout": { - "message": "无效的最大密码库超时时间。" + "maximumAllowedTimeoutRequired": { + "message": "必须填写最大允许的超时。" + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "时间无效。请更改至少一个值。" + }, + "sessionTimeoutAction": { + "message": "会话超时动作" + }, + "immediately": { + "message": "立即" + }, + "onSystemLock": { + "message": "系统锁定时" + }, + "onAppRestart": { + "message": "App 重启时" }, "hours": { "message": "小时" @@ -6510,8 +6573,23 @@ "minutes": { "message": "分钟" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "确定要将所有成员的最大允许的超时设置为「从不」吗?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "此选项将把成员的加密密钥保存在他们的设备上。若选择此选项,请确保他们的设备已受到充分保护。" + }, + "learnMoreAboutDeviceProtection": { + "message": "进一步了解设备保护" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "「系统锁定时」只适用于浏览器和桌面 App" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "由于不支持该选项,移动端和网页 App 将使用「App 重启时」作为其最大允许的超时。" + }, "vaultTimeoutPolicyInEffect": { - "message": "您的组织策略已将您最大允许的密码库超时时间设置为 $HOURS$ 小时 $MINUTES$ 分钟。", + "message": "您的组织策略已将您最大允许的密码库超时设置为 $HOURS$ 小时 $MINUTES$ 分钟。", "placeholders": { "hours": { "content": "$1", @@ -6537,7 +6615,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "您的组织策略正在影响您的密码库超时时间。最大允许的密码库超时时间是 $HOURS$ 小时 $MINUTES$ 分钟。您的密码库超时动作被设置为 $ACTION$。", + "message": "您的组织策略正在影响您的密码库超时。最大允许的密码库超时为 $HOURS$ 小时 $MINUTES$ 分钟。您的密码库超时动作被设置为 $ACTION$。", "placeholders": { "hours": { "content": "$1", @@ -6554,7 +6632,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "您的组织策略已将您的密码库超时动作设置为 $ACTION$。", + "message": "您的组织策略已将您的密码库超时动作设置为「$ACTION$」。", "placeholders": { "action": { "content": "$1", @@ -6563,10 +6641,10 @@ } }, "vaultTimeoutToLarge": { - "message": "您的密码库超时时间超出了您的组织设置的限制。" + "message": "您的密码库超时超出了您的组织设置的限制。" }, "vaultCustomTimeoutMinimum": { - "message": "自定义超时时间最小为 1 分钟。" + "message": "自定义超时最少为 1 分钟。" }, "vaultTimeoutRangeError": { "message": "密码库超时不在允许的范围内。" @@ -7246,7 +7324,7 @@ "description": "This is used by screen readers to indicate the organization that is currently being shown to the user." }, "accountLoggedInAsName": { - "message": "账户:已登录为 $NAME$", + "message": "账户:已以 $NAME$ 身份登录", "placeholders": { "name": { "content": "$1", @@ -11487,7 +11565,7 @@ "message": "您无法在没有浏览器扩展的情况下自动填充密码" }, "cannotAutofillPasswordsWithoutExtensionDesc": { - "message": "您确定现在不添加该扩展吗?" + "message": "确定现在不添加此扩展吗?" }, "skipToWebApp": { "message": "跳转到网页 App" @@ -11911,30 +11989,39 @@ "message": "查看商业方案" }, "updateEncryptionSettings": { - "message": "Update encryption settings" + "message": "更新加密设置" }, "updateYourEncryptionSettings": { - "message": "Update your encryption settings" + "message": "更新您的加密设置" }, "updateSettings": { - "message": "Update settings" + "message": "更新设置" }, "algorithm": { - "message": "Algorithm" + "message": "算法" }, "encryptionKeySettingsHowShouldWeEncryptYourData": { - "message": "Choose how Bitwarden should encrypt your vault data. All options are secure, but stronger methods offer better protection - especially against brute-force attacks. Bitwarden recommends the default setting for most users." + "message": "选择 Bitwarden 加密您的密码库数据的方式。所有选项都是安全的,但更强大的方式提供了更好的保护 - 特别是针对暴力攻击。Bitwarden 建议大多数用户使用默认设置。" }, "encryptionKeySettingsIncreaseImproveSecurity": { - "message": "Increasing the values above the default will improve security, but your vault may take longer to unlock as a result." + "message": "将值增加为高于默认值将提高安全性,但您的密码库相应需要更长时间才能解锁。" }, "encryptionKeySettingsAlgorithmPopoverTitle": { - "message": "About encryption algorithms" + "message": "关于加密算法" }, "encryptionKeySettingsAlgorithmPopoverPBKDF2": { - "message": "PBKDF2-SHA256 is a well-tested encryption method that balances security and performance. Good for all users." + "message": "PBKDF2-SHA256 是一种经过充分测试的加密方式,可以平衡安全性和性能。适合于所有用户。" }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { - "message": "Argon2id offers stronger protection against modern attacks. Best for advanced users with powerful devices." + "message": "Argon2id 为现代攻击提供了更强大的保护。最适合拥有强大设备的高级用户。" + }, + "zipPostalCodeLabel": { + "message": "ZIP / 邮政编码" + }, + "cardNumberLabel": { + "message": "卡号" + }, + "startFreeFamiliesTrial": { + "message": "开始免费家庭版试用" } } diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index c52c897e0e8..2f2e0e80c09 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -172,8 +172,8 @@ } } }, - "noAppsInOrgTitle": { - "message": "在$ORG NAME$中找無應用程式", + "noApplicationsInOrgTitle": { + "message": "No applications found for $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -181,8 +181,32 @@ } } }, - "noAppsInOrgDescription": { - "message": "在使用者儲存登入後,應用程式將會顯示在這裡,並同時顯示具有風險的密碼。標註重要應用程式並通知使用者以更新密碼。" + "noApplicationsInOrgDescription": { + "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + }, + "benefit1Title": { + "message": "Prioritize risks" + }, + "benefit1Description": { + "message": "Focus on applications that matter the most" + }, + "benefit2Title": { + "message": "Guide remediation" + }, + "benefit2Description": { + "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + }, + "benefit3Title": { + "message": "Monitor progress" + }, + "benefit3Description": { + "message": "Track changes over time to show security improvements" + }, + "noReportRunTitle": { + "message": "Run your first report to see applications" + }, + "noReportRunDescription": { + "message": "Generate a risk insights report to analyze your organization's applications and identify at-risk passwords that need attention. Running your first report will:" }, "noCriticalApplicationsTitle": { "message": "您尚未將任何應用程式標記為關鍵" @@ -328,6 +352,27 @@ "markAsCriticalPlaceholder": { "message": "標記為關鍵功能將在未來更新中實現" }, + "applicationReviewSaved": { + "message": "Application review saved" + }, + "applicationsMarkedAsCritical": { + "message": "$COUNT$ applications marked as critical", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, + "newApplicationsReviewed": { + "message": "New applications reviewed" + }, + "errorSavingReviewStatus": { + "message": "Error saving review status" + }, + "pleaseTryAgain": { + "message": "Please try again" + }, "unmarkAsCritical": { "message": "取消標記為關鍵" }, @@ -1314,6 +1359,9 @@ "useSingleSignOn": { "message": "使用單一登入" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "歡迎回來" }, @@ -4449,8 +4497,8 @@ "inviteAccepted": { "message": "邀請已接受" }, - "inviteAcceptedDesc": { - "message": "管理員確認您的成員身分後,您便可以存取此組織。到時我們會向您傳送電子郵件通知。" + "invitationAcceptedDesc": { + "message": "成功接受您的邀請。" }, "inviteInitAcceptedDesc": { "message": "您現在可以存取此組織了。" @@ -6492,17 +6540,32 @@ "tdeDisabledMasterPasswordRequired": { "message": "您的組織已更新您的解密選項。請設定主密碼以存取您的密碼庫。" }, - "maximumVaultTimeout": { - "message": "密碼庫逾時時間" + "sessionTimeoutPolicyTitle": { + "message": "工作階段逾時" }, - "maximumVaultTimeoutDesc": { - "message": "為成員設定最大密碼庫逾時時間。" + "sessionTimeoutPolicyDescription": { + "message": "為除擁有者外的所有成員設定最長工作階段逾時時間。" }, - "maximumVaultTimeoutLabel": { - "message": "最大密碼庫逾時時間" + "maximumAllowedTimeout": { + "message": "最大允許的逾時時間" }, - "invalidMaximumVaultTimeout": { - "message": "無效的最大密碼庫逾時時間。" + "maximumAllowedTimeoutRequired": { + "message": "需要最大允許的逾時時間" + }, + "sessionTimeoutPolicyInvalidTime": { + "message": "時間無效。請至少變更一個值。" + }, + "sessionTimeoutAction": { + "message": "工作階段逾時動作" + }, + "immediately": { + "message": "立即" + }, + "onSystemLock": { + "message": "於系統鎖定時" + }, + "onAppRestart": { + "message": "應用程式重新啟動時" }, "hours": { "message": "小時" @@ -6510,6 +6573,21 @@ "minutes": { "message": "分鐘" }, + "sessionTimeoutConfirmationNeverTitle": { + "message": "您確定要允許所有成員的最長逾時為「永不」嗎?" + }, + "sessionTimeoutConfirmationNeverDescription": { + "message": "此選項會將成員的加密金鑰儲存在其裝置上。若選擇此選項,請確保這些裝置具備充分的防護。" + }, + "learnMoreAboutDeviceProtection": { + "message": "進一步了解裝置保護" + }, + "sessionTimeoutConfirmationOnSystemLockTitle": { + "message": "「系統鎖定」僅適用於瀏覽器與桌面應用程式" + }, + "sessionTimeoutConfirmationOnSystemLockDescription": { + "message": "由於不支援此選項,行動與網頁應用程式將以「在應用程式重新啟動時」作為其最長允許逾時時間。" + }, "vaultTimeoutPolicyInEffect": { "message": "您的組織政策已限定您密碼庫逾時的時間長度。密碼庫逾時時間最高可以設定到 $HOURS$ 小時 $MINUTES$ 分鐘。", "placeholders": { @@ -11936,5 +12014,14 @@ }, "encryptionKeySettingsAlgorithmPopoverArgon2Id": { "message": "Argon2id 對抗現代攻擊提供更強的防護,最適合具有高效能裝置的進階使用者。" + }, + "zipPostalCodeLabel": { + "message": "郵編 / 郵政代碼" + }, + "cardNumberLabel": { + "message": "支付卡號碼" + }, + "startFreeFamiliesTrial": { + "message": "開始免費家庭試用" } } From dbe70bd51c27e6a83dc9c23bb6ca5f0a94960116 Mon Sep 17 00:00:00 2001 From: Shane Melton Date: Fri, 31 Oct 2025 10:23:18 -0700 Subject: [PATCH 45/66] [PM-24467] Introduce cipher risk service (#17009) * [PM-24467] Introduce CipherRiskService * [PM-24467] Introduce computeCipherRiskForUser() method * [PM-24467] Refactor buildPasswordReuseMap to use user SDK client * [PM-24467] Use switchMap instead of map * [PM-24467] Cleanup redundant tests * [PM-24467] Update SDK models * [PM-24467] Update @bitwarden/sdk-internal version * [PM-24467] Update @bitwarden/commercial-sdk-internal version --- .../vault/abstractions/cipher-risk.service.ts | 55 ++ .../default-cipher-risk.service.spec.ts | 538 ++++++++++++++++++ .../services/default-cipher-risk.service.ts | 115 ++++ 3 files changed, 708 insertions(+) create mode 100644 libs/common/src/vault/abstractions/cipher-risk.service.ts create mode 100644 libs/common/src/vault/services/default-cipher-risk.service.spec.ts create mode 100644 libs/common/src/vault/services/default-cipher-risk.service.ts diff --git a/libs/common/src/vault/abstractions/cipher-risk.service.ts b/libs/common/src/vault/abstractions/cipher-risk.service.ts new file mode 100644 index 00000000000..6bbd9d7791e --- /dev/null +++ b/libs/common/src/vault/abstractions/cipher-risk.service.ts @@ -0,0 +1,55 @@ +import type { + CipherRiskResult, + CipherRiskOptions, + ExposedPasswordResult, + PasswordReuseMap, + CipherId, +} from "@bitwarden/sdk-internal"; + +import { UserId } from "../../types/guid"; +import { CipherView } from "../models/view/cipher.view"; + +export abstract class CipherRiskService { + /** + * Compute password risks for multiple ciphers. + * Only processes Login ciphers with passwords. + * + * @param ciphers - The ciphers to evaluate for password risks + * @param userId - The user ID for SDK client context + * @param options - Optional configuration for risk computation (passwordMap, checkExposed) + * @returns Array of CipherRisk results from SDK containing password_strength, exposed_result, and reuse_count + */ + abstract computeRiskForCiphers( + ciphers: CipherView[], + userId: UserId, + options?: CipherRiskOptions, + ): Promise; + + /** + * Compute password risk for a single cipher by its ID. Will automatically build a password reuse map + * from all the user's ciphers via the CipherService. + * @param cipherId + * @param userId + * @param checkExposed - Whether to check if the password has been exposed in data breaches via HIBP + * @returns CipherRisk result from SDK containing password_strength, exposed_result, and reuse_count + */ + abstract computeCipherRiskForUser( + cipherId: CipherId, + userId: UserId, + checkExposed?: boolean, + ): Promise; + + /** + * Build a password reuse map for the given ciphers. + * Maps each password to the number of times it appears across ciphers. + * Only processes Login ciphers with passwords. + * + * @param ciphers - The ciphers to analyze for password reuse + * @param userId - The user ID for SDK client context + * @returns A map of password to count of occurrences + */ + abstract buildPasswordReuseMap(ciphers: CipherView[], userId: UserId): Promise; +} + +// Re-export SDK types for convenience +export type { CipherRiskResult, CipherRiskOptions, ExposedPasswordResult, PasswordReuseMap }; 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 new file mode 100644 index 00000000000..afd52bde6cf --- /dev/null +++ b/libs/common/src/vault/services/default-cipher-risk.service.spec.ts @@ -0,0 +1,538 @@ +import { mock } from "jest-mock-extended"; +import { BehaviorSubject } from "rxjs"; + +import type { CipherRiskOptions, CipherId, CipherRiskResult } from "@bitwarden/sdk-internal"; + +import { asUuid } from "../../platform/abstractions/sdk/sdk.service"; +import { MockSdkService } from "../../platform/spec/mock-sdk.service"; +import { UserId } from "../../types/guid"; +import { CipherService } from "../abstractions/cipher.service"; +import { CipherType } from "../enums/cipher-type"; +import { CipherView } from "../models/view/cipher.view"; +import { LoginView } from "../models/view/login.view"; + +import { DefaultCipherRiskService } from "./default-cipher-risk.service"; + +describe("DefaultCipherRiskService", () => { + let cipherRiskService: DefaultCipherRiskService; + let sdkService: MockSdkService; + let mockCipherService: jest.Mocked; + + const mockUserId = "test-user-id" as UserId; + const mockCipherId1 = "cbea34a8-bde4-46ad-9d19-b05001228ab2"; + const mockCipherId2 = "cbea34a8-bde4-46ad-9d19-b05001228ab3"; + const mockCipherId3 = "cbea34a8-bde4-46ad-9d19-b05001228ab4"; + + beforeEach(() => { + sdkService = new MockSdkService(); + mockCipherService = mock(); + cipherRiskService = new DefaultCipherRiskService(sdkService, mockCipherService); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe("computeRiskForCiphers", () => { + it("should call SDK cipher_risk().compute_risk() with correct parameters", async () => { + const mockClient = sdkService.simulate.userLogin(mockUserId); + const mockCipherRiskClient = mockClient.vault.mockDeep().cipher_risk.mockDeep(); + + const mockRiskResults: CipherRiskResult[] = [ + { + id: mockCipherId1 as any, + password_strength: 3, + exposed_result: { type: "NotChecked" }, + reuse_count: undefined, + }, + ]; + + mockCipherRiskClient.compute_risk.mockResolvedValue(mockRiskResults); + + const cipher = new CipherView(); + cipher.id = mockCipherId1; + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + cipher.login.password = "test-password"; + cipher.login.username = "test@example.com"; + + const options: CipherRiskOptions = { + checkExposed: true, + passwordMap: undefined, + hibpBaseUrl: undefined, + }; + + const results = await cipherRiskService.computeRiskForCiphers([cipher], mockUserId, options); + + expect(mockCipherRiskClient.compute_risk).toHaveBeenCalledWith( + [ + { + id: expect.anything(), + password: "test-password", + username: "test@example.com", + }, + ], + options, + ); + expect(results).toEqual(mockRiskResults); + }); + + it("should filter out non-Login ciphers", async () => { + const mockClient = sdkService.simulate.userLogin(mockUserId); + const mockCipherRiskClient = mockClient.vault.mockDeep().cipher_risk.mockDeep(); + mockCipherRiskClient.compute_risk.mockResolvedValue([]); + + const loginCipher = new CipherView(); + loginCipher.id = mockCipherId1; + loginCipher.type = CipherType.Login; + loginCipher.login = new LoginView(); + loginCipher.login.password = "password1"; + + const cardCipher = new CipherView(); + cardCipher.id = mockCipherId2; + cardCipher.type = CipherType.Card; + + const identityCipher = new CipherView(); + identityCipher.id = mockCipherId3; + identityCipher.type = CipherType.Identity; + + await cipherRiskService.computeRiskForCiphers( + [loginCipher, cardCipher, identityCipher], + mockUserId, + ); + + expect(mockCipherRiskClient.compute_risk).toHaveBeenCalledWith( + [ + expect.objectContaining({ + id: expect.anything(), + password: "password1", + }), + ], + expect.any(Object), + ); + }); + + it("should filter out Login ciphers without passwords", async () => { + const mockClient = sdkService.simulate.userLogin(mockUserId); + const mockCipherRiskClient = mockClient.vault.mockDeep().cipher_risk.mockDeep(); + mockCipherRiskClient.compute_risk.mockResolvedValue([]); + + const cipherWithPassword = new CipherView(); + cipherWithPassword.id = mockCipherId1; + cipherWithPassword.type = CipherType.Login; + cipherWithPassword.login = new LoginView(); + cipherWithPassword.login.password = "password1"; + + const cipherWithoutPassword = new CipherView(); + cipherWithoutPassword.id = mockCipherId2; + cipherWithoutPassword.type = CipherType.Login; + cipherWithoutPassword.login = new LoginView(); + cipherWithoutPassword.login.password = undefined; + + const cipherWithEmptyPassword = new CipherView(); + cipherWithEmptyPassword.id = mockCipherId3; + cipherWithEmptyPassword.type = CipherType.Login; + cipherWithEmptyPassword.login = new LoginView(); + cipherWithEmptyPassword.login.password = ""; + + await cipherRiskService.computeRiskForCiphers( + [cipherWithPassword, cipherWithoutPassword, cipherWithEmptyPassword], + mockUserId, + ); + + expect(mockCipherRiskClient.compute_risk).toHaveBeenCalledWith( + [ + expect.objectContaining({ + password: "password1", + }), + ], + expect.any(Object), + ); + }); + + it("should return empty array when no valid Login ciphers provided", async () => { + const cardCipher = new CipherView(); + cardCipher.type = CipherType.Card; + + const results = await cipherRiskService.computeRiskForCiphers([cardCipher], mockUserId); + + expect(results).toEqual([]); + }); + + it("should handle multiple Login ciphers", async () => { + const mockClient = sdkService.simulate.userLogin(mockUserId); + const mockCipherRiskClient = mockClient.vault.mockDeep().cipher_risk.mockDeep(); + + const mockRiskResults: CipherRiskResult[] = [ + { + id: mockCipherId1 as any, + password_strength: 3, + exposed_result: { type: "Found", value: 5 }, + reuse_count: 2, + }, + { + id: mockCipherId2 as any, + password_strength: 4, + exposed_result: { type: "NotChecked" }, + reuse_count: 1, + }, + ]; + + mockCipherRiskClient.compute_risk.mockResolvedValue(mockRiskResults); + + const cipher1 = new CipherView(); + cipher1.id = mockCipherId1; + cipher1.type = CipherType.Login; + cipher1.login = new LoginView(); + cipher1.login.password = "password1"; + cipher1.login.username = "user1@example.com"; + + const cipher2 = new CipherView(); + cipher2.id = mockCipherId2; + cipher2.type = CipherType.Login; + cipher2.login = new LoginView(); + cipher2.login.password = "password2"; + cipher2.login.username = "user2@example.com"; + + const results = await cipherRiskService.computeRiskForCiphers([cipher1, cipher2], mockUserId); + + expect(mockCipherRiskClient.compute_risk).toHaveBeenCalledWith( + [ + expect.objectContaining({ password: "password1", username: "user1@example.com" }), + expect.objectContaining({ password: "password2", username: "user2@example.com" }), + ], + expect.any(Object), + ); + expect(results).toEqual(mockRiskResults); + }); + + it("should use default options when options not provided", async () => { + const mockClient = sdkService.simulate.userLogin(mockUserId); + const mockCipherRiskClient = mockClient.vault.mockDeep().cipher_risk.mockDeep(); + mockCipherRiskClient.compute_risk.mockResolvedValue([]); + + const cipher = new CipherView(); + cipher.id = mockCipherId1; + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + cipher.login.password = "test-password"; + + await cipherRiskService.computeRiskForCiphers([cipher], mockUserId); + + expect(mockCipherRiskClient.compute_risk).toHaveBeenCalledWith(expect.any(Array), { + checkExposed: false, + passwordMap: undefined, + hibpBaseUrl: undefined, + }); + }); + + it("should handle ciphers without username", async () => { + const mockClient = sdkService.simulate.userLogin(mockUserId); + const mockCipherRiskClient = mockClient.vault.mockDeep().cipher_risk.mockDeep(); + mockCipherRiskClient.compute_risk.mockResolvedValue([]); + + const cipher = new CipherView(); + cipher.id = mockCipherId1; + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + cipher.login.password = "test-password"; + cipher.login.username = undefined; + + await cipherRiskService.computeRiskForCiphers([cipher], mockUserId); + + expect(mockCipherRiskClient.compute_risk).toHaveBeenCalledWith( + [ + expect.objectContaining({ + password: "test-password", + username: undefined, + }), + ], + expect.any(Object), + ); + }); + }); + + describe("buildPasswordReuseMap", () => { + it("should call SDK cipher_risk().password_reuse_map() with correct parameters", async () => { + const mockClient = sdkService.simulate.userLogin(mockUserId); + const mockCipherRiskClient = mockClient.vault.mockDeep().cipher_risk.mockDeep(); + + const mockReuseMap = { + password1: 2, + password2: 1, + }; + + mockCipherRiskClient.password_reuse_map.mockReturnValue(mockReuseMap); + + const cipher1 = new CipherView(); + cipher1.id = mockCipherId1; + cipher1.type = CipherType.Login; + cipher1.login = new LoginView(); + cipher1.login.password = "password1"; + + const cipher2 = new CipherView(); + cipher2.id = mockCipherId2; + cipher2.type = CipherType.Login; + cipher2.login = new LoginView(); + cipher2.login.password = "password2"; + + const result = await cipherRiskService.buildPasswordReuseMap([cipher1, cipher2], mockUserId); + + expect(mockCipherRiskClient.password_reuse_map).toHaveBeenCalledWith([ + expect.objectContaining({ password: "password1" }), + expect.objectContaining({ password: "password2" }), + ]); + expect(result).toEqual(mockReuseMap); + }); + }); + + describe("computeCipherRiskForUser", () => { + it("should compute risk for a single cipher with password reuse map", async () => { + const mockClient = sdkService.simulate.userLogin(mockUserId); + const mockCipherRiskClient = mockClient.vault.mockDeep().cipher_risk.mockDeep(); + + // Setup cipher data + const cipher1 = new CipherView(); + cipher1.id = mockCipherId1; + cipher1.type = CipherType.Login; + cipher1.login = new LoginView(); + cipher1.login.password = "password1"; + cipher1.login.username = "user1@example.com"; + + const cipher2 = new CipherView(); + cipher2.id = mockCipherId2; + cipher2.type = CipherType.Login; + cipher2.login = new LoginView(); + cipher2.login.password = "password1"; // Same password as cipher1 + cipher2.login.username = "user2@example.com"; + + const allCiphers = [cipher1, cipher2]; + + // Mock cipherViews$ observable + mockCipherService.cipherViews$.mockReturnValue(new BehaviorSubject(allCiphers)); + + // Mock password reuse map + const mockReuseMap = { password1: 2 }; + mockCipherRiskClient.password_reuse_map.mockReturnValue(mockReuseMap); + + // Mock compute_risk result + const mockRiskResult: CipherRiskResult = { + id: mockCipherId1 as any, + password_strength: 3, + exposed_result: { type: "NotChecked" }, + reuse_count: 2, + }; + mockCipherRiskClient.compute_risk.mockResolvedValue([mockRiskResult]); + + const result = await cipherRiskService.computeCipherRiskForUser( + asUuid(mockCipherId1), + mockUserId, + true, + ); + + // Verify cipherViews$ was called + expect(mockCipherService.cipherViews$).toHaveBeenCalledWith(mockUserId); + + // Verify password_reuse_map was called with all ciphers + expect(mockCipherRiskClient.password_reuse_map).toHaveBeenCalledWith([ + expect.objectContaining({ password: "password1", username: "user1@example.com" }), + expect.objectContaining({ password: "password1", username: "user2@example.com" }), + ]); + + // Verify compute_risk was called with target cipher and password map + expect(mockCipherRiskClient.compute_risk).toHaveBeenCalledWith( + [expect.objectContaining({ password: "password1", username: "user1@example.com" })], + { + passwordMap: mockReuseMap, + checkExposed: true, + }, + ); + + expect(result).toEqual(mockRiskResult); + }); + + it("should throw error when cipher is not found", async () => { + const cipher1 = new CipherView(); + cipher1.id = mockCipherId1; + cipher1.type = CipherType.Login; + cipher1.login = new LoginView(); + cipher1.login.password = "password1"; + + mockCipherService.cipherViews$.mockReturnValue(new BehaviorSubject([cipher1])); + + const nonExistentId = "00000000-0000-0000-0000-000000000000"; + await expect( + cipherRiskService.computeCipherRiskForUser(asUuid(nonExistentId), mockUserId), + ).rejects.toThrow(`Cipher with id ${asUuid(nonExistentId)} not found`); + }); + + it("should use checkExposed parameter correctly", async () => { + const mockClient = sdkService.simulate.userLogin(mockUserId); + const mockCipherRiskClient = mockClient.vault.mockDeep().cipher_risk.mockDeep(); + + const cipher = new CipherView(); + cipher.id = mockCipherId1; + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + cipher.login.password = "password1"; + + mockCipherService.cipherViews$.mockReturnValue(new BehaviorSubject([cipher])); + mockCipherRiskClient.password_reuse_map.mockReturnValue({}); + mockCipherRiskClient.compute_risk.mockResolvedValue([ + { + id: mockCipherId1 as any, + password_strength: 4, + exposed_result: { type: "NotChecked" }, + reuse_count: 1, + }, + ]); + + await cipherRiskService.computeCipherRiskForUser( + asUuid(mockCipherId1), + mockUserId, + false, + ); + + expect(mockCipherRiskClient.compute_risk).toHaveBeenCalledWith(expect.any(Array), { + passwordMap: expect.any(Object), + checkExposed: false, + }); + }); + + it("should default checkExposed to true when not provided", async () => { + const mockClient = sdkService.simulate.userLogin(mockUserId); + const mockCipherRiskClient = mockClient.vault.mockDeep().cipher_risk.mockDeep(); + + const cipher = new CipherView(); + cipher.id = mockCipherId1; + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + cipher.login.password = "password1"; + + mockCipherService.cipherViews$.mockReturnValue(new BehaviorSubject([cipher])); + mockCipherRiskClient.password_reuse_map.mockReturnValue({}); + mockCipherRiskClient.compute_risk.mockResolvedValue([ + { + id: mockCipherId1 as any, + password_strength: 4, + exposed_result: { type: "Found", value: 10 }, + reuse_count: 1, + }, + ]); + + await cipherRiskService.computeCipherRiskForUser(asUuid(mockCipherId1), mockUserId); + + expect(mockCipherRiskClient.compute_risk).toHaveBeenCalledWith(expect.any(Array), { + passwordMap: expect.any(Object), + checkExposed: true, + }); + }); + + it("should handle ciphers without passwords when building password map", async () => { + const mockClient = sdkService.simulate.userLogin(mockUserId); + const mockCipherRiskClient = mockClient.vault.mockDeep().cipher_risk.mockDeep(); + + const cipherWithPassword = new CipherView(); + cipherWithPassword.id = mockCipherId1; + cipherWithPassword.type = CipherType.Login; + cipherWithPassword.login = new LoginView(); + cipherWithPassword.login.password = "password1"; + + const cipherWithoutPassword = new CipherView(); + cipherWithoutPassword.id = mockCipherId2; + cipherWithoutPassword.type = CipherType.Login; + cipherWithoutPassword.login = new LoginView(); + cipherWithoutPassword.login.password = ""; + + mockCipherService.cipherViews$.mockReturnValue( + new BehaviorSubject([cipherWithPassword, cipherWithoutPassword]), + ); + mockCipherRiskClient.password_reuse_map.mockReturnValue({}); + mockCipherRiskClient.compute_risk.mockResolvedValue([ + { + id: mockCipherId1 as any, + password_strength: 4, + exposed_result: { type: "NotChecked" }, + reuse_count: 1, + }, + ]); + + await cipherRiskService.computeCipherRiskForUser(asUuid(mockCipherId1), mockUserId); + + // Verify password_reuse_map only received cipher with password + expect(mockCipherRiskClient.password_reuse_map).toHaveBeenCalledWith([ + expect.objectContaining({ password: "password1" }), + ]); + }); + + it("should handle non-Login ciphers in vault when building password map", async () => { + const mockClient = sdkService.simulate.userLogin(mockUserId); + const mockCipherRiskClient = mockClient.vault.mockDeep().cipher_risk.mockDeep(); + + const loginCipher = new CipherView(); + loginCipher.id = mockCipherId1; + loginCipher.type = CipherType.Login; + loginCipher.login = new LoginView(); + loginCipher.login.password = "password1"; + + const cardCipher = new CipherView(); + cardCipher.id = mockCipherId2; + cardCipher.type = CipherType.Card; + + const noteCipher = new CipherView(); + noteCipher.id = mockCipherId3; + noteCipher.type = CipherType.SecureNote; + + mockCipherService.cipherViews$.mockReturnValue( + new BehaviorSubject([loginCipher, cardCipher, noteCipher]), + ); + mockCipherRiskClient.password_reuse_map.mockReturnValue({}); + mockCipherRiskClient.compute_risk.mockResolvedValue([ + { + id: mockCipherId1 as any, + password_strength: 4, + exposed_result: { type: "NotChecked" }, + reuse_count: 1, + }, + ]); + + await cipherRiskService.computeCipherRiskForUser(asUuid(mockCipherId1), mockUserId); + + // Verify password_reuse_map only received Login cipher + expect(mockCipherRiskClient.password_reuse_map).toHaveBeenCalledWith([ + expect.objectContaining({ password: "password1" }), + ]); + }); + + it("should compute fresh password map on each call", async () => { + const mockClient = sdkService.simulate.userLogin(mockUserId); + const mockCipherRiskClient = mockClient.vault.mockDeep().cipher_risk.mockDeep(); + + const cipher = new CipherView(); + cipher.id = mockCipherId1; + cipher.type = CipherType.Login; + cipher.login = new LoginView(); + cipher.login.password = "password1"; + + mockCipherService.cipherViews$.mockReturnValue(new BehaviorSubject([cipher])); + mockCipherRiskClient.password_reuse_map.mockReturnValue({ password1: 1 }); + mockCipherRiskClient.compute_risk.mockResolvedValue([ + { + id: mockCipherId1 as any, + password_strength: 4, + exposed_result: { type: "NotChecked" }, + reuse_count: 1, + }, + ]); + + // First call + await cipherRiskService.computeCipherRiskForUser(asUuid(mockCipherId1), mockUserId); + + // Second call + await cipherRiskService.computeCipherRiskForUser(asUuid(mockCipherId1), mockUserId); + + // Verify password_reuse_map was called twice (fresh computation each time) + expect(mockCipherRiskClient.password_reuse_map).toHaveBeenCalledTimes(2); + }); + }); +}); diff --git a/libs/common/src/vault/services/default-cipher-risk.service.ts b/libs/common/src/vault/services/default-cipher-risk.service.ts new file mode 100644 index 00000000000..d9f0243edfe --- /dev/null +++ b/libs/common/src/vault/services/default-cipher-risk.service.ts @@ -0,0 +1,115 @@ +import { firstValueFrom, switchMap } from "rxjs"; + +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { + CipherLoginDetails, + CipherRiskOptions, + PasswordReuseMap, + CipherId, + CipherRiskResult, +} from "@bitwarden/sdk-internal"; + +import { SdkService, asUuid } from "../../platform/abstractions/sdk/sdk.service"; +import { UserId } from "../../types/guid"; +import { CipherRiskService as CipherRiskServiceAbstraction } from "../abstractions/cipher-risk.service"; +import { CipherType } from "../enums/cipher-type"; +import { CipherView } from "../models/view/cipher.view"; + +export class DefaultCipherRiskService implements CipherRiskServiceAbstraction { + constructor( + private sdkService: SdkService, + private cipherService: CipherService, + ) {} + + async computeRiskForCiphers( + ciphers: CipherView[], + userId: UserId, + options?: CipherRiskOptions, + ): Promise { + const loginDetails = this.mapToLoginDetails(ciphers); + + if (loginDetails.length === 0) { + return []; + } + + return await firstValueFrom( + this.sdkService.userClient$(userId).pipe( + switchMap(async (sdk) => { + using ref = sdk.take(); + const cipherRiskClient = ref.value.vault().cipher_risk(); + return await cipherRiskClient.compute_risk( + loginDetails, + options ?? { checkExposed: false }, + ); + }), + ), + ); + } + + async computeCipherRiskForUser( + cipherId: CipherId, + userId: UserId, + checkExposed: boolean = true, + ): Promise { + // Get all ciphers for the user + const allCiphers = await firstValueFrom(this.cipherService.cipherViews$(userId)); + + // Find the specific cipher + const targetCipher = allCiphers?.find((c) => asUuid(c.id) === cipherId); + if (!targetCipher) { + throw new Error(`Cipher with id ${cipherId} not found`); + } + + // Build fresh password reuse map from all ciphers + const passwordMap = await this.buildPasswordReuseMap(allCiphers, userId); + + // Call existing computeRiskForCiphers with single cipher and map + const results = await this.computeRiskForCiphers([targetCipher], userId, { + passwordMap, + checkExposed, + }); + + return results[0]; + } + + async buildPasswordReuseMap(ciphers: CipherView[], userId: UserId): Promise { + const loginDetails = this.mapToLoginDetails(ciphers); + + if (loginDetails.length === 0) { + return {}; + } + + return await firstValueFrom( + this.sdkService.userClient$(userId).pipe( + switchMap(async (sdk) => { + using ref = sdk.take(); + const cipherRiskClient = ref.value.vault().cipher_risk(); + return cipherRiskClient.password_reuse_map(loginDetails); + }), + ), + ); + } + + /** + * Maps CipherView array to CipherLoginDetails array for SDK consumption. + * Only includes Login ciphers with non-empty passwords. + */ + private mapToLoginDetails(ciphers: CipherView[]): CipherLoginDetails[] { + return ciphers + .filter((cipher) => { + return ( + cipher.type === CipherType.Login && + cipher.login?.password != null && + cipher.login.password !== "" + ); + }) + .map( + (cipher) => + ({ + id: asUuid(cipher.id), + password: cipher.login.password!, + username: cipher.login.username, + }) satisfies CipherLoginDetails, + ); + } +} From b7759abb209ad9269dfeafd1b7b827db59291bc7 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 31 Oct 2025 17:49:04 +0000 Subject: [PATCH 46/66] Autosync the updated translations (#17145) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/ar/messages.json | 57 +++++ apps/browser/src/_locales/az/messages.json | 57 +++++ apps/browser/src/_locales/be/messages.json | 57 +++++ apps/browser/src/_locales/bg/messages.json | 57 +++++ apps/browser/src/_locales/bn/messages.json | 67 +++++- apps/browser/src/_locales/bs/messages.json | 57 +++++ apps/browser/src/_locales/ca/messages.json | 57 +++++ apps/browser/src/_locales/cs/messages.json | 57 +++++ apps/browser/src/_locales/cy/messages.json | 57 +++++ apps/browser/src/_locales/da/messages.json | 57 +++++ apps/browser/src/_locales/de/messages.json | 57 +++++ apps/browser/src/_locales/el/messages.json | 179 ++++++++++----- apps/browser/src/_locales/en_GB/messages.json | 57 +++++ apps/browser/src/_locales/en_IN/messages.json | 57 +++++ apps/browser/src/_locales/es/messages.json | 57 +++++ apps/browser/src/_locales/et/messages.json | 57 +++++ apps/browser/src/_locales/eu/messages.json | 57 +++++ apps/browser/src/_locales/fa/messages.json | 57 +++++ apps/browser/src/_locales/fi/messages.json | 57 +++++ apps/browser/src/_locales/fil/messages.json | 57 +++++ apps/browser/src/_locales/fr/messages.json | 91 ++++++-- apps/browser/src/_locales/gl/messages.json | 57 +++++ apps/browser/src/_locales/he/messages.json | 57 +++++ apps/browser/src/_locales/hi/messages.json | 57 +++++ apps/browser/src/_locales/hr/messages.json | 57 +++++ apps/browser/src/_locales/hu/messages.json | 57 +++++ apps/browser/src/_locales/id/messages.json | 57 +++++ apps/browser/src/_locales/it/messages.json | 57 +++++ apps/browser/src/_locales/ja/messages.json | 57 +++++ apps/browser/src/_locales/ka/messages.json | 57 +++++ apps/browser/src/_locales/km/messages.json | 57 +++++ apps/browser/src/_locales/kn/messages.json | 57 +++++ apps/browser/src/_locales/ko/messages.json | 57 +++++ apps/browser/src/_locales/lt/messages.json | 57 +++++ apps/browser/src/_locales/lv/messages.json | 61 ++++- apps/browser/src/_locales/ml/messages.json | 57 +++++ apps/browser/src/_locales/mr/messages.json | 57 +++++ apps/browser/src/_locales/my/messages.json | 57 +++++ apps/browser/src/_locales/nb/messages.json | 57 +++++ apps/browser/src/_locales/ne/messages.json | 57 +++++ apps/browser/src/_locales/nl/messages.json | 57 +++++ apps/browser/src/_locales/nn/messages.json | 57 +++++ apps/browser/src/_locales/or/messages.json | 57 +++++ apps/browser/src/_locales/pl/messages.json | 57 +++++ apps/browser/src/_locales/pt_BR/messages.json | 57 +++++ apps/browser/src/_locales/pt_PT/messages.json | 59 ++++- apps/browser/src/_locales/ro/messages.json | 57 +++++ apps/browser/src/_locales/ru/messages.json | 65 +++++- apps/browser/src/_locales/si/messages.json | 57 +++++ apps/browser/src/_locales/sk/messages.json | 217 +++++++++++------- apps/browser/src/_locales/sl/messages.json | 57 +++++ apps/browser/src/_locales/sr/messages.json | 57 +++++ apps/browser/src/_locales/sv/messages.json | 57 +++++ apps/browser/src/_locales/ta/messages.json | 57 +++++ apps/browser/src/_locales/te/messages.json | 57 +++++ apps/browser/src/_locales/th/messages.json | 57 +++++ apps/browser/src/_locales/tr/messages.json | 63 ++++- apps/browser/src/_locales/uk/messages.json | 99 ++++++-- apps/browser/src/_locales/vi/messages.json | 77 ++++++- apps/browser/src/_locales/zh_CN/messages.json | 87 +++++-- apps/browser/src/_locales/zh_TW/messages.json | 57 +++++ apps/browser/store/locales/sk/copy.resx | 2 +- 62 files changed, 3697 insertions(+), 220 deletions(-) diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 35d21b59be9..ad36ba5854a 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "استخدام تسجيل الدخول الأحادي" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "مرحبًا بعودتك" }, @@ -588,6 +591,9 @@ "view": { "message": "عرض" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "تم تعديل العنصر" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "هل تريد حقاً أن ترسل إلى سلة المهملات؟" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "إظهار اقتراحات التعبئة التلقائية في حقول النموذج" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "عرض الهويات كاقتراحات" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "خطأ فك التشفير" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "تعذر على بتواردن فك تشفير العنصر (العناصر) المدرجة أدناه." }, @@ -4011,6 +4053,15 @@ "message": "ملء تلقائي عند تعيين تحميل الصفحة لاستخدام الإعداد الافتراضي.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index 2c9a496a95c..9a0239d2a34 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Vahid daxil olma üsulunu istifadə et" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Təşkilatınız, vahid daxil olma tələb edir." + }, "welcomeBack": { "message": "Yenidən xoş gəlmisiniz" }, @@ -588,6 +591,9 @@ "view": { "message": "Bax" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "Girişə bax" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Element saxlanıldı" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Həqiqətən tullantı qutusuna göndərmək istəyirsiniz?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Avto-doldurmanı söndür" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Avto-doldurma təkliflərini form xanalarında göstər" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Kimlikləri təklif kimi göstər" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Şifrə açma xətası" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden, aşağıda sadalanan seyf element(lər)inin şifrəsini aça bilmədi." }, @@ -4011,6 +4053,15 @@ "message": "\"Səhifə yüklənəndə avto-doldurma\" özəlliyi ilkin ayarı istifadə etmək üzrə ayarlandı.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Yan naviqasiyanı aç/bağla" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "Bu ayar, təşkilatınızın siyasəti tərəfindən sıradan çıxarılıb.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Poçt kodu" + }, + "cardNumberLabel": { + "message": "Kart nömrəsi" } } diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index f9fd41cf6e7..35aaddc13b2 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Выкарыстаць аднаразовы ўваход" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "З вяртаннем" }, @@ -588,6 +591,9 @@ "view": { "message": "Прагляд" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Элемент адрэдагаваны" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Вы сапраўды хочаце адправіць гэты элемент у сметніцу?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index d8c288d9fca..68b962837eb 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Използване на еднократна идентификация" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Вашата организация изисква еднократно удостоверяване." + }, "welcomeBack": { "message": "Добре дошли отново" }, @@ -588,6 +591,9 @@ "view": { "message": "Преглед" }, + "viewAll": { + "message": "Показване на всички" + }, "viewLogin": { "message": "Преглед на елемента за вписване" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Елементът е редактиран" }, + "savedWebsite": { + "message": "Запазен уеб сайт" + }, + "savedWebsites": { + "message": "Запазени уеб сайтове ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Наистина ли искате да изтриете елемента?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Изключване на автоматичното попълване" }, + "confirmAutofill": { + "message": "Потвърждаване на автоматичното попълване" + }, + "confirmAutofillDesc": { + "message": "Този уеб сайт не съвпада със запазените данни за вписване. Преди да попълните данните си, уверете се, че имате вяра на сайта." + }, "showInlineMenuLabel": { "message": "Показване на предложения за авт. попълване на полетата във формуляри" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "Как Битуорден защитава данните Ви от измами?" + }, + "currentWebsite": { + "message": "Текущ уеб сайт" + }, + "autofillAndAddWebsite": { + "message": "Автоматично попълване и добавяне на този уеб сайт" + }, + "autofillWithoutAdding": { + "message": "Автоматично попълване без добавяне" + }, + "doNotAutofill": { + "message": "Да не се попълва автоматично" + }, "showInlineMenuIdentitiesLabel": { "message": "Показване на идентичности като предложения" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Грешка при дешифриране" }, + "errorGettingAutoFillData": { + "message": "Грешка при получаването на данните за автоматично попълване" + }, "couldNotDecryptVaultItemsBelow": { "message": "Битоурден не може да дешифрира елементите от трезора посочени по-долу." }, @@ -4011,6 +4053,15 @@ "message": "Автоматичното попълване при зареждане на страницата използва настройката си по подразбиране.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Автоматичното попълване не може да бъде извършено" + }, + "cannotAutofillExactMatch": { + "message": "По подразбиране е зададена настройката „Точно съвпадение“. Текущият уеб сайт не съвпада точно със запазените данни за вход в този запис." + }, + "okay": { + "message": "Добре" + }, "toggleSideNavigation": { "message": "Превключване на страничната навигация" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "Тази настройка е изключена съгласно политиката на организацията Ви.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "Пощенски код" + }, + "cardNumberLabel": { + "message": "Номер на картата" } } diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index 1b8c289f717..25e37c06745 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -3,7 +3,7 @@ "message": "Bitwarden" }, "appLogoLabel": { - "message": "Bitwarden logo" + "message": "বিটওয়ার্ডেন লোগো" }, "extName": { "message": "Bitwarden Password Manager", @@ -31,8 +31,11 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { - "message": "Welcome back" + "message": "আবারও স্বাগতম" }, "setAStrongPassword": { "message": "Set a strong password" @@ -276,10 +279,10 @@ "message": "Send a verification code to your email" }, "sendCode": { - "message": "Send code" + "message": "কোড পাঠান" }, "codeSent": { - "message": "Code sent" + "message": "কোড পাঠানো হয়েছে" }, "verificationCode": { "message": "যাচাইকরণ কোড" @@ -300,7 +303,7 @@ "message": "Continue to Help Center?" }, "continueToHelpCenterDesc": { - "message": "Learn more about how to use Bitwarden on the Help Center." + "message": "সহায়তা কেন্দ্রে বিটওয়ার্ডেন কীভাবে ব্যবহার করতে হয় সে সম্পর্কে আরও জানুন।" }, "continueToBrowserExtensionStore": { "message": "Continue to browser extension store?" @@ -588,6 +591,9 @@ "view": { "message": "দেখুন" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "সম্পাদিত বস্তু" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "আপনি কি সত্যিই আবর্জনাতে পাঠাতে চান?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index 8cc0d947199..566f0e7077e 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "View" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Item saved" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Do you really want to send to the trash?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 4483967ab33..a5e00afae0c 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Inici de sessió únic" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Benvingut/da de nou" }, @@ -588,6 +591,9 @@ "view": { "message": "Visualitza" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Element guardat" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Esteu segur que voleu suprimir aquest element?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Mostra suggeriments d'emplenament automàtic als camps del formulari" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Mostra identitats com a suggeriments" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Error de desxifrat" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden no ha pogut desxifrar els elements de la caixa forta que s'indiquen a continuació." }, @@ -4011,6 +4053,15 @@ "message": "S'ha configurat l'emplenament automàtic en carregar la pàgina perquè utilitze la configuració predeterminada.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Canvia a la navegació lateral" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index b9383416eb4..5dd4a6a6efc 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Použít jednotné přihlášení" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Vaše organizace vyžaduje jednotné přihlášení." + }, "welcomeBack": { "message": "Vítejte zpět" }, @@ -588,6 +591,9 @@ "view": { "message": "Zobrazit" }, + "viewAll": { + "message": "Zobrazit vše" + }, "viewLogin": { "message": "Zobrazit přihlašovací údaje" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Položka byla uložena" }, + "savedWebsite": { + "message": "Uložená webová stránka" + }, + "savedWebsites": { + "message": "Uložené webové stránky ($COUNT$)", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Opravdu chcete položku přesunout do koše?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Vypnout automatické vyplňování" }, + "confirmAutofill": { + "message": "Potvrdit automatické vyplňování" + }, + "confirmAutofillDesc": { + "message": "Tato stránka neodpovídá Vašim uloženým přihlašovacím údajům. Před vyplněním přihlašovacích údajů se ujistěte, že se jedná o důvěryhodný web." + }, "showInlineMenuLabel": { "message": "Zobrazit návrhy automatického vyplňování v polích formuláře" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "Jak Bitwarden chrání Vaše data před phishingem?" + }, + "currentWebsite": { + "message": "Aktuální webová stránka" + }, + "autofillAndAddWebsite": { + "message": "Automatické vyplňování a přidání této stránky" + }, + "autofillWithoutAdding": { + "message": "Automatické vyplňování bez přidání" + }, + "doNotAutofill": { + "message": "Nevyplňovat automaticky" + }, "showInlineMenuIdentitiesLabel": { "message": "Zobrazit identity jako návrhy" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Chyba dešifrování" }, + "errorGettingAutoFillData": { + "message": "Chyba při načítání dat automatického vyplňování" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden nemohl dešifrovat níže uvedené položky v trezoru." }, @@ -4011,6 +4053,15 @@ "message": "Automatické vyplnění při načítání stránky bylo nastaveno na výchozí nastavení.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Nelze automaticky vyplňovat" + }, + "cannotAutofillExactMatch": { + "message": "Výchozí shoda je nastavena na \"Přesná shoda\". Aktuální web neodpovídá přesně uloženým přihlašovacím údajům pro tuto položku." + }, + "okay": { + "message": "OK" + }, "toggleSideNavigation": { "message": "Přepnout boční navigaci" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "Toto nastavení je zakázáno zásadami Vaší organizace.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / PSČ" + }, + "cardNumberLabel": { + "message": "Číslo karty" } } diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index c18633c281c..1f46f034f5e 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Croeso nôl" }, @@ -588,6 +591,9 @@ "view": { "message": "Gweld" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Eitem wedi'i chadw" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Ydych chi wir eisiau anfon i'r sbwriel?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 0f92552c9c1..7e1f66478cf 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Brug Single Sign-On" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Velkommen tilbage" }, @@ -588,6 +591,9 @@ "view": { "message": "Vis" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Element gemt" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Er du sikker på, at du sende til papirkurven?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Vis autoudfyld-menu i formularfelter" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Vis identiteter som forslag" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Dekrypteringsfejl" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden kunne ikke dekryptere boks-emne(r) anført nedenfor." }, @@ -4011,6 +4053,15 @@ "message": "Autoudfyldning ved sideindlæsning sat til standardindstillingen.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Slå sidenavigering til/fra" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index 411b73be447..9527c15e6a3 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Single Sign-on verwenden" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Willkommen zurück" }, @@ -588,6 +591,9 @@ "view": { "message": "Anzeigen" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "Zugangsdaten anzeigen" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Eintrag gespeichert" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Soll dieser Eintrag wirklich in den Papierkorb verschoben werden?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Auto-Ausfüllen deaktivieren" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Vorschläge zum Auto-Ausfüllen in Formularfeldern anzeigen" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Identitäten als Vorschläge anzeigen" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Entschlüsselungsfehler" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden konnte folgende(n) Tresor-Eintrag/Einträge nicht entschlüsseln." }, @@ -4011,6 +4053,15 @@ "message": "Auto-Ausfüllen beim Laden einer Seite wurde auf die Standardeinstellung gesetzt.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Seitennavigation umschalten" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "Diese Einstellung ist durch die Richtlinien deiner Organisation deaktiviert.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "PLZ / Postleitzahl" + }, + "cardNumberLabel": { + "message": "Kartennummer" } } diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 025a66c5cde..230f5d60423 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Χρήση ενιαίας σύνδεσης" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Καλώς ήρθατε" }, @@ -551,18 +554,18 @@ "message": "Επαναφορά αναζήτησης" }, "archiveNoun": { - "message": "Archive", + "message": "Αρχειοθήκη", "description": "Noun" }, "archiveVerb": { - "message": "Archive", + "message": "Αρχειοθέτηση", "description": "Verb" }, "unArchive": { "message": "Unarchive" }, "itemsInArchive": { - "message": "Items in archive" + "message": "Στοιχεία στην αρχειοθήκη" }, "noItemsInArchive": { "message": "No items in archive" @@ -588,8 +591,11 @@ "view": { "message": "Προβολή" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { - "message": "View login" + "message": "Προβολή σύνδεσης" }, "noItemsInList": { "message": "Δεν υπάρχουν στοιχεία στη λίστα." @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Το αντικείμενο αποθηκεύτηκε" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτό το στοιχείο;" }, @@ -1213,7 +1231,7 @@ "description": "Message prompting user to undertake completion of another security task." }, "saveFailure": { - "message": "Error saving", + "message": "Σφάλμα αποθήκευσης", "description": "Error message shown when the system fails to save login details." }, "saveFailureDetails": { @@ -1549,7 +1567,7 @@ "message": "Read security key" }, "readingPasskeyLoading": { - "message": "Reading passkey..." + "message": "Ανάγνωση κλειδιού πρόσβασης..." }, "passkeyAuthenticationFailed": { "message": "Passkey authentication failed" @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Απενεργοποίηση αυτόματης συμπλήρωσης" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Εμφάνιση μενού αυτόματης συμπλήρωσης στα πεδία της φόρμας" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Εμφάνιση ταυτοτήτων ως προτάσεις" }, @@ -1779,7 +1818,7 @@ "message": "Σύρετε για ταξινόμηση" }, "dragToReorder": { - "message": "Drag to reorder" + "message": "Σύρετε για αναδιάταξη" }, "cfTypeText": { "message": "Κείμενο" @@ -1865,7 +1904,7 @@ "message": "Κωδικός ασφαλείας" }, "cardNumber": { - "message": "card number" + "message": "αριθμός κάρτας" }, "ex": { "message": "πχ." @@ -1967,82 +2006,82 @@ "message": "Κλειδί SSH" }, "typeNote": { - "message": "Note" + "message": "Σημείωση" }, "newItemHeaderLogin": { - "message": "New Login", + "message": "Νέα σύνδεση", "description": "Header for new login item type" }, "newItemHeaderCard": { - "message": "New Card", + "message": "Νέα κάρτα", "description": "Header for new card item type" }, "newItemHeaderIdentity": { - "message": "New Identity", + "message": "Νέα ταυτότητα", "description": "Header for new identity item type" }, "newItemHeaderNote": { - "message": "New Note", + "message": "Νέα σημείωση", "description": "Header for new note item type" }, "newItemHeaderSshKey": { - "message": "New SSH key", + "message": "Νέο κλειδί SSH", "description": "Header for new SSH key item type" }, "newItemHeaderTextSend": { - "message": "New Text Send", + "message": "Νέο Send κειμένου", "description": "Header for new text send" }, "newItemHeaderFileSend": { - "message": "New File Send", + "message": "Νέο Send αρχείου", "description": "Header for new file send" }, "editItemHeaderLogin": { - "message": "Edit Login", + "message": "Επεξεργασία σύνδεσης", "description": "Header for edit login item type" }, "editItemHeaderCard": { - "message": "Edit Card", + "message": "Επεξεργασία κάρτας", "description": "Header for edit card item type" }, "editItemHeaderIdentity": { - "message": "Edit Identity", + "message": "Επεξεργασία ταυτότητας", "description": "Header for edit identity item type" }, "editItemHeaderNote": { - "message": "Edit Note", + "message": "Επεξεργασία σημείωσης", "description": "Header for edit note item type" }, "editItemHeaderSshKey": { - "message": "Edit SSH key", + "message": "Επεξεργασία κλειδιού SSH", "description": "Header for edit SSH key item type" }, "editItemHeaderTextSend": { - "message": "Edit Text Send", + "message": "Επεξεργασία Send κειμένου", "description": "Header for edit text send" }, "editItemHeaderFileSend": { - "message": "Edit File Send", + "message": "Επεξεργασία Send αρχείου", "description": "Header for edit file send" }, "viewItemHeaderLogin": { - "message": "View Login", + "message": "Προβολή σύνδεσης", "description": "Header for view login item type" }, "viewItemHeaderCard": { - "message": "View Card", + "message": "Προβολή κάρτας", "description": "Header for view card item type" }, "viewItemHeaderIdentity": { - "message": "View Identity", + "message": "Προβολή ταυτότητας", "description": "Header for view identity item type" }, "viewItemHeaderNote": { - "message": "View Note", + "message": "Προβολή σημείωσης", "description": "Header for view note item type" }, "viewItemHeaderSshKey": { - "message": "View SSH key", + "message": "Προβολή κλειδιού SSH", "description": "Header for view SSH key item type" }, "passwordHistory": { @@ -2594,7 +2633,7 @@ "message": "Αποκλεισμένοι τομείς" }, "learnMoreAboutBlockedDomains": { - "message": "Learn more about blocked domains" + "message": "Μάθετε περισσότερα για τους αποκλεισμένους τομείς" }, "excludedDomains": { "message": "Εξαιρούμενοι Τομείς" @@ -2618,7 +2657,7 @@ "message": "Αλλαγή" }, "changePassword": { - "message": "Change password", + "message": "Αλλαγή κωδικού πρόσβασής", "description": "Change password button for browser at risk notification on login." }, "changeButtonTitle": { @@ -2631,7 +2670,7 @@ } }, "atRiskPassword": { - "message": "At-risk password" + "message": "Κωδικός πρόσβασης σε κίνδυνο" }, "atRiskPasswords": { "message": "Κωδικοί πρόσβασης σε κίνδυνο" @@ -2726,7 +2765,7 @@ "message": "Illustration of the Bitwarden autofill menu displaying a generated password." }, "updateInBitwarden": { - "message": "Update in Bitwarden" + "message": "Ενημέρωση στο Bitwarden" }, "updateInBitwardenSlideDesc": { "message": "Bitwarden will then prompt you to update the password in the password manager.", @@ -3157,7 +3196,7 @@ "message": "A master password is no longer required for members of the following organization. Please confirm the domain below with your organization administrator." }, "organizationName": { - "message": "Organization name" + "message": "Όνομα οργανισμού" }, "keyConnectorDomain": { "message": "Key Connector domain" @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Σφάλμα αποκρυπτογράφησης" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Το Bitwarden δεν μπόρεσε να αποκρυπτογραφήσει τα αντικείμενα θησαυ/κίου που αναφέρονται παρακάτω." }, @@ -3582,10 +3624,10 @@ "message": "You denied a login attempt from another device. If this was you, try to log in with the device again." }, "device": { - "message": "Device" + "message": "Συσκευή" }, "loginStatus": { - "message": "Login status" + "message": "Κατάσταση σύνδεσης" }, "masterPasswordChanged": { "message": "Master password saved" @@ -3681,17 +3723,17 @@ "message": "Απομνημόνευση αυτής της συσκευής για την αυτόματες συνδέσεις στο μέλλον" }, "manageDevices": { - "message": "Manage devices" + "message": "Διαχείριση συσκευών" }, "currentSession": { - "message": "Current session" + "message": "Τρέχουσα συνεδρία" }, "mobile": { "message": "Mobile", "description": "Mobile app" }, "extension": { - "message": "Extension", + "message": "Επέκταση", "description": "Browser extension/addon" }, "desktop": { @@ -3715,7 +3757,7 @@ "message": "Request pending" }, "firstLogin": { - "message": "First login" + "message": "Πρώτη σύνδεση" }, "trusted": { "message": "Trusted" @@ -3724,10 +3766,10 @@ "message": "Needs approval" }, "devices": { - "message": "Devices" + "message": "Συσκευές" }, "accessAttemptBy": { - "message": "Access attempt by $EMAIL$", + "message": "Απόπειρα πρόσβασης από το $EMAIL$", "placeholders": { "email": { "content": "$1", @@ -3736,31 +3778,31 @@ } }, "confirmAccess": { - "message": "Confirm access" + "message": "Επιβεβαίωση πρόσβασης" }, "denyAccess": { - "message": "Deny access" + "message": "Άρνηση πρόσβασης" }, "time": { - "message": "Time" + "message": "Ώρα" }, "deviceType": { - "message": "Device Type" + "message": "Τύπος συσκευής" }, "loginRequest": { - "message": "Login request" + "message": "Αίτημα σύνδεσης" }, "thisRequestIsNoLongerValid": { - "message": "This request is no longer valid." + "message": "Αυτό το αίτημα δεν είναι πλέον έγκυρο." }, "loginRequestHasAlreadyExpired": { - "message": "Login request has already expired." + "message": "Το αίτημα σύνδεσης έχει ήδη λήξει." }, "justNow": { - "message": "Just now" + "message": "Μόλις τώρα" }, "requestedXMinutesAgo": { - "message": "Requested $MINUTES$ minutes ago", + "message": "Ζητήθηκε πριν από $MINUTES$ λεπτά", "placeholders": { "minutes": { "content": "$1", @@ -4011,6 +4053,15 @@ "message": "Η αυτόματη συμπλήρωση κατά τη φόρτωση της σελίδας ορίστηκε να χρησιμοποιεί τις προεπιλεγμένες ρυθμίσεις.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Εναλλαγή πλευρικής πλοήγησης" }, @@ -4476,7 +4527,7 @@ "description": "Link to match detection docs on warning dialog for advance match strategy" }, "uriAdvancedOption": { - "message": "Advanced options", + "message": "Σύνθετες επιλογές", "description": "Advanced option placeholder for uri option component" }, "confirmContinueToBrowserSettingsTitle": { @@ -4831,10 +4882,10 @@ "message": "Download from bitwarden.com now" }, "getItOnGooglePlay": { - "message": "Get it on Google Play" + "message": "Αποκτήστε το στο Google Play" }, "downloadOnTheAppStore": { - "message": "Download on the App Store" + "message": "Λήψη στο AppStore" }, "permanentlyDeleteAttachmentConfirmation": { "message": "Είστε σίγουροι ότι θέλετε να διαγράψετε οριστικά αυτό το συνημμένο;" @@ -5538,7 +5589,7 @@ "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." }, "introCarouselLabel": { - "message": "Welcome to Bitwarden" + "message": "Καλώς ορίσατε στο Bitwarden" }, "securityPrioritized": { "message": "Security, prioritized" @@ -5547,13 +5598,13 @@ "message": "Save logins, cards, and identities to your secure vault. Bitwarden uses zero-knowledge, end-to-end encryption to protect what’s important to you." }, "quickLogin": { - "message": "Quick and easy login" + "message": "Εύκολη και γρήγορη σύνδεση" }, "quickLoginBody": { "message": "Set up biometric unlock and autofill to log into your accounts without typing a single letter." }, "secureUser": { - "message": "Level up your logins" + "message": "Αναβαθμίστε τις συνδέσεις σας" }, "secureUserBody": { "message": "Use the generator to create and save strong, unique passwords for all your accounts." @@ -5600,7 +5651,7 @@ "description": "This is in multiple parts to allow for bold text in the middle of the sentence. A proper name precedes this." }, "phishingPageLearnMore": { - "message": "Learn more about phishing detection" + "message": "Μάθετε περισσότερα για την ανίχνευση ηλεκτρονικού «ψαρέματος»" }, "protectedBy": { "message": "Protected by $PRODUCT$", @@ -5621,7 +5672,7 @@ "message": "Search your vault for something else" }, "newLoginNudgeTitle": { - "message": "Save time with autofill" + "message": "Εξοικονομήστε χρόνο με την αυτόματη συμπλήρωση" }, "newLoginNudgeBodyOne": { "message": "Include a", @@ -5700,13 +5751,13 @@ "description": "'WebAssembly' is a technical term and should not be translated." }, "showMore": { - "message": "Show more" + "message": "Εμφάνιση περισσότερων" }, "showLess": { - "message": "Show less" + "message": "Εμφάνιση λιγότερων" }, "next": { - "message": "Next" + "message": "Επόμενο" }, "moreBreadcrumbs": { "message": "More breadcrumbs", @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 7fd3091ef75..2058d68c55b 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organisation requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "View" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Item saved" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Do you really want to send to the bin?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Auto-fill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organisation's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 88b95533ff1..6c1b1e01139 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organisation requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "View" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Edited item" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Are you sure you want to delete this item?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Auto-fill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organisation's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "PIN" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 2adf87d63f3..1284563a6e3 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Usar inicio de sesión único" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Bienvenido de nuevo" }, @@ -588,6 +591,9 @@ "view": { "message": "Ver" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Elemento editado" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "¿Seguro que quieres enviarlo a la papelera?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Desactivar autocompletado" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Mostrar sugerencias de autocompletado en campos de formulario" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Mostrar identidades como sugerencias" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Error de descifrado" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden no pudo descifrar el/los elemento(s) de la bóveda listados a continuación." }, @@ -4011,6 +4053,15 @@ "message": "El autorrellenado de la página está usando la configuración predeterminada.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Activar/desactivar navegación lateral" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 1500e20e3aa..3f163506214 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Tere tulemast tagasi" }, @@ -588,6 +591,9 @@ "view": { "message": "Vaata" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Kirje on muudetud" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Soovid tõesti selle kirje kustutada?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 81106464f69..f74233193ef 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Ongi etorri berriro ere" }, @@ -588,6 +591,9 @@ "view": { "message": "Erakutsi" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Elementua editatuta" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Ziur zaude elementu hau zakarrontzira bidali nahi duzula?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 6617ad085cc..6b52f1d4364 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "استفاده از ورود تک مرحله‌ای" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "خوش آمدید" }, @@ -588,6 +591,9 @@ "view": { "message": "مشاهده" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "مورد ذخیره شد" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "واقعاً می‌خواهید این مورد را به سطل زباله ارسال کنید؟" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "پر کردن خودکار را خاموش کنید" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "نمایش پیشنهادهای پر کردن خودکار روی فیلدهای فرم" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "نمایش هویت‌ها به‌عنوان پیشنهاد" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "خطای رمزگشایی" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden نتوانست مورد(های) گاوصندوق فهرست شده زیر را رمزگشایی کند." }, @@ -4011,6 +4053,15 @@ "message": "پر کردن خودکار در بارگیری صفحه برای استفاده از تنظیمات پیش‌فرض تنظیم شده است.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "تغییر وضعیت ناوبری کناری" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 57a6ecfedd0..a0e6fce06bd 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Käytä kertakirjautumista" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Tervetuloa takaisin" }, @@ -588,6 +591,9 @@ "view": { "message": "Näytä" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Kohde tallennettiin" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Haluatko varmasti siirtää roskakoriin?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Poista automaattitäyttö käytöstä" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Näytä automaattitäytön ehdotukset lomakekentissä" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Näytä identiteetit ehdotuksina" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Salauksen purkuvirhe" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden ei pystynyt purkamaan alla lueteltuja holvin kohteita." }, @@ -4011,6 +4053,15 @@ "message": "Automaattitäyttö sivun avautuessa käyttää oletusasetusta.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Näytä/piilota sivuvalikko" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 88b94d9b9c1..3c249b0a350 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "Tanaw" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Ang item ay nai-save" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Gusto mo bang talagang ipadala sa basura?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 15d1cdecacf..e3a153fe34f 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Utiliser l'authentification unique" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Content de vous revoir" }, @@ -559,7 +562,7 @@ "description": "Verb" }, "unArchive": { - "message": "Unarchive" + "message": "Désarchiver" }, "itemsInArchive": { "message": "Éléments dans l'archive" @@ -571,10 +574,10 @@ "message": "Les éléments archivés apparaîtront ici et seront exclus des résultats de recherche généraux et des suggestions de remplissage automatique." }, "itemWasSentToArchive": { - "message": "Item was sent to archive" + "message": "L'élément a été envoyé à l'archive" }, "itemUnarchived": { - "message": "Item was unarchived" + "message": "L'élément a été désarchivé" }, "archiveItem": { "message": "Archiver l'élément" @@ -588,6 +591,9 @@ "view": { "message": "Afficher" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "Afficher l'Identifiant" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Élément enregistré" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Êtes-vous sûr de vouloir supprimer cet identifiant ?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Désactiver la saisie automatique" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Afficher les suggestions de saisie automatique dans les champs d'un formulaire" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Afficher les identités sous forme de suggestions" }, @@ -2241,7 +2280,7 @@ "message": "Déverrouiller avec un code PIN" }, "setYourPinTitle": { - "message": "Définir PIN" + "message": "Définir NIP" }, "setYourPinButton": { "message": "Définir PIN" @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Erreur de déchiffrement" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden n’a pas pu déchiffrer le(s) élément(s) du coffre listé(s) ci-dessous." }, @@ -4011,6 +4053,15 @@ "message": "La saisie automatique au chargement de la page est configuré selon les paramètres par défaut.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Basculer la navigation latérale" }, @@ -4142,7 +4193,7 @@ "message": "Vérification requise pour cette action. Définissez un code PIN pour continuer." }, "setPin": { - "message": "Définir le code PIN" + "message": "Définir le code NIP" }, "verifyWithBiometrics": { "message": "Vérifier par biométrie" @@ -4344,7 +4395,7 @@ "message": "Code incorrect" }, "incorrectPin": { - "message": "Code PIN incorrect" + "message": "Code NIP incorrect" }, "multifactorAuthenticationFailed": { "message": "Authentification multifacteur échouée" @@ -5304,7 +5355,7 @@ "message": "Vous pouvez personnaliser vos paramètres de déverrouillage et de délai d'attente pour accéder plus rapidement à votre coffre-fort." }, "unlockPinSet": { - "message": "Déverrouiller l'ensemble de codes PIN" + "message": "Déverrouiller l'ensemble de codes NIP" }, "unlockWithBiometricSet": { "message": "Déverrouiller avec l'ensemble biométrique" @@ -5580,30 +5631,30 @@ "message": "Bienvenue dans votre coffre !" }, "phishingPageTitleV2": { - "message": "Phishing attempt detected" + "message": "Tentative d'hameçonnage détectée" }, "phishingPageSummary": { - "message": "The site you are attempting to visit is a known malicious site and a security risk." + "message": "Le site que vous essayez de visiter est un site malveillant connu et un risque de sécurité." }, "phishingPageCloseTabV2": { - "message": "Close this tab" + "message": "Fermer cet onglet" }, "phishingPageContinueV2": { - "message": "Continue to this site (not recommended)" + "message": "Continuer vers ce site (non recommandé)" }, "phishingPageExplanation1": { - "message": "This site was found in ", + "message": "Ce site a été trouvé dans ", "description": "This is in multiple parts to allow for bold text in the middle of the sentence. A proper name follows this." }, "phishingPageExplanation2": { - "message": ", an open-source list of known phishing sites used for stealing personal and sensitive information.", + "message": ", une liste open-source de sites d'hameçonnage connus et utilisés pour voler des informations personnelles et sensibles.", "description": "This is in multiple parts to allow for bold text in the middle of the sentence. A proper name precedes this." }, "phishingPageLearnMore": { - "message": "Learn more about phishing detection" + "message": "En savoir plus sur la détection d'hameçonnage" }, "protectedBy": { - "message": "Protected by $PRODUCT$", + "message": "Protégé par $PRODUCT$", "placeholders": { "product": { "content": "$1", @@ -5716,10 +5767,16 @@ "message": "Confirmez le domaine de Key Connector" }, "atRiskLoginsSecured": { - "message": "Great job securing your at-risk logins!" + "message": "Excellent travail pour sécuriser vos identifiants à risque !" }, "settingDisabledByPolicy": { - "message": "This setting is disabled by your organization's policy.", + "message": "Ce paramètre est désactivé par la politique de sécurité de votre organisation.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Code postal" + }, + "cardNumberLabel": { + "message": "Numéro de carte" } } diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 137576cfb1f..6a13ce033b1 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Usar inicio de sesión único" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Benvido de novo" }, @@ -588,6 +591,9 @@ "view": { "message": "Ver" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Entrada gardada" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Seguro que queres envialo ó lixo?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Amosar suxestións de autoenchido en formularios" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Amosar identidades como suxestións" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Erro de descifrado" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden non puido descifrar os seguintes elementos." }, @@ -4011,6 +4053,15 @@ "message": "Axuste de autoenchido ó cargar a páxina por defecto.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Activar/desactivar navegación lateral" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 2164d197b0e..3834745f8e9 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "השתמש בכניסה יחידה" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "ברוך שובך" }, @@ -588,6 +591,9 @@ "view": { "message": "הצג" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "הצג כניסה" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "הפריט נשמר" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "האם אתה בטוח שברצונך למחוק פריט זה?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "השבת מילוי אוטומטי" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "הצג הצעות למילוי אוטומטי על שדות טופס" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "הצג זהויות כהצעות" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "שגיאת פענוח" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden לא יכל לפענח את פריט(י) הכספת המפורט(ים) להלן." }, @@ -4011,6 +4053,15 @@ "message": "מילוי אוטומטי בעת טעינת הוגדר להשתמש בהגדרת ברירת מחדל.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "החלף מצב ניווט צדדי" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index bc36073156b..3172e767974 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "सिंगल साइन-ऑन प्रयोग करें" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "आपका पुन: स्वागत है!" }, @@ -588,6 +591,9 @@ "view": { "message": "देखें" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "संपादित आइटम " }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "क्या आप वास्तव में थ्रैश में भेजना चाहते हैं?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index e678f506387..9e4c8d34004 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Jedinstvena prijava (SSO)" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Dobro došli natrag" }, @@ -588,6 +591,9 @@ "view": { "message": "Prikaz" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "Prikaži prijavu" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Stavka izmijenjena" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Želiš li zaista poslati u smeće?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Isključi auto-ispunu" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Prikaži prijedloge auto-ispune na poljima obrazaca" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Prikaži identitete kao prijedloge" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Pogreška pri dešifriranju" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden nije mogao dešifrirati sljedeće stavke trezora." }, @@ -4011,6 +4053,15 @@ "message": "Auto-ispuna kod učitavanja stranice koristi zadane postavke.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "U/Isključi bočnu navigaciju" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "Ova je postavka onemogućena pravilima tvoje organizacije.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index e2674595f4b..a84487e5a1d 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Egyszeri bejelentkezés használata" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Üdvözlet újra" }, @@ -588,6 +591,9 @@ "view": { "message": "Nézet" }, + "viewAll": { + "message": "Összes megtekintése" + }, "viewLogin": { "message": "Bejelentkezés megtekintése" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Az elem szerkesztésre került." }, + "savedWebsite": { + "message": "Mentett webhely" + }, + "savedWebsites": { + "message": "Mentett webhelyek ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Biztosan törlésre kerüljön ezt az elem?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Automat kitöltés bekapcsolása" }, + "confirmAutofill": { + "message": "Automatikus kitöltés megerősítése" + }, + "confirmAutofillDesc": { + "message": "Ez a webhely nem egyezik a mentett bejelentkezési adatokkal. Mielőtt kitöltenénk a bejelentkezés hitelesítő adatokat, győződjünk meg arról, hogy megbízható webhelyről van-e szó." + }, "showInlineMenuLabel": { "message": "Automatikus kitöltési javaslatok megjelenítése űrlapmezőknél" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "Hogyan védi meg a Bitwarden az adathalászattól az adatokat?" + }, + "currentWebsite": { + "message": "Jelenlegi webhely" + }, + "autofillAndAddWebsite": { + "message": "Automatikus kitöltés és ezen webhely hozzáadása" + }, + "autofillWithoutAdding": { + "message": "Automatikus kitöltés hozzáadás nélkül" + }, + "doNotAutofill": { + "message": "Ne legyen automatikus kitöltés" + }, "showInlineMenuIdentitiesLabel": { "message": "Az identitások megjelenítése javaslatként" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Visszafejtési hiba" }, + "errorGettingAutoFillData": { + "message": "Hiba történt az automatikus kitöltési adatok beolvasásakor." + }, "couldNotDecryptVaultItemsBelow": { "message": "A Bitwarden nem tudta visszafejteni az alább felsorolt ​​széf elemeket." }, @@ -4011,6 +4053,15 @@ "message": "Az automatikus kitöltés az oldal betöltésekor az alapértelmezett beállítás használatára lett beállítva.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Nem lehetséges az automatikus kitöltés." + }, + "cannotAutofillExactMatch": { + "message": "Az alapértelmezett egyezés beállítása 'Pontos egyezés'. Az aktuális webhely nem egyezik pontosan az ezzel az elemmel mentett bejelentkezési adatokkal." + }, + "okay": { + "message": "OK" + }, "toggleSideNavigation": { "message": "Oldalnavigáció váltás" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "Ezt a beállítást a szervezet házirendje letiltotta.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "Irányítószám" + }, + "cardNumberLabel": { + "message": "Kártya szám" } } diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index a5757e38caf..a94709a1be1 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Gunakan masuk tunggal" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Selamat datang kembali" }, @@ -588,6 +591,9 @@ "view": { "message": "Tampilan" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Item yang Diedit" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Apakah Anda yakin ingin menghapus item ini?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Matikan isi otomatis" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Tampilkan saran isi otomatis pada kolom formulir" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Tampilkan identitas sebagai saran" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Kesalahan dekripsi" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden tidak bisa mendekripsi butir brankas yang tercantum di bawah." }, @@ -4011,6 +4053,15 @@ "message": "Isi otomatis ketika halaman dimuat telah diatur untuk menggunakan pengaturan bawaan.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Saklar bilah isi navigasi" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 233ae413e5f..05cd6937246 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Usa il Single Sign-On" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Bentornato/a" }, @@ -588,6 +591,9 @@ "view": { "message": "Visualizza" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "Visualizza login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Elemento salvato" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Sei sicuro di voler eliminare questo elemento?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Disattiva il riempimento automatico" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Mostra suggerimenti di riempimento automatico nei campi del modulo" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Mostra identità come consigli" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Errore di decifrazione" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden non può decifrare gli elementi elencati di seguito." }, @@ -4011,6 +4053,15 @@ "message": "Riempimento automatico al caricamento della pagina impostato con l'impostazione predefinita.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Attiva/Disattiva navigazione laterale" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "Questa impostazione è disabilitata dalle restrizioni della tua organizzazione.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index 4ab3cdc9c1b..54405f69157 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "シングルサインオンを使用する" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "ようこそ" }, @@ -588,6 +591,9 @@ "view": { "message": "表示" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "編集されたアイテム" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "このアイテムを削除しますか?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "フォームフィールドに自動入力の候補を表示する" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "ID を候補として表示する" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "復号エラー" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden は以下の保管庫のアイテムを復号できませんでした。" }, @@ -4011,6 +4053,15 @@ "message": "ページ読み込み時の自動入力はデフォルトの設定を使うよう設定しました。", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "サイドナビゲーションの切り替え" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index 4ea5ab3390a..82f18caf79f 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "ხედი" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Item saved" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Do you really want to send to the trash?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index e3e6953b0df..f160e9a8cfa 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "View" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Item saved" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Do you really want to send to the trash?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 271db811810..f1c9e0ee8ab 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "ವೀಕ್ಷಣೆ" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "ಐಟಂ ಸಂಪಾದಿಸಲಾಗಿದೆ" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "ನೀವು ನಿಜವಾಗಿಯೂ ಅನುಪಯುಕ್ತಕ್ಕೆ ಕಳುಹಿಸಲು ಬಯಸುವಿರಾ?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index c45532076da..c5a414fc81f 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "통합인증(SSO) 사용하기" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "돌아온 것을 환영합니다." }, @@ -588,6 +591,9 @@ "view": { "message": "보기" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "로그인 보기" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "항목 편집함" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "정말로 휴지통으로 이동시킬까요?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "양식 필드에 자동 완성 제안 표시" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "신원을 제안으로 표시" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "페이지 로드 시 자동 완성이 기본 설정을 사용하도록 설정되었습니다.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "사이드 내비게이션 전환" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index e97a1cafcf9..df55af589bf 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Naudoti vieningo prisijungimo sistemą" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Sveiki sugrįžę" }, @@ -588,6 +591,9 @@ "view": { "message": "Peržiūrėti" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Redaguotas elementas" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Ar tikrai norite perkelti į šiukšlinę?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Automatinis pildymas įkeliant puslapį nustatytas naudoti numatytąjį nustatymą.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Perjungti šoninę naršymą" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index e1189450671..6ca99492c50 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Izmantot vienoto pieteikšanos" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Tava apvienība pieprasa vienoto pieteikšanos." + }, "welcomeBack": { "message": "Laipni lūdzam atpakaļ" }, @@ -588,6 +591,9 @@ "view": { "message": "Skatīt" }, + "viewAll": { + "message": "Apskatīt visu" + }, "viewLogin": { "message": "Apskatīt pieteikšanās vienumu" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Vienums labots" }, + "savedWebsite": { + "message": "Saglabāta tīmekļvietne" + }, + "savedWebsites": { + "message": "Saglabātas tīmekļvietnes ($COUNT$)", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Vai tiešām pārvietot uz atkritni?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Izslēgt automātisko aizpildi" }, + "confirmAutofill": { + "message": "Apstiprināt automātisko aizpildi" + }, + "confirmAutofillDesc": { + "message": "Šī vietne neatbilst saglabātā pieteikšanās vienumam. Pirms pieteikšanās datu aizpildīšanas jāpārliecinās, ka tā ir uzticama." + }, "showInlineMenuLabel": { "message": "Rādīt automātiskās aizpildes ieteikumuis veidlapu laukos" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "Kā Bitwarden aizsargā datus no pikšķerēšanas?" + }, + "currentWebsite": { + "message": "Pašreizējā tīmekļvietne" + }, + "autofillAndAddWebsite": { + "message": "Automātiski aizpildīt un pievienot šo tīmekļvietni" + }, + "autofillWithoutAdding": { + "message": "Automātiski aizpildīt bez pievienošanas" + }, + "doNotAutofill": { + "message": "Neaizpildīt automātiski" + }, "showInlineMenuIdentitiesLabel": { "message": "Attēlot identitātes kā ieteikumus" }, @@ -3217,7 +3256,7 @@ } }, "exportingOrganizationVaultFromPasswordManagerWithDataOwnershipDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported.", + "message": "Tiks izgūta tikai apvienības glabātava, kas ir saistīta ar $ORGANIZATION$.", "placeholders": { "organization": { "content": "$1", @@ -3226,7 +3265,7 @@ } }, "exportingOrganizationVaultFromAdminConsoleWithDataOwnershipDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. My items collections will not be included.", + "message": "Tiks izgūta tikai apvienības glabātava, kas ir saistīta ar $ORGANIZATION$. Mani vienumu krājumi netiks iekļauti.", "placeholders": { "organization": { "content": "$1", @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Atšifrēšanas kļūda" }, + "errorGettingAutoFillData": { + "message": "Kļūda automātiskās aizpildes datu iegūšanā" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden nevarēja atšifrēt zemāk uzskaitītos glabātavas vienumus." }, @@ -4011,6 +4053,15 @@ "message": "Automātiskā aizpilde lapas ielādes brīdī iestatīta izmantot noklusējuma iestatījumu.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Nevar automātiski aizpildīt" + }, + "cannotAutofillExactMatch": { + "message": "Noklusējuma atbilstības noteikšana ir iestatīta uz “Pilnīga atbilstība”. Pašreizējā tīmekļvietne pilnībā neabilst saglabātajai pieteikšanās informācijai šajā vienumā." + }, + "okay": { + "message": "Labi" + }, "toggleSideNavigation": { "message": "Pārslēgt sānu pārvietošanās joslu" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "Šis iestatījums ir atspējots apvienības pamatnostādnēs.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Pasta indekss" + }, + "cardNumberLabel": { + "message": "Kartes numurs" } } diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index fcf73a37e45..75eeb54c176 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "കാണുക" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "തിരുത്തപ്പെട്ട ഇനം" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "ഈ ഇനം ഇല്ലാതാക്കാൻ നിങ്ങൾ ആഗ്രഹിക്കുന്നുണ്ടോ?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 93f78303a5c..333dda2a2f8 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "View" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Item saved" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Do you really want to send to the trash?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index e3e6953b0df..f160e9a8cfa 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "View" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Item saved" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Do you really want to send to the trash?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 66d1ce615e1..3d632a60d3c 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Bruk singulær pålogging" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Velkommen tilbake" }, @@ -588,6 +591,9 @@ "view": { "message": "Vis" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Redigerte elementet" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Er du sikker på at du vil slette dette elementet?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Skru av autoutfylling" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Vis autoutfyll-forslag i tekstbokser" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Vis identiteter som forslag" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Dekrypteringsfeil" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Skru av/på sidenavigering" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index e3e6953b0df..f160e9a8cfa 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "View" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Item saved" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Do you really want to send to the trash?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 73b8afa2966..d413149bd18 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Single sign-on gebruiken" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welkom terug" }, @@ -588,6 +591,9 @@ "view": { "message": "Weergeven" }, + "viewAll": { + "message": "Alles weergeven" + }, "viewLogin": { "message": "Login bekijken" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Item is bewerkt" }, + "savedWebsite": { + "message": "Opgeslagen website" + }, + "savedWebsites": { + "message": "Opgeslagen websites ( $COUNT$)", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Weet je zeker dat je dit naar de prullenbak wilt verplaatsen?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Automatisch invullen uitschakelen" }, + "confirmAutofill": { + "message": "Automatisch aanvullen bevestigen" + }, + "confirmAutofillDesc": { + "message": "Deze website komt past niet bij je opgeslagen inloggegevens. Verzeker jezelf ervan dat het een vertrouwde website is, voordat je je inloggegevens invult." + }, "showInlineMenuLabel": { "message": "Suggesties voor automatisch invullen op formuliervelden weergeven" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "Hoe beschermt Bitwarden je gegevens tegen phishing?" + }, + "currentWebsite": { + "message": "Huidige website" + }, + "autofillAndAddWebsite": { + "message": "Automatisch invullen en deze website toevoegen" + }, + "autofillWithoutAdding": { + "message": "Automatisch invullen zonder toevoegen" + }, + "doNotAutofill": { + "message": "Niet automatisch invullen" + }, "showInlineMenuIdentitiesLabel": { "message": "Identiteiten als suggesties weergeven" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Ontsleutelingsfout" }, + "errorGettingAutoFillData": { + "message": "Fout bij ophalen van gegevens voor automatisch vullen" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden kon de onderstaande kluisitem(s) niet ontsleutelen." }, @@ -4011,6 +4053,15 @@ "message": "Automatisch invullen bij het laden van een pagina ingesteld op de standaardinstelling.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Kan niet automatisch invullen" + }, + "cannotAutofillExactMatch": { + "message": "Standaard overeenkomst is ingesteld op 'Exacte Match'. De huidige website komt niet precies overeen met de opgeslagen inloggegevens voor dit item." + }, + "okay": { + "message": "OK" + }, "toggleSideNavigation": { "message": "Zijnavigatie schakelen" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "Deze instelling is uitgeschakeld door het beleid van uw organisatie.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "Postcode" + }, + "cardNumberLabel": { + "message": "Kaartnummer" } } diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index e3e6953b0df..f160e9a8cfa 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "View" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Item saved" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Do you really want to send to the trash?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index e3e6953b0df..f160e9a8cfa 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "View" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Item saved" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Do you really want to send to the trash?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 78fb5e832a6..6c9bea95451 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Użyj logowania jednokrotnego" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Witaj ponownie" }, @@ -588,6 +591,9 @@ "view": { "message": "Pokaż" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "Pokaż dane logowania" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Element został zapisany" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Czy na pewno chcesz usunąć?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Wyłącz autouzupełnianie" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Pokaż sugestie autouzupełniania na polach formularza" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Pokaż tożsamości w sugestiach" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Błąd odszyfrowywania" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden nie mógł odszyfrować poniższych elementów sejfu." }, @@ -4011,6 +4053,15 @@ "message": "Autouzupełnianie po załadowaniu strony zostało ustawione do domyślnych ustawień.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Przełącz nawigację boczną" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "Kod pocztowy" + }, + "cardNumberLabel": { + "message": "Numer karty" } } diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index e3a82f42ca7..1496455e85b 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Usar autenticação única" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Boas-vindas de volta" }, @@ -588,6 +591,9 @@ "view": { "message": "Ver" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "Ver credencial" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Item salvo" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Você tem certeza que deseja enviar este item para a lixeira?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Desativar o preenchimento automático" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Mostrar sugestões de preenchimento automático em campos de formulário" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Exibir identidades como sugestões" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Erro ao descriptografar" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "O Bitwarden não conseguiu descriptografar o(s) item(ns) do cofre listado abaixo." }, @@ -4011,6 +4053,15 @@ "message": "O preenchimento automático ao carregar a página está usando a configuração padrão.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Habilitar navegação lateral" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "Essa configuração está desativada pela política da sua organização.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "CEP / Código postal" + }, + "cardNumberLabel": { + "message": "Número do cartão" } } diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index db2eb776d7f..d3acb309860 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Utilizar início de sessão único" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "A sua organização exige o início de sessão único." + }, "welcomeBack": { "message": "Bem-vindo de volta" }, @@ -588,6 +591,9 @@ "view": { "message": "Ver" }, + "viewAll": { + "message": "Ver tudo" + }, "viewLogin": { "message": "Ver credencial" }, @@ -788,7 +794,7 @@ "message": "4 horas" }, "onLocked": { - "message": "No bloqueio do sistema" + "message": "Ao bloquear o sistema" }, "onRestart": { "message": "Ao reiniciar o navegador" @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Item guardado" }, + "savedWebsite": { + "message": "Site guardado" + }, + "savedWebsites": { + "message": "Sites guardados ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Tem a certeza de que pretende eliminar este item?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Desativar o preenchimento automático" }, + "confirmAutofill": { + "message": "Confirmar preenchimento automático" + }, + "confirmAutofillDesc": { + "message": "Este site não corresponde às suas credenciais guardadas. Antes de preencher as suas credenciais, certifique-se de que se trata de um site fiável." + }, "showInlineMenuLabel": { "message": "Mostrar sugestões de preenchimento automático nos campos do formulário" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "Como o Bitwarden protege os seus dados contra phishing?" + }, + "currentWebsite": { + "message": "Site atual" + }, + "autofillAndAddWebsite": { + "message": "Preencher automaticamente e adicionar este site" + }, + "autofillWithoutAdding": { + "message": "Preencher automaticamente sem adicionar" + }, + "doNotAutofill": { + "message": "Não preencher automaticamente" + }, "showInlineMenuIdentitiesLabel": { "message": "Apresentar as identidades como sugestões" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Erro de desencriptação" }, + "errorGettingAutoFillData": { + "message": "Erro ao obter dados de preenchimento automático" + }, "couldNotDecryptVaultItemsBelow": { "message": "O Bitwarden não conseguiu desencriptar o(s) item(ns) do cofre listado(s) abaixo." }, @@ -4011,6 +4053,15 @@ "message": "Preencher automaticamente ao carregar a página definido para utilizar a predefinição.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Não é possível preencher automaticamente" + }, + "cannotAutofillExactMatch": { + "message": "A correspondência padrão está definida como \"Correspondência exata\". O site atual não corresponde exatamente às credenciais guardadas para este item." + }, + "okay": { + "message": "OK" + }, "toggleSideNavigation": { "message": "Ativar/desativar navegação lateral" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "Esta configuração está desativada pela política da sua organização.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "Código postal" + }, + "cardNumberLabel": { + "message": "Número do cartão" } } diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 4b2913ce55b..0206f473448 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Autentificare unică" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Bine ați revenit" }, @@ -588,6 +591,9 @@ "view": { "message": "Afișare" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Articol salvat" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Sigur doriți să trimiteți în coșul de reciclare?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Completarea automată la încărcarea paginii este setată la valoarea implicită.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 8661d78552e..b69494d472e 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Использовать единый вход" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Ваша организация требует единого входа." + }, "welcomeBack": { "message": "С возвращением" }, @@ -588,6 +591,9 @@ "view": { "message": "Просмотр" }, + "viewAll": { + "message": "Посмотреть все" + }, "viewLogin": { "message": "Просмотр логина" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Элемент сохранен" }, + "savedWebsite": { + "message": "Сохраненный сайт" + }, + "savedWebsites": { + "message": "Сохраненные сайты ( $COUNT$)", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Вы действительно хотите отправить в корзину?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Отключить автозаполнение" }, + "confirmAutofill": { + "message": "Подтвердите автозаполнение" + }, + "confirmAutofillDesc": { + "message": "Этот сайт не соответствует вашим сохраненным логинам. Прежде чем заполнять логин, убедитесь, что это надежный сайт." + }, "showInlineMenuLabel": { "message": "Показывать предположения автозаполнения в полях формы" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "Как Bitwarden защищает ваши данные от фишинга?" + }, + "currentWebsite": { + "message": "Текущий сайт" + }, + "autofillAndAddWebsite": { + "message": "Заполнить и добавить этот сайт" + }, + "autofillWithoutAdding": { + "message": "Заполнить без добавления" + }, + "doNotAutofill": { + "message": "Не заполнять" + }, "showInlineMenuIdentitiesLabel": { "message": "Показывать Личную информацию как предложения" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Ошибка расшифровки" }, + "errorGettingAutoFillData": { + "message": "Ошибка получения данных автозаполнения" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden не удалось расшифровать элемент(ы) хранилища, перечисленные ниже." }, @@ -4011,6 +4053,15 @@ "message": "Автозаполнение при загрузке страницы использует настройку по умолчанию.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Не удалось заполнить" + }, + "cannotAutofillExactMatch": { + "message": "По умолчанию установлено значение 'Точное соответствие'. Текущий сайт не полностью соответствует сохраненным для этого элемента логинам." + }, + "okay": { + "message": "OK" + }, "toggleSideNavigation": { "message": "Переключить боковую навигацию" }, @@ -5526,10 +5577,10 @@ "message": "Изменить пароль, подверженный риску" }, "changeAtRiskPasswordAndAddWebsite": { - "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." + "message": "Этот логин подвержен риску и у него отсутствует веб-сайт. Добавьте веб-сайт и смените пароль для большей безопасности." }, "missingWebsite": { - "message": "Missing website" + "message": "Отсутствует сайт" }, "settingsVaultOptions": { "message": "Настройки хранилища" @@ -5709,17 +5760,23 @@ "message": "Далее" }, "moreBreadcrumbs": { - "message": "More breadcrumbs", + "message": "Дополнительная навигация", "description": "This is used in the context of a breadcrumb navigation, indicating that there are more items in the breadcrumb trail that are not currently displayed." }, "confirmKeyConnectorDomain": { "message": "Подтвердите домен соединителя ключей" }, "atRiskLoginsSecured": { - "message": "Great job securing your at-risk logins!" + "message": "Отличная работа по защите ваших логинов, подверженных риску!" }, "settingDisabledByPolicy": { "message": "Этот параметр отключен политикой вашей организации.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "Почтовый индекс" + }, + "cardNumberLabel": { + "message": "Номер карты" } } diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 649556ca64b..60ce2436254 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "දකින්න" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "සංස්කරණය කරන ලද අයිතමය" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "ඔබට ඇත්තටම කුණු කූඩයට යැවීමට අවශ්යද?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index fe86ad298c9..46ff6837c70 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -6,7 +6,7 @@ "message": "Logo Bitwarden" }, "extName": { - "message": "Bitwarden – Bezplatný správca hesiel", + "message": "Bitwarden – správca hesiel", "description": "Extension name, MUST be less than 40 characters (Safari restriction)" }, "extDesc": { @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Použiť jednotné prihlásenie" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Vaša organizácia vyžaduje jednotné prihlasovanie." + }, "welcomeBack": { "message": "Vitajte späť" }, @@ -53,7 +56,7 @@ "message": "Potvrdiť" }, "emailAddress": { - "message": "Emailová adresa" + "message": "E-mailová adresa" }, "masterPass": { "message": "Hlavné heslo" @@ -588,6 +591,9 @@ "view": { "message": "Zobraziť" }, + "viewAll": { + "message": "Zobraziť všetky" + }, "viewLogin": { "message": "Zobraziť prihlásenie" }, @@ -676,7 +682,7 @@ "message": "Nastavte metódu odomknutia, aby ste zmenili akciu pri vypršaní času trezoru." }, "unlockMethodNeeded": { - "message": "Nastavte metódu odomknutia v Nastaveniach" + "message": "Nastavte metódu odomknutia v nastaveniach" }, "sessionTimeoutHeader": { "message": "Časový limit relácie" @@ -877,7 +883,7 @@ } }, "autofillError": { - "message": "Na tejto stránke sa nedajú automaticky vyplniť prihlasovacie údaje. Namiesto toho skopírujte/vložte prihlasovacie údaje manuálne." + "message": "Nie je možné automaticky vyplniť vybranú položku na tejto stránke. Namiesto toho skopírujte a vložte prihlasovacie údaje." }, "totpCaptureError": { "message": "Nie je možné naskenovať QR kód z aktuálnej webovej stránky" @@ -967,10 +973,10 @@ "message": "Meno je povinné." }, "addedFolder": { - "message": "Pridaný priečinok" + "message": "Priečinok bol pridaný" }, "twoStepLoginConfirmation": { - "message": "Dvojstupňové prihlasovanie robí váš účet bezpečnejším vďaka vyžadovaniu bezpečnostného kódu z overovacej aplikácie vždy, keď sa prihlásite. Dvojstupňové prihlasovanie môžete povoliť vo webovom trezore bitwarden.com. Chcete navštíviť túto stránku teraz?" + "message": "Dvojstupňové prihlásenie zvyšuje bezpečnosť vášho účtu tým, že vyžaduje overenie prihlásenia pomocou iného zariadenia, napríklad bezpečnostného kľúča, overovacej aplikácie, SMS, telefonického hovoru alebo e-mailu. Dvojstupňové prihlásenie môžete nastaviť na bitwarden.com. Chcete stránku navštíviť teraz?" }, "twoStepLoginConfirmationContent": { "message": "Zabezpečte svoj účet nastavením dvojstupňového prihlasovania vo webovej aplikácii Bitwarden." @@ -979,22 +985,22 @@ "message": "Pokračovať vo webovej aplikácii?" }, "editedFolder": { - "message": "Priečinok upravený" + "message": "Priečinok bol upravený" }, "deleteFolderConfirmation": { "message": "Naozaj chcete odstrániť tento priečinok?" }, "deletedFolder": { - "message": "Odstránený priečinok" + "message": "Priečinok bol odstránený" }, "gettingStartedTutorial": { - "message": "Začiatočnícka príručka" + "message": "Úvodná príručka" }, "gettingStartedTutorialVideo": { "message": "Pozrite našu príručku pre začiatočníkov, v ktorej sa dozviete, ako získať maximum z nášho rozšírenia pre prehliadač." }, "syncingComplete": { - "message": "Synchronizácia kompletná" + "message": "Synchronizácia bola dokončená" }, "syncingFailed": { "message": "Synchronizácia zlyhala" @@ -1028,11 +1034,23 @@ "editedItem": { "message": "Položka upravená" }, + "savedWebsite": { + "message": "Uložená webstránka" + }, + "savedWebsites": { + "message": "Uložené webstránky ($COUNT$)", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Naozaj chcete odstrániť túto položku?" }, "deletedItem": { - "message": "Položka odstránená" + "message": "Položka bola presunutá do koša" }, "overwritePassword": { "message": "Prepísať heslo" @@ -1260,7 +1278,7 @@ "message": "Zobraziť možnosti kontextovej ponuky" }, "contextMenuItemDesc": { - "message": "Sekundárnym kliknutím získate prístup k vygenerovaniu hesiel a zodpovedajúcim prihláseniam pre webovú stránku. " + "message": "Kliknutím pravého tlačidla myši získate prístup k vygenerovaniu hesiel a zodpovedajúcim prihláseniam pre webovú stránku." }, "contextMenuItemDescAlt": { "message": "Sekundárnym kliknutím získate prístup k vygenerovaniu hesiel a zodpovedajúcim prihláseniam pre webovú stránku. Platí pre všetky prihlásené účty." @@ -1296,7 +1314,7 @@ "message": "Export trezoru" }, "fileFormat": { - "message": "Formát Súboru" + "message": "Formát súboru" }, "fileEncryptedExportWarningDesc": { "message": "Tento exportovaný súbor bude chránený heslom a na dešifrovanie bude potrebné heslo súboru." @@ -1374,7 +1392,7 @@ "message": "Zistiť viac" }, "authenticatorKeyTotp": { - "message": "Kľúč overovateľa (TOTP)" + "message": "Overovací kľúč (TOTP)" }, "verificationCodeTotp": { "message": "Overovací kód (TOTP)" @@ -1392,7 +1410,7 @@ "message": "Naozaj chcete odstrániť túto prílohu?" }, "deletedAttachment": { - "message": "Odstránená príloha" + "message": "Príloha bola odstránená" }, "newAttachment": { "message": "Pridať novú prílohu" @@ -1401,7 +1419,7 @@ "message": "Žiadne prílohy." }, "attachmentSaved": { - "message": "Príloha bola uložená." + "message": "Príloha bola uložená" }, "file": { "message": "Súbor" @@ -1437,13 +1455,13 @@ "message": "Momentálne nie ste prémiovým členom." }, "premiumSignUpAndGet": { - "message": "Zaregistrujte sa pre prémiové členstvo a získajte:" + "message": "Zaregistrujte sa na prémiové členstvo a získajte:" }, "ppremiumSignUpStorage": { "message": "1 GB šifrovaného úložiska na prílohy." }, "premiumSignUpEmergency": { - "message": "Núdzový prístup" + "message": "Núdzový prístup." }, "premiumSignUpTwoStepOptions": { "message": "Proprietárne možnosti dvojstupňového prihlásenia ako napríklad YubiKey a Duo." @@ -1473,7 +1491,7 @@ "message": "Ďakujeme, že podporujete Bitwarden." }, "premiumFeatures": { - "message": "Povýšte na premium a získajte:" + "message": "Inovujte na prémium a získajte:" }, "premiumPrice": { "message": "Všetko len za %price% /rok!", @@ -1494,22 +1512,22 @@ } }, "refreshComplete": { - "message": "Obnova kompletná" + "message": "Obnova bola dokončená" }, "enableAutoTotpCopy": { "message": "Automaticky kopírovať TOTP" }, "disableAutoTotpCopyDesc": { - "message": "Ak je kľúč overovateľa spojený s vašim prihlásením, TOTP verifikačný kód bude automaticky skopírovaný do schránky vždy, keď použijete automatické vypĺňanie." + "message": "Ak je overovací kľúč spojený s vašim prihlásením, overovací kód TOTP bude automaticky skopírovaný do schránky vždy, keď použijete automatické vypĺňanie." }, "enableAutoBiometricsPrompt": { "message": "Pri spustení požiadať o biometriu" }, "premiumRequired": { - "message": "Vyžaduje prémiový účet" + "message": "Vyžaduje sa prémiový účet" }, "premiumRequiredDesc": { - "message": "Pre použitie tejto funkcie je potrebné prémiové členstvo." + "message": "Na použitie tejto funkcie je potrebné prémiové členstvo." }, "authenticationTimeout": { "message": "Časový limit overenia" @@ -1576,7 +1594,7 @@ "message": "Vyberte metódu dvojstupňového prihlásenia" }, "recoveryCodeTitle": { - "message": "Záchranný kód" + "message": "Kód na obnovenie" }, "authenticatorAppTitle": { "message": "Overovacia aplikácia" @@ -1596,14 +1614,14 @@ "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "duoOrganizationDesc": { - "message": "Overiť s Duo Security vašej organizácie použitím Duo Mobile aplikácie, SMS, telefonátu alebo U2F bezpečnostným kľúčom.", + "message": "Overenie pomocou Duo Security pre vašu organizáciu pomocou aplikácie Duo Mobile, SMS, telefonického hovoru alebo bezpečnostného kľúča U2F.", "description": "'Duo Security' and 'Duo Mobile' are product names and should not be translated." }, "webAuthnTitle": { "message": "FIDO2 WebAuthn" }, "webAuthnDesc": { - "message": "Použiť akýkoľvek WebAuthn bezpečnostný kľúč pre prístup k vášmu účtu." + "message": "Použiť akýkoľvek kompatibilný bezpečnostný kľúč WebAuthn na prístup k svojmu účtu." }, "emailTitle": { "message": "Email" @@ -1643,13 +1661,13 @@ "message": "URL servera identít" }, "notificationsUrl": { - "message": "URL adresa servera pre oznámenia" + "message": "URL servera pre upozornenia" }, "iconsUrl": { - "message": "URL servera ikôn" + "message": "URL servera ikon" }, "environmentSaved": { - "message": "URL prostredia boli uložené." + "message": "URL adresy prostredia boli uložené" }, "showAutoFillMenuOnFormFields": { "message": "Zobraziť ponuku automatického vypĺňania na poliach formulára", @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Vypnúť automatické vypĺňanie" }, + "confirmAutofill": { + "message": "Potvrdenie automatického vypĺňania" + }, + "confirmAutofillDesc": { + "message": "Táto stránka nezodpovedá vašim uloženým prihlasovacím údajom. Pred vyplnením prihlasovacích údajov sa uistite, že ide o dôveryhodnú stránku." + }, "showInlineMenuLabel": { "message": "Zobraziť návrhy automatického vypĺňania v poliach formulára" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "Ako Bitwarden chráni vaše údaje pred phishingom?" + }, + "currentWebsite": { + "message": "Aktuálna webstránka" + }, + "autofillAndAddWebsite": { + "message": "Automaticky vyplniť a pridať túto webstránku" + }, + "autofillWithoutAdding": { + "message": "Automaticky vyplniť bez pridania" + }, + "doNotAutofill": { + "message": "Nevyplniť automaticky" + }, "showInlineMenuIdentitiesLabel": { "message": "Zobrazovať identity ako návrhy" }, @@ -1706,32 +1745,32 @@ "description": "Overlay appearance select option for showing the field on focus of the input element" }, "autofillOverlayVisibilityOnButtonClick": { - "message": "Keď je vybratá ikona automatického vypĺňania", + "message": "Keď je vybraná ikona automatického vypĺňania", "description": "Overlay appearance select option for showing the field on click of the overlay icon" }, "enableAutoFillOnPageLoadSectionTitle": { "message": "Povoliť automatické vypĺňanie pri načítaní stránky" }, "enableAutoFillOnPageLoad": { - "message": "Povoliť automatické vypĺňanie pri načítaní stránky" + "message": "Automaticky vyplniť pri načítaní stránky" }, "enableAutoFillOnPageLoadDesc": { - "message": "Ak je detekovaný prihlasovací formulár, automaticky vykonať vypĺňanie pri načítaní stránky." + "message": "Ak sa zistí prihlasovací formulár, pri načítaní webovej stránky sa automaticky vyplní." }, "experimentalFeature": { - "message": "Skompromitované alebo nedôveryhodné stránky môžu pri svojom načítaní zneužiť automatické dopĺňanie." + "message": "Kompromitované alebo nedôveryhodné webové lokality môžu zneužiť automatické vypĺňanie pri načítaní stránky." }, "learnMoreAboutAutofillOnPageLoadLinkText": { "message": "Viac informácií o rizikách" }, "learnMoreAboutAutofill": { - "message": "Dozvedieť sa viac o automatickom dopĺňaní" + "message": "Viac informácií o automatickom vypĺňaní" }, "defaultAutoFillOnPageLoad": { "message": "Predvolené nastavenie automatického vypĺňania pre prihlasovacie položky" }, "defaultAutoFillOnPageLoadDesc": { - "message": "Pri úprave položky prihlásenia môžete individuálne zapnúť alebo vypnúť automatické vypĺňanie pri načítaní stránky pre danú položku." + "message": "Automatické vypĺňanie pri načítaní stránky môžete vypnúť, pre jednotlivé položky prihlásenia, pri úprave položky." }, "autoFillOnPageLoadUseDefault": { "message": "Pôvodné nastavenia" @@ -1808,7 +1847,7 @@ "message": "Zobraziť ikony webových stránok a načítať adresy URL na zmenu hesla" }, "cardholderName": { - "message": "Meno vlastníka karty" + "message": "Meno držiteľa karty" }, "number": { "message": "Číslo" @@ -1817,10 +1856,10 @@ "message": "Značka" }, "expirationMonth": { - "message": "Mesiac expirácie" + "message": "Mesiac exspirácie" }, "expirationYear": { - "message": "Rok expirácie" + "message": "Rok exspirácie" }, "expiration": { "message": "Expirácia" @@ -1892,7 +1931,7 @@ "message": "Krstné meno" }, "middleName": { - "message": "Druhé meno" + "message": "Stredné meno" }, "lastName": { "message": "Priezvisko" @@ -1907,13 +1946,13 @@ "message": "Spoločnosť" }, "ssn": { - "message": "Číslo poistenca sociálnej poisťovne" + "message": "Číslo sociálneho poistenia" }, "passportNumber": { "message": "Číslo pasu" }, "licenseNumber": { - "message": "Číslo vodičského preukazu" + "message": "Licenčné číslo" }, "email": { "message": "Email" @@ -2153,7 +2192,7 @@ "message": "Voľby prepínača" }, "toggleCurrentUris": { - "message": "Prepnúť zobrazovanie aktuálnej URI", + "message": "Prepnúť zobrazenie aktuálnej URI", "description": "Toggle the display of the URIs of the currently open tabs in the browser." }, "currentUri": { @@ -2344,13 +2383,13 @@ "message": "Naozaj chcete narvalo odstrániť túto položku?" }, "permanentlyDeletedItem": { - "message": "Natrvalo odstrániť položku" + "message": "Položka bola natrvalo odstránená" }, "restoreItem": { "message": "Obnoviť položku" }, "restoredItem": { - "message": "Obnovená položka" + "message": "Položka bola obnovená" }, "alreadyHaveAccount": { "message": "Už máte účet?" @@ -2362,13 +2401,13 @@ "message": "Potvrdenie akcie pri vypršaní časového limitu" }, "autoFillAndSave": { - "message": "Auto-vyplniť a Uložiť" + "message": "Automaticky vyplniť a uložiť" }, "fillAndSave": { "message": "Vyplniť a uložiť" }, "autoFillSuccessAndSavedUri": { - "message": "Automatické vypĺnenie a uloženie úspešné" + "message": "Položka bola automaticky vyplnená a URI uložený" }, "autoFillSuccess": { "message": "Automaticky vyplnené" @@ -2380,7 +2419,7 @@ "message": "Prajete si napriek tomu vyplniť prihlasovacie údaje?" }, "autofillIframeWarning": { - "message": "Formulár je hosťovaný inou doménou ako má URI uložených prihlasovacích údajov. Zvoľte OK ak chcete aj tak automaticky vyplniť údaje, alebo Zrušiť pre zastavenie." + "message": "Formulár je umiestnený na inej doméne ako URI vašich prihlasovacích údajov. Vyberte OK, ak chcete aj tak použiť automatické vyplnenie, alebo Zrušiť, ak chcete automatické vyplnenie zastaviť." }, "autofillIframeWarningTip": { "message": "Ak chcete tomuto upozorneniu v budúcnosti zabrániť, uložte URI, $HOSTNAME$, do položky prihlásenia Bitwardenu pre túto stránku.", @@ -2503,10 +2542,10 @@ "message": "Spustiť desktopovú aplikáciu Bitwarden Desktop" }, "startDesktopDesc": { - "message": "Aplikácia Bitwarden Desktop musí byť pred použitím odomknutia pomocou biometrických údajov spustená." + "message": "Aplikácia Bitwarden Desktop musí byť spustená pred použitím odomknutia pomocou biometrických údajov." }, "errorEnableBiometricTitle": { - "message": "Nie je môžné povoliť biometrické údaje" + "message": "Nie je možné povoliť biometrické údaje" }, "errorEnableBiometricDesc": { "message": "Akcia bola zrušená desktopovou aplikáciou" @@ -2557,7 +2596,7 @@ "message": "Biometria zlyhala" }, "biometricsFailedDesc": { - "message": "Biometria nebola vykonaná. Zvážte použitie hlavného hesla, alebo sa odhláste. Ak tento problém pretrváva, kontaktujte podporu Bitwarden." + "message": "Biometria nebola dokončená. Zvážte použitie hlavného hesla, alebo sa odhláste. Ak tento problém pretrváva, kontaktujte podporu Bitwarden." }, "nativeMessaginPermissionErrorTitle": { "message": "Povolenie nebolo udelené" @@ -2833,10 +2872,10 @@ "message": "Odstrániť" }, "removedPassword": { - "message": "Heslo odstránené" + "message": "Heslo bolo odstránené" }, "deletedSend": { - "message": "Odstrániť Send", + "message": "Send bol odstránený", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { @@ -2895,7 +2934,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createSend": { - "message": "Vytvoriť nový Send", + "message": "Nový Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "newPassword": { @@ -2910,7 +2949,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSend": { - "message": "Send vytvorený", + "message": "Send bol vytvorený", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createdSendSuccessfully": { @@ -2950,7 +2989,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editedSend": { - "message": "Send upravený", + "message": "Send bol upravený", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendFilePopoutDialogText": { @@ -3016,7 +3055,7 @@ "message": "Hlavné heslo bolo úspešne nastavené" }, "updatedMasterPassword": { - "message": "Hlavné heslo aktualizované" + "message": "Hlavné heslo bolo aktualizované" }, "updateMasterPassword": { "message": "Aktualizovať hlavné heslo" @@ -3074,7 +3113,7 @@ "message": "Na možnosti pri vypršaní časového limitu boli uplatnené požiadavky pravidiel spoločnosti" }, "vaultTimeoutPolicyInEffect": { - "message": "Zásady vašej organizácie ovplyvňujú časový limit trezoru. Maximálny povolený časový limit trezoru je $HOURS$ h a $MINUTES$ m", + "message": "Zásady vašej organizácie ovplyvňujú časový limit trezoru. Maximálny povolený časový limit trezoru je $HOURS$ h a $MINUTES$ m.", "placeholders": { "hours": { "content": "$1", @@ -3142,10 +3181,10 @@ "message": "Časový limit vášho trezora prekračuje obmedzenia nastavené vašou organizáciou." }, "vaultExportDisabled": { - "message": "Export trezoru je zakázaný" + "message": "Export trezoru nie je dostupný" }, "personalVaultExportPolicyInEffect": { - "message": "Jedna alebo viacero zásad organizácie vám bráni exportovať váš osobný trezor." + "message": "Jedno alebo viacero pravidiel organizácie vám bráni exportovať váš osobný trezor." }, "copyCustomFieldNameInvalidElement": { "message": "Nie je možné identifikovať platný prvok formulára. Skúste namiesto toho preskúmať HTML." @@ -3169,7 +3208,7 @@ "message": "Odstrániť hlavné heslo" }, "removedMasterPassword": { - "message": "Hlavné heslo bolo odstránené." + "message": "Hlavné heslo bolo odstránené" }, "leaveOrganizationConfirmation": { "message": "Naozaj chcete opustiť túto organizáciu?" @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Chyba dešifrovania" }, + "errorGettingAutoFillData": { + "message": "Chyba pri získavaní údajov automatického vypĺňania" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden nedokázal dešifrovať nižšie uvedené položky trezoru." }, @@ -3331,7 +3373,7 @@ "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { - "message": "$SERVICENAME$ chyba: $ERRORMESSAGE$", + "message": "Chyba $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", "placeholders": { "servicename": { @@ -3473,10 +3515,10 @@ "message": "Vyžaduje sa Premiové predplatné" }, "organizationIsDisabled": { - "message": "Organizácia je vypnutá." + "message": "Organizácia je pozastavená." }, "disabledOrganizationFilterError": { - "message": "K položkám vo vypnutej organizácii nie je možné pristupovať. Požiadajte o pomoc vlastníka organizácie." + "message": "K položkám v pozastavenej organizácii nie je možné pristupovať. Požiadajte o pomoc vlastníka organizácie." }, "loggingInTo": { "message": "Prihlásenie do $DOMAIN$", @@ -3506,7 +3548,7 @@ } }, "lastSeenOn": { - "message": "naposledy videné $DATE$", + "message": "naposledy videný: $DATE$", "placeholders": { "date": { "content": "$1", @@ -3524,10 +3566,10 @@ "message": "Zapamätať si e-mail" }, "loginWithDevice": { - "message": "Prihlásiť pomocou zariadenia" + "message": "Prihlásiť sa pomocou zariadenia" }, "fingerprintPhraseHeader": { - "message": "Fráza odtlačku prsta" + "message": "Jedinečný identifikátor" }, "fingerprintMatchInfo": { "message": "Uistite sa, že je váš trezor odomknutý a fráza odtlačku prsta sa zhoduje s frázou na druhom zariadení." @@ -3591,16 +3633,16 @@ "message": "Hlavné heslo uložené" }, "exposedMasterPassword": { - "message": "Odhalené hlavné heslo" + "message": "Uniknuté hlavné heslo" }, "exposedMasterPasswordDesc": { - "message": "Nájdené heslo v uniknuných údajoch. Na ochranu svojho účtu používajte jedinečné heslo. Naozaj chcete používať odhalené heslo?" + "message": "Heslo bolo nájdené v uniknutých údajoch. Na ochranu svojho účtu používajte jedinečné heslo. Naozaj chcete používať odhalené heslo?" }, "weakAndExposedMasterPassword": { - "message": "Slabé a odhalené hlavné heslo" + "message": "Slabé a uniknuté hlavné heslo" }, "weakAndBreachedMasterPasswordDesc": { - "message": "Nájdené slabé heslo v uniknuných údajoch. Na ochranu svojho účtu používajte silné a jedinečné heslo. Naozaj chcete používať toto heslo?" + "message": "Nájdené slabé heslo v uniknutých údajoch. Na ochranu svojho účtu používajte silné a jedinečné heslo. Naozaj chcete používať toto heslo?" }, "checkForBreaches": { "message": "Skontrolovať známe úniky údajov pre toto heslo" @@ -3612,7 +3654,7 @@ "message": "Vaše hlavné heslo sa nebude dať obnoviť, ak ho zabudnete!" }, "characterMinimum": { - "message": "Minimálny počet znakov $LENGTH$", + "message": "Minimálny počet znakov: $LENGTH$", "placeholders": { "length": { "content": "$1", @@ -3633,7 +3675,7 @@ } }, "autofillSelectInfoWithoutCommand": { - "message": "Vyberte položku z ponuky alebo preskúmajte ďalšie možnosti nastavenia." + "message": "Vyberte položku z ponuky, alebo preskúmajte ďalšie možnosti nastavenia." }, "gotIt": { "message": "Chápem" @@ -4008,9 +4050,18 @@ "message": "Alias doména" }, "autofillOnPageLoadSetToDefault": { - "message": "Automatické vypĺňanie pri načítaní stránky nastavené na pôvodnú predvoľbu.", + "message": "Automatické vypĺňanie pri načítaní stránky nastavené na pôvodné nastavenie.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Automatické vypĺňanie nie je možné" + }, + "cannotAutofillExactMatch": { + "message": "Predvolená zhoda je nastavená na \"Presná zhoda\". Aktuálna webová stránka sa presne nezhoduje s uloženými prihlasovacími údajmi pre túto položku." + }, + "okay": { + "message": "OK" + }, "toggleSideNavigation": { "message": "Prepnúť bočnú navigáciu" }, @@ -4070,7 +4121,7 @@ "description": "Button text to display in overlay when there are no matching items" }, "addNewVaultItem": { - "message": "Pridať novú položku trezoru", + "message": "Pridať novú položku trezora", "description": "Screen reader text (aria-label) for new item button in overlay" }, "newLogin": { @@ -4275,7 +4326,7 @@ "message": "Prihlásený!" }, "passkeyNotCopied": { - "message": "Prístupový kód sa neskopíruje" + "message": "Prístupový kľúč sa neskopíruje" }, "passkeyNotCopiedAlert": { "message": "Prístupový kľúč sa do klonovanej položky neskopíruje. Chcete pokračovať v klonovaní tejto položky?" @@ -4305,7 +4356,7 @@ "message": "Uložiť prístupový kľúč" }, "savePasskeyNewLogin": { - "message": "Uložiť prístupový kľúč ako nové prihlasovacie údaje" + "message": "Uložiť prístupový kľúč ako nové prihlásenie" }, "chooseCipherForPasskeySave": { "message": "Vyberte prihlasovacie údaje, do ktorých chcete uložiť prístupový kľúč" @@ -4435,10 +4486,10 @@ "message": "server" }, "hostedAt": { - "message": "hotované na" + "message": "hostované na" }, "useDeviceOrHardwareKey": { - "message": "Použiť vaše zariadenie alebo hardvérový kľúč" + "message": "Použiť svoje zariadenie alebo hardvérový kľúč" }, "justOnce": { "message": "Iba raz" @@ -4512,7 +4563,7 @@ "description": "Dialog message facilitating the ability to override a chrome browser's default autofill behavior" }, "overrideDefaultBrowserAutoFillSettings": { - "message": "Nastaviť Bitwarden ako predvolený správca hesiel", + "message": "Nastaviť Bitwarden ako predvoleného správcu hesiel", "description": "Label for the setting that allows overriding the default browser autofill settings" }, "privacyPermissionAdditionNotGrantedTitle": { @@ -4816,7 +4867,7 @@ "message": "Stiahnuť Bitwarden na všetky zariadenia" }, "getTheMobileApp": { - "message": "Získajte mobilnú aplikáciu" + "message": "Získať mobilnú aplikáciu" }, "getTheMobileAppDesc": { "message": "Majte prístup k heslám na cestách pomocou mobilnej aplikácie Bitwarden." @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "Politika organizácie vypla toto nastavenie.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "PSČ" + }, + "cardNumberLabel": { + "message": "Číslo karty" } } diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index cd7eda9a4fa..53f7a9d8f03 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "Pogled" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Element shranjen" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Ali ste prepričani, da želite to izbrisati?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 3421dc1fae1..169713c5047 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Употребити једнократну пријаву" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Добродошли назад" }, @@ -588,6 +591,9 @@ "view": { "message": "Приказ" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "Преглед пријаве" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Ставка уређена" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Сигурно послати ову ставку у отпад?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Угасити ауто-пуњење" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Прикажи предлоге за ауто-попуњавање у пољима обрасца" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Приказати идентитете као предлоге" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Грешка при декрипцији" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden није могао да декриптује ставке из трезора наведене испод." }, @@ -4011,6 +4053,15 @@ "message": "Ауто-попуњавање при учитавању странице је подешено да користи подразумевано подешавање.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Укључите бочну навигацију" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 07ed7a491f1..3b84369db47 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Använd Single Sign-On" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Välkommen tillbaka" }, @@ -588,6 +591,9 @@ "view": { "message": "Visa" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "Visa inloggning" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Objekt sparat" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Är du säker på att du vill radera detta objekt?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Stäng av autofyll" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Visa förslag för autofyll i formulärfält" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Visa identiteter som förslag" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Dekrypteringsfel" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden kunde inte dekryptera valvföremålet/valvföremålen som listas nedan." }, @@ -4011,6 +4053,15 @@ "message": "Aktivera automatisk ifyllnad vid sidhämtning sattes till att använda standardinställningen.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Växla sidonavigering" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "Denna inställning är inaktiverad enligt din organisations policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/ta/messages.json b/apps/browser/src/_locales/ta/messages.json index c4f0fffd143..a72a4910ef8 100644 --- a/apps/browser/src/_locales/ta/messages.json +++ b/apps/browser/src/_locales/ta/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "ஒற்றை உள்நுழைவைப் பயன்படுத்தவும்" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "மீண்டும் வருக" }, @@ -588,6 +591,9 @@ "view": { "message": "காண்" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "உள்நுழைவைக் காண்க" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "உருப்படி சேமிக்கப்பட்டது" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "நீங்கள் உண்மையிலேயே குப்பைக்கு அனுப்ப விரும்புகிறீர்களா?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "ஆட்டோஃபில்லை முடக்கு" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "படிவப் புலங்களில் ஆட்டோஃபில் பரிந்துரைகளைக் காட்டு" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "பரிந்துரைகளாக அடையாளங்களைக் காட்டு" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "குறியாக்கம் நீக்கப் பிழை" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden கீழே பட்டியலிடப்பட்ட பெட்டக பொருளை குறியாக்கம் நீக்க முடியவில்லை." }, @@ -4011,6 +4053,15 @@ "message": "பக்க ஏற்றத்தில் தானியங்கு நிரப்புதல் இயல்புநிலை அமைப்பைப் பயன்படுத்த அமைக்கப்பட்டுள்ளது.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "பக்க வழிசெலுத்தலை மாற்று" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index e3e6953b0df..f160e9a8cfa 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Use single sign-on" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Welcome back" }, @@ -588,6 +591,9 @@ "view": { "message": "View" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Item saved" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Do you really want to send to the trash?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 7487dea84bd..dd27da81316 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "ใช้การลงชื่อเพียงครั้งเดียว" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "ยินดีต้อนรับกลับมา" }, @@ -588,6 +591,9 @@ "view": { "message": "แสดง" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "View login" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "แก้ไขรายการแล้ว" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "คุณต้องการส่งไปยังถังขยะใช่หรือไม่?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Turn off autofill" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Show autofill suggestions on form fields" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Display identities as suggestions" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Decryption error" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden could not decrypt the vault item(s) listed below." }, @@ -4011,6 +4053,15 @@ "message": "Autofill on page load set to use default setting.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Toggle side navigation" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index e33addd805c..d982d0f3a1a 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Çoklu oturum açma kullan" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Tekrar hoş geldiniz" }, @@ -588,6 +591,9 @@ "view": { "message": "Görüntüle" }, + "viewAll": { + "message": "Tümünü göster" + }, "viewLogin": { "message": "Hesabı göster" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Hesap kaydedildi" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Çöp kutusuna göndermek istediğinizden emin misiniz?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Otomatik doldurmayı kapat" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Form alanlarında otomatik doldurma önerilerini göster" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Kimlikleri öneri olarak göster" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Şifre çözme sorunu" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden aşağıdaki kasa öğelerini deşifre edemedi." }, @@ -4011,6 +4053,15 @@ "message": "Sayfa yüklenince otomatik doldurma, varsayılan ayarı kullanacak şekilde ayarlandı.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Kenar menüsünü aç/kapat" }, @@ -5580,16 +5631,16 @@ "message": "Kasanıza hoş geldiniz!" }, "phishingPageTitleV2": { - "message": "Phishing attempt detected" + "message": "Dolandırıcılık girişimi tespit edildi" }, "phishingPageSummary": { - "message": "The site you are attempting to visit is a known malicious site and a security risk." + "message": "Girmeye çalıştığınız site kötü amaçlı ve güvenlik riski taşıyan bir sitedir." }, "phishingPageCloseTabV2": { "message": "Bu sekmeyi kapat" }, "phishingPageContinueV2": { - "message": "Continue to this site (not recommended)" + "message": "Siteye devam et (önerilmez)" }, "phishingPageExplanation1": { "message": "This site was found in ", @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "This setting is disabled by your organization's policy.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / posta kodu" + }, + "cardNumberLabel": { + "message": "Kart numarası" } } diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index dba38faaec6..aa118c0b93e 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Використати єдиний вхід" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "З поверненням" }, @@ -551,15 +554,15 @@ "message": "Скинути пошук" }, "archiveNoun": { - "message": "Archive", + "message": "Архів", "description": "Noun" }, "archiveVerb": { - "message": "Archive", + "message": "Архівувати", "description": "Verb" }, "unArchive": { - "message": "Unarchive" + "message": "Видобути" }, "itemsInArchive": { "message": "Записи в архіві" @@ -571,10 +574,10 @@ "message": "Архівовані записи з'являтимуться тут і будуть виключені з результатів звичайного пошуку та пропозицій автозаповнення." }, "itemWasSentToArchive": { - "message": "Item was sent to archive" + "message": "Запис архівовано" }, "itemUnarchived": { - "message": "Item was unarchived" + "message": "Запис розархівовано" }, "archiveItem": { "message": "Архівувати запис" @@ -588,6 +591,9 @@ "view": { "message": "Переглянути" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "Переглянути запис" }, @@ -734,7 +740,7 @@ "message": "Неправильний головний пароль" }, "invalidMasterPasswordConfirmEmailAndHost": { - "message": "Invalid master password. Confirm your email is correct and your account was created on $HOST$.", + "message": "Неправильний головний пароль. Перевірте правильність адреси електронної пошти та розміщення облікового запису на $HOST$.", "placeholders": { "host": { "content": "$1", @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Запис збережено" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Ви дійсно хочете перенести до смітника?" }, @@ -1549,13 +1567,13 @@ "message": "Зчитати ключ безпеки" }, "readingPasskeyLoading": { - "message": "Reading passkey..." + "message": "Читання ключа доступу..." }, "passkeyAuthenticationFailed": { - "message": "Passkey authentication failed" + "message": "Збій автентифікації ключа доступу" }, "useADifferentLogInMethod": { - "message": "Use a different log in method" + "message": "Використати інший спосіб входу" }, "awaitingSecurityKeyInteraction": { "message": "Очікується взаємодія з ключем безпеки..." @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Вимкніть автозаповнення" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Пропозиції автозаповнення на полях форм" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Показувати посвідчення як пропозиції" }, @@ -3217,7 +3256,7 @@ } }, "exportingOrganizationVaultFromPasswordManagerWithDataOwnershipDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported.", + "message": "Буде експортовано лише сховище організації, пов'язане з $ORGANIZATION$.", "placeholders": { "organization": { "content": "$1", @@ -3226,7 +3265,7 @@ } }, "exportingOrganizationVaultFromAdminConsoleWithDataOwnershipDesc": { - "message": "Only the organization vault associated with $ORGANIZATION$ will be exported. My items collections will not be included.", + "message": "Буде експортовано лише сховище організації, пов'язане з $ORGANIZATION$. Записи моїх збірок не будуть включені.", "placeholders": { "organization": { "content": "$1", @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Помилка розшифрування" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden не зміг розшифрувати вказані нижче елементи сховища." }, @@ -4011,6 +4053,15 @@ "message": "Автозаповнення на сторінці налаштовано з типовими параметрами.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Перемкнути бічну навігацію" }, @@ -5580,30 +5631,30 @@ "message": "Вітаємо у вашому сховищі!" }, "phishingPageTitleV2": { - "message": "Phishing attempt detected" + "message": "Виявлено спробу шахрайства" }, "phishingPageSummary": { - "message": "The site you are attempting to visit is a known malicious site and a security risk." + "message": "Ви намагаєтеся відвідати відомий зловмисний вебсайт, який має ризики безпеки." }, "phishingPageCloseTabV2": { - "message": "Close this tab" + "message": "Закрити цю вкладку" }, "phishingPageContinueV2": { - "message": "Continue to this site (not recommended)" + "message": "Перейти на цей сайт (не рекомендується)" }, "phishingPageExplanation1": { - "message": "This site was found in ", + "message": "Цей сайт знайдено в ", "description": "This is in multiple parts to allow for bold text in the middle of the sentence. A proper name follows this." }, "phishingPageExplanation2": { - "message": ", an open-source list of known phishing sites used for stealing personal and sensitive information.", + "message": "– відкритий список відомих шахрайських сайтів, що використовуються для викрадення особистої та конфіденційної інформації.", "description": "This is in multiple parts to allow for bold text in the middle of the sentence. A proper name precedes this." }, "phishingPageLearnMore": { - "message": "Learn more about phishing detection" + "message": "Докладніше про виявлення шахрайства" }, "protectedBy": { - "message": "Protected by $PRODUCT$", + "message": "Захищено $PRODUCT$", "placeholders": { "product": { "content": "$1", @@ -5716,10 +5767,16 @@ "message": "Підтвердити домен Key Connector" }, "atRiskLoginsSecured": { - "message": "Great job securing your at-risk logins!" + "message": "Ви чудово впоралися із захистом своїх ризикованих записів!" }, "settingDisabledByPolicy": { - "message": "This setting is disabled by your organization's policy.", + "message": "Цей параметр вимкнено політикою вашої організації.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 055e5155955..fff32a542cc 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "Dùng đăng nhập một lần" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "Chào mừng bạn trở lại" }, @@ -588,6 +591,9 @@ "view": { "message": "Xem" }, + "viewAll": { + "message": "View all" + }, "viewLogin": { "message": "Xem đăng nhập" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "Đã lưu mục" }, + "savedWebsite": { + "message": "Saved website" + }, + "savedWebsites": { + "message": "Saved websites ( $COUNT$ )", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "Bạn có chắc muốn cho nó vào thùng rác?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "Tắt tự động điền" }, + "confirmAutofill": { + "message": "Confirm autofill" + }, + "confirmAutofillDesc": { + "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + }, "showInlineMenuLabel": { "message": "Hiển thị các gợi ý tự động điền trên các trường biểu mẫu" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "How does Bitwarden protect your data from phishing?" + }, + "currentWebsite": { + "message": "Current website" + }, + "autofillAndAddWebsite": { + "message": "Autofill and add this website" + }, + "autofillWithoutAdding": { + "message": "Autofill without adding" + }, + "doNotAutofill": { + "message": "Do not autofill" + }, "showInlineMenuIdentitiesLabel": { "message": "Hiển thị danh tính dưới dạng gợi ý" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "Lỗi giải mã" }, + "errorGettingAutoFillData": { + "message": "Error getting autofill data" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden không thể giải mã các mục trong kho lưu trữ được liệt kê bên dưới." }, @@ -4011,6 +4053,15 @@ "message": "Tự động điền khi tải trang được đặt thành mặc định trong cài đặt.", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "Cannot autofill" + }, + "cannotAutofillExactMatch": { + "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + }, + "okay": { + "message": "Okay" + }, "toggleSideNavigation": { "message": "Ẩn/hiện thanh điều hướng bên" }, @@ -5580,30 +5631,30 @@ "message": "Chào mừng đến với kho lưu trữ của bạn!" }, "phishingPageTitleV2": { - "message": "Phishing attempt detected" + "message": "Đã phát hiện nỗ lực lừa đảo" }, "phishingPageSummary": { - "message": "The site you are attempting to visit is a known malicious site and a security risk." + "message": "Trang web bạn đang cố gắng truy cập là một trang độc hại đã được báo cáo và có nguy cơ bảo mật." }, "phishingPageCloseTabV2": { - "message": "Close this tab" + "message": "Đóng thẻ này" }, "phishingPageContinueV2": { - "message": "Continue to this site (not recommended)" + "message": "Tiếp tục truy cập trang web này (không khuyến khích)" }, "phishingPageExplanation1": { - "message": "This site was found in ", + "message": "Trang web này được tìm thấy trong ", "description": "This is in multiple parts to allow for bold text in the middle of the sentence. A proper name follows this." }, "phishingPageExplanation2": { - "message": ", an open-source list of known phishing sites used for stealing personal and sensitive information.", + "message": ", một danh sách nguồn mở các trang lừa đảo đã biết được sử dụng để đánh cắp thông tin cá nhân và nhạy cảm.", "description": "This is in multiple parts to allow for bold text in the middle of the sentence. A proper name precedes this." }, "phishingPageLearnMore": { - "message": "Learn more about phishing detection" + "message": "Tìm hiểu thêm về phát hiện lừa đảo" }, "protectedBy": { - "message": "Protected by $PRODUCT$", + "message": "Được bảo vệ bởi $PRODUCT$", "placeholders": { "product": { "content": "$1", @@ -5716,10 +5767,16 @@ "message": "Xác nhận tên miền Key Connector" }, "atRiskLoginsSecured": { - "message": "Great job securing your at-risk logins!" + "message": "Thật tuyệt khi bảo vệ các đăng nhập có nguy cơ của bạn!" }, "settingDisabledByPolicy": { - "message": "This setting is disabled by your organization's policy.", + "message": "Cài đặt này bị vô hiệu hóa bởi chính sách tổ chức của bạn.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / Postal code" + }, + "cardNumberLabel": { + "message": "Card number" } } diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index 1d1a6674e18..16f41e4e987 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "使用单点登录" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "欢迎回来" }, @@ -407,7 +410,7 @@ "message": "创建文件夹以整理您的密码库项目" }, "deleteFolderPermanently": { - "message": "您确定要永久删除这个文件夹吗?" + "message": "确定要永久删除此文件夹吗?" }, "deleteFolder": { "message": "删除文件夹" @@ -588,6 +591,9 @@ "view": { "message": "查看" }, + "viewAll": { + "message": "查看全部" + }, "viewLogin": { "message": "查看登录" }, @@ -682,7 +688,7 @@ "message": "会话超时" }, "vaultTimeoutHeader": { - "message": "密码库超时时间" + "message": "密码库超时" }, "otherOptions": { "message": "其他选项" @@ -743,10 +749,10 @@ } }, "vaultTimeout": { - "message": "密码库超时时间" + "message": "密码库超时" }, "vaultTimeout1": { - "message": "超时" + "message": "超时时间" }, "lockNow": { "message": "立即锁定" @@ -1028,6 +1034,18 @@ "editedItem": { "message": "项目已保存" }, + "savedWebsite": { + "message": "保存的网站" + }, + "savedWebsites": { + "message": "保存的网站 ($COUNT$)", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "您确定要将其发送到回收站吗?" }, @@ -1221,7 +1239,7 @@ "description": "Detailed error message shown when saving login details fails." }, "changePasswordWarning": { - "message": "更改密码后,您需要使用新密码登录。 在其他设备上的活动会话将在一小时内注销。" + "message": "更改密码后,您需要使用新密码登录。在其他设备上的活动会话将在一小时内注销。" }, "accountRecoveryUpdateMasterPasswordSubtitle": { "message": "更改您的主密码以完成账户恢复。" @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "停用自动填充" }, + "confirmAutofill": { + "message": "确认自动填充" + }, + "confirmAutofillDesc": { + "message": "此网站与您保存的登录信息不匹配。在填写您的登录凭据之前,请确保它是一个可信的网站。" + }, "showInlineMenuLabel": { "message": "在表单字段中显示自动填充建议" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "Bitwarden 如何保护您的数据免遭网络钓鱼?" + }, + "currentWebsite": { + "message": "当前网站" + }, + "autofillAndAddWebsite": { + "message": "自动填充并添加此网站" + }, + "autofillWithoutAdding": { + "message": "自动填充但不添加" + }, + "doNotAutofill": { + "message": "不自动填充" + }, "showInlineMenuIdentitiesLabel": { "message": "将身份显示为建议" }, @@ -2356,7 +2395,7 @@ "message": "已经有账户了吗?" }, "vaultTimeoutLogOutConfirmation": { - "message": "超时后注销账户将解除对密码库的所有访问权限,并需要进行在线身份验证。确定使用此设置吗?" + "message": "超时后注销账户将解除对密码库的所有访问权限,并需要进行在线身份验证。确定要使用此设置吗?" }, "vaultTimeoutLogOutConfirmationTitle": { "message": "超时动作确认" @@ -2858,7 +2897,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendPermanentConfirmation": { - "message": "您确定要永久删除这个 Send 吗?", + "message": "确定要永久删除这个 Send 吗?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "editSend": { @@ -3074,7 +3113,7 @@ "message": "企业策略要求已应用到您的超时选项中" }, "vaultTimeoutPolicyInEffect": { - "message": "您的组织策略已将您最大允许的密码库超时时间设置为 $HOURS$ 小时 $MINUTES$ 分钟。", + "message": "您的组织策略已将您最大允许的密码库超时设置为 $HOURS$ 小时 $MINUTES$ 分钟。", "placeholders": { "hours": { "content": "$1", @@ -3100,7 +3139,7 @@ } }, "vaultTimeoutPolicyMaximumError": { - "message": "超时时间超出了您组织设置的限制:最多 $HOURS$ 小时 $MINUTES$ 分钟", + "message": "超时超出了您组织设置的限制:最多 $HOURS$ 小时 $MINUTES$ 分钟", "placeholders": { "hours": { "content": "$1", @@ -3113,7 +3152,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "您的组织策略正在影响您的密码库超时时间。最大允许的密码库超时时间是 $HOURS$ 小时 $MINUTES$ 分钟。您的密码库超时动作被设置为 $ACTION$。", + "message": "您的组织策略正在影响您的密码库超时。最大允许的密码库超时为 $HOURS$ 小时 $MINUTES$ 分钟。您的密码库超时动作被设置为 $ACTION$。", "placeholders": { "hours": { "content": "$1", @@ -3130,7 +3169,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "您的组织策略已将您的密码库超时动作设置为 $ACTION$。", + "message": "您的组织策略已将您的密码库超时动作设置为「$ACTION$」。", "placeholders": { "action": { "content": "$1", @@ -3139,7 +3178,7 @@ } }, "vaultTimeoutTooLarge": { - "message": "您的密码库超时时间超出了组织设置的限制。" + "message": "您的密码库超时超出了您组织设置的限制。" }, "vaultExportDisabled": { "message": "密码库导出已禁用" @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "解密错误" }, + "errorGettingAutoFillData": { + "message": "获取自动填充数据时出错" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden 无法解密下列密码库项目。" }, @@ -4011,6 +4053,15 @@ "message": "页面加载时自动填充设置为使用默认设置。", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "无法自动填充" + }, + "cannotAutofillExactMatch": { + "message": "默认匹配被设置为「精确匹配」。当前网站与此项目保存的登录信息不完全匹配。" + }, + "okay": { + "message": "确定" + }, "toggleSideNavigation": { "message": "切换侧边导航" }, @@ -4837,7 +4888,7 @@ "message": "从 App Store 下载" }, "permanentlyDeleteAttachmentConfirmation": { - "message": "您确定要永久删除此附件吗?" + "message": "确定要永久删除此附件吗?" }, "premium": { "message": "高级会员" @@ -5238,7 +5289,7 @@ "message": "重试" }, "vaultCustomTimeoutMinimum": { - "message": "自定义超时时间最小为 1 分钟。" + "message": "自定义超时最少为 1 分钟。" }, "fileSavedToDevice": { "message": "文件已保存到设备。可以在设备下载中进行管理。" @@ -5716,10 +5767,16 @@ "message": "确认 Key Connector 域名" }, "atRiskLoginsSecured": { - "message": "Great job securing your at-risk logins!" + "message": "很好地保护了存在风险的登录!" }, "settingDisabledByPolicy": { "message": "此设置被您组织的策略禁用了。", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "ZIP / 邮政编码" + }, + "cardNumberLabel": { + "message": "卡号" } } diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index e2d9ff2068f..d3c0319e488 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -31,6 +31,9 @@ "useSingleSignOn": { "message": "使用單一登入" }, + "yourOrganizationRequiresSingleSignOn": { + "message": "Your organization requires single sign-on." + }, "welcomeBack": { "message": "歡迎回來" }, @@ -588,6 +591,9 @@ "view": { "message": "檢視" }, + "viewAll": { + "message": "檢視全部" + }, "viewLogin": { "message": "檢視登入" }, @@ -1028,6 +1034,18 @@ "editedItem": { "message": "項目已儲存" }, + "savedWebsite": { + "message": "已儲存的網站" + }, + "savedWebsites": { + "message": "已儲存的網站($COUNT$)", + "placeholders": { + "count": { + "content": "$1", + "example": "3" + } + } + }, "deleteItemConfirmation": { "message": "確定要刪除此項目嗎?" }, @@ -1676,9 +1694,30 @@ "turnOffAutofill": { "message": "停用自動填入" }, + "confirmAutofill": { + "message": "確認自動填入" + }, + "confirmAutofillDesc": { + "message": "此網站與您儲存的登入資料不相符。在填入登入憑證前,請確認這是受信任的網站。" + }, "showInlineMenuLabel": { "message": "在表單欄位上顯示自動填入選單" }, + "howDoesBitwardenProtectFromPhishing": { + "message": "Bitwarden 如何保護您的資料免於網路釣魚攻擊?" + }, + "currentWebsite": { + "message": "目網站" + }, + "autofillAndAddWebsite": { + "message": "自動填充並新增此網站" + }, + "autofillWithoutAdding": { + "message": "自動填入但不新增" + }, + "doNotAutofill": { + "message": "不要自動填入" + }, "showInlineMenuIdentitiesLabel": { "message": "顯示身分建議" }, @@ -3240,6 +3279,9 @@ "decryptionError": { "message": "解密發生錯誤" }, + "errorGettingAutoFillData": { + "message": "取得自動填入資料時發生錯誤" + }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden 無法解密您密碼庫中下面的項目。" }, @@ -4011,6 +4053,15 @@ "message": "將頁面載入時使用自動填入功能設定為預設。", "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, + "cannotAutofill": { + "message": "無法自動填入" + }, + "cannotAutofillExactMatch": { + "message": "預設比對方式為「完全相符」。目前的網站與此項目的已儲存登入資料不完全相符。" + }, + "okay": { + "message": "確定" + }, "toggleSideNavigation": { "message": "切換側邊欄" }, @@ -5721,5 +5772,11 @@ "settingDisabledByPolicy": { "message": "此設定已被你的組織原則停用。", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." + }, + "zipPostalCodeLabel": { + "message": "郵編 / 郵政代碼" + }, + "cardNumberLabel": { + "message": "支付卡號碼" } } diff --git a/apps/browser/store/locales/sk/copy.resx b/apps/browser/store/locales/sk/copy.resx index 2b7e903fe52..816f1bf7e8c 100644 --- a/apps/browser/store/locales/sk/copy.resx +++ b/apps/browser/store/locales/sk/copy.resx @@ -118,7 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Bitwarden – Bezplatný správca hesiel + Bitwarden – správca hesiel Bitwarden zabezpečí všetky vaše heslá, prístupové kľúče a citlivé informácie doma, v práci alebo na cestách. From 4908d738042043d43697e26e3488e0aefd946edd Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Fri, 31 Oct 2025 14:24:31 -0400 Subject: [PATCH 47/66] [PM-27500] update cipher.service to align with sdk types (#17147) --- libs/common/src/abstractions/api.service.ts | 7 +++++-- libs/common/src/vault/models/response/cipher.response.ts | 5 +++++ libs/common/src/vault/services/cipher.service.ts | 8 +++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/libs/common/src/abstractions/api.service.ts b/libs/common/src/abstractions/api.service.ts index 761038c2e46..f7ca1964b76 100644 --- a/libs/common/src/abstractions/api.service.ts +++ b/libs/common/src/abstractions/api.service.ts @@ -91,7 +91,7 @@ import { CipherShareRequest } from "../vault/models/request/cipher-share.request import { CipherRequest } from "../vault/models/request/cipher.request"; import { AttachmentUploadDataResponse } from "../vault/models/response/attachment-upload-data.response"; import { AttachmentResponse } from "../vault/models/response/attachment.response"; -import { CipherResponse } from "../vault/models/response/cipher.response"; +import { CipherMiniResponse, CipherResponse } from "../vault/models/response/cipher.response"; import { OptionalCipherResponse } from "../vault/models/response/optional-cipher.response"; /** @@ -215,7 +215,10 @@ export abstract class ApiService { id: string, request: CipherCollectionsRequest, ): Promise; - abstract putCipherCollectionsAdmin(id: string, request: CipherCollectionsRequest): Promise; + abstract putCipherCollectionsAdmin( + id: string, + request: CipherCollectionsRequest, + ): Promise; abstract postPurgeCiphers( request: SecretVerificationRequest, organizationId?: string, diff --git a/libs/common/src/vault/models/response/cipher.response.ts b/libs/common/src/vault/models/response/cipher.response.ts index bf01e0f08de..28979302eb4 100644 --- a/libs/common/src/vault/models/response/cipher.response.ts +++ b/libs/common/src/vault/models/response/cipher.response.ts @@ -14,6 +14,11 @@ import { SshKeyApi } from "../api/ssh-key.api"; import { AttachmentResponse } from "./attachment.response"; import { PasswordHistoryResponse } from "./password-history.response"; +export type CipherMiniResponse = Omit< + CipherResponse, + "edit" | "viewPassword" | "folderId" | "favorite" | "permissions" +>; + export class CipherResponse extends BaseResponse { id: string; organizationId: string; diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index efe7bc2b89b..1e7e5302d41 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -1117,7 +1117,13 @@ export class CipherService implements CipherServiceAbstraction { async saveCollectionsWithServerAdmin(cipher: Cipher): Promise { const request = new CipherCollectionsRequest(cipher.collectionIds); const response = await this.apiService.putCipherCollectionsAdmin(cipher.id, request); - const data = new CipherData(response); + // The response will be incomplete with several properties missing values + // We will assign those properties values so the SDK decryption can complete + const completedResponse = new CipherResponse(response); + completedResponse.edit = true; + completedResponse.viewPassword = true; + completedResponse.favorite = false; + const data = new CipherData(completedResponse); return new Cipher(data); } From 23cb3e092c778d5a21c005a0e46665e7e75c0088 Mon Sep 17 00:00:00 2001 From: Leslie Tilton <23057410+Banrion@users.noreply.github.com> Date: Fri, 31 Oct 2025 13:59:11 -0500 Subject: [PATCH 48/66] [PM-27694] Handle empty report response (#17162) * Consolidate loading state and handle null report from api response * Fix jumping of page when ciphers are still loading * Fix type errors * Fix loading state --- .../risk-insights/models/report-models.ts | 12 +- .../services/api/risk-insights-api.service.ts | 4 +- ...risk-insights-orchestrator.service.spec.ts | 35 +--- .../risk-insights-orchestrator.service.ts | 178 ++++++++---------- .../domain/risk-insights-report.service.ts | 6 +- .../view/risk-insights-data.service.ts | 28 ++- .../activity/all-activity.component.html | 2 +- .../activity/all-activity.component.ts | 3 + .../all-applications.component.html | 2 +- .../all-applications.component.ts | 6 +- .../critical-applications.component.html | 10 +- .../critical-applications.component.ts | 1 - .../risk-insights.component.html | 171 +++++++++-------- .../risk-insights.component.ts | 82 ++------ 14 files changed, 237 insertions(+), 303 deletions(-) diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-models.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-models.ts index cada0acb694..eecd8256c7f 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-models.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/models/report-models.ts @@ -98,6 +98,15 @@ export type ReportResult = CipherView & { scoreKey: number; }; +export const ReportStatus = Object.freeze({ + Initializing: 1, + Loading: 2, + Complete: 3, + Error: 4, +} as const); + +export type ReportStatus = (typeof ReportStatus)[keyof typeof ReportStatus]; + export interface RiskInsightsData { id: OrganizationReportId; creationDate: Date; @@ -108,10 +117,9 @@ export interface RiskInsightsData { } export interface ReportState { - loading: boolean; + status: ReportStatus; error: string | null; data: RiskInsightsData | null; - organizationId?: string; } // TODO Make Versioned models for structure changes diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.ts index 5b094e8dec0..e221d68ca7e 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/api/risk-insights-api.service.ts @@ -29,7 +29,9 @@ export class RiskInsightsApiService { true, ); return from(dbResponse).pipe( - map((response) => new GetRiskInsightsReportResponse(response)), + // As of this change, the server doesn't return a 404 if a report is not found + // Handle null response if server returns nothing + map((response) => (response ? new GetRiskInsightsReportResponse(response) : null)), catchError((error: unknown) => { if (error instanceof ErrorResponse && error.statusCode === 404) { return of(null); // Handle 404 by returning null or an appropriate default value diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.spec.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.spec.ts index 9894c6522a8..bc0d4fc7891 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.spec.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.spec.ts @@ -10,7 +10,7 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { LogService } from "@bitwarden/logging"; import { createNewSummaryData } from "../../helpers"; -import { RiskInsightsData, SaveRiskInsightsReportResponse } from "../../models"; +import { ReportStatus, RiskInsightsData, SaveRiskInsightsReportResponse } from "../../models"; import { RiskInsightsMetrics } from "../../models/domain/risk-insights-metrics"; import { mockMemberCipherDetailsResponse } from "../../models/mocks/member-cipher-details-response.mock"; import { @@ -105,34 +105,6 @@ describe("RiskInsightsOrchestratorService", () => { }); describe("fetchReport", () => { - it("should call with correct org and user IDs and emit ReportState", (done) => { - // Arrange - const privateOrganizationDetailsSubject = service["_organizationDetailsSubject"]; - const privateUserIdSubject = service["_userIdSubject"]; - - // Set up organization and user context - privateOrganizationDetailsSubject.next({ - organizationId: mockOrgId, - organizationName: mockOrgName, - }); - privateUserIdSubject.next(mockUserId); - - // Act - service.fetchReport(); - - // Assert - service.rawReportData$.subscribe((state) => { - if (!state.loading) { - expect(mockReportService.getRiskInsightsReport$).toHaveBeenCalledWith( - mockOrgId, - mockUserId, - ); - expect(state.data).toEqual(reportState); - done(); - } - }); - }); - it("should emit error ReportState when getRiskInsightsReport$ throws", (done) => { // Setup error passed via constructor for this test case mockReportService.getRiskInsightsReport$ = jest @@ -157,9 +129,8 @@ describe("RiskInsightsOrchestratorService", () => { organizationName: mockOrgName, }); _userIdSubject.next(mockUserId); - testService.fetchReport(); testService.rawReportData$.subscribe((state) => { - if (!state.loading) { + if (state.status != ReportStatus.Loading) { expect(state.error).toBe("Failed to fetch report"); expect(state.data).toBeNull(); done(); @@ -199,7 +170,7 @@ describe("RiskInsightsOrchestratorService", () => { // Assert service.rawReportData$.subscribe((state) => { - if (!state.loading && state.data) { + if (state.status != ReportStatus.Loading && state.data) { expect(mockMemberCipherDetailsApiService.getMemberCipherDetails).toHaveBeenCalledWith( mockOrgId, ); diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts index 472aa7f5f68..650726628c6 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-orchestrator.service.ts @@ -54,6 +54,7 @@ import { MemberDetails, OrganizationReportApplication, OrganizationReportSummary, + ReportStatus, ReportState, } from "../../models/report-models"; import { MemberCipherDetailsApiService } from "../api/member-cipher-details-api.service"; @@ -79,13 +80,16 @@ export class RiskInsightsOrchestratorService { } | null>(null); organizationDetails$ = this._organizationDetailsSubject.asObservable(); - // ------------------------- Raw data ------------------------- + // ------------------------- Cipher data ------------------------- private _ciphersSubject = new BehaviorSubject(null); private _ciphers$ = this._ciphersSubject.asObservable(); + private _hasCiphersSubject$ = new BehaviorSubject(null); + hasCiphers$ = this._hasCiphersSubject$.asObservable(); + // ------------------------- Report Variables ---------------- private _rawReportDataSubject = new BehaviorSubject({ - loading: true, + status: ReportStatus.Initializing, error: null, data: null, }); @@ -116,14 +120,10 @@ export class RiskInsightsOrchestratorService { // --------------------------- Critical Application data --------------------- criticalReportResults$: Observable = of(null); - // --------------------------- Vault Items Check --------------------- - hasVaultItems$: Observable = of(false); - // --------------------------- Trigger subjects --------------------- private _initializeOrganizationTriggerSubject = new Subject(); - private _fetchReportTriggerSubject = new Subject(); - private _markUnmarkUpdatesSubject = new Subject(); - private _markUnmarkUpdates$ = this._markUnmarkUpdatesSubject.asObservable(); + private _flagForUpdatesSubject = new Subject(); + private _flagForUpdates$ = this._flagForUpdatesSubject.asObservable(); private _reportStateSubscription: Subscription | null = null; private _migrationSubscription: Subscription | null = null; @@ -144,7 +144,6 @@ export class RiskInsightsOrchestratorService { this._setupCriticalApplicationContext(); this._setupCriticalApplicationReport(); this._setupEnrichedReportData(); - this._setupHasVaultItems(); this._setupInitializationPipeline(); this._setupMigrationAndCleanup(); this._setupReportState(); @@ -163,14 +162,6 @@ export class RiskInsightsOrchestratorService { this._destroy$.complete(); } - /** - * Fetches the latest report for the current organization and user - */ - fetchReport(): void { - this.logService.debug("[RiskInsightsOrchestratorService] Fetch report triggered"); - this._fetchReportTriggerSubject.next(); - } - /** * Generates a new report for the current organization and user */ @@ -187,7 +178,6 @@ export class RiskInsightsOrchestratorService { initializeForOrganization(organizationId: OrganizationId) { this.logService.debug("[RiskInsightsOrchestratorService] Initializing for org", organizationId); this._initializeOrganizationTriggerSubject.next(organizationId); - this.fetchReport(); } /** @@ -202,7 +192,7 @@ export class RiskInsightsOrchestratorService { ); return this.rawReportData$.pipe( take(1), - filter((data) => !data.loading && data.data != null), + filter((data) => data.status != ReportStatus.Loading && data.data != null), withLatestFrom( this.organizationDetails$.pipe(filter((org) => !!org && !!org.organizationId)), this._userId$.pipe(filter((userId) => !!userId)), @@ -311,9 +301,8 @@ export class RiskInsightsOrchestratorService { return forkJoin([updateApplicationsCall, updateSummaryCall]).pipe( map(() => updatedState), tap((finalState) => { - this._markUnmarkUpdatesSubject.next({ + this._flagForUpdatesSubject.next({ ...finalState, - organizationId: reportState.organizationId, }); }), catchError((error: unknown) => { @@ -331,7 +320,7 @@ export class RiskInsightsOrchestratorService { ); return this.rawReportData$.pipe( take(1), - filter((data) => !data.loading && data.data != null), + filter((data) => data.status != ReportStatus.Loading && data.data != null), withLatestFrom( this.organizationDetails$.pipe(filter((org) => !!org && !!org.organizationId)), this._userId$.pipe(filter((userId) => !!userId)), @@ -440,9 +429,8 @@ export class RiskInsightsOrchestratorService { return forkJoin([updateApplicationsCall, updateSummaryCall]).pipe( map(() => updatedState), tap((finalState) => { - this._markUnmarkUpdatesSubject.next({ + this._flagForUpdatesSubject.next({ ...finalState, - organizationId: reportState.organizationId, }); }), catchError((error: unknown) => { @@ -470,7 +458,7 @@ export class RiskInsightsOrchestratorService { return this.rawReportData$.pipe( take(1), - filter((data) => !data.loading && data.data != null), + filter((data) => data.status != ReportStatus.Loading && data.data != null), withLatestFrom( this.organizationDetails$.pipe(filter((org) => !!org && !!org.organizationId)), this._userId$.pipe(filter((userId) => !!userId)), @@ -545,9 +533,6 @@ export class RiskInsightsOrchestratorService { ) .pipe( map(() => updatedState), - tap((finalState) => { - this._markUnmarkUpdatesSubject.next(finalState); - }), catchError((error: unknown) => { this.logService.error( "[RiskInsightsOrchestratorService] Failed to save review status", @@ -565,16 +550,20 @@ export class RiskInsightsOrchestratorService { tap(() => this.logService.debug("[RiskInsightsOrchestratorService] Fetching report")), map((result): ReportState => { return { - loading: false, + status: ReportStatus.Complete, error: null, - data: result ?? null, - organizationId, + data: result, }; }), - catchError(() => - of({ loading: false, error: "Failed to fetch report", data: null, organizationId }), - ), - startWith({ loading: true, error: null, data: null, organizationId }), + catchError((error: unknown) => { + this.logService.error("[RiskInsightsOrchestratorService] Failed to fetch report", error); + return of({ + status: ReportStatus.Error, + error: "Failed to fetch report", + data: null, + organizationId, + }); + }), ); } @@ -650,7 +639,7 @@ export class RiskInsightsOrchestratorService { map((mappedResult): ReportState => { const { id, report, summary, applications, contentEncryptionKey } = mappedResult; return { - loading: false, + status: ReportStatus.Complete, error: null, data: { id, @@ -660,18 +649,20 @@ export class RiskInsightsOrchestratorService { creationDate: new Date(), contentEncryptionKey, }, - organizationId, }; }), catchError((): Observable => { return of({ - loading: false, + status: ReportStatus.Error, error: "Failed to generate or save report", data: null, - organizationId, }); }), - startWith({ loading: true, error: null, data: null, organizationId }), + startWith({ + status: ReportStatus.Loading, + error: null, + data: null, + }), ); } @@ -879,34 +870,6 @@ export class RiskInsightsOrchestratorService { } // Setup the pipeline to load critical applications when organization or user changes - /** - * Sets up an observable to check if the organization has any vault items (ciphers). - * This is used to determine which empty state to show in the UI. - */ - private _setupHasVaultItems() { - this.hasVaultItems$ = this.organizationDetails$.pipe( - switchMap((orgDetails) => { - if (!orgDetails?.organizationId) { - return of(false); - } - return from( - this.cipherService.getAllFromApiForOrganization(orgDetails.organizationId), - ).pipe( - map((ciphers) => ciphers.length > 0), - catchError((error: unknown) => { - this.logService.error( - "[RiskInsightsOrchestratorService] Error checking vault items", - error, - ); - return of(false); - }), - ); - }), - shareReplay({ bufferSize: 1, refCount: true }), - takeUntil(this._destroy$), - ); - } - private _setupCriticalApplicationContext() { this.organizationDetails$ .pipe( @@ -1003,6 +966,7 @@ export class RiskInsightsOrchestratorService { orgDetails.organizationId, ); this._ciphersSubject.next(ciphers); + this._hasCiphersSubject$.next(ciphers.length > 0); }), takeUntil(this._destroy$), ) @@ -1052,28 +1016,28 @@ export class RiskInsightsOrchestratorService { this._userId$.pipe(filter((user) => !!user)), ]).pipe(shareReplay({ bufferSize: 1, refCount: true })); - // A stream for the initial report fetch - const initialReportLoad$ = reportDependencies$.pipe( - take(1), - exhaustMap(([orgDetails, userId]) => this._fetchReport$(orgDetails!.organizationId, userId!)), - ); - - // A stream for manually triggered fetches - const manualReportFetch$ = this._fetchReportTriggerSubject.pipe( - withLatestFrom(reportDependencies$), - exhaustMap(([_, [orgDetails, userId]]) => - this._fetchReport$(orgDetails!.organizationId, userId!), + // A stream that continuously watches dependencies and fetches a new report every time they change + const continuousReportFetch$: Observable = reportDependencies$.pipe( + switchMap(([orgDetails, userId]) => + this._fetchReport$(orgDetails!.organizationId, userId!).pipe( + startWith({ status: ReportStatus.Initializing, error: null, data: null }), + ), ), ); // A stream for generating a new report - const newReportGeneration$ = this.generatingReport$.pipe( + const newReportGeneration$: Observable = this.generatingReport$.pipe( distinctUntilChanged(), filter((isRunning) => isRunning), withLatestFrom(reportDependencies$), exhaustMap(([_, [orgDetails, userId]]) => this._generateNewApplicationsReport$(orgDetails!.organizationId, userId!), ), + startWith({ + status: ReportStatus.Loading, + error: null, + data: null, + }), tap(() => { this._generateReportTriggerSubject.next(false); }), @@ -1081,30 +1045,44 @@ export class RiskInsightsOrchestratorService { // Combine all triggers and update the single report state const mergedReportState$ = merge( - initialReportLoad$, - manualReportFetch$, + continuousReportFetch$, newReportGeneration$, - this._markUnmarkUpdates$, + this._flagForUpdates$, ).pipe( - scan((prevState: ReportState, currState: ReportState) => { - // If organization changed, use new state completely (don't preserve old data) - // This allows null data to clear old org's data when switching orgs - if (currState.organizationId && prevState.organizationId !== currState.organizationId) { - return { - ...currState, - data: currState.data, // Allow null to clear old org's data - }; - } - - // Same org (or no org ID): preserve data when currState.data is null - // This preserves critical flags during loading states within the same org + startWith({ + status: ReportStatus.Initializing, + error: null, + data: null, + }), + withLatestFrom(this.organizationDetails$), + map(([reportState, orgDetails]) => { return { - ...prevState, - ...currState, - data: currState.data !== null ? currState.data : prevState.data, + reportState, + organizationId: orgDetails?.organizationId, + }; + }), + + // 3. NOW, scan receives a simple object for both prevState and currState + scan((prevState, currState) => { + const hasOrganizationChanged = prevState.organizationId !== currState.organizationId; + // Don't override initial status until complete + const keepInitializeStatus = + prevState.reportState.status == ReportStatus.Initializing && + currState.reportState.status == ReportStatus.Loading; + return { + reportState: { + status: keepInitializeStatus + ? prevState.reportState.status + : (currState.reportState.status ?? prevState.reportState.status), + error: currState.reportState.error ?? prevState.reportState.error, + data: + currState.reportState.data !== null || hasOrganizationChanged + ? currState.reportState.data + : prevState.reportState.data, + }, + organizationId: currState.organizationId, }; }), - startWith({ loading: false, error: null, data: null }), shareReplay({ bufferSize: 1, refCount: true }), takeUntil(this._destroy$), ); @@ -1112,7 +1090,7 @@ export class RiskInsightsOrchestratorService { this._reportStateSubscription = mergedReportState$ .pipe(takeUntil(this._destroy$)) .subscribe((state) => { - this._rawReportDataSubject.next(state); + this._rawReportDataSubject.next(state.reportState); }); } diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.ts index 5ea66a71221..d49d7a4a40f 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/domain/risk-insights-report.service.ts @@ -1,4 +1,4 @@ -import { catchError, EMPTY, from, map, Observable, switchMap, throwError } from "rxjs"; +import { catchError, EMPTY, from, map, Observable, of, switchMap, throwError } from "rxjs"; import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import { OrganizationId, OrganizationReportId, UserId } from "@bitwarden/common/types/guid"; @@ -146,12 +146,12 @@ export class RiskInsightsReportService { getRiskInsightsReport$( organizationId: OrganizationId, userId: UserId, - ): Observable { + ): Observable { return this.riskInsightsApiService.getRiskInsightsReport$(organizationId).pipe( switchMap((response) => { if (!response) { // Return an empty report and summary if response is falsy - return EMPTY; + return of(null as unknown as RiskInsightsData); } if (!response.contentEncryptionKey || response.contentEncryptionKey.data == "") { return throwError(() => new Error("Report key not found")); diff --git a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/view/risk-insights-data.service.ts b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/view/risk-insights-data.service.ts index a827200001a..2dc669f5727 100644 --- a/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/view/risk-insights-data.service.ts +++ b/bitwarden_license/bit-common/src/dirt/reports/risk-insights/services/view/risk-insights-data.service.ts @@ -4,7 +4,13 @@ import { distinctUntilChanged, map } from "rxjs/operators"; import { OrganizationId } from "@bitwarden/common/types/guid"; import { getAtRiskApplicationList, getAtRiskMemberList } from "../../helpers"; -import { ReportState, DrawerDetails, DrawerType, RiskInsightsEnrichedData } from "../../models"; +import { + ReportState, + DrawerDetails, + DrawerType, + RiskInsightsEnrichedData, + ReportStatus, +} from "../../models"; import { RiskInsightsOrchestratorService } from "../domain/risk-insights-orchestrator.service"; export class RiskInsightsDataService { @@ -24,11 +30,12 @@ export class RiskInsightsDataService { // -------------------------- Orchestrator-driven state ------------- // The full report state (for internal facade use or complex components) private readonly reportState$: Observable; - readonly isLoading$: Observable = of(false); + readonly reportStatus$: Observable = of(ReportStatus.Initializing); + readonly hasReportData$: Observable = of(false); readonly enrichedReportData$: Observable = of(null); readonly isGeneratingReport$: Observable = of(false); readonly criticalReportResults$: Observable = of(null); - readonly hasVaultItems$: Observable = of(false); + readonly hasCiphers$: Observable = of(null); // New applications that need review (reviewedDate === null) readonly newApplications$: Observable = of([]); @@ -52,14 +59,19 @@ export class RiskInsightsDataService { this.organizationDetails$ = this.orchestrator.organizationDetails$; this.enrichedReportData$ = this.orchestrator.enrichedReportData$; this.criticalReportResults$ = this.orchestrator.criticalReportResults$; - this.hasVaultItems$ = this.orchestrator.hasVaultItems$; this.newApplications$ = this.orchestrator.newApplications$; + this.hasCiphers$ = this.orchestrator.hasCiphers$.pipe(distinctUntilChanged()); + // Expose the loading state - this.isLoading$ = this.reportState$.pipe( - map((state) => state.loading), + this.reportStatus$ = this.reportState$.pipe( + map((state) => state.status), distinctUntilChanged(), // Prevent unnecessary component re-renders ); + this.hasReportData$ = this.reportState$.pipe( + map((state) => state.data != null), + distinctUntilChanged(), + ); } destroy(): void { @@ -76,10 +88,6 @@ export class RiskInsightsDataService { this.orchestrator.generateReport(); } - fetchReport(): void { - this.orchestrator.fetchReport(); - } - // ------------------------- Drawer functions ----------------------------- isActiveDrawerType = (drawerType: DrawerType): boolean => { return this.drawerDetailsSubject.value.activeDrawerType === drawerType; diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/all-activity.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/all-activity.component.html index 9fffded215e..82137c9acca 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/all-activity.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/all-activity.component.html @@ -1,4 +1,4 @@ -@if (dataService.isLoading$ | async) { +@if ((dataService.reportStatus$ | async) == ReportStatusEnum.Loading) { } @else {