From 35f35c43610a834b9d03166fa3ebe6dd8d8ea7b2 Mon Sep 17 00:00:00 2001 From: Thomas Avery <43214426+Thomas-Avery@users.noreply.github.com> Date: Thu, 13 Nov 2025 14:06:56 -0600 Subject: [PATCH 01/75] [PM-26498] Add proofOfDecryption method to MasterPasswordUnlockService (#17322) * Add proofOfDecryption method to MasterPasswordUnlockService --- .../service-container/service-container.ts | 1 + .../src/services/jslib-services.module.ts | 2 +- .../master-password-unlock.service.ts | 14 +++ ...ult-master-password-unlock.service.spec.ts | 94 ++++++++++++++++++- .../default-master-password-unlock.service.ts | 40 ++++++++ 5 files changed, 148 insertions(+), 3 deletions(-) diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index c9f1d11210b..ebfb76eab2f 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -495,6 +495,7 @@ export class ServiceContainer { this.masterPasswordUnlockService = new DefaultMasterPasswordUnlockService( this.masterPasswordService, this.keyService, + this.logService, ); this.appIdService = new AppIdService(this.storageService, this.logService); diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 97aa1869575..18c21024a6a 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -1087,7 +1087,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: MasterPasswordUnlockService, useClass: DefaultMasterPasswordUnlockService, - deps: [InternalMasterPasswordServiceAbstraction, KeyService], + deps: [InternalMasterPasswordServiceAbstraction, KeyService, LogService], }), safeProvider({ provide: KeyConnectorServiceAbstraction, diff --git a/libs/common/src/key-management/master-password/abstractions/master-password-unlock.service.ts b/libs/common/src/key-management/master-password/abstractions/master-password-unlock.service.ts index 4448206b2f6..63ee7d69719 100644 --- a/libs/common/src/key-management/master-password/abstractions/master-password-unlock.service.ts +++ b/libs/common/src/key-management/master-password/abstractions/master-password-unlock.service.ts @@ -7,7 +7,21 @@ export abstract class MasterPasswordUnlockService { * Unlocks the user's account using the master password. * @param masterPassword The master password provided by the user. * @param userId The ID of the active user. + * @throws If the master password provided is null/undefined/empty. + * @throws If the userId provided is null/undefined. + * @throws if the masterPasswordUnlockData for the user is not found. + * @throws If unwrapping the user key fails. * @returns the user's decrypted userKey. */ abstract unlockWithMasterPassword(masterPassword: string, userId: UserId): Promise; + + /** + * For the given master password and user ID, verifies whether the user can decrypt their user key stored in state. + * @param masterPassword The master password provided by the user. + * @param userId The ID of the active user. + * @throws If the master password provided is null/undefined/empty. + * @throws If the userId provided is null/undefined. + * @returns true if the userKey can be decrypted, false otherwise. + */ + abstract proofOfDecryption(masterPassword: string, userId: UserId): Promise; } diff --git a/libs/common/src/key-management/master-password/services/default-master-password-unlock.service.spec.ts b/libs/common/src/key-management/master-password/services/default-master-password-unlock.service.spec.ts index 75668e8e6bd..1c95090f04b 100644 --- a/libs/common/src/key-management/master-password/services/default-master-password-unlock.service.spec.ts +++ b/libs/common/src/key-management/master-password/services/default-master-password-unlock.service.spec.ts @@ -4,6 +4,8 @@ import { of } from "rxjs"; import { newGuid } from "@bitwarden/guid"; // eslint-disable-next-line no-restricted-imports import { Argon2KdfConfig, KeyService } from "@bitwarden/key-management"; +import { LogService } from "@bitwarden/logging"; +import { CryptoError } from "@bitwarden/sdk-internal"; import { UserId } from "@bitwarden/user-core"; import { HashPurpose } from "../../../platform/enums"; @@ -23,6 +25,7 @@ describe("DefaultMasterPasswordUnlockService", () => { let masterPasswordService: MockProxy; let keyService: MockProxy; + let logService: MockProxy; const mockMasterPassword = "testExample"; const mockUserId = newGuid() as UserId; @@ -41,8 +44,9 @@ describe("DefaultMasterPasswordUnlockService", () => { beforeEach(() => { masterPasswordService = mock(); keyService = mock(); + logService = mock(); - sut = new DefaultMasterPasswordUnlockService(masterPasswordService, keyService); + sut = new DefaultMasterPasswordUnlockService(masterPasswordService, keyService, logService); masterPasswordService.masterPasswordUnlockData$.mockReturnValue( of(mockMasterPasswordUnlockData), @@ -73,7 +77,7 @@ describe("DefaultMasterPasswordUnlockService", () => { ); test.each([null as unknown as UserId, undefined as unknown as UserId])( - "throws when the provided master password is %s", + "throws when the provided userID is %s", async (userId) => { await expect(sut.unlockWithMasterPassword(mockMasterPassword, userId)).rejects.toThrow( "User ID is required", @@ -151,4 +155,90 @@ describe("DefaultMasterPasswordUnlockService", () => { expect(masterPasswordService.setMasterKey).not.toHaveBeenCalled(); }); }); + + describe("proofOfDecryption", () => { + test.each([null as unknown as string, undefined as unknown as string, ""])( + "throws when the provided master password is %s", + async (masterPassword) => { + await expect(sut.proofOfDecryption(masterPassword, mockUserId)).rejects.toThrow( + "Master password is required", + ); + expect(masterPasswordService.masterPasswordUnlockData$).not.toHaveBeenCalled(); + expect( + masterPasswordService.unwrapUserKeyFromMasterPasswordUnlockData, + ).not.toHaveBeenCalled(); + }, + ); + + test.each([null as unknown as UserId, undefined as unknown as UserId])( + "throws when the provided userID is %s", + async (userId) => { + await expect(sut.proofOfDecryption(mockMasterPassword, userId)).rejects.toThrow( + "User ID is required", + ); + }, + ); + + it("returns false when the user doesn't have masterPasswordUnlockData", async () => { + masterPasswordService.masterPasswordUnlockData$.mockReturnValue(of(null)); + + const result = await sut.proofOfDecryption(mockMasterPassword, mockUserId); + + expect(result).toBe(false); + expect(masterPasswordService.masterPasswordUnlockData$).toHaveBeenCalledWith(mockUserId); + expect( + masterPasswordService.unwrapUserKeyFromMasterPasswordUnlockData, + ).not.toHaveBeenCalled(); + expect(logService.warning).toHaveBeenCalledWith( + `[DefaultMasterPasswordUnlockService] No master password unlock data found for user ${mockUserId} returning false.`, + ); + }); + + it("returns true when the master password is correct", async () => { + const result = await sut.proofOfDecryption(mockMasterPassword, mockUserId); + + expect(result).toBe(true); + expect(masterPasswordService.masterPasswordUnlockData$).toHaveBeenCalledWith(mockUserId); + expect(masterPasswordService.unwrapUserKeyFromMasterPasswordUnlockData).toHaveBeenCalledWith( + mockMasterPassword, + mockMasterPasswordUnlockData, + ); + }); + + it("returns false when the master password is incorrect", async () => { + const error = new Error("Incorrect password") as CryptoError; + error.name = "CryptoError"; + error.variant = "InvalidKey"; + masterPasswordService.unwrapUserKeyFromMasterPasswordUnlockData.mockRejectedValue(error); + + const result = await sut.proofOfDecryption(mockMasterPassword, mockUserId); + + expect(result).toBe(false); + expect(masterPasswordService.masterPasswordUnlockData$).toHaveBeenCalledWith(mockUserId); + expect(masterPasswordService.unwrapUserKeyFromMasterPasswordUnlockData).toHaveBeenCalledWith( + mockMasterPassword, + mockMasterPasswordUnlockData, + ); + expect(logService.debug).toHaveBeenCalledWith( + `[DefaultMasterPasswordUnlockService] Error during proof of decryption for user ${mockUserId} returning false: ${error}`, + ); + }); + + it("returns false when a generic error occurs", async () => { + const error = new Error("Generic error"); + masterPasswordService.unwrapUserKeyFromMasterPasswordUnlockData.mockRejectedValue(error); + + const result = await sut.proofOfDecryption(mockMasterPassword, mockUserId); + + expect(result).toBe(false); + expect(masterPasswordService.masterPasswordUnlockData$).toHaveBeenCalledWith(mockUserId); + expect(masterPasswordService.unwrapUserKeyFromMasterPasswordUnlockData).toHaveBeenCalledWith( + mockMasterPassword, + mockMasterPasswordUnlockData, + ); + expect(logService.error).toHaveBeenCalledWith( + `[DefaultMasterPasswordUnlockService] Unexpected error during proof of decryption for user ${mockUserId} returning false: ${error}`, + ); + }); + }); }); diff --git a/libs/common/src/key-management/master-password/services/default-master-password-unlock.service.ts b/libs/common/src/key-management/master-password/services/default-master-password-unlock.service.ts index 87114000abf..89a87403e49 100644 --- a/libs/common/src/key-management/master-password/services/default-master-password-unlock.service.ts +++ b/libs/common/src/key-management/master-password/services/default-master-password-unlock.service.ts @@ -2,6 +2,8 @@ import { firstValueFrom } from "rxjs"; // eslint-disable-next-line no-restricted-imports import { KeyService } from "@bitwarden/key-management"; +import { LogService } from "@bitwarden/logging"; +import { isCryptoError } from "@bitwarden/sdk-internal"; import { UserId } from "@bitwarden/user-core"; import { HashPurpose } from "../../../platform/enums"; @@ -14,6 +16,7 @@ export class DefaultMasterPasswordUnlockService implements MasterPasswordUnlockS constructor( private readonly masterPasswordService: InternalMasterPasswordServiceAbstraction, private readonly keyService: KeyService, + private readonly logService: LogService, ) {} async unlockWithMasterPassword(masterPassword: string, userId: UserId): Promise { @@ -37,6 +40,43 @@ export class DefaultMasterPasswordUnlockService implements MasterPasswordUnlockS return userKey; } + async proofOfDecryption(masterPassword: string, userId: UserId): Promise { + this.validateInput(masterPassword, userId); + + try { + const masterPasswordUnlockData = await firstValueFrom( + this.masterPasswordService.masterPasswordUnlockData$(userId), + ); + + if (masterPasswordUnlockData == null) { + this.logService.warning( + `[DefaultMasterPasswordUnlockService] No master password unlock data found for user ${userId} returning false.`, + ); + return false; + } + + const userKey = await this.masterPasswordService.unwrapUserKeyFromMasterPasswordUnlockData( + masterPassword, + masterPasswordUnlockData, + ); + + return userKey != null; + } catch (error) { + // masterPasswordService.unwrapUserKeyFromMasterPasswordUnlockData is expected to throw if the password is incorrect. + // Currently this throws CryptoError:InvalidKey if decrypting the user key fails at all. + if (isCryptoError(error) && error.variant === "InvalidKey") { + this.logService.debug( + `[DefaultMasterPasswordUnlockService] Error during proof of decryption for user ${userId} returning false: ${error}`, + ); + } else { + this.logService.error( + `[DefaultMasterPasswordUnlockService] Unexpected error during proof of decryption for user ${userId} returning false: ${error}`, + ); + } + return false; + } + } + private validateInput(masterPassword: string, userId: UserId): void { if (masterPassword == null || masterPassword === "") { throw new Error("Master password is required"); From 9586057a328a6f3ec34cab34f0faa0e47ba2e39c Mon Sep 17 00:00:00 2001 From: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> Date: Thu, 13 Nov 2025 21:14:45 +0100 Subject: [PATCH 02/75] [PM-26162] [Chromium importer] Add fallback name in case empty when loading browser profiles (#16664) * Added a fallback to use a browser profile folder-name in case the name of the profile is empty --- .../chromium_importer/src/chromium/mod.rs | 125 +++++++++++++++++- 1 file changed, 119 insertions(+), 6 deletions(-) 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 aec8a84b5c1..369e63e0ad1 100644 --- a/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::path::{Path, PathBuf}; use std::sync::LazyLock; @@ -148,13 +149,13 @@ pub(crate) struct LocalState { #[derive(serde::Deserialize, Clone)] struct AllProfiles { - info_cache: std::collections::HashMap, + info_cache: HashMap, } #[derive(serde::Deserialize, Clone)] struct OneProfile { name: String, - gaia_name: Option, + gaia_id: Option, user_name: Option, } @@ -197,10 +198,14 @@ fn get_profile_info(local_state: &LocalState) -> Vec { .profile .info_cache .iter() - .map(|(name, info)| ProfileInfo { - name: info.name.clone(), - folder: name.clone(), - account_name: info.gaia_name.clone(), + .map(|(folder, info)| ProfileInfo { + name: if !info.name.trim().is_empty() { + info.name.clone() + } else { + folder.clone() + }, + folder: folder.clone(), + account_name: info.gaia_id.clone(), account_email: info.user_name.clone(), }) .collect() @@ -348,3 +353,111 @@ async fn decrypt_login( }), } } + +#[cfg(test)] +mod tests { + use super::*; + + fn make_local_state(profiles: Vec<(&str, &str, Option<&str>, Option<&str>)>) -> LocalState { + let info_cache = profiles + .into_iter() + .map(|(folder, name, gaia_id, user_name)| { + ( + folder.to_string(), + OneProfile { + name: name.to_string(), + gaia_id: gaia_id.map(|s| s.to_string()), + user_name: user_name.map(|s| s.to_string()), + }, + ) + }) + .collect::>(); + + LocalState { + profile: AllProfiles { info_cache }, + os_crypt: None, + } + } + + #[test] + fn test_get_profile_info_basic() { + let local_state = make_local_state(vec![ + ( + "Profile 1", + "User 1", + Some("Account 1"), + Some("email1@example.com"), + ), + ( + "Profile 2", + "User 2", + Some("Account 2"), + Some("email2@example.com"), + ), + ]); + let infos = get_profile_info(&local_state); + assert_eq!(infos.len(), 2); + + let profile1 = infos.iter().find(|p| p.folder == "Profile 1").unwrap(); + assert_eq!(profile1.name, "User 1"); + assert_eq!(profile1.account_name.as_deref(), Some("Account 1")); + assert_eq!( + profile1.account_email.as_deref(), + Some("email1@example.com") + ); + + let profile2 = infos.iter().find(|p| p.folder == "Profile 2").unwrap(); + assert_eq!(profile2.name, "User 2"); + assert_eq!(profile2.account_name.as_deref(), Some("Account 2")); + assert_eq!( + profile2.account_email.as_deref(), + Some("email2@example.com") + ); + } + + #[test] + fn test_get_profile_info_empty_name() { + let local_state = make_local_state(vec![( + "ProfileX", + "", + Some("AccountX"), + Some("emailx@example.com"), + )]); + let infos = get_profile_info(&local_state); + assert_eq!(infos.len(), 1); + assert_eq!(infos[0].name, "ProfileX"); + assert_eq!(infos[0].folder, "ProfileX"); + } + + #[test] + fn test_get_profile_info_none_fields() { + let local_state = make_local_state(vec![("ProfileY", "NameY", None, None)]); + let infos = get_profile_info(&local_state); + assert_eq!(infos.len(), 1); + assert_eq!(infos[0].name, "NameY"); + assert_eq!(infos[0].account_name, None); + assert_eq!(infos[0].account_email, None); + } + + #[test] + fn test_get_profile_info_multiple_profiles() { + let local_state = make_local_state(vec![ + ("P1", "N1", Some("A1"), Some("E1")), + ("P2", "", None, None), + ("P3", "N3", Some("A3"), None), + ]); + let infos = get_profile_info(&local_state); + assert_eq!(infos.len(), 3); + + let p1 = infos.iter().find(|p| p.folder == "P1").unwrap(); + assert_eq!(p1.name, "N1"); + + let p2 = infos.iter().find(|p| p.folder == "P2").unwrap(); + assert_eq!(p2.name, "P2"); + + let p3 = infos.iter().find(|p| p.folder == "P3").unwrap(); + assert_eq!(p3.name, "N3"); + assert_eq!(p3.account_name.as_deref(), Some("A3")); + assert_eq!(p3.account_email, None); + } +} From ccf7bb1753ec7cea7cd79750c71e6099ef2b3209 Mon Sep 17 00:00:00 2001 From: Bryan Cunningham Date: Thu, 13 Nov 2025 15:53:05 -0500 Subject: [PATCH 03/75] [CL-736] migrate chip select to use signals (#17136) * migrate chip select to use signals * Have Claude address feedback and create spec file * remove eslint disable comment * fix failing tests * remove unnecessary tests * improved documentation * remove unnecessary test logic * consolidate tests and remove fragile selectors --- .../chip-select/chip-select.component.html | 18 +- .../chip-select/chip-select.component.spec.ts | 486 ++++++++++++++++++ .../src/chip-select/chip-select.component.ts | 97 ++-- 3 files changed, 545 insertions(+), 56 deletions(-) create mode 100644 libs/components/src/chip-select/chip-select.component.spec.ts diff --git a/libs/components/src/chip-select/chip-select.component.html b/libs/components/src/chip-select/chip-select.component.html index d43e09e8338..ee252b35fe9 100644 --- a/libs/components/src/chip-select/chip-select.component.html +++ b/libs/components/src/chip-select/chip-select.component.html @@ -3,11 +3,11 @@ class="tw-inline-flex tw-items-center tw-rounded-full tw-w-full tw-border-solid tw-border tw-gap-1.5 tw-group/chip-select" [ngClass]="{ 'tw-bg-text-muted hover:tw-bg-secondary-700 tw-text-contrast hover:!tw-border-secondary-700': - selectedOption && !disabled, + selectedOption && !disabled(), 'tw-bg-transparent hover:tw-border-secondary-700 !tw-text-muted hover:tw-bg-secondary-100': - !selectedOption && !disabled, - 'tw-bg-secondary-300 tw-text-muted tw-border-transparent': disabled, - 'tw-border-text-muted': !disabled, + !selectedOption && !disabled(), + 'tw-bg-secondary-300 tw-text-muted tw-border-transparent': disabled(), + 'tw-border-text-muted': !disabled(), 'tw-ring-2 tw-ring-primary-600 tw-ring-offset-1': focusVisibleWithin(), }" > @@ -17,11 +17,11 @@ class="tw-inline-flex tw-gap-1.5 tw-items-center tw-justify-between tw-bg-transparent hover:tw-bg-transparent tw-border-none tw-outline-none tw-w-full tw-py-1 tw-ps-3 last:tw-pe-3 [&:not(:last-child)]:tw-pe-0 tw-truncate tw-text-[color:inherit] tw-text-[length:inherit]" data-fvw-target [ngClass]="{ - 'tw-cursor-not-allowed': disabled, - 'group-hover/chip-select:tw-text-secondary-700': !selectedOption && !disabled, + 'tw-cursor-not-allowed': disabled(), + 'group-hover/chip-select:tw-text-secondary-700': !selectedOption && !disabled(), }" [bitMenuTriggerFor]="menu" - [disabled]="disabled" + [disabled]="disabled()" [title]="label" #menuTrigger="menuTrigger" (click)="setMenuWidth()" @@ -45,10 +45,10 @@ - * click button to hide this content - * ``` - * + * The `bit-disclosure` component is used in tandem with the `bitDisclosureTriggerFor` directive to create an accessible content area whose visibility is controlled by a trigger button. + * + * To compose a disclosure and trigger: + * + * 1. Create a trigger component (see "Supported Trigger Components" section below) + * 2. Create a `bit-disclosure` + * 3. Set a template reference on the `bit-disclosure` + * 4. Use the `bitDisclosureTriggerFor` directive on the trigger component, and pass it the `bit-disclosure` template reference + * 5. Set the `open` property on the `bit-disclosure` to init the disclosure as either currently expanded or currently collapsed. The disclosure will default to `false`, meaning it defaults to being hidden. + * + * @example + * + * ```html + * + * click button to hide this content + * ``` */ -// 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-disclosure", template: ``, + changeDetection: ChangeDetectionStrategy.OnPush, + host: { + "[class]": "classList()", + "[id]": "id", + }, }) export class DisclosureComponent { - /** Emits the visibility of the disclosure content */ - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref - @Output() openChange = new EventEmitter(); - - private _open?: boolean; /** - * Optionally init the disclosure in its opened state + * Controls the visibility of the disclosure content. */ - // TODO: Skipped for signal migration because: - // Accessor inputs cannot be migrated as they are too complex. - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input({ transform: booleanAttribute }) set open(isOpen: boolean) { - this._open = isOpen; - this.openChange.emit(isOpen); - } - get open(): boolean { - return !!this._open; - } + readonly open = model(false); - @HostBinding("class") get classList() { - return this.open ? "" : "tw-hidden"; - } + /** + * Autogenerated id. + */ + readonly id = `bit-disclosure-${nextId++}`; - @HostBinding("id") id = `bit-disclosure-${nextId++}`; + protected readonly classList = computed(() => (this.open() ? "" : "tw-hidden")); } diff --git a/libs/components/src/disclosure/disclosure.mdx b/libs/components/src/disclosure/disclosure.mdx index 50ccf936acc..9f01df8d3d9 100644 --- a/libs/components/src/disclosure/disclosure.mdx +++ b/libs/components/src/disclosure/disclosure.mdx @@ -11,7 +11,7 @@ import { DisclosureComponent, DisclosureTriggerForDirective } from "@bitwarden/c <Description /> -<Canvas of={stories.DisclosureWithIconButton} /> +<Canvas of={stories.DisclosureOpen} /> ## Supported Trigger Components diff --git a/libs/components/src/disclosure/disclosure.stories.ts b/libs/components/src/disclosure/disclosure.stories.ts index 3ed6903060c..cd9d9e02360 100644 --- a/libs/components/src/disclosure/disclosure.stories.ts +++ b/libs/components/src/disclosure/disclosure.stories.ts @@ -36,13 +36,30 @@ export default { type Story = StoryObj<DisclosureComponent>; -export const DisclosureWithIconButton: Story = { +export const DisclosureOpen: Story = { + args: { + open: true, + }, render: (args) => ({ props: args, template: /*html*/ ` - <button type="button" label="Settings" bitIconButton="bwi-sliders" [buttonType]="'muted'" [bitDisclosureTriggerFor]="disclosureRef"> + <button type="button" label="Settings" bitIconButton="bwi-sliders" buttonType="muted" [bitDisclosureTriggerFor]="disclosureRef"> </button> - <bit-disclosure #disclosureRef class="tw-text-main tw-block" open>click button to hide this content</bit-disclosure> + <bit-disclosure #disclosureRef class="tw-text-main tw-block" [(open)]="open">click button to hide this content</bit-disclosure> + `, + }), +}; + +export const DisclosureClosed: Story = { + args: { + open: false, + }, + render: (args) => ({ + props: args, + template: /*html*/ ` + <button type="button" label="Settings" bitIconButton="bwi-sliders" buttonType="muted" [bitDisclosureTriggerFor]="disclosureRef"> + </button> + <bit-disclosure #disclosureRef class="tw-text-main tw-block" [(open)]="open">click button to hide this content</bit-disclosure> `, }), }; From 9a3ba9e05b4199d28de12b68ae9bd291be1494ef Mon Sep 17 00:00:00 2001 From: Vince Grassia <593223+vgrassia@users.noreply.github.com> Date: Thu, 13 Nov 2025 21:56:37 -0500 Subject: [PATCH 06/75] Fix workflow formatting (#17382) --- .github/workflows/publish-cli.yml | 22 ++++++----- .github/workflows/publish-desktop.yml | 53 +++++++++------------------ .github/workflows/release-browser.yml | 10 ++--- .github/workflows/release-desktop.yml | 6 +-- .github/workflows/release-web.yml | 9 ++--- 5 files changed, 42 insertions(+), 58 deletions(-) diff --git a/.github/workflows/publish-cli.yml b/.github/workflows/publish-cli.yml index bcae79d077e..08d3f1de503 100644 --- a/.github/workflows/publish-cli.yml +++ b/.github/workflows/publish-cli.yml @@ -66,15 +66,17 @@ jobs: - name: Version output id: version-output env: - _INPUT_VERSION: ${{ inputs.version }} + INPUT_VERSION: ${{ inputs.version }} run: | - if [[ "$_INPUT_VERSION" == "latest" || "$_INPUT_VERSION" == "" ]]; then - VERSION=$(curl "https://api.github.com/repos/bitwarden/clients/releases" | jq -c '.[] | select(.tag_name | contains("cli")) | .tag_name' | head -1 | grep -ohE '20[0-9]{2}\.([1-9]|1[0-2])\.[0-9]+') + if [[ "$INPUT_VERSION" == "latest" || "$INPUT_VERSION" == "" ]]; then + TAG_NAME=$(curl -s "https://api.github.com/repos/bitwarden/clients/releases" \ + | jq -r '.[] | select(.tag_name | contains("cli")) | .tag_name' | head -1) + VERSION="${TAG_NAME#cli-v}" echo "Latest Released Version: $VERSION" echo "version=$VERSION" >> "$GITHUB_OUTPUT" else - echo "Release Version: $_INPUT_VERSION" - echo "version=$_INPUT_VERSION" >> "$GITHUB_OUTPUT" + echo "Release Version: $INPUT_VERSION" + echo "version=$INPUT_VERSION" >> "$GITHUB_OUTPUT" fi - name: Create GitHub deployment @@ -126,14 +128,14 @@ jobs: uses: samuelmeuli/action-snapcraft@fceeb3c308e76f3487e72ef608618de625fb7fe8 # v3.0.1 - name: Download artifacts - run: wget "https://github.com/bitwarden/clients/releases/download/cli-v$_PKG_VERSION/bw_$_PKG_VERSION_amd64.snap" + run: wget "https://github.com/bitwarden/clients/releases/download/cli-v${_PKG_VERSION}/bw_${_PKG_VERSION}_amd64.snap" - name: Publish Snap & logout if: ${{ inputs.publish_type != 'Dry Run' }} env: SNAPCRAFT_STORE_CREDENTIALS: ${{ steps.retrieve-secrets.outputs.snapcraft-store-token }} run: | - snapcraft upload "bw_$_PKG_VERSION_amd64.snap" --release stable + snapcraft upload "bw_${_PKG_VERSION}_amd64.snap" --release stable snapcraft logout choco: @@ -179,7 +181,7 @@ jobs: run: New-Item -ItemType directory -Path ./dist - name: Download artifacts - run: Invoke-WebRequest -Uri "https://github.com/bitwarden/clients/releases/download/cli-v$_PKG_VERSION/bitwarden-cli.$_PKG_VERSION.nupkg" -OutFile bitwarden-cli.$_PKG_VERSION.nupkg + run: Invoke-WebRequest -Uri "https://github.com/bitwarden/clients/releases/download/cli-v$($env:_PKG_VERSION)/bitwarden-cli.$($env:_PKG_VERSION).nupkg" -OutFile bitwarden-cli.$($env:_PKG_VERSION).nupkg working-directory: apps/cli/dist - name: Push to Chocolatey @@ -227,8 +229,8 @@ jobs: - name: Download and set up artifact run: | mkdir -p build - wget "https://github.com/bitwarden/clients/releases/download/cli-v$_PKG_VERSION/bitwarden-cli-$_PKG_VERSION-npm-build.zip" - unzip "bitwarden-cli-$_PKG_VERSION-npm-build.zip" -d build + wget "https://github.com/bitwarden/clients/releases/download/cli-v${_PKG_VERSION}/bitwarden-cli-${_PKG_VERSION}-npm-build.zip" + unzip "bitwarden-cli-${_PKG_VERSION}-npm-build.zip" -d build - name: Publish NPM if: ${{ inputs.publish_type != 'Dry Run' }} diff --git a/.github/workflows/publish-desktop.yml b/.github/workflows/publish-desktop.yml index 15a0ec77d5b..f42f9811d77 100644 --- a/.github/workflows/publish-desktop.yml +++ b/.github/workflows/publish-desktop.yml @@ -73,12 +73,11 @@ jobs: - name: Check Publish Version id: version env: - _INPUT_VERSION: ${{ inputs.version }} + INPUT_VERSION: ${{ inputs.version }} run: | - if [[ "$_INPUT_VERSION" == "latest" || "$_INPUT_VERSION" == "" ]]; then - TAG_NAME=$(curl "https://api.github.com/repos/bitwarden/clients/releases" \ - | jq -c '.[] | select(.tag_name | contains("desktop")) | .tag_name' \ - | head -1 | cut -d '"' -f 2) + if [[ "$INPUT_VERSION" == "latest" || "$INPUT_VERSION" == "" ]]; then + TAG_NAME=$(curl -s "https://api.github.com/repos/bitwarden/clients/releases" \ + | jq -r '.[] | select(.tag_name | contains("desktop")) | .tag_name' | head -1) VERSION="${TAG_NAME#desktop-v}" echo "Latest Released Version: $VERSION" @@ -87,7 +86,7 @@ jobs: echo "Tag name: $TAG_NAME" echo "tag_name=$TAG_NAME" >> "$GITHUB_OUTPUT" else - VERSION="$_INPUT_VERSION" + VERSION="$INPUT_VERSION" TAG_NAME="desktop-v$VERSION" echo "Release Version: $VERSION" @@ -100,9 +99,9 @@ jobs: - name: Get Version Channel id: release_channel env: - _VERSION: ${{ steps.version.outputs.version }} + VERSION: ${{ steps.version.outputs.version }} run: | - case "${_VERSION}" in + case "${VERSION}" in *"alpha"*) echo "channel=alpha" >> "$GITHUB_OUTPUT" echo "[!] We do not yet support 'alpha'" @@ -192,22 +191,6 @@ jobs: --recursive \ --quiet - - name: Update deployment status to Success - if: ${{ inputs.publish_type != 'Dry Run' && success() }} - uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3 - with: - token: '${{ secrets.GITHUB_TOKEN }}' - state: 'success' - deployment-id: ${{ needs.setup.outputs.deployment_id }} - - - name: Update deployment status to Failure - if: ${{ inputs.publish_type != 'Dry Run' && failure() }} - uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3 - with: - token: '${{ secrets.GITHUB_TOKEN }}' - state: 'failure' - deployment-id: ${{ needs.setup.outputs.deployment_id }} - snap: name: Deploy Snap runs-on: ubuntu-22.04 @@ -251,14 +234,14 @@ jobs: - name: Download artifacts working-directory: apps/desktop/dist - run: wget "https://github.com/bitwarden/clients/releases/download/$_RELEASE_TAG/bitwarden_$_PKG_VERSION_amd64.snap" + run: wget "https://github.com/bitwarden/clients/releases/download/${_RELEASE_TAG}/bitwarden_${_PKG_VERSION}_amd64.snap" - name: Deploy to Snap Store if: ${{ inputs.publish_type != 'Dry Run' }} env: SNAPCRAFT_STORE_CREDENTIALS: ${{ steps.retrieve-secrets.outputs.snapcraft-store-token }} run: | - snapcraft upload "bitwarden_$_PKG_VERSION_amd64.snap" --release stable + snapcraft upload "bitwarden_${_PKG_VERSION}_amd64.snap" --release stable snapcraft logout working-directory: apps/desktop/dist @@ -312,7 +295,7 @@ jobs: - name: Download artifacts working-directory: apps/desktop/dist - run: Invoke-WebRequest -Uri "https://github.com/bitwarden/clients/releases/download/$_RELEASE_TAG/bitwarden.$_PKG_VERSION.nupkg" -OutFile "bitwarden.$_PKG_VERSION.nupkg" + run: Invoke-WebRequest -Uri "https://github.com/bitwarden/clients/releases/download/$($env:_RELEASE_TAG)/bitwarden.$($env:_PKG_VERSION).nupkg" -OutFile "bitwarden.$($env:_PKG_VERSION).nupkg" - name: Push to Chocolatey if: ${{ inputs.publish_type != 'Dry Run' }} @@ -337,7 +320,7 @@ jobs: persist-credentials: false - name: Validate release notes for MAS - if: inputs.mas_publish && (inputs.release_notes == '' || inputs.release_notes == null) + if: inputs.release_notes == '' || inputs.release_notes == null run: | echo "❌ Release notes are required when publishing to Mac App Store" echo "Please provide release notes using the 'Release Notes' input field" @@ -345,7 +328,7 @@ jobs: - name: Download MacOS App Store build number working-directory: apps/desktop - run: wget "https://github.com/bitwarden/clients/releases/download/$_RELEASE_TAG/macos-build-number.json" + run: wget "https://github.com/bitwarden/clients/releases/download/${_RELEASE_TAG}/macos-build-number.json" - name: Setup Ruby and Install Fastlane uses: ruby/setup-ruby@d5126b9b3579e429dd52e51e68624dda2e05be25 # v1.267.0 @@ -379,20 +362,20 @@ jobs: env: APP_STORE_CONNECT_TEAM_ISSUER: ${{ steps.get-kv-secrets.outputs.APP-STORE-CONNECT-TEAM-ISSUER }} APP_STORE_CONNECT_AUTH_KEY: ${{ steps.get-kv-secrets.outputs.APP-STORE-CONNECT-AUTH-KEY }} - _RELEASE_NOTES: ${{ inputs.release_notes }} - _PUBLISH_TYPE: ${{ inputs.publish_type }} + CHANGELOG: ${{ inputs.release_notes }} + PUBLISH_TYPE: ${{ inputs.publish_type }} working-directory: apps/desktop run: | BUILD_NUMBER=$(jq -r '.buildNumber' macos-build-number.json) - CHANGELOG="$_RELEASE_NOTES" - IS_DRY_RUN="$_PUBLISH_TYPE == 'Dry Run'" - if [ "$IS_DRY_RUN" = "true" ]; then + if [ "$PUBLISH_TYPE" = "Dry Run" ]; then echo "🧪 DRY RUN MODE - Testing without actual App Store submission" echo "📦 Would publish build $BUILD_NUMBER to Mac App Store" + IS_DRY_RUN="true" else echo "🚀 PRODUCTION MODE - Publishing to Mac App Store" echo "📦 Publishing build $BUILD_NUMBER to Mac App Store" + IS_DRY_RUN="false" fi echo "📝 Release notes (${#CHANGELOG} chars): ${CHANGELOG:0:100}..." @@ -404,7 +387,7 @@ jobs: fi fastlane publish --verbose \ - app_version:"$PKG_VERSION" \ + app_version:"${_PKG_VERSION}" \ build_number:"$BUILD_NUMBER" \ changelog:"$CHANGELOG" \ dry_run:"$IS_DRY_RUN" diff --git a/.github/workflows/release-browser.yml b/.github/workflows/release-browser.yml index c7faefb2ce9..53382539b89 100644 --- a/.github/workflows/release-browser.yml +++ b/.github/workflows/release-browser.yml @@ -132,11 +132,11 @@ jobs: env: PACKAGE_VERSION: ${{ needs.setup.outputs.release_version }} run: | - mv browser-source.zip "browser-source-$PACKAGE_VERSION.zip" - mv dist-chrome.zip "dist-chrome-$PACKAGE_VERSION.zip" - mv dist-opera.zip "dist-opera-$PACKAGE_VERSION.zip" - mv dist-firefox.zip "dist-firefox-$PACKAGE_VERSION.zip" - mv dist-edge.zip "dist-edge-$PACKAGE_VERSION.zip" + mv browser-source.zip "browser-source-${PACKAGE_VERSION}.zip" + mv dist-chrome.zip "dist-chrome-${PACKAGE_VERSION}.zip" + mv dist-opera.zip "dist-opera-${PACKAGE_VERSION}.zip" + mv dist-firefox.zip "dist-firefox-${PACKAGE_VERSION}.zip" + mv dist-edge.zip "dist-edge-${PACKAGE_VERSION}.zip" - name: Create release if: ${{ github.event.inputs.release_type != 'Dry Run' }} diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 35fc8bed8a9..53132d8647c 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -58,9 +58,9 @@ jobs: - name: Get Version Channel id: release_channel env: - _VERSION: ${{ steps.version.outputs.version }} + VERSION: ${{ steps.version.outputs.version }} run: | - case "$_VERSION" in + case "$VERSION" in *"alpha"*) echo "channel=alpha" >> "$GITHUB_OUTPUT" echo "[!] We do not yet support 'alpha'" @@ -96,7 +96,7 @@ jobs: env: PKG_VERSION: ${{ steps.version.outputs.version }} working-directory: apps/desktop/artifacts - run: mv "Bitwarden-$PKG_VERSION-universal.pkg" "Bitwarden-$PKG_VERSION-universal.pkg.archive" + run: mv "Bitwarden-${PKG_VERSION}-universal.pkg" "Bitwarden-${PKG_VERSION}-universal.pkg.archive" - name: Create Release uses: ncipollo/release-action@b7eabc95ff50cbeeedec83973935c8f306dfcd0b # v1.20.0 diff --git a/.github/workflows/release-web.yml b/.github/workflows/release-web.yml index 59022657398..9203769bc77 100644 --- a/.github/workflows/release-web.yml +++ b/.github/workflows/release-web.yml @@ -52,8 +52,7 @@ jobs: release: name: Create GitHub Release runs-on: ubuntu-22.04 - needs: - - setup + needs: setup permissions: contents: write steps: @@ -82,10 +81,10 @@ jobs: - name: Rename assets working-directory: apps/web/artifacts env: - _RELEASE_VERSION: ${{ needs.setup.outputs.release_version }} + RELEASE_VERSION: ${{ needs.setup.outputs.release_version }} run: | - mv web-*-selfhosted-COMMERCIAL.zip "web-$_RELEASE_VERSION-selfhosted-COMMERCIAL.zip" - mv web-*-selfhosted-open-source.zip "web-$_RELEASE_VERSION-selfhosted-open-source.zip" + mv web-*-selfhosted-COMMERCIAL.zip "web-${RELEASE_VERSION}-selfhosted-COMMERCIAL.zip" + mv web-*-selfhosted-open-source.zip "web-${RELEASE_VERSION}-selfhosted-open-source.zip" - name: Create release if: ${{ github.event.inputs.release_type != 'Dry Run' }} From a55d0f02f23220e446cd38a48fa399806f8e9cb7 Mon Sep 17 00:00:00 2001 From: Mark Youssef <141061617+mark-youssef-bitwarden@users.noreply.github.com> Date: Thu, 13 Nov 2025 18:59:03 -0800 Subject: [PATCH 07/75] [CL-672] update mobile design of dialog (#14828) --------- Co-authored-by: Vicki League <vleague@bitwarden.com> --- .../await-desktop-dialog.component.ts | 11 ++- ...ktop-sync-verification-dialog.component.ts | 2 + ...-file-popout-dialog-container.component.ts | 6 +- .../about-page/about-page-v2.component.ts | 6 +- .../at-risk-carousel-dialog.component.ts | 2 + ...wser-sync-verification-dialog.component.ts | 9 ++- ...erify-native-messaging-dialog.component.ts | 9 ++- ...t-organization-data-ownership.component.ts | 6 +- .../access-selector-dialog.stories.ts | 2 +- .../navigation-switcher.component.spec.ts | 2 +- .../setup-extension.component.ts | 2 + .../vault-item-dialog.component.ts | 2 + .../bulk-delete-dialog.component.ts | 6 +- .../overview/overview.component.ts | 3 +- .../project/project-secrets.component.ts | 3 +- .../secrets/dialog/secret-dialog.component.ts | 2 + .../secrets/secrets.component.ts | 8 ++- .../secrets-manager/trash/trash.component.ts | 4 +- .../premium-upgrade-dialog.component.ts | 5 +- .../fingerprint-dialog.component.ts | 13 +++- libs/components/src/dialog/dialog.service.ts | 67 ++++++++++++++++++- .../src/dialog/dialog/dialog.component.html | 6 +- .../src/dialog/dialog/dialog.component.ts | 43 ++++++++---- .../src/dialog/dialog/dialog.stories.ts | 15 +++-- .../simple-dialog.service.stories.ts | 5 +- .../src/navigation/side-nav.service.ts | 20 +++--- libs/components/src/utils/responsive-utils.ts | 27 ++++++++ libs/components/tailwind.config.base.js | 14 ++++ .../advanced-uri-option-dialog.component.ts | 2 + .../decryption-failure-dialog.component.ts | 6 +- 30 files changed, 255 insertions(+), 53 deletions(-) create mode 100644 libs/components/src/utils/responsive-utils.ts diff --git a/apps/browser/src/auth/popup/settings/await-desktop-dialog.component.ts b/apps/browser/src/auth/popup/settings/await-desktop-dialog.component.ts index a64cea1ef3e..12cf669d89b 100644 --- a/apps/browser/src/auth/popup/settings/await-desktop-dialog.component.ts +++ b/apps/browser/src/auth/popup/settings/await-desktop-dialog.component.ts @@ -1,7 +1,12 @@ import { Component } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components"; +import { + ButtonModule, + CenterPositionStrategy, + DialogModule, + DialogService, +} from "@bitwarden/components"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @@ -11,6 +16,8 @@ import { ButtonModule, DialogModule, DialogService } from "@bitwarden/components }) export class AwaitDesktopDialogComponent { static open(dialogService: DialogService) { - return dialogService.open<boolean>(AwaitDesktopDialogComponent); + return dialogService.open<boolean>(AwaitDesktopDialogComponent, { + positionStrategy: new CenterPositionStrategy(), + }); } } 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 510348927ce..e1774dbbddd 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 @@ -9,6 +9,7 @@ import { ButtonModule, DialogModule, DialogService, + CenterPositionStrategy, } from "@bitwarden/components"; export type DesktopSyncVerificationDialogParams = { @@ -49,6 +50,7 @@ export class DesktopSyncVerificationDialogComponent implements OnDestroy, OnInit static open(dialogService: DialogService, data: DesktopSyncVerificationDialogParams) { return dialogService.open(DesktopSyncVerificationDialogComponent, { data, + positionStrategy: new CenterPositionStrategy(), }); } } diff --git a/apps/browser/src/tools/popup/send-v2/send-file-popout-dialog/send-file-popout-dialog-container.component.ts b/apps/browser/src/tools/popup/send-v2/send-file-popout-dialog/send-file-popout-dialog-container.component.ts index 56b8bcbb9f5..1f0d9f2a0c9 100644 --- a/apps/browser/src/tools/popup/send-v2/send-file-popout-dialog/send-file-popout-dialog-container.component.ts +++ b/apps/browser/src/tools/popup/send-v2/send-file-popout-dialog/send-file-popout-dialog-container.component.ts @@ -3,7 +3,7 @@ import { Component, input, OnInit } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; -import { DialogService } from "@bitwarden/components"; +import { CenterPositionStrategy, DialogService } from "@bitwarden/components"; import { SendFormConfig } from "@bitwarden/send-ui"; import { FilePopoutUtilsService } from "../../services/file-popout-utils.service"; @@ -33,7 +33,9 @@ export class SendFilePopoutDialogContainerComponent implements OnInit { this.config().mode === "add" && this.filePopoutUtilsService.showFilePopoutMessage(window) ) { - this.dialogService.open(SendFilePopoutDialogComponent); + this.dialogService.open(SendFilePopoutDialogComponent, { + positionStrategy: new CenterPositionStrategy(), + }); } } } diff --git a/apps/browser/src/tools/popup/settings/about-page/about-page-v2.component.ts b/apps/browser/src/tools/popup/settings/about-page/about-page-v2.component.ts index 2ef830d9d94..88f6ad96807 100644 --- a/apps/browser/src/tools/popup/settings/about-page/about-page-v2.component.ts +++ b/apps/browser/src/tools/popup/settings/about-page/about-page-v2.component.ts @@ -7,7 +7,7 @@ import { JslibModule } from "@bitwarden/angular/jslib.module"; import { DeviceType } from "@bitwarden/common/enums"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService, ItemModule } from "@bitwarden/components"; +import { CenterPositionStrategy, DialogService, ItemModule } from "@bitwarden/components"; import { BrowserApi } from "../../../../platform/browser/browser-api"; import { PopOutComponent } from "../../../../platform/popup/components/pop-out.component"; @@ -51,7 +51,9 @@ export class AboutPageV2Component { ) {} about() { - this.dialogService.open(AboutDialogComponent); + this.dialogService.open(AboutDialogComponent, { + positionStrategy: new CenterPositionStrategy(), + }); } async launchHelp() { diff --git a/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts b/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts index f81bccc760c..1b83c316f41 100644 --- a/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts +++ b/apps/browser/src/vault/popup/components/at-risk-carousel-dialog/at-risk-carousel-dialog.component.ts @@ -7,6 +7,7 @@ import { DialogModule, DialogService, TypographyModule, + CenterPositionStrategy, } from "@bitwarden/components"; import { I18nPipe } from "@bitwarden/ui-common"; import { DarkImageSourceDirective, VaultCarouselModule } from "@bitwarden/vault"; @@ -52,6 +53,7 @@ export class AtRiskCarouselDialogComponent { static open(dialogService: DialogService) { return dialogService.open<AtRiskCarouselDialogResult>(AtRiskCarouselDialogComponent, { disableClose: true, + positionStrategy: new CenterPositionStrategy(), }); } } 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 5d3c777f333..d65df60a8ce 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 @@ -1,7 +1,13 @@ import { Component, Inject } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { DIALOG_DATA, ButtonModule, DialogModule, DialogService } from "@bitwarden/components"; +import { + DIALOG_DATA, + ButtonModule, + DialogModule, + DialogService, + CenterPositionStrategy, +} from "@bitwarden/components"; export type BrowserSyncVerificationDialogParams = { fingerprint: string[]; @@ -19,6 +25,7 @@ export class BrowserSyncVerificationDialogComponent { static open(dialogService: DialogService, data: BrowserSyncVerificationDialogParams) { return dialogService.open(BrowserSyncVerificationDialogComponent, { data, + positionStrategy: new CenterPositionStrategy(), }); } } 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 14c2b137d73..6f9695f856a 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 @@ -1,7 +1,13 @@ import { Component, Inject } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; -import { DIALOG_DATA, ButtonModule, DialogModule, DialogService } from "@bitwarden/components"; +import { + DIALOG_DATA, + ButtonModule, + DialogModule, + DialogService, + CenterPositionStrategy, +} from "@bitwarden/components"; export type VerifyNativeMessagingDialogData = { applicationName: string; @@ -19,6 +25,7 @@ export class VerifyNativeMessagingDialogComponent { static open(dialogService: DialogService, data: VerifyNativeMessagingDialogData) { return dialogService.open<boolean>(VerifyNativeMessagingDialogComponent, { data, + positionStrategy: new CenterPositionStrategy(), }); } } diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts index a15c51ebf70..a0d425d5886 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts @@ -9,7 +9,7 @@ import { EncryptService } from "@bitwarden/common/key-management/crypto/abstract import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { OrgKey } from "@bitwarden/common/types/key"; -import { DialogService } from "@bitwarden/components"; +import { CenterPositionStrategy, DialogService } from "@bitwarden/components"; import { EncString } from "@bitwarden/sdk-internal"; import { SharedModule } from "../../../../shared"; @@ -58,7 +58,9 @@ export class vNextOrganizationDataOwnershipPolicyComponent override async confirm(): Promise<boolean> { if (this.policyResponse?.enabled && !this.enabled.value) { - const dialogRef = this.dialogService.open(this.warningContent); + const dialogRef = this.dialogService.open(this.warningContent, { + positionStrategy: new CenterPositionStrategy(), + }); const result = await lastValueFrom(dialogRef.closed); return Boolean(result); } diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector-dialog.stories.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector-dialog.stories.ts index 5cb61197b99..3e23eff13a9 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector-dialog.stories.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/access-selector-dialog.stories.ts @@ -25,7 +25,7 @@ const render: Story["render"] = (args) => ({ ...args, }, template: ` - <bit-dialog [dialogSize]="dialogSize" [disablePadding]="disablePadding"> + <bit-dialog [dialogSize]="dialogSize" [disablePadding]="disablePadding" disableAnimations> <span bitDialogTitle>Access selector</span> <span bitDialogContent> <bit-access-selector diff --git a/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.spec.ts b/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.spec.ts index 873b306a450..9f6c8f6b194 100644 --- a/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.spec.ts +++ b/apps/web/src/app/layouts/product-switcher/navigation-switcher/navigation-switcher.component.spec.ts @@ -28,7 +28,7 @@ class MockUpgradeNavButtonComponent {} Object.defineProperty(window, "matchMedia", { writable: true, value: jest.fn().mockImplementation((query) => ({ - matches: false, + matches: true, media: query, onchange: null, addListener: jest.fn(), // deprecated diff --git a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts index b5c0d096944..974e73bc91e 100644 --- a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts +++ b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts @@ -16,6 +16,7 @@ import { getWebStoreUrl } from "@bitwarden/common/vault/utils/get-web-store-url" import { AnonLayoutWrapperDataService, ButtonComponent, + CenterPositionStrategy, DialogRef, DialogService, IconModule, @@ -151,6 +152,7 @@ export class SetupExtensionComponent implements OnInit, OnDestroy { data: { onDismiss: this.dismissExtensionPage.bind(this), }, + positionStrategy: new CenterPositionStrategy(), }, ); } diff --git a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts index 98922fb114f..8508596a67b 100644 --- a/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts +++ b/apps/web/src/app/vault/components/vault-item-dialog/vault-item-dialog.component.ts @@ -48,6 +48,7 @@ import { DialogService, ItemModule, ToastService, + CenterPositionStrategy, } from "@bitwarden/components"; import { AttachmentDialogCloseResult, @@ -331,6 +332,7 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy { if (this.cipher.decryptionFailure) { this.dialogService.open(DecryptionFailureDialogComponent, { data: { cipherIds: [this.cipher.id] }, + positionStrategy: new CenterPositionStrategy(), }); this.dialogRef.close(); return; diff --git a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts index 3856bb65324..5f139ade144 100644 --- a/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts +++ b/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.component.ts @@ -14,6 +14,7 @@ import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.servi import { CipherBulkDeleteRequest } from "@bitwarden/common/vault/models/request/cipher-bulk-delete.request"; import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; import { + CenterPositionStrategy, DIALOG_DATA, DialogConfig, DialogRef, @@ -48,7 +49,10 @@ export const openBulkDeleteDialog = ( ) => { return dialogService.open<BulkDeleteDialogResult, BulkDeleteDialogParams>( BulkDeleteDialogComponent, - config, + { + positionStrategy: new CenterPositionStrategy(), + ...config, + }, ); }; 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 12a5432c4b8..6995549e845 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 @@ -26,7 +26,7 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService } from "@bitwarden/components"; +import { CenterPositionStrategy, DialogService } from "@bitwarden/components"; import { OrganizationCounts } from "../models/view/counts.view"; import { ProjectListView } from "../models/view/project-list.view"; @@ -341,6 +341,7 @@ export class OverviewComponent implements OnInit, OnDestroy { data: { secrets: event, }, + positionStrategy: new CenterPositionStrategy(), }); } 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 7112a28010f..9cd570a734a 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 @@ -21,7 +21,7 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService } from "@bitwarden/components"; +import { CenterPositionStrategy, DialogService } from "@bitwarden/components"; import { ProjectView } from "../../models/view/project.view"; import { SecretListView } from "../../models/view/secret-list.view"; @@ -126,6 +126,7 @@ export class ProjectSecretsComponent implements OnInit { data: { secrets: event, }, + positionStrategy: new CenterPositionStrategy(), }); } 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 6376b58423d..53325fe2f54 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 @@ -19,6 +19,7 @@ import { DialogService, BitValidators, ToastService, + CenterPositionStrategy, } from "@bitwarden/components"; import { SecretAccessPoliciesView } from "../../models/view/access-policies/secret-access-policies.view"; @@ -225,6 +226,7 @@ export class SecretDialogComponent implements OnInit, OnDestroy { data: { secrets: secretListView, }, + positionStrategy: new CenterPositionStrategy(), }, ); 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 46cccb1d95d..92b33a06d4f 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 @@ -13,7 +13,12 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogRef, DialogService, ToastService } from "@bitwarden/components"; +import { + CenterPositionStrategy, + DialogRef, + DialogService, + ToastService, +} from "@bitwarden/components"; import { openEntityEventsDialog } from "@bitwarden/web-vault/app/admin-console/organizations/manage/entity-events.component"; import { SecretListView } from "../models/view/secret-list.view"; @@ -180,6 +185,7 @@ export class SecretsComponent implements OnInit { data: { secrets: event, }, + positionStrategy: new CenterPositionStrategy(), }); } 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 b4da7769127..1e6483dcf92 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 @@ -6,7 +6,7 @@ import { combineLatestWith, Observable, startWith, switchMap } from "rxjs"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; -import { DialogService } from "@bitwarden/components"; +import { CenterPositionStrategy, DialogService } from "@bitwarden/components"; import { SecretListView } from "../models/view/secret-list.view"; import { SecretService } from "../secrets/secret.service"; @@ -64,6 +64,7 @@ export class TrashComponent implements OnInit { secretIds: secretIds, organizationId: this.organizationId, }, + positionStrategy: new CenterPositionStrategy(), }); } @@ -73,6 +74,7 @@ export class TrashComponent implements OnInit { secretIds: secretIds, organizationId: this.organizationId, }, + positionStrategy: new CenterPositionStrategy(), }); } diff --git a/libs/angular/src/billing/components/premium-upgrade-dialog/premium-upgrade-dialog.component.ts b/libs/angular/src/billing/components/premium-upgrade-dialog/premium-upgrade-dialog.component.ts index 48286a5d18c..ea9def03bd2 100644 --- a/libs/angular/src/billing/components/premium-upgrade-dialog/premium-upgrade-dialog.component.ts +++ b/libs/angular/src/billing/components/premium-upgrade-dialog/premium-upgrade-dialog.component.ts @@ -17,6 +17,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { ButtonModule, ButtonType, + CenterPositionStrategy, DialogModule, DialogRef, DialogService, @@ -114,6 +115,8 @@ export class PremiumUpgradeDialogComponent { * @returns A dialog reference object */ static open(dialogService: DialogService): DialogRef<PremiumUpgradeDialogComponent> { - return dialogService.open(PremiumUpgradeDialogComponent); + return dialogService.open(PremiumUpgradeDialogComponent, { + positionStrategy: new CenterPositionStrategy(), + }); } } diff --git a/libs/auth/src/angular/fingerprint-dialog/fingerprint-dialog.component.ts b/libs/auth/src/angular/fingerprint-dialog/fingerprint-dialog.component.ts index 6ef36a32448..5e4d1cbdb49 100644 --- a/libs/auth/src/angular/fingerprint-dialog/fingerprint-dialog.component.ts +++ b/libs/auth/src/angular/fingerprint-dialog/fingerprint-dialog.component.ts @@ -3,7 +3,13 @@ import { Component, Inject } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; // 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 { DIALOG_DATA, ButtonModule, DialogModule, DialogService } from "@bitwarden/components"; +import { + DIALOG_DATA, + ButtonModule, + DialogModule, + DialogService, + CenterPositionStrategy, +} from "@bitwarden/components"; export type FingerprintDialogData = { fingerprint: string[]; @@ -19,6 +25,9 @@ export class FingerprintDialogComponent { constructor(@Inject(DIALOG_DATA) protected data: FingerprintDialogData) {} static open(dialogService: DialogService, data: FingerprintDialogData) { - return dialogService.open(FingerprintDialogComponent, { data }); + return dialogService.open(FingerprintDialogComponent, { + data, + positionStrategy: new CenterPositionStrategy(), + }); } } diff --git a/libs/components/src/dialog/dialog.service.ts b/libs/components/src/dialog/dialog.service.ts index 409bf0a5b55..1fc452418e1 100644 --- a/libs/components/src/dialog/dialog.service.ts +++ b/libs/components/src/dialog/dialog.service.ts @@ -5,7 +5,7 @@ import { DIALOG_DATA, DialogCloseOptions, } from "@angular/cdk/dialog"; -import { ComponentType, ScrollStrategy } from "@angular/cdk/overlay"; +import { ComponentType, GlobalPositionStrategy, ScrollStrategy } from "@angular/cdk/overlay"; import { ComponentPortal, Portal } from "@angular/cdk/portal"; import { Injectable, Injector, TemplateRef, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; @@ -17,6 +17,7 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { DrawerService } from "../drawer/drawer.service"; +import { isAtOrLargerThanBreakpoint } from "../utils/responsive-utils"; import { SimpleConfigurableDialogComponent } from "./simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component"; import { SimpleDialogOptions } from "./simple-dialog/types"; @@ -63,6 +64,68 @@ export type DialogConfig<D = unknown, R = unknown> = Pick< "data" | "disableClose" | "ariaModal" | "positionStrategy" | "height" | "width" >; +/** + * A responsive position strategy that adjusts the dialog position based on the screen size. + */ +class ResponsivePositionStrategy extends GlobalPositionStrategy { + private abortController: AbortController | null = null; + + /** + * The previous breakpoint to avoid unnecessary updates. + * `null` means no previous breakpoint has been set. + */ + private prevBreakpoint: "small" | "large" | null = null; + + constructor() { + super(); + if (typeof window !== "undefined") { + this.abortController = new AbortController(); + this.updatePosition(); // Initial position update + window.addEventListener("resize", this.updatePosition.bind(this), { + signal: this.abortController.signal, + }); + } + } + + override dispose() { + this.abortController?.abort(); + this.abortController = null; + super.dispose(); + } + + updatePosition() { + const isSmallScreen = !isAtOrLargerThanBreakpoint("md"); + const currentBreakpoint = isSmallScreen ? "small" : "large"; + if (this.prevBreakpoint === currentBreakpoint) { + return; // No change in breakpoint, no need to update position + } + this.prevBreakpoint = currentBreakpoint; + if (isSmallScreen) { + this.bottom().centerHorizontally(); + } else { + this.centerVertically().centerHorizontally(); + } + this.apply(); + } +} + +/** + * Position strategy that centers dialogs regardless of screen size. + * Use this for simple dialogs and custom dialogs that should not use + * the responsive bottom-sheet behavior on mobile. + * + * @example + * dialogService.open(MyComponent, { + * positionStrategy: new CenterPositionStrategy() + * }); + */ +export class CenterPositionStrategy extends GlobalPositionStrategy { + constructor() { + super(); + this.centerHorizontally().centerVertically(); + } +} + class DrawerDialogRef<R = unknown, C = unknown> implements DialogRef<R, C> { readonly isDrawer = true; @@ -172,6 +235,7 @@ export class DialogService { const _config = { backdropClass: this.backDropClasses, scrollStrategy: this.defaultScrollStrategy, + positionStrategy: config?.positionStrategy ?? new ResponsivePositionStrategy(), injector, ...config, }; @@ -226,6 +290,7 @@ export class DialogService { return this.open<boolean, SimpleDialogOptions>(SimpleConfigurableDialogComponent, { data: simpleDialogOptions, disableClose: simpleDialogOptions.disableClose, + positionStrategy: new CenterPositionStrategy(), }); } diff --git a/libs/components/src/dialog/dialog/dialog.component.html b/libs/components/src/dialog/dialog/dialog.component.html index 5774d83e349..83cfa21ed21 100644 --- a/libs/components/src/dialog/dialog/dialog.component.html +++ b/libs/components/src/dialog/dialog/dialog.component.html @@ -1,8 +1,10 @@ @let isDrawer = dialogRef?.isDrawer; <section class="tw-flex tw-w-full tw-flex-col tw-self-center tw-overflow-hidden tw-border tw-border-solid tw-border-secondary-100 tw-bg-background tw-text-main" - [ngClass]="[width, isDrawer ? 'tw-h-screen tw-border-t-0' : 'tw-rounded-xl tw-shadow-lg']" - @fadeIn + [ngClass]="[ + width, + isDrawer ? 'tw-h-screen tw-border-t-0' : 'tw-rounded-t-xl md:tw-rounded-xl tw-shadow-lg', + ]" cdkTrapFocus cdkTrapFocusAutoCapture > diff --git a/libs/components/src/dialog/dialog/dialog.component.ts b/libs/components/src/dialog/dialog/dialog.component.ts index 71d594ef19e..954f03aabe2 100644 --- a/libs/components/src/dialog/dialog/dialog.component.ts +++ b/libs/components/src/dialog/dialog/dialog.component.ts @@ -3,13 +3,14 @@ import { CdkScrollable } from "@angular/cdk/scrolling"; import { CommonModule } from "@angular/common"; import { Component, - HostBinding, inject, viewChild, input, booleanAttribute, ElementRef, DestroyRef, + computed, + signal, } from "@angular/core"; import { toObservable } from "@angular/core/rxjs-interop"; import { combineLatest, switchMap } from "rxjs"; @@ -21,7 +22,6 @@ import { SpinnerComponent } from "../../spinner"; import { TypographyDirective } from "../../typography/typography.directive"; import { hasScrollableContent$ } from "../../utils/"; import { hasScrolledFrom } from "../../utils/has-scrolled-from"; -import { fadeIn } from "../animations"; import { DialogRef } from "../dialog.service"; import { DialogCloseDirective } from "../directives/dialog-close.directive"; import { DialogTitleContainerDirective } from "../directives/dialog-title-container.directive"; @@ -31,9 +31,10 @@ import { DialogTitleContainerDirective } from "../directives/dialog-title-contai @Component({ selector: "bit-dialog", templateUrl: "./dialog.component.html", - animations: [fadeIn], host: { + "[class]": "classes()", "(keydown.esc)": "handleEsc($event)", + "(animationend)": "onAnimationEnd()", }, imports: [ CommonModule, @@ -87,22 +88,34 @@ export class DialogComponent { */ readonly disablePadding = input(false, { transform: booleanAttribute }); + /** + * Disable animations for the dialog. + */ + readonly disableAnimations = input(false, { transform: booleanAttribute }); + /** * Mark the dialog as loading which replaces the content with a spinner. */ readonly loading = input(false); - @HostBinding("class") get classes() { + private readonly animationCompleted = signal(false); + + protected readonly classes = computed(() => { // `tw-max-h-[90vh]` is needed to prevent dialogs from overlapping the desktop header - return ["tw-flex", "tw-flex-col", "tw-w-screen"] - .concat( - this.width, - this.dialogRef?.isDrawer - ? ["tw-min-h-screen", "md:tw-w-[23rem]"] - : ["tw-p-4", "tw-w-screen", "tw-max-h-[90vh]"], - ) - .flat(); - } + const baseClasses = ["tw-flex", "tw-flex-col", "tw-w-screen"]; + const sizeClasses = this.dialogRef?.isDrawer + ? ["tw-min-h-screen", "md:tw-w-[23rem]"] + : ["md:tw-p-4", "tw-w-screen", "tw-max-h-[90vh]"]; + + const animationClasses = + this.disableAnimations() || this.animationCompleted() || this.dialogRef?.isDrawer + ? [] + : this.dialogSize() === "small" + ? ["tw-animate-slide-down"] + : ["tw-animate-slide-up", "md:tw-animate-slide-down"]; + + return [...baseClasses, this.width, ...sizeClasses, ...animationClasses]; + }); handleEsc(event: Event) { if (!this.dialogRef?.disableClose) { @@ -124,4 +137,8 @@ export class DialogComponent { } } } + + onAnimationEnd() { + this.animationCompleted.set(true); + } } diff --git a/libs/components/src/dialog/dialog/dialog.stories.ts b/libs/components/src/dialog/dialog/dialog.stories.ts index d645d32764d..1f33ab7e877 100644 --- a/libs/components/src/dialog/dialog/dialog.stories.ts +++ b/libs/components/src/dialog/dialog/dialog.stories.ts @@ -57,6 +57,7 @@ export default { args: { loading: false, dialogSize: "small", + disableAnimations: true, }, argTypes: { _disablePadding: { @@ -71,6 +72,9 @@ export default { defaultValue: "default", }, }, + disableAnimations: { + control: { type: "boolean" }, + }, }, parameters: { design: { @@ -86,7 +90,7 @@ export const Default: Story = { render: (args) => ({ props: args, template: /*html*/ ` - <bit-dialog [dialogSize]="dialogSize" [title]="title" [subtitle]="subtitle" [loading]="loading" [disablePadding]="disablePadding"> + <bit-dialog [dialogSize]="dialogSize" [title]="title" [subtitle]="subtitle" [loading]="loading" [disablePadding]="disablePadding" [disableAnimations]="disableAnimations"> <ng-container bitDialogTitle> <span bitBadge variant="success">Foobar</span> </ng-container> @@ -158,7 +162,7 @@ export const ScrollingContent: Story = { render: (args) => ({ props: args, template: /*html*/ ` - <bit-dialog title="Scrolling Example" [background]="background" [dialogSize]="dialogSize" [loading]="loading" [disablePadding]="disablePadding"> + <bit-dialog title="Scrolling Example" [background]="background" [dialogSize]="dialogSize" [loading]="loading" [disablePadding]="disablePadding" [disableAnimations]="disableAnimations"> <span bitDialogContent> Dialog body text goes here.<br /> <ng-container *ngFor="let _ of [].constructor(100)"> @@ -175,6 +179,7 @@ export const ScrollingContent: Story = { }), args: { dialogSize: "small", + disableAnimations: true, }, }; @@ -182,7 +187,7 @@ export const TabContent: Story = { render: (args) => ({ props: args, template: /*html*/ ` - <bit-dialog title="Tab Content Example" [background]="background" [dialogSize]="dialogSize" [disablePadding]="disablePadding"> + <bit-dialog title="Tab Content Example" [background]="background" [dialogSize]="dialogSize" [disablePadding]="disablePadding" [disableAnimations]="disableAnimations"> <span bitDialogContent> <bit-tab-group> <bit-tab label="First Tab">First Tab Content</bit-tab> @@ -200,6 +205,7 @@ export const TabContent: Story = { args: { dialogSize: "large", disablePadding: true, + disableAnimations: true, }, parameters: { docs: { @@ -219,7 +225,7 @@ export const WithCards: Story = { }, template: /*html*/ ` <form [formGroup]="formObj"> - <bit-dialog [dialogSize]="dialogSize" [background]="background" [title]="title" [subtitle]="subtitle" [loading]="loading" [disablePadding]="disablePadding"> + <bit-dialog [dialogSize]="dialogSize" [background]="background" [title]="title" [subtitle]="subtitle" [loading]="loading" [disablePadding]="disablePadding" [disableAnimations]="disableAnimations"> <ng-container bitDialogContent> <bit-section> <bit-section-header> @@ -283,5 +289,6 @@ export const WithCards: Story = { title: "Default", subtitle: "Subtitle", background: "alt", + disableAnimations: true, }, }; diff --git a/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts b/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts index 5c94a959f25..b682b9f772a 100644 --- a/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts +++ b/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts @@ -9,7 +9,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { ButtonModule } from "../../button"; import { I18nMockService } from "../../utils/i18n-mock.service"; import { DialogModule } from "../dialog.module"; -import { DialogService } from "../dialog.service"; +import { CenterPositionStrategy, DialogService } from "../dialog.service"; interface Animal { animal: string; @@ -37,6 +37,7 @@ class StoryDialogComponent { data: { animal: "panda", }, + positionStrategy: new CenterPositionStrategy(), }); } @@ -46,6 +47,7 @@ class StoryDialogComponent { animal: "panda", }, disableClose: true, + positionStrategy: new CenterPositionStrategy(), }); } @@ -55,6 +57,7 @@ class StoryDialogComponent { animal: "panda", }, disableClose: true, + positionStrategy: new CenterPositionStrategy(), }); } } diff --git a/libs/components/src/navigation/side-nav.service.ts b/libs/components/src/navigation/side-nav.service.ts index 979cba1e3de..ce44811c7e0 100644 --- a/libs/components/src/navigation/side-nav.service.ts +++ b/libs/components/src/navigation/side-nav.service.ts @@ -2,32 +2,30 @@ import { Injectable } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { BehaviorSubject, Observable, combineLatest, fromEvent, map, startWith } from "rxjs"; -type CollapsePreference = "open" | "closed" | null; +import { BREAKPOINTS, isAtOrLargerThanBreakpoint } from "../utils/responsive-utils"; -const SMALL_SCREEN_BREAKPOINT_PX = 768; +type CollapsePreference = "open" | "closed" | null; @Injectable({ providedIn: "root", }) export class SideNavService { - private _open$ = new BehaviorSubject<boolean>( - !window.matchMedia(`(max-width: ${SMALL_SCREEN_BREAKPOINT_PX}px)`).matches, - ); + private _open$ = new BehaviorSubject<boolean>(isAtOrLargerThanBreakpoint("md")); open$ = this._open$.asObservable(); - private isSmallScreen$ = media(`(max-width: ${SMALL_SCREEN_BREAKPOINT_PX}px)`); + private isLargeScreen$ = media(`(min-width: ${BREAKPOINTS.md}px)`); private _userCollapsePreference$ = new BehaviorSubject<CollapsePreference>(null); userCollapsePreference$ = this._userCollapsePreference$.asObservable(); - isOverlay$ = combineLatest([this.open$, this.isSmallScreen$]).pipe( - map(([open, isSmallScreen]) => open && isSmallScreen), + isOverlay$ = combineLatest([this.open$, this.isLargeScreen$]).pipe( + map(([open, isLargeScreen]) => open && !isLargeScreen), ); constructor() { - combineLatest([this.isSmallScreen$, this.userCollapsePreference$]) + combineLatest([this.isLargeScreen$, this.userCollapsePreference$]) .pipe(takeUntilDestroyed()) - .subscribe(([isSmallScreen, userCollapsePreference]) => { - if (isSmallScreen) { + .subscribe(([isLargeScreen, userCollapsePreference]) => { + if (!isLargeScreen) { this.setClose(); } else if (userCollapsePreference !== "closed") { // Auto-open when user hasn't set preference (null) or prefers open diff --git a/libs/components/src/utils/responsive-utils.ts b/libs/components/src/utils/responsive-utils.ts new file mode 100644 index 00000000000..a9c2499c275 --- /dev/null +++ b/libs/components/src/utils/responsive-utils.ts @@ -0,0 +1,27 @@ +/** + * Breakpoint definitions in pixels matching Tailwind CSS default breakpoints. + * These values must stay in sync with tailwind.config.base.js theme.extend configuration. + * + * @see {@link https://tailwindcss.com/docs/responsive-design} for tailwind default breakpoints + * @see {@link /libs/components/src/stories/responsive-design.mdx} for design system usage + */ +export const BREAKPOINTS = { + sm: 640, + md: 768, + lg: 1024, + xl: 1280, + "2xl": 1536, +}; + +/** + * Checks if the current viewport is at or larger than the specified breakpoint. + * @param size The breakpoint to check. + * @returns True if the viewport is at or larger than the breakpoint, false otherwise. + */ +export const isAtOrLargerThanBreakpoint = (size: keyof typeof BREAKPOINTS): boolean => { + if (typeof window === "undefined" || !window.matchMedia) { + return false; + } + const query = `(min-width: ${BREAKPOINTS[size]}px)`; + return window.matchMedia(query).matches; +}; diff --git a/libs/components/tailwind.config.base.js b/libs/components/tailwind.config.base.js index ce399d860c1..e41cff16e48 100644 --- a/libs/components/tailwind.config.base.js +++ b/libs/components/tailwind.config.base.js @@ -167,6 +167,20 @@ module.exports = { container: { "@5xl": "1100px", }, + keyframes: { + slideUp: { + "0%": { opacity: "0", transform: "translateY(50px)" }, + "100%": { opacity: "1", transform: "translateY(0)" }, + }, + slideDown: { + "0%": { opacity: "0", transform: "translateY(-50px)" }, + "100%": { opacity: "1", transform: "translateY(0)" }, + }, + }, + animation: { + "slide-up": "slideUp 0.3s ease-out", + "slide-down": "slideDown 0.3s ease-out", + }, }, }, plugins: [ diff --git a/libs/vault/src/cipher-form/components/autofill-options/advanced-uri-option-dialog.component.ts b/libs/vault/src/cipher-form/components/autofill-options/advanced-uri-option-dialog.component.ts index f78c2c170f8..3580b1fada8 100644 --- a/libs/vault/src/cipher-form/components/autofill-options/advanced-uri-option-dialog.component.ts +++ b/libs/vault/src/cipher-form/components/autofill-options/advanced-uri-option-dialog.component.ts @@ -9,6 +9,7 @@ import { DialogService, DIALOG_DATA, DialogRef, + CenterPositionStrategy, } from "@bitwarden/components"; export type AdvancedUriOptionDialogParams = { @@ -55,6 +56,7 @@ export class AdvancedUriOptionDialogComponent { return dialogService.open<boolean>(AdvancedUriOptionDialogComponent, { data: params, disableClose: true, + positionStrategy: new CenterPositionStrategy(), }); } } diff --git a/libs/vault/src/components/decryption-failure-dialog/decryption-failure-dialog.component.ts b/libs/vault/src/components/decryption-failure-dialog/decryption-failure-dialog.component.ts index 628de79b3da..6b1a0e0d8aa 100644 --- a/libs/vault/src/components/decryption-failure-dialog/decryption-failure-dialog.component.ts +++ b/libs/vault/src/components/decryption-failure-dialog/decryption-failure-dialog.component.ts @@ -13,6 +13,7 @@ import { DialogModule, DialogService, TypographyModule, + CenterPositionStrategy, } from "@bitwarden/components"; export type DecryptionFailureDialogParams = { @@ -56,6 +57,9 @@ export class DecryptionFailureDialogComponent { } static open(dialogService: DialogService, params: DecryptionFailureDialogParams) { - return dialogService.open(DecryptionFailureDialogComponent, { data: params }); + return dialogService.open(DecryptionFailureDialogComponent, { + data: params, + positionStrategy: new CenterPositionStrategy(), + }); } } From ed2d8b9549e5ab1ccdbd67a24b8dbcaecf7e67a0 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu <acoroiu@bitwarden.com> Date: Fri, 14 Nov 2025 08:51:38 +0100 Subject: [PATCH 08/75] [PM-18046] Implement session storage (#17346) * feat: add support for IPC client managed session storage * feat: update SDK * fix: using undecorated service in jslib module directly * feat: add test case for web * chore: document why we use any type * fix: `ipc` too short * typo: omg * Revert "typo: omg" This reverts commit 559b05eb5ab8522b9c5455fc76e8d39afcf8c3d6. * Revert "fix: `ipc` too short" This reverts commit 35fc99e10b60daff993115024fd703a1f0960d12. * fix: use camelCase --- .../browser/src/background/main.background.ts | 9 +++- .../platform/ipc/ipc-background.service.ts | 6 ++- .../src/app/platform/ipc/web-ipc.service.ts | 12 ++++- .../src/services/jslib-services.module.ts | 6 +++ libs/common/src/platform/ipc/index.ts | 1 + .../ipc/ipc-session-repository.spec.ts | 49 ++++++++++++++++++ .../platform/ipc/ipc-session-repository.ts | 51 +++++++++++++++++++ libs/state/src/core/state-definitions.ts | 1 + package-lock.json | 16 +++--- package.json | 4 +- 10 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 libs/common/src/platform/ipc/ipc-session-repository.spec.ts create mode 100644 libs/common/src/platform/ipc/ipc-session-repository.ts diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 97bfe804411..cff783942fe 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -131,7 +131,7 @@ import { } from "@bitwarden/common/platform/abstractions/storage.service"; import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/platform/abstractions/system.service"; import { ActionsService } from "@bitwarden/common/platform/actions/actions-service"; -import { IpcService } from "@bitwarden/common/platform/ipc"; +import { IpcService, IpcSessionRepository } from "@bitwarden/common/platform/ipc"; import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging"; // eslint-disable-next-line no-restricted-imports -- Used for dependency creation import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal"; @@ -1476,7 +1476,12 @@ export default class MainBackground { ); this.ipcContentScriptManagerService = new IpcContentScriptManagerService(this.configService); - this.ipcService = new IpcBackgroundService(this.platformUtilsService, this.logService); + const ipcSessionRepository = new IpcSessionRepository(this.stateProvider); + this.ipcService = new IpcBackgroundService( + this.platformUtilsService, + this.logService, + ipcSessionRepository, + ); this.endUserNotificationService = new DefaultEndUserNotificationService( this.stateProvider, diff --git a/apps/browser/src/platform/ipc/ipc-background.service.ts b/apps/browser/src/platform/ipc/ipc-background.service.ts index 911ca931c70..9fc2ca24b6a 100644 --- a/apps/browser/src/platform/ipc/ipc-background.service.ts +++ b/apps/browser/src/platform/ipc/ipc-background.service.ts @@ -8,6 +8,7 @@ import { OutgoingMessage, ipcRegisterDiscoverHandler, IpcClient, + IpcSessionRepository, } from "@bitwarden/sdk-internal"; import { BrowserApi } from "../browser/browser-api"; @@ -18,6 +19,7 @@ export class IpcBackgroundService extends IpcService { constructor( private platformUtilsService: PlatformUtilsService, private logService: LogService, + private sessionRepository: IpcSessionRepository, ) { super(); } @@ -70,7 +72,9 @@ export class IpcBackgroundService extends IpcService { ); }); - await super.initWithClient(new IpcClient(this.communicationBackend)); + await super.initWithClient( + IpcClient.newWithClientManagedSessions(this.communicationBackend, this.sessionRepository), + ); if (this.platformUtilsService.isDev()) { await ipcRegisterDiscoverHandler(this.client, { diff --git a/apps/web/src/app/platform/ipc/web-ipc.service.ts b/apps/web/src/app/platform/ipc/web-ipc.service.ts index 590c1f36cc4..c6614759b44 100644 --- a/apps/web/src/app/platform/ipc/web-ipc.service.ts +++ b/apps/web/src/app/platform/ipc/web-ipc.service.ts @@ -3,7 +3,12 @@ import { inject } from "@angular/core"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; -import { IpcMessage, IpcService, isIpcMessage } from "@bitwarden/common/platform/ipc"; +import { + IpcMessage, + IpcService, + isIpcMessage, + IpcSessionRepository, +} from "@bitwarden/common/platform/ipc"; import { IncomingMessage, IpcClient, @@ -15,6 +20,7 @@ import { export class WebIpcService extends IpcService { private logService = inject(LogService); private platformUtilsService = inject(PlatformUtilsService); + private sessionRepository = inject(IpcSessionRepository); private communicationBackend?: IpcCommunicationBackend; override async init() { @@ -70,7 +76,9 @@ export class WebIpcService extends IpcService { ); }); - await super.initWithClient(new IpcClient(this.communicationBackend)); + await super.initWithClient( + IpcClient.newWithClientManagedSessions(this.communicationBackend, this.sessionRepository), + ); if (this.platformUtilsService.isDev()) { await ipcRegisterDiscoverHandler(this.client, { diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 18c21024a6a..9dbc6679963 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -228,6 +228,7 @@ import { SystemService } from "@bitwarden/common/platform/abstractions/system.se import { ValidationService as ValidationServiceAbstraction } from "@bitwarden/common/platform/abstractions/validation.service"; import { ActionsService } from "@bitwarden/common/platform/actions"; import { UnsupportedActionsService } from "@bitwarden/common/platform/actions/unsupported-actions.service"; +import { IpcSessionRepository } from "@bitwarden/common/platform/ipc"; import { Message, MessageListener, MessageSender } from "@bitwarden/common/platform/messaging"; // eslint-disable-next-line no-restricted-imports -- Used for dependency injection import { SubjectMessageSender } from "@bitwarden/common/platform/messaging/internal"; @@ -1750,6 +1751,11 @@ const safeProviders: SafeProvider[] = [ useClass: DefaultNewDeviceVerificationComponentService, deps: [], }), + safeProvider({ + provide: IpcSessionRepository, + useClass: IpcSessionRepository, + deps: [StateProvider], + }), safeProvider({ provide: PremiumInterestStateService, useClass: NoopPremiumInterestStateService, diff --git a/libs/common/src/platform/ipc/index.ts b/libs/common/src/platform/ipc/index.ts index f1acccdddbf..3fa6aeb627d 100644 --- a/libs/common/src/platform/ipc/index.ts +++ b/libs/common/src/platform/ipc/index.ts @@ -1,2 +1,3 @@ export * from "./ipc-message"; export * from "./ipc.service"; +export * from "./ipc-session-repository"; diff --git a/libs/common/src/platform/ipc/ipc-session-repository.spec.ts b/libs/common/src/platform/ipc/ipc-session-repository.spec.ts new file mode 100644 index 00000000000..62437455b08 --- /dev/null +++ b/libs/common/src/platform/ipc/ipc-session-repository.spec.ts @@ -0,0 +1,49 @@ +import { FakeActiveUserAccessor, FakeStateProvider } from "../../../spec"; +import { UserId } from "../../types/guid"; + +import { IpcSessionRepository } from "./ipc-session-repository"; + +describe("IpcSessionRepository", () => { + const userId = "user-id" as UserId; + let stateProvider!: FakeStateProvider; + let repository!: IpcSessionRepository; + + beforeEach(() => { + stateProvider = new FakeStateProvider(new FakeActiveUserAccessor(userId)); + repository = new IpcSessionRepository(stateProvider); + }); + + it("returns undefined when empty", async () => { + const result = await repository.get("BrowserBackground"); + + expect(result).toBeUndefined(); + }); + + it("saves and retrieves a session", async () => { + const session = { some: "data" }; + await repository.save("BrowserBackground", session); + + const result = await repository.get("BrowserBackground"); + + expect(result).toEqual(session); + }); + + it("saves and retrieves a web session", async () => { + const session = { some: "data" }; + await repository.save({ Web: { id: 9001 } }, session); + + const result = await repository.get({ Web: { id: 9001 } }); + + expect(result).toEqual(session); + }); + + it("removes a session", async () => { + const session = { some: "data" }; + await repository.save("BrowserBackground", session); + + await repository.remove("BrowserBackground"); + const result = await repository.get("BrowserBackground"); + + expect(result).toBeUndefined(); + }); +}); diff --git a/libs/common/src/platform/ipc/ipc-session-repository.ts b/libs/common/src/platform/ipc/ipc-session-repository.ts new file mode 100644 index 00000000000..c9f5fe4a355 --- /dev/null +++ b/libs/common/src/platform/ipc/ipc-session-repository.ts @@ -0,0 +1,51 @@ +import { firstValueFrom, map } from "rxjs"; + +import { Endpoint, IpcSessionRepository as SdkIpcSessionRepository } from "@bitwarden/sdk-internal"; + +import { GlobalState, IPC_MEMORY, KeyDefinition, StateProvider } from "../state"; + +const IPC_SESSIONS = KeyDefinition.record<object, string>(IPC_MEMORY, "ipcSessions", { + deserializer: (value: object) => value, +}); + +/** + * Implementation of SDK-defined repository interface/trait. Do not use directly. + * All error handling is done by the caller (the SDK). + * For more information see IPC docs. + * + * Interface uses `any` type as defined by the SDK until we get a concrete session type. + */ +export class IpcSessionRepository implements SdkIpcSessionRepository { + private states: GlobalState<Record<string, any>>; + + constructor(private stateProvider: StateProvider) { + this.states = this.stateProvider.getGlobal(IPC_SESSIONS); + } + + get(endpoint: Endpoint): Promise<any | undefined> { + return firstValueFrom(this.states.state$.pipe(map((s) => s?.[endpointToString(endpoint)]))); + } + + async save(endpoint: Endpoint, session: any): Promise<void> { + await this.states.update((s) => ({ + ...s, + [endpointToString(endpoint)]: session, + })); + } + + async remove(endpoint: Endpoint): Promise<void> { + await this.states.update((s) => { + const newState = { ...s }; + delete newState[endpointToString(endpoint)]; + return newState; + }); + } +} + +function endpointToString(endpoint: Endpoint): string { + if (typeof endpoint === "object" && "Web" in endpoint) { + return `Web(${endpoint.Web.id})`; + } + + return endpoint; +} diff --git a/libs/state/src/core/state-definitions.ts b/libs/state/src/core/state-definitions.ts index 7b1d75b2985..9d404f14dd7 100644 --- a/libs/state/src/core/state-definitions.ts +++ b/libs/state/src/core/state-definitions.ts @@ -127,6 +127,7 @@ export const CRYPTO_MEMORY = new StateDefinition("crypto", "memory"); export const DESKTOP_SETTINGS_DISK = new StateDefinition("desktopSettings", "disk"); export const ENVIRONMENT_DISK = new StateDefinition("environment", "disk"); export const ENVIRONMENT_MEMORY = new StateDefinition("environment", "memory"); +export const IPC_MEMORY = new StateDefinition("interProcessCommunication", "memory"); export const POPUP_VIEW_MEMORY = new StateDefinition("popupView", "memory", { browser: "memory-large-object", }); diff --git a/package-lock.json b/package-lock.json index d2a034e05a9..46b70931f65 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.374", - "@bitwarden/sdk-internal": "0.2.0-main.374", + "@bitwarden/commercial-sdk-internal": "0.2.0-main.375", + "@bitwarden/sdk-internal": "0.2.0-main.375", "@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.374", - "resolved": "https://registry.npmjs.org/@bitwarden/commercial-sdk-internal/-/commercial-sdk-internal-0.2.0-main.374.tgz", - "integrity": "sha512-OYNjEv9Z9Y1vCDWtlp7m49+Fu0WxCyJt+DDupF8T73JqWIl2SdY3ugLtLnCUnqause5VY7OAfa4eOxwn2ONKZg==", + "version": "0.2.0-main.375", + "resolved": "https://registry.npmjs.org/@bitwarden/commercial-sdk-internal/-/commercial-sdk-internal-0.2.0-main.375.tgz", + "integrity": "sha512-UMVfLjMh79+5et1if7qqOi+pSGP5Ay3AcGp4E5oLZ0p0yFsN2Q54UFv+SLju0/oI0qTvVZP1RkEtTJXHdNrpTg==", "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.374", - "resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.374.tgz", - "integrity": "sha512-P9td//6M22Eg8YcVOVtcvkD9wfdbnwNe7lZ1HGn74o3CTgDtNq0mE5x00rDeNZq0ctBaUDaqw6XS0jC/tehcag==", + "version": "0.2.0-main.375", + "resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.375.tgz", + "integrity": "sha512-kf2SKFkAdSmV2/ORo6u1eegwYW2ha62NHUsx2ij2uPWmm7mzXUoNa7z8mqhJV1ozg5o7yBqBuXd6Wqo9Ww+/RA==", "license": "GPL-3.0", "dependencies": { "type-fest": "^4.41.0" diff --git a/package.json b/package.json index 3eb6b1619cc..f9757aa1e68 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.374", - "@bitwarden/commercial-sdk-internal": "0.2.0-main.374", + "@bitwarden/sdk-internal": "0.2.0-main.375", + "@bitwarden/commercial-sdk-internal": "0.2.0-main.375", "@electron/fuses": "1.8.0", "@emotion/css": "11.13.5", "@koa/multer": "4.0.0", From dfc640d365d7e83dfbfd4a770878bed9152472fb Mon Sep 17 00:00:00 2001 From: Oscar Hinton <Hinton@users.noreply.github.com> Date: Fri, 14 Nov 2025 12:28:37 +0100 Subject: [PATCH 09/75] Enable more angular-eslint rules (#17383) --- eslint.config.mjs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 6c362a4dc43..bfa8b6ec079 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -62,16 +62,15 @@ export default tseslint.config( // TODO: Enable these. "@angular-eslint/component-class-suffix": 0, - "@angular-eslint/contextual-lifecycle": 0, + "@angular-eslint/contextual-lifecycle": "error", "@angular-eslint/directive-class-suffix": 0, "@angular-eslint/no-empty-lifecycle-method": 0, - "@angular-eslint/no-host-metadata-property": 0, "@angular-eslint/no-input-rename": 0, - "@angular-eslint/no-inputs-metadata-property": 0, + "@angular-eslint/no-inputs-metadata-property": "error", "@angular-eslint/no-output-native": 0, "@angular-eslint/no-output-on-prefix": 0, - "@angular-eslint/no-output-rename": 0, - "@angular-eslint/no-outputs-metadata-property": 0, + "@angular-eslint/no-output-rename": "error", + "@angular-eslint/no-outputs-metadata-property": "error", "@angular-eslint/prefer-on-push-component-change-detection": "error", "@angular-eslint/prefer-output-emitter-ref": "error", "@angular-eslint/prefer-signals": "error", From 4fd65965e85098b97aa425856f40ca665aa32cf1 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 14 Nov 2025 12:39:05 +0100 Subject: [PATCH 10/75] Autosync the updated translations (#17379) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/ar/messages.json | 18 ++ apps/browser/src/_locales/az/messages.json | 70 +++-- apps/browser/src/_locales/be/messages.json | 18 ++ apps/browser/src/_locales/bg/messages.json | 18 ++ apps/browser/src/_locales/bn/messages.json | 18 ++ apps/browser/src/_locales/bs/messages.json | 18 ++ apps/browser/src/_locales/ca/messages.json | 18 ++ apps/browser/src/_locales/cs/messages.json | 18 ++ apps/browser/src/_locales/cy/messages.json | 18 ++ apps/browser/src/_locales/da/messages.json | 18 ++ apps/browser/src/_locales/de/messages.json | 24 +- apps/browser/src/_locales/el/messages.json | 18 ++ apps/browser/src/_locales/en_GB/messages.json | 18 ++ apps/browser/src/_locales/en_IN/messages.json | 18 ++ apps/browser/src/_locales/es/messages.json | 18 ++ apps/browser/src/_locales/et/messages.json | 18 ++ apps/browser/src/_locales/eu/messages.json | 18 ++ apps/browser/src/_locales/fa/messages.json | 18 ++ apps/browser/src/_locales/fi/messages.json | 18 ++ apps/browser/src/_locales/fil/messages.json | 18 ++ apps/browser/src/_locales/fr/messages.json | 20 +- apps/browser/src/_locales/gl/messages.json | 18 ++ apps/browser/src/_locales/he/messages.json | 18 ++ apps/browser/src/_locales/hi/messages.json | 18 ++ apps/browser/src/_locales/hr/messages.json | 18 ++ apps/browser/src/_locales/hu/messages.json | 20 +- apps/browser/src/_locales/id/messages.json | 18 ++ apps/browser/src/_locales/it/messages.json | 18 ++ apps/browser/src/_locales/ja/messages.json | 244 ++++++++++-------- apps/browser/src/_locales/ka/messages.json | 18 ++ apps/browser/src/_locales/km/messages.json | 18 ++ apps/browser/src/_locales/kn/messages.json | 18 ++ apps/browser/src/_locales/ko/messages.json | 18 ++ apps/browser/src/_locales/lt/messages.json | 18 ++ apps/browser/src/_locales/lv/messages.json | 18 ++ apps/browser/src/_locales/ml/messages.json | 18 ++ apps/browser/src/_locales/mr/messages.json | 18 ++ apps/browser/src/_locales/my/messages.json | 18 ++ apps/browser/src/_locales/nb/messages.json | 18 ++ apps/browser/src/_locales/ne/messages.json | 18 ++ apps/browser/src/_locales/nl/messages.json | 18 ++ apps/browser/src/_locales/nn/messages.json | 18 ++ apps/browser/src/_locales/or/messages.json | 18 ++ apps/browser/src/_locales/pl/messages.json | 18 ++ apps/browser/src/_locales/pt_BR/messages.json | 18 ++ apps/browser/src/_locales/pt_PT/messages.json | 18 ++ apps/browser/src/_locales/ro/messages.json | 18 ++ apps/browser/src/_locales/ru/messages.json | 18 ++ apps/browser/src/_locales/si/messages.json | 18 ++ apps/browser/src/_locales/sk/messages.json | 18 ++ apps/browser/src/_locales/sl/messages.json | 18 ++ apps/browser/src/_locales/sr/messages.json | 76 +++--- apps/browser/src/_locales/sv/messages.json | 18 ++ apps/browser/src/_locales/ta/messages.json | 18 ++ apps/browser/src/_locales/te/messages.json | 18 ++ apps/browser/src/_locales/th/messages.json | 18 ++ apps/browser/src/_locales/tr/messages.json | 18 ++ apps/browser/src/_locales/uk/messages.json | 18 ++ apps/browser/src/_locales/vi/messages.json | 18 ++ apps/browser/src/_locales/zh_CN/messages.json | 20 +- apps/browser/src/_locales/zh_TW/messages.json | 20 +- 61 files changed, 1273 insertions(+), 175 deletions(-) diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 053fb3b101f..79d54193b59 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "عند قفل النظام" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "عند إعادة تشغيل المتصفح" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index 68ea40b6808..b67d5ace0d4 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -592,7 +592,10 @@ "message": "Bax" }, "viewAll": { - "message": "View all" + "message": "Hamısına bax" + }, + "viewLess": { + "message": "View less" }, "viewLogin": { "message": "Girişə bax" @@ -625,7 +628,7 @@ "message": "Element sevimlilərə əlavə edildi" }, "itemRemovedFromFavorites": { - "message": "Element sevimlilərdən çıxarıldı" + "message": "Element sevimlilərdən xaric edildi" }, "notes": { "message": "Notlar" @@ -685,7 +688,7 @@ "message": "Ayarlarda bir kilid açma üsulu qurun" }, "sessionTimeoutHeader": { - "message": "Seans vaxt bitməsi" + "message": "Sessiya vaxt bitməsi" }, "vaultTimeoutHeader": { "message": "Seyf vaxtının bitməsi" @@ -796,6 +799,12 @@ "onLocked": { "message": "Sistem kilidlənəndə" }, + "onIdle": { + "message": "Sistem boşda olduqda" + }, + "onSleep": { + "message": "Sistem yuxu rejimində olduqda" + }, "onRestart": { "message": "Brauzer yenidən başladılanda" }, @@ -916,7 +925,7 @@ "message": "Hesabınızdan çıxış etmisiniz." }, "loginExpired": { - "message": "Giriş seansınızın müddəti bitdi." + "message": "Giriş sessiyanızın müddəti bitdi." }, "logIn": { "message": "Giriş et" @@ -1035,10 +1044,10 @@ "message": "Element saxlanıldı" }, "savedWebsite": { - "message": "Saved website" + "message": "Saxlanılan veb sayt" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "Saxlanılan veb sayt ( $COUNT$ )", "placeholders": { "count": { "content": "$1", @@ -1239,7 +1248,7 @@ "description": "Detailed error message shown when saving login details fails." }, "changePasswordWarning": { - "message": "Parolunuzu dəyişdirdikdən sonra yeni parolunuzla giriş etməli olacaqsınız. Digər cihazlardakı aktiv seanslar bir saat ərzində çıxış sonlandırılacaq." + "message": "Parolunuzu dəyişdirdikdən sonra yeni parolunuzla giriş etməli olacaqsınız. Digər cihazlardakı aktiv sessiyalar bir saat ərzində çıxış sonlandırılacaq." }, "accountRecoveryUpdateMasterPasswordSubtitle": { "message": "Hesabın geri qaytarılması prosesini tamamlamaq üçün ana parolunuzu dəyişdirin." @@ -1527,7 +1536,7 @@ "message": "Kimlik doğrulama vaxtı bitdi" }, "authenticationSessionTimedOut": { - "message": "Kimlik doğrulama seansının vaxtı bitdi. Lütfən giriş prosesini yenidən başladın." + "message": "Kimlik doğrulama sessiyasının vaxtı bitdi. Lütfən giriş prosesini yenidən başladın." }, "verificationCodeEmailSent": { "message": "Doğrulama poçtu $EMAIL$ ünvanına göndərildi.", @@ -1692,28 +1701,28 @@ "message": "Avto-doldurmanı söndür" }, "confirmAutofill": { - "message": "Confirm autofill" + "message": "Avto-doldurmanı təsdiqlə" }, "confirmAutofillDesc": { - "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + "message": "Bu sayt, saxlanılmış giriş məlumatlarınızla uyuşmur. Giriş məlumatlarınızı doldurmazdan əvvəl, güvənli sayt olduğuna əmin olun." }, "showInlineMenuLabel": { "message": "Avto-doldurma təkliflərini form xanalarında göstər" }, "howDoesBitwardenProtectFromPhishing": { - "message": "How does Bitwarden protect your data from phishing?" + "message": "Bitwarden verilərinizi fişinqdən necə qoruyur?" }, "currentWebsite": { - "message": "Current website" + "message": "Hazırkı veb sayt" }, "autofillAndAddWebsite": { - "message": "Autofill and add this website" + "message": "Avto-doldur və bu veb saytı əlavə et" }, "autofillWithoutAdding": { - "message": "Autofill without adding" + "message": "Əlavə etmədən avto-doldur" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "Avto-doldurulmasın" }, "showInlineMenuIdentitiesLabel": { "message": "Kimlikləri təklif kimi göstər" @@ -2149,7 +2158,7 @@ } }, "passwordSafe": { - "message": "Bu parol, veri pozuntularında qeydə alınmayıb. Rahatlıqla istifadə edə bilərsiniz." + "message": "Bu parol, veri pozuntularında qeydə alınmayıb. Əmniyyətlə istifadə edə bilərsiniz." }, "baseDomain": { "message": "Baza domeni", @@ -2219,7 +2228,7 @@ "message": "Təzəlikcə heç nə yaratmamısınız" }, "remove": { - "message": "Çıxart" + "message": "Xaric et" }, "default": { "message": "İlkin" @@ -3058,10 +3067,10 @@ "message": "Ana parolu güncəllə" }, "updateMasterPasswordWarning": { - "message": "Ana parolunuz təzəlikcə təşkilatınızdakı bir inzibatçı tərəfindən dəyişdirildi. Seyfə erişmək üçün onu indi güncəlləməlisiniz. Davam etsəniz, hazırkı seansdan çıxış edəcəksiniz və təkrar giriş etməli olacaqsınız. Digər cihazlardakı aktiv seanslar bir saata qədər aktiv qalmağa davam edə bilər." + "message": "Ana parolunuz təzəlikcə təşkilatınızdakı bir inzibatçı tərəfindən dəyişdirildi. Seyfə erişmək üçün onu indi güncəlləməlisiniz. Davam etsəniz, hazırkı sessiyadan çıxış edəcəksiniz və təkrar giriş etməli olacaqsınız. Digər cihazlardakı aktiv sessiyalar bir saata qədər aktiv qalmağa davam edə bilər." }, "updateWeakMasterPasswordWarning": { - "message": "Ana parolunuz təşkilatınızdakı siyasətlərdən birinə və ya bir neçəsinə uyğun gəlmir. Seyfə erişmək üçün ana parolunuzu indi güncəlləməlisiniz. Davam etsəniz, hazırkı seansdan çıxış etmiş və təkrar giriş etməli olacaqsınız. Digər cihazlardakı aktiv seanslar bir saata qədər aktiv qalmağa davam edə bilər." + "message": "Ana parolunuz təşkilatınızdakı siyasətlərdən birinə və ya bir neçəsinə uyğun gəlmir. Seyfə erişmək üçün ana parolunuzu indi güncəlləməlisiniz. Davam etsəniz, hazırkı sessiyadan çıxış etmiş və təkrar giriş etməli olacaqsınız. Digər cihazlardakı aktiv sessiyalar bir saata qədər aktiv qalmağa davam edə bilər." }, "tdeDisabledMasterPasswordRequired": { "message": "Təşkilatınız, güvənli cihaz şifrələməsini sıradan çıxartdı. Seyfinizə erişmək üçün lütfən ana parol təyin edin." @@ -3217,7 +3226,7 @@ "message": "Simvol sayını dəyişdir" }, "sessionTimeout": { - "message": "Seansınızın vaxtı bitdi. Lütfən geri qayıdıb yenidən giriş etməyə cəhd edin." + "message": "Sessiyanızın vaxtı bitdi. Lütfən geri qayıdıb yenidən giriş etməyə cəhd edin." }, "exportingPersonalVaultTitle": { "message": "Fərdi seyfin xaricə köçürülməsi" @@ -3277,7 +3286,7 @@ "message": "Şifrə açma xətası" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "Avto-doldurma verilərini alma xətası" }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden, aşağıda sadalanan seyf element(lər)inin şifrəsini aça bilmədi." @@ -3723,7 +3732,7 @@ "message": "Cihazları idarə et" }, "currentSession": { - "message": "Hazırkı seans" + "message": "Hazırkı sessiya" }, "mobile": { "message": "Mobil", @@ -3920,7 +3929,7 @@ "message": "İstifadəçiyə güvən" }, "sendsTitleNoItems": { - "message": "Send, həssas məlumatlar təhlükəsizdir", + "message": "Send ilə həssas məlumatlar əmniyyətdədir", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendsBodyNoItems": { @@ -4051,13 +4060,13 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "Avto-doldurula bilmir" }, "cannotAutofillExactMatch": { "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." }, "okay": { - "message": "Okay" + "message": "Oldu" }, "toggleSideNavigation": { "message": "Yan naviqasiyanı aç/bağla" @@ -5709,7 +5718,7 @@ "message": "Kimliklərinizlə, uzun qeydiyyat və ya əlaqə xanalarını daha tez avtomatik doldurun." }, "newNoteNudgeTitle": { - "message": "Həssas verilərinizi güvənli şəkildə saxlayın" + "message": "Həssas verilərinizi əmniyyətdə saxlayın" }, "newNoteNudgeBody": { "message": "Notlarla, bankçılıq və ya sığorta təfsilatları kimi həssas veriləri təhlükəsiz saxlayın." @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "\"Premium\"a yüksəlt" }, + "loadingVault": { + "message": "Seyf yüklənir" + }, + "vaultLoaded": { + "message": "Seyf yükləndi" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Kart nömrəsi" + }, + "sessionTimeoutSettingsAction": { + "message": "Vaxt bitmə əməliyyatı" } } diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index b0735109b41..450fb6e3df5 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Пры блакіраванні сістэмы" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Пры перазапуску браўзера" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index 30386fe625e..0a71e453c21 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Показване на всички" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "Преглед на елемента за вписване" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "При заключване на системата" }, + "onIdle": { + "message": "При бездействие на системата" + }, + "onSleep": { + "message": "При заспиване на системата" + }, "onRestart": { "message": "При повторно пускане на браузъра" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Надградете до Платения план" }, + "loadingVault": { + "message": "Зареждане на трезора" + }, + "vaultLoaded": { + "message": "Трезорът е зареден" + }, "settingDisabledByPolicy": { "message": "Тази настройка е изключена съгласно политиката на организацията Ви.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Номер на картата" + }, + "sessionTimeoutSettingsAction": { + "message": "Действие при изтичането на времето за достъп" } } diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index d68d19b0a05..f43e3fdad29 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "সিস্টেম লকে" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "ব্রাউজার পুনঃসূচনাই" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index 74f47fac7df..4fbcccd9aae 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 824f37f069e..15a309fa8fd 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "En bloquejar el sistema" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "En reiniciar el navegador" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index 46f5f414a1a..f9f572d87d8 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Zobrazit vše" }, + "viewLess": { + "message": "Zobrazit méně" + }, "viewLogin": { "message": "Zobrazit přihlašovací údaje" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Při uzamknutí systému" }, + "onIdle": { + "message": "Při nečinnosti systému" + }, + "onSleep": { + "message": "Při přechodu do režimu spánku" + }, "onRestart": { "message": "Při restartu prohlížeče" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Aktualizovat na Premium" }, + "loadingVault": { + "message": "Načítání trezoru" + }, + "vaultLoaded": { + "message": "Trezor byl načten" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Číslo karty" + }, + "sessionTimeoutSettingsAction": { + "message": "Akce vypršení časového limitu" } } diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 07c5a68e3ec..33c68b338a0 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "wrth ailgychwyn y porwr" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index 93b311e158b..a5999945692 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Når systemet låses" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Ved genstart af browseren" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index d88c396bb80..b72cc2decb4 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -29,7 +29,7 @@ "message": "Mit Passkey anmelden" }, "useSingleSignOn": { - "message": "Single Sign-on verwenden" + "message": "Single Sign-On verwenden" }, "yourOrganizationRequiresSingleSignOn": { "message": "Deine Organisation erfordert Single Sign-On." @@ -44,7 +44,7 @@ "message": "Schließe die Erstellung deines Kontos ab, indem du ein Passwort festlegst" }, "enterpriseSingleSignOn": { - "message": "Enterprise Single-Sign-On" + "message": "Enterprise Single Sign-On" }, "cancel": { "message": "Abbrechen" @@ -594,6 +594,9 @@ "viewAll": { "message": "Alles anzeigen" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "Zugangsdaten anzeigen" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Wenn System gesperrt" }, + "onIdle": { + "message": "Bei Systeminaktivität" + }, + "onSleep": { + "message": "Im Standby" + }, "onRestart": { "message": "Bei Browser-Neustart" }, @@ -5789,7 +5798,7 @@ "message": "Notfallzugriff" }, "breachMonitoring": { - "message": "Datenpannen-Überwachung" + "message": "Datendiebstahl-Überwachung" }, "andMoreFeatures": { "message": "Und mehr!" @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade auf Premium" }, + "loadingVault": { + "message": "Tresor wird geladen" + }, + "vaultLoaded": { + "message": "Tresor geladen" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Kartennummer" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout-Aktion" } } diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index 7fb60530511..e0de7e5e9e0 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "Προβολή σύνδεσης" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Κατά το Κλείδωμα Συστήματος" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Κατά την Επανεκκίνηση του Browser" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index 8ab541c569e..a9c57e157e6 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index 68bf5497e37..cd8c91f8437 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 060da79a4ff..470cf2ab35a 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Al bloquear el sistema" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Al reiniciar el navegador" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index acb440b2aa6..9220d61e466 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Arvutist väljalogimisel" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Brauseri taaskäivitamisel" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index 016381e17f8..c360bed28e0 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Sistema blokeatzean" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Nabigatzailea berrabiaraztean" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 4f8529b2710..774a02f50d3 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "هنگام قفل سیستم" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "هنگام راه‌اندازی مجدد" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index e3a5b44ea91..8766632a91e 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Näytä kaikki" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Kun järjestelmä lukitaan" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Kun selain käynnistetään uudelleen" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 2b58095d950..6c7154a1ba5 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Sa pag-lock ng sistema" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Sa pag-restart ng browser" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index afb58afcc25..0e701750c5b 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "Afficher l'Identifiant" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Au verrouillage" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Au redémarrage du navigateur" }, @@ -3283,7 +3292,7 @@ "message": "Bitwarden n’a pas pu déchiffrer le(s) élément(s) du coffre listé(s) ci-dessous." }, "contactCSToAvoidDataLossPart1": { - "message": "Contacter le service clientèle", + "message": "Contacter succès client", "description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'" }, "contactCSToAvoidDataLossPart2": { @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Numéro de carte" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index 6d0410f112c..c61325ef8de 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Ó bloquear o sistema" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Ó reiniciar o navegador" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index cc78e1a154a..81ac1e176f0 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "הצג הכל" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "הצג כניסה" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "בנעילת המערכת" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "בהפעלת הדפדפן מחדש" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "הגדרה זו מושבתת על ידי מדיניות של הארגון שלך.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "מספר כרטיס" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index c27fa6f7eb7..ff24818f821 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On Locked" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On Restart" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 9d7539a9bd5..052fae33683 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Vidi sve" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "Prikaži prijavu" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Pri zaključavanju sustava" }, + "onIdle": { + "message": "U stanju besposlenosti" + }, + "onSleep": { + "message": "U stanju mirovanja sustava" + }, "onRestart": { "message": "Pri pokretanju preglednika" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": " Nadogradi na Premium" }, + "loadingVault": { + "message": "Učitavanje trezora" + }, + "vaultLoaded": { + "message": "Trezor učitan" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Broj kartice" + }, + "sessionTimeoutSettingsAction": { + "message": "Radnja kod isteka" } } diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index 9f10494258a..fb94c4f4665 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Összes megtekintése" }, + "viewLess": { + "message": "kevesebb megjelenítése" + }, "viewLogin": { "message": "Bejelentkezés megtekintése" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Rendszerzároláskor" }, + "onIdle": { + "message": "Rendszer üresjárat esetén" + }, + "onSleep": { + "message": "Rendszer alvó mód esetén" + }, "onRestart": { "message": "Böngésző újraindításkor" }, @@ -4975,7 +4984,7 @@ } }, "defaultLabelWithValue": { - "message": "Default ( $VALUE$ )", + "message": "Alapértelmezett ($VALUE$)", "description": "A label that indicates the default value for a field with the current default value in parentheses.", "placeholders": { "value": { @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Áttérés Prémium csomagra" }, + "loadingVault": { + "message": "Széf betöltése" + }, + "vaultLoaded": { + "message": "A széf betöltésre került." + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Kártya szám" + }, + "sessionTimeoutSettingsAction": { + "message": "Időkifutási művelet" } } diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 26a2b8dc6bd..1cb79804923 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Saat Komputer Terkunci" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Saat Peramban Dimulai Ulang" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index 60c97d7157a..dc69cb13cb3 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "Visualizza login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Al blocco del computer" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Al riavvio del browser" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index 7c4420508a2..8b3b0e2cc6d 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -3,7 +3,7 @@ "message": "Bitwarden" }, "appLogoLabel": { - "message": "Bitwarden logo" + "message": "Bitwarden ロゴ" }, "extName": { "message": "Bitwarden パスワードマネージャー", @@ -32,7 +32,7 @@ "message": "シングルサインオンを使用する" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "あなたの組織ではシングルサインオン (SSO) を使用する必要があります。" }, "welcomeBack": { "message": "ようこそ" @@ -554,15 +554,15 @@ "message": "Reset search" }, "archiveNoun": { - "message": "Archive", + "message": "アーカイブ", "description": "Noun" }, "archiveVerb": { - "message": "Archive", + "message": "アーカイブ", "description": "Verb" }, "unArchive": { - "message": "Unarchive" + "message": "アーカイブ解除" }, "itemsInArchive": { "message": "Items in archive" @@ -594,8 +594,11 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { - "message": "View login" + "message": "ログイン情報を表示" }, "noItemsInList": { "message": "表示するアイテムがありません" @@ -796,6 +799,12 @@ "onLocked": { "message": "ロック時" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "ブラウザ再起動時" }, @@ -1035,10 +1044,10 @@ "message": "編集されたアイテム" }, "savedWebsite": { - "message": "Saved website" + "message": "保存されたウェブサイト" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "保存されたウェブサイト ($COUNT$ 件)", "placeholders": { "count": { "content": "$1", @@ -1145,10 +1154,10 @@ "description": "Tooltip and Aria label for edit button on cipher item" }, "newNotification": { - "message": "New notification" + "message": "新しい通知" }, "labelWithNotification": { - "message": "$LABEL$: New notification", + "message": "$LABEL$: 新しい通知", "description": "Label for the notification with a new login suggestion.", "placeholders": { "label": { @@ -1190,11 +1199,11 @@ "description": "User prompt to take action in order to save the login they just entered." }, "saveLogin": { - "message": "Save login", + "message": "ログインを保存", "description": "Prompt asking the user if they want to save their login details." }, "updateLogin": { - "message": "Update existing login", + "message": "既存のログイン情報を更新", "description": "Prompt asking the user if they want to update an existing login entry." }, "loginSaveSuccess": { @@ -1561,13 +1570,13 @@ "message": "セキュリティキーの読み取り" }, "readingPasskeyLoading": { - "message": "Reading passkey..." + "message": "パスキーを読み込み中…" }, "passkeyAuthenticationFailed": { - "message": "Passkey authentication failed" + "message": "パスキー認証に失敗しました" }, "useADifferentLogInMethod": { - "message": "Use a different log in method" + "message": "別のログイン方法を使用する" }, "awaitingSecurityKeyInteraction": { "message": "セキュリティキーとの通信を待ち受け中…" @@ -1636,7 +1645,7 @@ "message": "ベース サーバー URL または少なくとも 1 つのカスタム環境を追加する必要があります。" }, "selfHostedEnvMustUseHttps": { - "message": "URLs must use HTTPS." + "message": "URL は HTTPS を使用する必要があります。" }, "customEnvironment": { "message": "カスタム環境" @@ -1689,7 +1698,7 @@ } }, "turnOffAutofill": { - "message": "Turn off autofill" + "message": "自動入力をオフにする" }, "confirmAutofill": { "message": "Confirm autofill" @@ -1713,7 +1722,7 @@ "message": "Autofill without adding" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "自動入力しない" }, "showInlineMenuIdentitiesLabel": { "message": "ID を候補として表示する" @@ -1901,7 +1910,7 @@ "message": "セキュリティコード" }, "cardNumber": { - "message": "card number" + "message": "カード番号" }, "ex": { "message": "例:" @@ -2003,30 +2012,30 @@ "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": { @@ -2034,11 +2043,11 @@ "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": { @@ -2046,15 +2055,15 @@ "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": { @@ -2062,11 +2071,11 @@ "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": { @@ -2074,11 +2083,11 @@ "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": { @@ -2337,7 +2346,7 @@ "message": "このパスワードを使用する" }, "useThisPassphrase": { - "message": "Use this passphrase" + "message": "このパスフレーズを使用" }, "useThisUsername": { "message": "このユーザー名を使用する" @@ -2654,7 +2663,7 @@ "message": "変更" }, "changePassword": { - "message": "Change password", + "message": "パスワードを変更", "description": "Change password button for browser at risk notification on login." }, "changeButtonTitle": { @@ -2667,7 +2676,7 @@ } }, "atRiskPassword": { - "message": "At-risk password" + "message": "リスクがあるパスワード" }, "atRiskPasswords": { "message": "リスクがあるパスワード" @@ -2843,7 +2852,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "maxAccessCountReached": { - "message": "Max access count reached", + "message": "最大アクセス数に達しました", "description": "This text will be displayed after a Send has been accessed the maximum amount of times." }, "hideTextByDefault": { @@ -3193,7 +3202,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" @@ -3605,7 +3614,7 @@ "message": "リクエストが送信されました" }, "loginRequestApprovedForEmailOnDevice": { - "message": "Login request approved for $EMAIL$ on $DEVICE$", + "message": "$EMAIL$ に $DEVICE$ でのログインを承認しました", "placeholders": { "email": { "content": "$1", @@ -3618,16 +3627,16 @@ } }, "youDeniedLoginAttemptFromAnotherDevice": { - "message": "You denied a login attempt from another device. If this was you, try to log in with the device again." + "message": "別のデバイスからのログイン試行を拒否しました。自分自身である場合は、もう一度デバイスでログインしてください。" }, "device": { - "message": "Device" + "message": "デバイス" }, "loginStatus": { - "message": "Login status" + "message": "ログイン状態" }, "masterPasswordChanged": { - "message": "Master password saved" + "message": "マスターパスワードが保存されました" }, "exposedMasterPassword": { "message": "流出したマスターパスワード" @@ -3720,28 +3729,28 @@ "message": "このデバイスを記憶して今後のログインをシームレスにする" }, "manageDevices": { - "message": "Manage devices" + "message": "デバイスを管理" }, "currentSession": { - "message": "Current session" + "message": "現在のセッション" }, "mobile": { - "message": "Mobile", + "message": "モバイル", "description": "Mobile app" }, "extension": { - "message": "Extension", + "message": "拡張機能", "description": "Browser extension/addon" }, "desktop": { - "message": "Desktop", + "message": "デスクトップ", "description": "Desktop app" }, "webVault": { - "message": "Web vault" + "message": "ウェブ保管庫" }, "webApp": { - "message": "Web app" + "message": "Web アプリ" }, "cli": { "message": "CLI" @@ -3751,22 +3760,22 @@ "description": "Software Development Kit" }, "requestPending": { - "message": "Request pending" + "message": "保留中のリクエスト" }, "firstLogin": { - "message": "First login" + "message": "初回ログイン" }, "trusted": { - "message": "Trusted" + "message": "信頼済み" }, "needsApproval": { - "message": "Needs approval" + "message": "承認が必要" }, "devices": { - "message": "Devices" + "message": "デバイス" }, "accessAttemptBy": { - "message": "Access attempt by $EMAIL$", + "message": "$EMAIL$ によるログインの試行", "placeholders": { "email": { "content": "$1", @@ -3775,31 +3784,31 @@ } }, "confirmAccess": { - "message": "Confirm access" + "message": "アクセスの確認" }, "denyAccess": { - "message": "Deny access" + "message": "アクセスを拒否" }, "time": { - "message": "Time" + "message": "時間" }, "deviceType": { - "message": "Device Type" + "message": "デバイス種別" }, "loginRequest": { - "message": "Login request" + "message": "ログインリクエスト" }, "thisRequestIsNoLongerValid": { - "message": "This request is no longer valid." + "message": "このリクエストは無効になりました。" }, "loginRequestHasAlreadyExpired": { - "message": "Login request has already expired." + "message": "ログインリクエストの有効期限が切れています。" }, "justNow": { - "message": "Just now" + "message": "たった今" }, "requestedXMinutesAgo": { - "message": "Requested $MINUTES$ minutes ago", + "message": "$MINUTES$ 分前に要求されました", "placeholders": { "minutes": { "content": "$1", @@ -3829,7 +3838,7 @@ "message": "管理者の承認を要求する" }, "unableToCompleteLogin": { - "message": "Unable to complete login" + "message": "ログインを完了できません" }, "loginOnTrustedDeviceOrAskAdminToAssignPassword": { "message": "You need to log in on a trusted device or ask your administrator to assign you a password." @@ -3899,13 +3908,13 @@ "message": "Trust organization" }, "trust": { - "message": "Trust" + "message": "信頼する" }, "doNotTrust": { - "message": "Do not trust" + "message": "信頼しない" }, "organizationNotTrusted": { - "message": "Organization is not trusted" + "message": "組織は信頼されていません" }, "emergencyAccessTrustWarning": { "message": "For the security of your account, only confirm if you have granted emergency access to this user and their fingerprint matches what is displayed in their account" @@ -3920,11 +3929,11 @@ "message": "Trust user" }, "sendsTitleNoItems": { - "message": "Send sensitive information safely", + "message": "機密情報を安全に送信", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendsBodyNoItems": { - "message": "Share files and data securely with anyone, on any platform. Your information will remain end-to-end encrypted while limiting exposure.", + "message": "どのプラットフォームでも、誰とでも安全にファイルとデータを共有できます。流出を防止しながら、あなたの情報はエンドツーエンドで暗号化されます。", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "inputRequired": { @@ -4051,13 +4060,13 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "自動入力できません" }, "cannotAutofillExactMatch": { "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." }, "okay": { - "message": "Okay" + "message": "OK" }, "toggleSideNavigation": { "message": "サイドナビゲーションの切り替え" @@ -4268,10 +4277,10 @@ "message": "コレクションを選択" }, "importTargetHintCollection": { - "message": "Select this option if you want the imported file contents moved to a collection" + "message": "インポートしたファイルコンテンツをコレクションに移動したい場合は、このオプションを選択してください" }, "importTargetHintFolder": { - "message": "Select this option if you want the imported file contents moved to a folder" + "message": "インポートしたファイルコンテンツをフォルダーに移動したい場合は、このオプションを選択してください" }, "importUnassignedItemsError": { "message": "割り当てられていないアイテムがファイルに含まれています。" @@ -4524,7 +4533,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": { @@ -4711,7 +4720,7 @@ } }, "copyFieldCipherName": { - "message": "Copy $FIELD$, $CIPHERNAME$", + "message": "$FIELD$ ($CIPHERNAME$) をコピー", "description": "Title for a button that copies a field value to the clipboard.", "placeholders": { "field": { @@ -4858,31 +4867,31 @@ } }, "downloadBitwarden": { - "message": "Download Bitwarden" + "message": "Bitwarden をダウンロード" }, "downloadBitwardenOnAllDevices": { - "message": "Download Bitwarden on all devices" + "message": "すべてのデバイスに Bitwarden をダウンロード" }, "getTheMobileApp": { - "message": "Get the mobile app" + "message": "モバイルアプリを入手" }, "getTheMobileAppDesc": { "message": "Access your passwords on the go with the Bitwarden mobile app." }, "getTheDesktopApp": { - "message": "Get the desktop app" + "message": "デスクトップアプリを入手" }, "getTheDesktopAppDesc": { "message": "Access your vault without a browser, then set up unlock with biometrics to expedite unlocking in both the desktop app and browser extension." }, "downloadFromBitwardenNow": { - "message": "Download from bitwarden.com now" + "message": "bitwarden.com から今すぐダウンロード" }, "getItOnGooglePlay": { - "message": "Get it on Google Play" + "message": "Google Play で入手" }, "downloadOnTheAppStore": { - "message": "Download on the App Store" + "message": "App Store からダウンロード" }, "permanentlyDeleteAttachmentConfirmation": { "message": "この添付ファイルを完全に削除してもよろしいですか?" @@ -4975,7 +4984,7 @@ } }, "defaultLabelWithValue": { - "message": "Default ( $VALUE$ )", + "message": "既定 ( $VALUE$ )", "description": "A label that indicates the default value for a field with the current default value in parentheses.", "placeholders": { "value": { @@ -5248,7 +5257,7 @@ "message": "拡張機能アイコンにログイン自動入力の候補の数を表示する" }, "accountAccessRequested": { - "message": "Account access requested" + "message": "アカウントへのアクセスが要求されました" }, "confirmAccessAttempt": { "message": "Confirm access attempt for $EMAIL$", @@ -5365,7 +5374,7 @@ "message": "Unlock PIN set" }, "unlockWithBiometricSet": { - "message": "Unlock with biometrics set" + "message": "生体認証でロック解除を設定しました" }, "authenticating": { "message": "認証中" @@ -5379,7 +5388,7 @@ "description": "Notification message for when a password has been regenerated" }, "saveToBitwarden": { - "message": "Save to Bitwarden", + "message": "Bitwarden へ保存", "description": "Confirmation message for saving a login to Bitwarden" }, "spaceCharacterDescriptor": { @@ -5587,13 +5596,13 @@ "message": "This login is at-risk and missing a website. Add a website and change the password for stronger security." }, "missingWebsite": { - "message": "Missing website" + "message": "ウェブサイトがありません" }, "settingsVaultOptions": { "message": "保管庫オプション" }, "emptyVaultDescription": { - "message": "The vault protects more than just your passwords. Store secure logins, IDs, cards and notes securely here." + "message": "保管庫はパスワードだけではなく、ログイン情報、ID、カード、メモを安全に保管できます。" }, "introCarouselLabel": { "message": "Bitwarden へようこそ" @@ -5623,19 +5632,19 @@ "message": "Bitwarden のモバイル、ブラウザ、デスクトップアプリでは、保存できるパスワード数やデバイス数に制限はありません。" }, "nudgeBadgeAria": { - "message": "1 notification" + "message": "1件の通知" }, "emptyVaultNudgeTitle": { - "message": "Import existing passwords" + "message": "既存のパスワードをインポート" }, "emptyVaultNudgeBody": { "message": "Use the importer to quickly transfer logins to Bitwarden without manually adding them." }, "emptyVaultNudgeButton": { - "message": "Import now" + "message": "今すぐインポート" }, "hasItemsVaultNudgeTitle": { - "message": "Welcome to your vault!" + "message": "保管庫へようこそ!" }, "phishingPageTitleV2": { "message": "Phishing attempt detected" @@ -5644,7 +5653,7 @@ "message": "The site you are attempting to visit is a known malicious site and a security risk." }, "phishingPageCloseTabV2": { - "message": "Close this tab" + "message": "このタブを閉じる" }, "phishingPageContinueV2": { "message": "Continue to this site (not recommended)" @@ -5661,7 +5670,7 @@ "message": "Learn more about phishing detection" }, "protectedBy": { - "message": "Protected by $PRODUCT$", + "message": "$PRODUCT$ によって保護されています", "placeholders": { "product": { "content": "$1", @@ -5687,7 +5696,7 @@ "example": "Include a Website so this login appears as an autofill suggestion." }, "newLoginNudgeBodyBold": { - "message": "Website", + "message": "ウェブサイト", "description": "This is in multiple parts to allow for bold text in the middle of the sentence.", "example": "Include a Website so this login appears as an autofill suggestion." }, @@ -5715,20 +5724,20 @@ "message": "With notes, securely store sensitive data like banking or insurance details." }, "newSshNudgeTitle": { - "message": "Developer-friendly SSH access" + "message": "開発者フレンドリーの SSH アクセス" }, "newSshNudgeBodyOne": { - "message": "Store your keys and connect with the SSH agent for fast, encrypted authentication.", + "message": "SSHエージェントにキーを登録することで、高速かつ暗号化された認証が可能になります。", "description": "Two part message", "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" }, "newSshNudgeBodyTwo": { - "message": "Learn more about SSH agent", + "message": "SSH エージェントに関する詳細", "description": "Two part message", "example": "Store your keys and connect with the SSH agent for fast, encrypted authentication. Learn more about SSH agent" }, "generatorNudgeTitle": { - "message": "Quickly create passwords" + "message": "パスワードをすばやく作成" }, "generatorNudgeBodyOne": { "message": "Easily create strong and unique passwords by clicking on", @@ -5745,7 +5754,7 @@ "description": "Aria label for the body content of the generator nudge" }, "aboutThisSetting": { - "message": "About this setting" + "message": "この設定について" }, "permitCipherDetailsDescription": { "message": "Bitwarden will use saved login URIs to identify which icon or change password URL should be used to improve your experience. No information is collected or saved when you use this service." @@ -5758,13 +5767,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", @@ -5777,16 +5786,16 @@ "message": "Great job securing your at-risk logins!" }, "upgradeNow": { - "message": "Upgrade now" + "message": "今すぐアップグレード" }, "builtInAuthenticator": { - "message": "Built-in authenticator" + "message": "認証機を内蔵" }, "secureFileStorage": { "message": "Secure file storage" }, "emergencyAccess": { - "message": "Emergency access" + "message": "緊急アクセス" }, "breachMonitoring": { "message": "Breach monitoring" @@ -5798,16 +5807,25 @@ "message": "Complete online security" }, "upgradeToPremium": { - "message": "Upgrade to Premium" + "message": "プレミアムにアップグレード" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" }, "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" + "message": "郵便番号" }, "cardNumberLabel": { - "message": "Card number" + "message": "カード番号" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index eaa5bc43021..5c7a8da23a7 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 39e6c0be881..13e74f8d807 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index e5adcfce833..3e929bc6533 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "ಸಿಸ್ಟಮ್ ಲಾಕ್‌ನಲ್ಲಿ" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "ಬ್ರೌಸರ್ ಮರುಪ್ರಾರಂಭದಲ್ಲಿ" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 6037d208b42..5a21928c233 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "로그인 보기" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "시스템 잠금 시" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "브라우저 재시작 시" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index 8e858de4f47..ac598394a8c 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Užrakinant sistemą" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Paleidus iš naujo naršyklę" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index a4be22d433a..70f46e0f068 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Apskatīt visu" }, + "viewLess": { + "message": "Skatīt mazāk" + }, "viewLogin": { "message": "Apskatīt pieteikšanās vienumu" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Pēc sistēmas aizslēgšanas" }, + "onIdle": { + "message": "Sistēmas dīkstāvē" + }, + "onSleep": { + "message": "Pēc sistēmas iemigšanas" + }, "onRestart": { "message": "Pēc pārlūka pārsāknēšanas" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Uzlabot uz Premium" }, + "loadingVault": { + "message": "Ielādē glabātavu" + }, + "vaultLoaded": { + "message": "Glabātava ielādēta" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Kartes numurs" + }, + "sessionTimeoutSettingsAction": { + "message": "Noildzes darbība" } } diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index cda9ec03923..d139531315b 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "സിസ്റ്റം ലോക്കിൽ" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "ബ്രൌസർ പുനരാരംഭിക്കുമ്പോൾ" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 57624a82381..438cc750557 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 39e6c0be881..13e74f8d807 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 1268c960c8f..4ae8a01a12f 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Ved maskinlåsing" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Ved nettleseromstart" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 39e6c0be881..13e74f8d807 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index 441ea71d840..b3463c9f1b3 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Alles weergeven" }, + "viewLess": { + "message": "Minder weergeven" + }, "viewLogin": { "message": "Login bekijken" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Bij systeemvergrendeling" }, + "onIdle": { + "message": "Bij systeeminactiviteit" + }, + "onSleep": { + "message": "Bij slaapmodus" + }, "onRestart": { "message": "Bij herstart van de browser" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Opwaarderen naar Premium" }, + "loadingVault": { + "message": "Kluis laden" + }, + "vaultLoaded": { + "message": "Kluis geladen" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Kaartnummer" + }, + "sessionTimeoutSettingsAction": { + "message": "Time-out actie" } } diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 39e6c0be881..13e74f8d807 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 39e6c0be881..13e74f8d807 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 1047ac9466e..77b6cc436d7 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "Pokaż dane logowania" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Po zablokowaniu urządzenia" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Po uruchomieniu przeglądarki" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Numer karty" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index a4da9025a8e..c3d96145944 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Ver tudo" }, + "viewLess": { + "message": "Ver menos" + }, "viewLogin": { "message": "Ver credencial" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Ao bloquear o sistema" }, + "onIdle": { + "message": "Quando o sistema ficar inativo" + }, + "onSleep": { + "message": "Quando o sistema hibernar" + }, "onRestart": { "message": "Ao reiniciar o navegador" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Faça upgrade para o Premium" }, + "loadingVault": { + "message": "Carregando cofre" + }, + "vaultLoaded": { + "message": "Cofre carregado" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Número do cartão" + }, + "sessionTimeoutSettingsAction": { + "message": "Ação do tempo limite" } } diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 15c993ab768..10fbc3db004 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Ver tudo" }, + "viewLess": { + "message": "Ver menos" + }, "viewLogin": { "message": "Ver credencial" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Ao bloquear o sistema" }, + "onIdle": { + "message": "Na inatividade do sistema" + }, + "onSleep": { + "message": "Na suspensão do sistema" + }, "onRestart": { "message": "Ao reiniciar o navegador" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Atualizar para o Premium" }, + "loadingVault": { + "message": "A carregar o cofre" + }, + "vaultLoaded": { + "message": "Cofre carregado" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Número do cartão" + }, + "sessionTimeoutSettingsAction": { + "message": "Ação de tempo limite" } } diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 4e1ac8ae832..5fe7c61f9cc 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "La blocarea sistemului" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "La repornirea browserului" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index d59fc34f736..349e68c5194 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Посмотреть все" }, + "viewLess": { + "message": "Свернуть" + }, "viewLogin": { "message": "Просмотр логина" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Вместе с компьютером" }, + "onIdle": { + "message": "При бездействии" + }, + "onSleep": { + "message": "В режиме сна" + }, "onRestart": { "message": "При перезапуске браузера" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Обновить до Премиум" }, + "loadingVault": { + "message": "Загрузка хранилища" + }, + "vaultLoaded": { + "message": "Хранилище загружено" + }, "settingDisabledByPolicy": { "message": "Этот параметр отключен политикой вашей организации.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Номер карты" + }, + "sessionTimeoutSettingsAction": { + "message": "Тайм-аут действия" } } diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 8c5961153eb..9b36684dc5a 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "පද්ධතිය ලොක් මත" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "බ්රව්සරය නැවත ආරම්භ" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index b459c86c236..a269756a414 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Zobraziť všetky" }, + "viewLess": { + "message": "Zobraziť menej" + }, "viewLogin": { "message": "Zobraziť prihlásenie" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Keď je systém uzamknutý" }, + "onIdle": { + "message": "Pri nečinnosti systému" + }, + "onSleep": { + "message": "V režime spánku" + }, "onRestart": { "message": "Po reštarte prehliadača" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgradovať na Prémium" }, + "loadingVault": { + "message": "Načítava sa trezor" + }, + "vaultLoaded": { + "message": "Trezor sa načítal" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Číslo karty" + }, + "sessionTimeoutSettingsAction": { + "message": "Akcia pri vypršaní časového limitu" } } diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 0a6266636b3..3cbd9a11342 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Ob zaklepu sistema" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Ob ponovnem zagonu brskalnika" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index 0158ca6ba2b..d13939f8656 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -32,7 +32,7 @@ "message": "Употребити једнократну пријаву" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "Ваша организација захтева јединствену пријаву." }, "welcomeBack": { "message": "Добродошли назад" @@ -592,7 +592,10 @@ "message": "Приказ" }, "viewAll": { - "message": "View all" + "message": "Прегледај све" + }, + "viewLess": { + "message": "View less" }, "viewLogin": { "message": "Преглед пријаве" @@ -796,6 +799,12 @@ "onLocked": { "message": "На закључавање система" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "На покретање прегледача" }, @@ -1035,10 +1044,10 @@ "message": "Ставка уређена" }, "savedWebsite": { - "message": "Saved website" + "message": "Сачувана веб локација" }, "savedWebsites": { - "message": "Saved websites ( $COUNT$ )", + "message": "Сачувана веб локација ($COUNT$)", "placeholders": { "count": { "content": "$1", @@ -1636,7 +1645,7 @@ "message": "Морате додати или основни УРЛ сервера или бар једно прилагођено окружење." }, "selfHostedEnvMustUseHttps": { - "message": "URLs must use HTTPS." + "message": "Везе морају да користе HTTPS." }, "customEnvironment": { "message": "Прилагођено окружење" @@ -1692,28 +1701,28 @@ "message": "Угасити ауто-пуњење" }, "confirmAutofill": { - "message": "Confirm autofill" + "message": "Потврди аутопуњење" }, "confirmAutofillDesc": { - "message": "This site doesn't match your saved login details. Before you fill in your login credentials, make sure it's a trusted site." + "message": "Овај сајт се не подудара са вашим сачуваним подацима за пријаву. Пре него што унесете своје акредитиве за пријаву, уверите се да је то поуздан сајт." }, "showInlineMenuLabel": { "message": "Прикажи предлоге за ауто-попуњавање у пољима обрасца" }, "howDoesBitwardenProtectFromPhishing": { - "message": "How does Bitwarden protect your data from phishing?" + "message": "Како Bitwarden штити ваше податке од фишинга?" }, "currentWebsite": { - "message": "Current website" + "message": "Тренутни сајт" }, "autofillAndAddWebsite": { - "message": "Autofill and add this website" + "message": "Ауто-попуни и додај овај сајт" }, "autofillWithoutAdding": { - "message": "Autofill without adding" + "message": "Ауто-попуни без додавања" }, "doNotAutofill": { - "message": "Do not autofill" + "message": "Не попуни" }, "showInlineMenuIdentitiesLabel": { "message": "Приказати идентитете као предлоге" @@ -3277,7 +3286,7 @@ "message": "Грешка при декрипцији" }, "errorGettingAutoFillData": { - "message": "Error getting autofill data" + "message": "Грешка при преузимању података за ауто-попуњавање" }, "couldNotDecryptVaultItemsBelow": { "message": "Bitwarden није могао да декриптује ставке из трезора наведене испод." @@ -4051,13 +4060,13 @@ "description": "Toast message for informing the user that autofill on page load has been set to the default setting." }, "cannotAutofill": { - "message": "Cannot autofill" + "message": "Не може да се ауто-попуни" }, "cannotAutofillExactMatch": { - "message": "Default matching is set to 'Exact Match'. The current website does not exactly match the saved login details for this item." + "message": "Подразумевано подударање је подешено на „Тачно подударање“. Тренутна веб локација не одговара тачно сачуваним детаљима за пријаву за ову ставку." }, "okay": { - "message": "Okay" + "message": "У реду" }, "toggleSideNavigation": { "message": "Укључите бочну навигацију" @@ -4975,7 +4984,7 @@ } }, "defaultLabelWithValue": { - "message": "Default ( $VALUE$ )", + "message": "Подразумевано ($VALUE$)", "description": "A label that indicates the default value for a field with the current default value in parentheses.", "placeholders": { "value": { @@ -5774,40 +5783,49 @@ "message": "Потврдите домен конектора кључа" }, "atRiskLoginsSecured": { - "message": "Great job securing your at-risk logins!" + "message": "Сјајан посао обезбеђивања ваших ризичних пријава!" }, "upgradeNow": { - "message": "Upgrade now" + "message": "Надогради сада" }, "builtInAuthenticator": { - "message": "Built-in authenticator" + "message": "Уграђени аутентификатор" }, "secureFileStorage": { - "message": "Secure file storage" + "message": "Сигурно складиштење датотека" }, "emergencyAccess": { - "message": "Emergency access" + "message": "Хитан приступ" }, "breachMonitoring": { - "message": "Breach monitoring" + "message": "Праћење повreda безбедности" }, "andMoreFeatures": { - "message": "And more!" + "message": "И још више!" }, "planDescPremium": { - "message": "Complete online security" + "message": "Потпуна онлајн безбедност" }, "upgradeToPremium": { - "message": "Upgrade to Premium" + "message": "Надоградите на Premium" + }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" }, "settingDisabledByPolicy": { - "message": "This setting is disabled by your organization's policy.", + "message": "Ово подешавање је онемогућено смерницама ваше организације.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." }, "zipPostalCodeLabel": { - "message": "ZIP / Postal code" + "message": "ZIP/Поштански број" }, "cardNumberLabel": { - "message": "Card number" + "message": "Број картице" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 057a7ca746c..9f84e9d714c 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Visa alla" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "Visa inloggning" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Vid låsning av datorn" }, + "onIdle": { + "message": "När systemet är overksamt" + }, + "onSleep": { + "message": "När systemet är i strömsparläge" + }, "onRestart": { "message": "Vid omstart" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Uppgradera till Premium" }, + "loadingVault": { + "message": "Läser in valv" + }, + "vaultLoaded": { + "message": "Valvet lästes in" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Kortnummer" + }, + "sessionTimeoutSettingsAction": { + "message": "Tidsgränsåtgärd" } } diff --git a/apps/browser/src/_locales/ta/messages.json b/apps/browser/src/_locales/ta/messages.json index 43944875889..cbefd26424c 100644 --- a/apps/browser/src/_locales/ta/messages.json +++ b/apps/browser/src/_locales/ta/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "உள்நுழைவைக் காண்க" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "சிஸ்டம் பூட்டப்பட்டவுடன்" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "உலாவி மறுதொடக்கம் செய்யப்பட்டவுடன்" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 39e6c0be881..13e74f8d807 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On system lock" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On browser restart" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index e92192dafa0..594bc6d7a94 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "View all" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "View login" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "On Locked" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "On Restart" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Card number" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index 543560810fe..7f234b8750a 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Tümünü göster" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "Hesabı göster" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Sistem kilitlenince" }, + "onIdle": { + "message": "Sistem boştayken" + }, + "onSleep": { + "message": "Sistem uyuyunca" + }, "onRestart": { "message": "Tarayıcı yeniden başlatılınca" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Premium'a yükselt" }, + "loadingVault": { + "message": "Kasa yükleniyor" + }, + "vaultLoaded": { + "message": "Kasa yüklendi" + }, "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." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Kart numarası" + }, + "sessionTimeoutSettingsAction": { + "message": "Zaman aşımı eylemi" } } diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index 2c6fa4eb15b..a17033ee6e8 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Переглянути все" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "Переглянути запис" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "З блокуванням системи" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "З перезапуском браузера" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Покращити до Premium" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "Цей параметр вимкнено політикою вашої організації.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Номер картки" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 8029f5b2c46..2fdba62adeb 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "Xem tất cả" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "Xem đăng nhập" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "Mỗi khi khóa máy" }, + "onIdle": { + "message": "On system idle" + }, + "onSleep": { + "message": "On system sleep" + }, "onRestart": { "message": "Mỗi khi khởi động lại trình duyệt" }, @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "Nâng cấp lên gói Cao cấp" }, + "loadingVault": { + "message": "Loading vault" + }, + "vaultLoaded": { + "message": "Vault loaded" + }, "settingDisabledByPolicy": { "message": "Cài đặt này bị vô hiệu hóa bởi chính sách tổ chức của bạn.", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "Số thẻ" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" } } diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index e59a74e358d..52d8a03b769 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "查看全部" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "查看登录" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "系统锁定时" }, + "onIdle": { + "message": "系统空闲时" + }, + "onSleep": { + "message": "系统睡眠时" + }, "onRestart": { "message": "浏览器重启时" }, @@ -3726,7 +3735,7 @@ "message": "当前会话" }, "mobile": { - "message": "移动", + "message": "移动端", "description": "Mobile app" }, "extension": { @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "升级为高级版" }, + "loadingVault": { + "message": "正在加载密码库" + }, + "vaultLoaded": { + "message": "密码库已加载" + }, "settingDisabledByPolicy": { "message": "此设置被您组织的策略禁用了。", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "卡号" + }, + "sessionTimeoutSettingsAction": { + "message": "超时动作" } } diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index 63f3ea59f60..370c147871b 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -594,6 +594,9 @@ "viewAll": { "message": "檢視全部" }, + "viewLess": { + "message": "View less" + }, "viewLogin": { "message": "檢視登入" }, @@ -796,6 +799,12 @@ "onLocked": { "message": "於系統鎖定時" }, + "onIdle": { + "message": "系統閒置時" + }, + "onSleep": { + "message": "系統睡眠時" + }, "onRestart": { "message": "於瀏覽器重新啟動時" }, @@ -4975,7 +4984,7 @@ } }, "defaultLabelWithValue": { - "message": "Default ( $VALUE$ )", + "message": "預設 ($VALUE$)", "description": "A label that indicates the default value for a field with the current default value in parentheses.", "placeholders": { "value": { @@ -5800,6 +5809,12 @@ "upgradeToPremium": { "message": "升級到 Premium" }, + "loadingVault": { + "message": "正在載入密碼庫" + }, + "vaultLoaded": { + "message": "已載入密碼庫" + }, "settingDisabledByPolicy": { "message": "此設定已被你的組織原則停用。", "description": "This hint text is displayed when a user setting is disabled due to an organization policy." @@ -5809,5 +5824,8 @@ }, "cardNumberLabel": { "message": "支付卡號碼" + }, + "sessionTimeoutSettingsAction": { + "message": "逾時後動作" } } From 1ce33a0a985ac4ab17266eb040003fab7f735bc8 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 14 Nov 2025 12:44:13 +0100 Subject: [PATCH 11/75] Autosync the updated translations (#17377) 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 | 17 +- 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 | 45 ++-- 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 | 13 +- 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 | 11 +- 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 | 9 +- 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 | 9 +- apps/desktop/src/locales/pt_BR/messages.json | 241 ++++++++++--------- apps/desktop/src/locales/pt_PT/messages.json | 11 +- apps/desktop/src/locales/ro/messages.json | 9 +- apps/desktop/src/locales/ru/messages.json | 9 +- apps/desktop/src/locales/si/messages.json | 9 +- apps/desktop/src/locales/sk/messages.json | 47 ++-- apps/desktop/src/locales/sl/messages.json | 9 +- apps/desktop/src/locales/sr/messages.json | 41 ++-- 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 | 9 +- apps/desktop/src/locales/zh_CN/messages.json | 11 +- apps/desktop/src/locales/zh_TW/messages.json | 11 +- 64 files changed, 563 insertions(+), 371 deletions(-) diff --git a/apps/desktop/src/locales/af/messages.json b/apps/desktop/src/locales/af/messages.json index 6da1c7e9c8b..1c6a2bc49c9 100644 --- a/apps/desktop/src/locales/af/messages.json +++ b/apps/desktop/src/locales/af/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Ongelukkig word blaaierintegrasie tans slegs in die weergawe vir die Mac-toepwinkel ondersteun." - }, "browserIntegrationWindowsStoreDesc": { "message": "Ongelukkig word blaaierintegrasie tans nie in die weergawe vir die Windows-winkel ondersteun nie." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/ar/messages.json b/apps/desktop/src/locales/ar/messages.json index a6a7e881db9..ca404f4e179 100644 --- a/apps/desktop/src/locales/ar/messages.json +++ b/apps/desktop/src/locales/ar/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "حدث خطأ أثناء تمكين دمج المتصفح." }, - "browserIntegrationMasOnlyDesc": { - "message": "للأسف، لا يتم دعم تكامل المتصفح إلا في إصدار متجر تطبيقات ماك في الوقت الحالي." - }, "browserIntegrationWindowsStoreDesc": { "message": "للأسف، لا يتم دعم تكامل المتصفح في إصدار متجر ويندوز في الوقت الحالي." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/az/messages.json b/apps/desktop/src/locales/az/messages.json index 81b24cadfe8..55c2bdcd677 100644 --- a/apps/desktop/src/locales/az/messages.json +++ b/apps/desktop/src/locales/az/messages.json @@ -511,7 +511,7 @@ "description": "This describes a value that is 'linked' (related) to another value." }, "remove": { - "message": "Çıxart" + "message": "Xaric et" }, "nameRequired": { "message": "Ad lazımdır." @@ -1659,7 +1659,7 @@ } }, "passwordSafe": { - "message": "Bu parol, veri pozuntularında qeydə alınmayıb. Rahatlıqla istifadə edə bilərsiniz." + "message": "Bu parol, veri pozuntularında qeydə alınmayıb. Əmniyyətlə istifadə edə bilərsiniz." }, "baseDomain": { "message": "Baza domeni", @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Brauzer inteqrasiyasını fəallaşdırarkən bir xəta baş verdi." }, - "browserIntegrationMasOnlyDesc": { - "message": "Təəssüf ki, brauzer inteqrasiyası indilik yalnız Mac App Store versiyasında dəstəklənir." - }, "browserIntegrationWindowsStoreDesc": { "message": "Təəssüf ki, brauzer inteqrasiyası hal-hazırda Windows Store versiyasında dəstəklənmir." }, @@ -3906,7 +3903,7 @@ "message": "Ana qovluğun adından sonra \"/\" əlavə edərək qovluğu ardıcıl yerləşdirin. Nümunə: Social/Forums" }, "sendsTitleNoItems": { - "message": "Send, həssas məlumatlar təhlükəsizdir", + "message": "Send ilə həssas məlumatlar əmniyyətdədir", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendsBodyNoItems": { @@ -3961,7 +3958,7 @@ "message": "Kimliklərinizlə, uzun qeydiyyat və ya əlaqə xanalarını daha tez avtomatik doldurun." }, "newNoteNudgeTitle": { - "message": "Həssas verilərinizi güvənli şəkildə saxlayın" + "message": "Həssas verilərinizi əmniyyətdə saxlayın" }, "newNoteNudgeBody": { "message": "Notlarla, bankçılıq və ya sığorta təfsilatları kimi həssas veriləri təhlükəsiz saxlayın." @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "\"Premium\"a yüksəlt" + }, + "sessionTimeoutSettingsAction": { + "message": "Vaxt bitmə əməliyyatı" + }, + "sessionTimeoutHeader": { + "message": "Sessiya vaxt bitməsi" } } diff --git a/apps/desktop/src/locales/be/messages.json b/apps/desktop/src/locales/be/messages.json index cead61915ca..b2e4db47b32 100644 --- a/apps/desktop/src/locales/be/messages.json +++ b/apps/desktop/src/locales/be/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "На жаль, інтэграцыя з браўзерам зараз падтрымліваецца толькі ў версіі для Mac App Store." - }, "browserIntegrationWindowsStoreDesc": { "message": "На жаль, інтэграцыя з браўзерам у цяперашні час не падтрымліваецца ў версіі для Microsoft Store." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/bg/messages.json b/apps/desktop/src/locales/bg/messages.json index c2c2e236d37..ad03c2cc023 100644 --- a/apps/desktop/src/locales/bg/messages.json +++ b/apps/desktop/src/locales/bg/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Възникна грешка при включването на интеграцията с браузъра." }, - "browserIntegrationMasOnlyDesc": { - "message": "За жалост в момента интеграцията с браузър не се поддържа във версията за магазина на Mac." - }, "browserIntegrationWindowsStoreDesc": { "message": "За жалост в момента интеграцията с браузър не се поддържа във версията за магазина на Windows." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Надградете до Платения план" + }, + "sessionTimeoutSettingsAction": { + "message": "Действие при изтичането на времето за достъп" + }, + "sessionTimeoutHeader": { + "message": "Изтичане на времето за сесията" } } diff --git a/apps/desktop/src/locales/bn/messages.json b/apps/desktop/src/locales/bn/messages.json index 5c932d4ed21..d6c61c1ab51 100644 --- a/apps/desktop/src/locales/bn/messages.json +++ b/apps/desktop/src/locales/bn/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/bs/messages.json b/apps/desktop/src/locales/bs/messages.json index 08793959da6..569f1072c4b 100644 --- a/apps/desktop/src/locales/bs/messages.json +++ b/apps/desktop/src/locales/bs/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Nažalost, za sada je integracija sa preglednikom podržana samo u Mac App Store verziji aplikacije." - }, "browserIntegrationWindowsStoreDesc": { "message": "Nažalost, integracija sa preglednikom nije podržana u Windows Store verziji aplikacije." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/ca/messages.json b/apps/desktop/src/locales/ca/messages.json index 8ee8f7030bc..de468f1e8b3 100644 --- a/apps/desktop/src/locales/ca/messages.json +++ b/apps/desktop/src/locales/ca/messages.json @@ -42,7 +42,7 @@ "message": "Cerca en la caixa forta" }, "resetSearch": { - "message": "Reset search" + "message": "Restableix la cerca" }, "addItem": { "message": "Afegeix element" @@ -70,7 +70,7 @@ } }, "noEditPermissions": { - "message": "You don't have permission to edit this item" + "message": "No teniu permisos per editar aquest element" }, "welcomeBack": { "message": "Benvingut/da de nou" @@ -706,10 +706,10 @@ "message": "S'ha guardat el fitxer adjunt." }, "addAttachment": { - "message": "Add attachment" + "message": "Afig adjunt" }, "maxFileSizeSansPunctuation": { - "message": "Maximum file size is 500 MB" + "message": "La mida màxima del fitxer és de 500 MB" }, "file": { "message": "Fitxer" @@ -757,7 +757,7 @@ "message": "Inicia sessió a Bitwarden" }, "enterTheCodeSentToYourEmail": { - "message": "Enter the code sent to your email" + "message": "Introduïu el codi que us hem enviat al correu electrònic" }, "enterTheCodeFromYourAuthenticatorApp": { "message": "Introduïu el codi de la vostra aplicació d'autenticació" @@ -954,14 +954,14 @@ } }, "dontAskAgainOnThisDeviceFor30Days": { - "message": "Don't ask again on this device for 30 days" + "message": "No ho torneu a preguntar en aquest dispositiu durant 30 dies" }, "selectAnotherMethod": { "message": "Select another method", "description": "Select another two-step login method" }, "useYourRecoveryCode": { - "message": "Use your recovery code" + "message": "Utilitzeu el codi de recuperació" }, "insertU2f": { "message": "Introduïu la vostra clau de seguretat al port USB de l'ordinador. Si té un botó, premeu-lo." @@ -1467,7 +1467,7 @@ "description": "Copy credit card security code (CVV)" }, "cardNumber": { - "message": "card number" + "message": "núm. targeta de crèdit" }, "premiumMembership": { "message": "Subscripció Premium" @@ -1862,10 +1862,10 @@ "message": "Bloqueja amb la contrasenya mestra en reiniciar" }, "requireMasterPasswordOrPinOnAppRestart": { - "message": "Require master password or PIN on app restart" + "message": "Sol·licita la contrasenya mestra o el PIN en reiniciar l'aplicació" }, "requireMasterPasswordOnAppRestart": { - "message": "Require master password on app restart" + "message": "Sol·licita la contrasenya mestra en reiniciar l'aplicació" }, "deleteAccount": { "message": "Suprimeix el compte" @@ -2023,7 +2023,7 @@ "message": "Make 2-step verification seamless" }, "totpHelper": { - "message": "Bitwarden can store and fill 2-step verification codes. Copy and paste the key into this field." + "message": "Bitwarden pot emmagatzemar i omplir codis de verificació en dos passos. Copieu i enganxeu la clau en aquest camp." }, "totpHelperWithCapture": { "message": "Bitwarden can store and fill 2-step verification codes. Select the camera icon to take a screenshot of this website's authenticator QR code, or copy and paste the key into this field." @@ -2048,7 +2048,7 @@ } }, "cardExpiredTitle": { - "message": "Expired card" + "message": "Targeta de crèdit caducada" }, "cardExpiredMessage": { "message": "If you've renewed it, update the card's information" @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "S'ha produït un error en activar la integració del navegador." }, - "browserIntegrationMasOnlyDesc": { - "message": "Malauradament, la integració del navegador només és compatible amb la versió de Mac App Store." - }, "browserIntegrationWindowsStoreDesc": { "message": "Malauradament, la integració del navegador només és compatible amb la versió de Microsoft Store." }, @@ -3092,18 +3089,18 @@ "message": "You denied a login attempt from another device. If this was you, try to log in with the device again." }, "webApp": { - "message": "Web app" + "message": "Aplicació web" }, "mobile": { - "message": "Mobile", + "message": "Mòbil", "description": "Mobile app" }, "extension": { - "message": "Extension", + "message": "Extensió", "description": "Browser extension/addon" }, "desktop": { - "message": "Desktop", + "message": "Escriptori", "description": "Desktop app" }, "cli": { @@ -3114,10 +3111,10 @@ "description": "Software Development Kit" }, "server": { - "message": "Server" + "message": "Servidor" }, "loginRequest": { - "message": "Login request" + "message": "Petició d'inici de sessió" }, "deviceType": { "message": "Tipus de dispositiu" @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/cs/messages.json b/apps/desktop/src/locales/cs/messages.json index 578d0607cc2..c02dbabbc93 100644 --- a/apps/desktop/src/locales/cs/messages.json +++ b/apps/desktop/src/locales/cs/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Vyskytla se chyba při povolování integrace prohlížeče." }, - "browserIntegrationMasOnlyDesc": { - "message": "Integrace prohlížeče je podporována jen ve verzi pro Mac App Store." - }, "browserIntegrationWindowsStoreDesc": { "message": "Integrace prohlížeče není ve verzi pro Windows Store podporována." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Aktualizovat na Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Akce vypršení časového limitu" + }, + "sessionTimeoutHeader": { + "message": "Časový limit relace" } } diff --git a/apps/desktop/src/locales/cy/messages.json b/apps/desktop/src/locales/cy/messages.json index 278196f9d04..25b52fcc101 100644 --- a/apps/desktop/src/locales/cy/messages.json +++ b/apps/desktop/src/locales/cy/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/da/messages.json b/apps/desktop/src/locales/da/messages.json index fad9b9c1af4..1d135a533f2 100644 --- a/apps/desktop/src/locales/da/messages.json +++ b/apps/desktop/src/locales/da/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "En fejl opstod under aktivering af webbrowserintegration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Desværre understøttes browserintegration indtil videre kun i Mac App Store-versionen." - }, "browserIntegrationWindowsStoreDesc": { "message": "Desværre understøttes browserintegration pt. ikke i Microsoft Store-versionen." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/de/messages.json b/apps/desktop/src/locales/de/messages.json index 3f5caa00c4c..2f8daec5b68 100644 --- a/apps/desktop/src/locales/de/messages.json +++ b/apps/desktop/src/locales/de/messages.json @@ -772,7 +772,7 @@ "message": "Anmelden mit einem anderen Gerät" }, "useSingleSignOn": { - "message": "Single Sign-on verwenden" + "message": "Single Sign-On verwenden" }, "yourOrganizationRequiresSingleSignOn": { "message": "Deine Organisation erfordert Single Sign-On." @@ -1979,7 +1979,7 @@ "message": "Timeout-Aktion bestätigen" }, "enterpriseSingleSignOn": { - "message": "Enterprise Single-Sign-On" + "message": "Enterprise Single Sign-On" }, "setMasterPassword": { "message": "Master-Passwort festlegen" @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Beim Aktivieren der Browser-Integration ist ein Fehler aufgetreten." }, - "browserIntegrationMasOnlyDesc": { - "message": "Leider wird die Browser-Integration derzeit nur in der Mac App Store Version unterstützt." - }, "browserIntegrationWindowsStoreDesc": { "message": "Leider wird die Browser-Integration derzeit nicht in der Microsoft Store Version unterstützt." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade auf Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout-Aktion" + }, + "sessionTimeoutHeader": { + "message": "Sitzungs-Timeout" } } diff --git a/apps/desktop/src/locales/el/messages.json b/apps/desktop/src/locales/el/messages.json index 55a3c4fe170..0b869c1e02f 100644 --- a/apps/desktop/src/locales/el/messages.json +++ b/apps/desktop/src/locales/el/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Παρουσιάστηκε σφάλμα κατά την ενεργοποίηση ενσωμάτωσης του περιηγητή." }, - "browserIntegrationMasOnlyDesc": { - "message": "Δυστυχώς η ενσωμάτωση του προγράμματος περιήγησης υποστηρίζεται μόνο στην έκδοση Mac App Store για τώρα." - }, "browserIntegrationWindowsStoreDesc": { "message": "Δυστυχώς η ενσωμάτωση του περιηγητή, δεν υποστηρίζεται προς το παρόν στην έκδοση Windows Store." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/en_GB/messages.json b/apps/desktop/src/locales/en_GB/messages.json index 63e0cf96742..16af69361c6 100644 --- a/apps/desktop/src/locales/en_GB/messages.json +++ b/apps/desktop/src/locales/en_GB/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/en_IN/messages.json b/apps/desktop/src/locales/en_IN/messages.json index 832025c8c0e..c6f1253bb59 100644 --- a/apps/desktop/src/locales/en_IN/messages.json +++ b/apps/desktop/src/locales/en_IN/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Windows Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/eo/messages.json b/apps/desktop/src/locales/eo/messages.json index a3a8643a8f6..28a9f3b8bce 100644 --- a/apps/desktop/src/locales/eo/messages.json +++ b/apps/desktop/src/locales/eo/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/es/messages.json b/apps/desktop/src/locales/es/messages.json index 01163c7ad29..9966fa1064c 100644 --- a/apps/desktop/src/locales/es/messages.json +++ b/apps/desktop/src/locales/es/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Se ha producido un error mientras se habilitaba la integración del navegador." }, - "browserIntegrationMasOnlyDesc": { - "message": "Por desgracia la integración del navegador sólo está soportada por ahora en la versión de la Mac App Store." - }, "browserIntegrationWindowsStoreDesc": { "message": "Lamentablemente, la integración del navegador no está actualmente soportada en la versión de Microsoft Store." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/et/messages.json b/apps/desktop/src/locales/et/messages.json index 25489871b3b..d85c52bb763 100644 --- a/apps/desktop/src/locales/et/messages.json +++ b/apps/desktop/src/locales/et/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Midagi läks valesti brauseriga ühendamisel." }, - "browserIntegrationMasOnlyDesc": { - "message": "Paraku on brauseri integratsioon hetkel toetatud ainult Mac App Store'i versioonis." - }, "browserIntegrationWindowsStoreDesc": { "message": "Paraku ei ole brauseri integratsioon hetkel Microsoft Store versioonis toetatud." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/eu/messages.json b/apps/desktop/src/locales/eu/messages.json index c1007d7d71c..36401df0078 100644 --- a/apps/desktop/src/locales/eu/messages.json +++ b/apps/desktop/src/locales/eu/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Zoritxarrez, Mac App Storeren bertsioan soilik onartzen da oraingoz nabigatzailearen integrazioa." - }, "browserIntegrationWindowsStoreDesc": { "message": "Zoritxarrez, nabigatzailearen integrazioa ez da onartzen Windows Storen bertsioan." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/fa/messages.json b/apps/desktop/src/locales/fa/messages.json index eb62c711628..caa241eb036 100644 --- a/apps/desktop/src/locales/fa/messages.json +++ b/apps/desktop/src/locales/fa/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "خطایی هنگام فعال‌سازی یکپارچه سازی مرورگر رخ داده است." }, - "browserIntegrationMasOnlyDesc": { - "message": "متأسفانه در حال حاضر ادغام مرورگر فقط در نسخه Mac App Store پشتیبانی می‌شود." - }, "browserIntegrationWindowsStoreDesc": { "message": "متأسفانه در حال حاضر ادغام مرورگر در نسخه فروشگاه ویندوز پشتیبانی نمی‌شود." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/fi/messages.json b/apps/desktop/src/locales/fi/messages.json index 06f0338e1f3..e2952659d03 100644 --- a/apps/desktop/src/locales/fi/messages.json +++ b/apps/desktop/src/locales/fi/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Otettaessa selainintegraatiota käyttöön tapahtui virhe." }, - "browserIntegrationMasOnlyDesc": { - "message": "Valitettavasti selainintegraatiota tuetaan toistaiseksi vain Mac App Store -versiossa." - }, "browserIntegrationWindowsStoreDesc": { "message": "Valitettavasti selainintegraatiota ei toistaiseksi tueta Microsoft Store -versiossa." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/fil/messages.json b/apps/desktop/src/locales/fil/messages.json index 6a32df33ecb..6eaa5577807 100644 --- a/apps/desktop/src/locales/fil/messages.json +++ b/apps/desktop/src/locales/fil/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Sa kasamaang palad ang pagsasama ng browser ay suportado lamang sa bersyon ng Mac App Store para sa ngayon." - }, "browserIntegrationWindowsStoreDesc": { "message": "Sa kasamaang palad ang pagsasama ng browser ay kasalukuyang hindi suportado sa bersyon ng Microsoft Store." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/fr/messages.json b/apps/desktop/src/locales/fr/messages.json index 0cd10d0582c..acd5037bb6d 100644 --- a/apps/desktop/src/locales/fr/messages.json +++ b/apps/desktop/src/locales/fr/messages.json @@ -283,7 +283,7 @@ "message": "Bitwarden n'a pas pu déchiffrer le(s) élément(s) du coffre listé(s) ci-dessous." }, "contactCSToAvoidDataLossPart1": { - "message": "Contacter le service clientèle", + "message": "Contacter succès client", "description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'" }, "contactCSToAvoidDataLossPart2": { @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Une erreur s'est produite lors de l'action de l'intégration du navigateur." }, - "browserIntegrationMasOnlyDesc": { - "message": "Malheureusement l'intégration avec le navigateur est uniquement supportée dans la version Mac App Store pour le moment." - }, "browserIntegrationWindowsStoreDesc": { "message": "Malheureusement l'intégration avec le navigateur n'est pas supportée dans la version Windows Store pour le moment." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/gl/messages.json b/apps/desktop/src/locales/gl/messages.json index 70d4c7cb494..d607bb8d097 100644 --- a/apps/desktop/src/locales/gl/messages.json +++ b/apps/desktop/src/locales/gl/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index abe445f83d5..87fac938a34 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "אירעה שגיאה בעת אפשור שילוב דפדפן." }, - "browserIntegrationMasOnlyDesc": { - "message": "למרבה הצער שילוב דפדפן נתמך רק בגרסת Mac App Store לעת עתה." - }, "browserIntegrationWindowsStoreDesc": { "message": "למרבה הצער שילוב דפדפן אינו נתמך כרגע בגרסת ה־Microsoft Store." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/hi/messages.json b/apps/desktop/src/locales/hi/messages.json index 84676c4d941..2ab323eedc9 100644 --- a/apps/desktop/src/locales/hi/messages.json +++ b/apps/desktop/src/locales/hi/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/hr/messages.json b/apps/desktop/src/locales/hr/messages.json index d1c2ba68779..0f7a8185118 100644 --- a/apps/desktop/src/locales/hr/messages.json +++ b/apps/desktop/src/locales/hr/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Pogreška prillikom integracije s preglednikom." }, - "browserIntegrationMasOnlyDesc": { - "message": "Nažalost, za sada je integracija s preglednikom podržana samo u Mac App Store verziji aplikacije." - }, "browserIntegrationWindowsStoreDesc": { "message": "Nažalost, integracija s preglednikom trenutno nije podržana u Windows Store verziji aplikacije." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": " Nadogradi na Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Radnja nakon isteka" + }, + "sessionTimeoutHeader": { + "message": "Istek sesije" } } diff --git a/apps/desktop/src/locales/hu/messages.json b/apps/desktop/src/locales/hu/messages.json index 9d296a7d2cc..9a6dd787f8c 100644 --- a/apps/desktop/src/locales/hu/messages.json +++ b/apps/desktop/src/locales/hu/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Hiba történt a böngésző integrációjának engedélyezése közben." }, - "browserIntegrationMasOnlyDesc": { - "message": "Sajnos a böngésző integrációt egyelőre csak a Mac App Store verzió támogatja." - }, "browserIntegrationWindowsStoreDesc": { "message": "A böngésző integrációt egyelőre csak a Windows Store verzió támogatja." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Áttérés Prémium csomagra" + }, + "sessionTimeoutSettingsAction": { + "message": "Időkifutási művelet" + }, + "sessionTimeoutHeader": { + "message": "Munkamenet időkifutás" } } diff --git a/apps/desktop/src/locales/id/messages.json b/apps/desktop/src/locales/id/messages.json index 03da4bbd030..188ee153da1 100644 --- a/apps/desktop/src/locales/id/messages.json +++ b/apps/desktop/src/locales/id/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Sayangnya integrasi browser hanya didukung di versi Mac App Store untuk saat ini." - }, "browserIntegrationWindowsStoreDesc": { "message": "Sayangnya integrasi browser saat ini tidak didukung di versi Windows Store." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/it/messages.json b/apps/desktop/src/locales/it/messages.json index 4881d96b44a..1656a301b42 100644 --- a/apps/desktop/src/locales/it/messages.json +++ b/apps/desktop/src/locales/it/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Si è verificato un errore durante l'attivazione dell'integrazione del browser." }, - "browserIntegrationMasOnlyDesc": { - "message": "Purtroppo l'integrazione del browser è supportata solo nella versione nell'App Store per ora." - }, "browserIntegrationWindowsStoreDesc": { "message": "Purtroppo l'integrazione del browser non è supportata nella versione del Microsoft Store per ora." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/ja/messages.json b/apps/desktop/src/locales/ja/messages.json index 1dfb5a42ead..ca50828b12c 100644 --- a/apps/desktop/src/locales/ja/messages.json +++ b/apps/desktop/src/locales/ja/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "ブラウザー統合の有効化中にエラーが発生しました。" }, - "browserIntegrationMasOnlyDesc": { - "message": "残念ながら、ブラウザ統合は、Mac App Storeのバージョンでのみサポートされています。" - }, "browserIntegrationWindowsStoreDesc": { "message": "残念ながらお使いの Microsoft Store のバージョンではブラウザの統合に対応していません。" }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/ka/messages.json b/apps/desktop/src/locales/ka/messages.json index edaa68e7302..9337286d3fd 100644 --- a/apps/desktop/src/locales/ka/messages.json +++ b/apps/desktop/src/locales/ka/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/km/messages.json b/apps/desktop/src/locales/km/messages.json index 70d4c7cb494..d607bb8d097 100644 --- a/apps/desktop/src/locales/km/messages.json +++ b/apps/desktop/src/locales/km/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/kn/messages.json b/apps/desktop/src/locales/kn/messages.json index b880c845f4f..d1375efee8c 100644 --- a/apps/desktop/src/locales/kn/messages.json +++ b/apps/desktop/src/locales/kn/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "ದುರದೃಷ್ಟವಶಾತ್ ಬ್ರೌಸರ್ ಏಕೀಕರಣವನ್ನು ಇದೀಗ ಮ್ಯಾಕ್ ಆಪ್ ಸ್ಟೋರ್ ಆವೃತ್ತಿಯಲ್ಲಿ ಮಾತ್ರ ಬೆಂಬಲಿಸಲಾಗುತ್ತದೆ." - }, "browserIntegrationWindowsStoreDesc": { "message": "ದುರದೃಷ್ಟವಶಾತ್ ವಿಂಡೋಸ್ ಸ್ಟೋರ್ ಆವೃತ್ತಿಯಲ್ಲಿ ಬ್ರೌಸರ್ ಏಕೀಕರಣವನ್ನು ಪ್ರಸ್ತುತ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/ko/messages.json b/apps/desktop/src/locales/ko/messages.json index 7ef1645febf..2e40b8d7f23 100644 --- a/apps/desktop/src/locales/ko/messages.json +++ b/apps/desktop/src/locales/ko/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "브라우저와 연결은 현재 Mac App Store 버전에서만 지원됩니다." - }, "browserIntegrationWindowsStoreDesc": { "message": "현재 Microsoft Store 버전에서는 브라우저와 연결이 지원되지 않습니다." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/lt/messages.json b/apps/desktop/src/locales/lt/messages.json index 471bde7b410..16f328d6240 100644 --- a/apps/desktop/src/locales/lt/messages.json +++ b/apps/desktop/src/locales/lt/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Deja, bet naršyklės integravimas kol kas palaikomas tik Mac App Store versijoje." - }, "browserIntegrationWindowsStoreDesc": { "message": "Deja, bet naršyklės integravimas nepalaikomas Microsoft Store versijoje." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/lv/messages.json b/apps/desktop/src/locales/lv/messages.json index cc6cad0fd40..7800a4e9024 100644 --- a/apps/desktop/src/locales/lv/messages.json +++ b/apps/desktop/src/locales/lv/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Atgadījās kļūda pārlūka saistīšanas iespējošanas laikā." }, - "browserIntegrationMasOnlyDesc": { - "message": "Diemžēl sasaistīšāna ar pārlūku pagaidām ir nodrošināta tikai Mac App Store laidienā." - }, "browserIntegrationWindowsStoreDesc": { "message": "Diemžēl sasaistīšana ar pārlūku pagaidām nav nodrošināta Windows veikala laidienā." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Uzlabot uz Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Noildzes darbība" + }, + "sessionTimeoutHeader": { + "message": "Sesijas noildze" } } diff --git a/apps/desktop/src/locales/me/messages.json b/apps/desktop/src/locales/me/messages.json index a67fa99079b..29e3cefee0c 100644 --- a/apps/desktop/src/locales/me/messages.json +++ b/apps/desktop/src/locales/me/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/ml/messages.json b/apps/desktop/src/locales/ml/messages.json index 4eee4cb0c0d..662ce9a1fc6 100644 --- a/apps/desktop/src/locales/ml/messages.json +++ b/apps/desktop/src/locales/ml/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/mr/messages.json b/apps/desktop/src/locales/mr/messages.json index 70d4c7cb494..d607bb8d097 100644 --- a/apps/desktop/src/locales/mr/messages.json +++ b/apps/desktop/src/locales/mr/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/my/messages.json b/apps/desktop/src/locales/my/messages.json index 047f272b564..bcbd26cede3 100644 --- a/apps/desktop/src/locales/my/messages.json +++ b/apps/desktop/src/locales/my/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/nb/messages.json b/apps/desktop/src/locales/nb/messages.json index 9bede02bfcf..42fb6d479c0 100644 --- a/apps/desktop/src/locales/nb/messages.json +++ b/apps/desktop/src/locales/nb/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Nettleserintegrasjon støttes dessverre bare i Mac App Store-versjonen for øyeblikket." - }, "browserIntegrationWindowsStoreDesc": { "message": "Nettleserintegrasjon er for øyeblikket dessverre ikke støttet i Windows Store-versjonen." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/ne/messages.json b/apps/desktop/src/locales/ne/messages.json index 093896ca17a..cce8f6a2ba5 100644 --- a/apps/desktop/src/locales/ne/messages.json +++ b/apps/desktop/src/locales/ne/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/nl/messages.json b/apps/desktop/src/locales/nl/messages.json index de0820ac5ed..82b51b018c5 100644 --- a/apps/desktop/src/locales/nl/messages.json +++ b/apps/desktop/src/locales/nl/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Er is iets misgegaan bij het tijdens het inschakelen van de browserintegratie." }, - "browserIntegrationMasOnlyDesc": { - "message": "Helaas wordt browserintegratie momenteel alleen ondersteund in de Mac App Store-versie." - }, "browserIntegrationWindowsStoreDesc": { "message": "Helaas wordt browserintegratie momenteel niet ondersteund in de Windows Store-versie." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Opwaarderen naar Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Time-out actie" + }, + "sessionTimeoutHeader": { + "message": "Sessietime-out" } } diff --git a/apps/desktop/src/locales/nn/messages.json b/apps/desktop/src/locales/nn/messages.json index c582356f115..08567979e8b 100644 --- a/apps/desktop/src/locales/nn/messages.json +++ b/apps/desktop/src/locales/nn/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/or/messages.json b/apps/desktop/src/locales/or/messages.json index 89489e1db87..4ca05acaac5 100644 --- a/apps/desktop/src/locales/or/messages.json +++ b/apps/desktop/src/locales/or/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/pl/messages.json b/apps/desktop/src/locales/pl/messages.json index b9eb4b6232b..c05e7f05cb1 100644 --- a/apps/desktop/src/locales/pl/messages.json +++ b/apps/desktop/src/locales/pl/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Wystąpił błąd podczas włączania połączenia z przeglądarką." }, - "browserIntegrationMasOnlyDesc": { - "message": "Połączenie z przeglądarką jest obsługiwane tylko z wersją aplikacji ze sklepu Mac App Store." - }, "browserIntegrationWindowsStoreDesc": { "message": "Połączenie z przeglądarką nie jest obecnie obsługiwane w aplikacji w wersji Windows Store." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/pt_BR/messages.json b/apps/desktop/src/locales/pt_BR/messages.json index 9749d280516..7871ac72533 100644 --- a/apps/desktop/src/locales/pt_BR/messages.json +++ b/apps/desktop/src/locales/pt_BR/messages.json @@ -70,7 +70,7 @@ } }, "noEditPermissions": { - "message": "You don't have permission to edit this item" + "message": "Você não tem permissão para editar este item" }, "welcomeBack": { "message": "Boas-vindas de volta" @@ -283,7 +283,7 @@ "message": "O Bitwarden não pôde descriptografar o(s) item(ns) listados abaixo do cofre." }, "contactCSToAvoidDataLossPart1": { - "message": "Contate o sucesso do consumidor", + "message": "Contate o costumer success", "description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'" }, "contactCSToAvoidDataLossPart2": { @@ -473,7 +473,7 @@ "message": "Use caixas de seleção se gostaria de preencher automaticamente a caixa de seleção de um formulário, como um lembrar e-mail" }, "linkedHelpText": { - "message": "Use um campo vinculado quando você estiver experienciando problemas de preenchimento automático em um site específico." + "message": "Use um campo vinculado quando você estiver experienciando problemas com o preenchimento automático em um site específico." }, "linkedLabelHelpText": { "message": "Digite o ID html, nome, aria-label, ou placeholder do campo." @@ -1635,7 +1635,7 @@ "message": "Copiado com sucesso" }, "errorRefreshingAccessToken": { - "message": "Erro ao acessar token de recarregamento" + "message": "Erro de atualização do token de acesso" }, "errorRefreshingAccessTokenDesc": { "message": "Nenhum token de recarregamento ou chave de API foi encontrado. Tente desconectar-se e conectar-se novamente." @@ -1749,10 +1749,10 @@ "message": "Use a chave de criptografia da sua conta, derivada do nome de usuário e senha principal da sua conta, para criptografar a exportação e restringir a importação para apenas a conta atual do Bitwarden." }, "passwordProtected": { - "message": "Protegido por senha" + "message": "Protegida por senha" }, "passwordProtectedOptionDescription": { - "message": "Defina uma senha de arquivo para criptografar a exportação e importá-la para qualquer conta do Bitwarden usando a senha para descriptografia." + "message": "Configure uma senha de arquivo para criptografar a exportação e importá-la para qualquer conta do Bitwarden usando a senha para descriptografá-la." }, "exportTypeHeading": { "message": "Tipo da exportação" @@ -1761,10 +1761,10 @@ "message": "Restrita à conta" }, "restrictCardTypeImport": { - "message": "Não é possível importar tipos de item de cartão" + "message": "Não é possível importar itens do tipo de cartão" }, "restrictCardTypeImportDesc": { - "message": "Uma política definida por 1 ou mais organizações impedem que você importe cartões em seus cofres." + "message": "Uma política configurada por uma ou mais organizações impedem que você importe cartões em seus cofres." }, "filePasswordAndConfirmFilePasswordDoNotMatch": { "message": "\"Senha do arquivo\" e \"Confirmar senha do arquivo\" não correspondem." @@ -1780,7 +1780,7 @@ "message": "Confirmar exportação do cofre" }, "exportWarningDesc": { - "message": "Esta exportação contém os dados do seu cofre em um formato não criptografado. Você não deve armazenar ou enviar o arquivo exportado por canais inseguros (como e-mail). Exclua o arquivo imediatamente após terminar de usá-lo." + "message": "Esta exportação contém os dados do seu cofre em um formato não criptografado. Você não deve armazenar ou enviar o arquivo exportado por canais inseguros (como e-mail). Apague o arquivo imediatamente após terminar de usá-lo." }, "encExportKeyWarningDesc": { "message": "Esta exportação criptografa seus dados usando a chave de criptografia da sua conta. Se você rotacionar a chave de criptografia da sua conta, você deve exportar novamente, já que você não será capaz de descriptografar este arquivo de exportação." @@ -1789,7 +1789,7 @@ "message": "As chaves de criptografia de conta são únicas para cada conta de usuário do Bitwarden, então você não pode importar uma exportação criptografada para uma conta diferente." }, "noOrganizationsList": { - "message": "Você não pertence a nenhuma organização. Organizações permitem-lhe compartilhar itens em segurança com outros usuários." + "message": "Você não faz parte de uma organização. Organizações permitem-lhe compartilhar itens com segurança com outros usuários." }, "noCollectionsInList": { "message": "Não há coleções para listar." @@ -1798,7 +1798,7 @@ "message": "Propriedade" }, "whoOwnsThisItem": { - "message": "Quem possui este item?" + "message": "Quem é o proprietário deste item?" }, "strong": { "message": "Forte", @@ -1816,17 +1816,17 @@ "message": "Senha principal fraca" }, "weakMasterPasswordDesc": { - "message": "A senha principal que você selecionou está fraca. Você deve usar uma senha principal forte (ou uma frase-passe) para proteger a sua conta Bitwarden adequadamente. Tem certeza que deseja usar esta senha principal?" + "message": "A senha principal que você escolheu é fraca. Você deve usar uma senha principal forte (ou uma frase secreta) para proteger a sua conta Bitwarden adequadamente. Tem certeza que deseja usar esta senha principal?" }, "pin": { "message": "PIN", "description": "PIN code. Ex. The short code (often numeric) that you use to unlock a device." }, "unlockWithPin": { - "message": "Desbloquear com o PIN" + "message": "Desbloquear com PIN" }, "setYourPinCode": { - "message": "Defina o seu código PIN para desbloquear o Bitwarden. Suas configurações de PIN serão redefinidas se alguma vez você encerrar completamente toda a sessão do aplicativo." + "message": "Configure o seu código PIN para desbloquear o Bitwarden. Suas configurações de PIN serão redefinidas se você desconectar-se totalmente do aplicativo." }, "pinRequired": { "message": "O código PIN é necessário." @@ -1841,7 +1841,7 @@ "message": "Desbloquear com o Windows Hello" }, "unlockWithPolkit": { - "message": "Desbloquear com autenticação de sistema" + "message": "Desbloquear com a autenticação do sistema" }, "windowsHelloConsentMessage": { "message": "Verifique para o Bitwarden." @@ -1850,16 +1850,16 @@ "message": "Desbloquear com o Touch ID" }, "additionalTouchIdSettings": { - "message": "Configurações adicionais de Touch ID" + "message": "Configurações adicionais do Touch ID" }, "touchIdConsentMessage": { "message": "desbloquear o seu cofre" }, "autoPromptTouchId": { - "message": "Pedir pelo Touch ID ao iniciar" + "message": "Pedir o Touch ID ao abrir" }, "lockWithMasterPassOnRestart1": { - "message": "Bloquear com senha principal ao reiniciar" + "message": "Bloquear com a senha principal ao reiniciar" }, "requireMasterPasswordOrPinOnAppRestart": { "message": "Exigir senha principal ou PIN ao reiniciar o app" @@ -1886,7 +1886,7 @@ "message": "Conta apagada" }, "accountDeletedDesc": { - "message": "A sua conta foi fechada e todos os dados associados foram excluídos." + "message": "A sua conta foi fechada e todos os dados associados foram apagados." }, "preferences": { "message": "Preferências" @@ -1898,7 +1898,7 @@ "message": "Sempre mostrar um ícone na barra de menu." }, "hideToMenuBar": { - "message": "Ocultar para a barra de menu" + "message": "Esconder na barra de menu" }, "selectOneCollection": { "message": "Você deve selecionar pelo menos uma coleção." @@ -1910,10 +1910,10 @@ "message": "Restaurar" }, "premiumManageAlertAppStore": { - "message": "Você pode gerenciar sua assinatura da App Store. Você quer visitar a App Store agora?" + "message": "Você pode gerenciar sua assinatura pela App Store. Você quer visitar a App Store agora?" }, "legal": { - "message": "Aspectos Legais", + "message": "Jurídico", "description": "Noun. As in 'legal documents', like our terms of service and privacy policy." }, "termsOfService": { @@ -1923,7 +1923,7 @@ "message": "Política de Privacidade" }, "unsavedChangesConfirmation": { - "message": "Você tem certeza que deseja sair? Se sair agora, as suas informações atuais não serão salvas." + "message": "Tem certeza que quer sair? Se sair agora, as suas informações atuais não serão salvas." }, "unsavedChangesTitle": { "message": "Alterações não salvas" @@ -1932,7 +1932,7 @@ "message": "Clonar" }, "passwordGeneratorPolicyInEffect": { - "message": "Uma ou mais políticas da organização estão afetando as suas configurações do gerador." + "message": "Uma ou mais políticas da organização estão afetando as configurações do seu gerador." }, "vaultTimeoutAction": { "message": "Ação do tempo limite do cofre" @@ -1961,7 +1961,7 @@ "message": "Apagar item para sempre" }, "permanentlyDeleteItemConfirmation": { - "message": "Você tem certeza que deseja excluir permanentemente esse item?" + "message": "Tem certeza que quer apagar este item para sempre?" }, "permanentlyDeletedItem": { "message": "Item apagado para sempre" @@ -1985,11 +1985,11 @@ "message": "Configurar senha principal" }, "orgPermissionsUpdatedMustSetPassword": { - "message": "As permissões da sua organização foram atualizadas, exigindo que você defina uma senha principal.", + "message": "As permissões da sua organização foram atualizadas, exigindo que você configure uma senha principal.", "description": "Used as a card title description on the set password page to explain why the user is there" }, "orgRequiresYouToSetPassword": { - "message": "Sua organização requer que você defina uma senha principal.", + "message": "Sua organização requer que você configure uma senha principal.", "description": "Used as a card title description on the set password page to explain why the user is there" }, "cardMetrics": { @@ -2023,10 +2023,10 @@ "message": "Torne a verificação em 2 etapas mais simples" }, "totpHelper": { - "message": "Bitwarden pode armazenar e preencher códigos de verificação de 2 etapas. Copie e cole a chave nesse campo." + "message": "O Bitwarden pode armazenar e preencher códigos de verificação de 2 etapas. Copie e cole a chave nesse campo." }, "totpHelperWithCapture": { - "message": "Bitwarden pode armazenar e preencher códigos de verificação de 2 etapas. Selecione o ícone da câmera e tire uma captura de tela do código QR de autenticação desse site ou copie e cole a chave nesse campo." + "message": "O Bitwarden pode armazenar e preencher códigos de verificação de 2 etapas. Selecione o ícone da câmera e tire uma captura de tela do código QR do autenticador nesse site ou copie e cole a chave nesse campo." }, "premium": { "message": "Premium", @@ -2048,10 +2048,10 @@ } }, "cardExpiredTitle": { - "message": "Cartão expirado" + "message": "Cartão vencido" }, "cardExpiredMessage": { - "message": "Se você fez uma renovação recente, atualize as informações do cartão" + "message": "Se você o renovou, atualize as informações do cartão" }, "verificationRequired": { "message": "Verificação necessária", @@ -2133,7 +2133,7 @@ "message": "Permitir integração com o navegador" }, "enableBrowserIntegrationDesc1": { - "message": "Usado para permitir desbloqueio biométrico em navegadores que não são o Safari." + "message": "Usado para permitir o desbloqueio biométrico em navegadores que não são o Safari." }, "enableDuckDuckGoBrowserIntegration": { "message": "Permitir integração com o navegador DuckDuckGo" @@ -2145,16 +2145,13 @@ "message": "Integração com o navegador não suportada" }, "browserIntegrationErrorTitle": { - "message": "Erro ao ativar a integração do navegador" + "message": "Erro ao ativar a integração com o navegador" }, "browserIntegrationErrorDesc": { - "message": "Ocorreu um erro ao ativar a integração do navegador." - }, - "browserIntegrationMasOnlyDesc": { - "message": "Infelizmente, por ora, a integração do navegador só é suportada na versão da Mac App Store." + "message": "Ocorreu um erro ao ativar a integração com o navegador." }, "browserIntegrationWindowsStoreDesc": { - "message": "Infelizmente, a integração do navegador não é suportada na versão da Microsoft Store." + "message": "Infelizmente, a integração com o navegador não é suportada na versão da Microsoft Store no momento." }, "browserIntegrationLinuxDesc": { "message": "Infelizmente, a integração do navegador não é suportada na versão linux no momento." @@ -2163,22 +2160,22 @@ "message": "Exigir verificação para integração com o navegador" }, "enableBrowserIntegrationFingerprintDesc": { - "message": "Adicione uma camada adicional de segurança, exigindo validação de frase biométrica ao estabelecer uma ligação entre o computador e o navegador. Quando ativado, isto requer intervenção do usuário e verificação cada vez que uma conexão é estabelecida." + "message": "Adicione uma camada adicional de segurança, exigindo a validação da frase biométrica ao estabelecer uma ligação entre o computador e o navegador. Requer intervenção do usuário e verificação cada vez que uma conexão é estabelecida." }, "enableHardwareAcceleration": { - "message": "Utilizar aceleração de hardware" + "message": "Usar aceleração de hardware" }, "enableHardwareAccelerationDesc": { - "message": "Por padrão esta configuração está ativada. Desligue apenas se tiver problemas gráficos. Reiniciar é necessário." + "message": "Por padrão esta configuração está ativada. Desative apenas se tiver problemas gráficos. Reiniciar é necessário." }, "approve": { "message": "Aprovar" }, "verifyBrowserTitle": { - "message": "Verificar conexão do navegador" + "message": "Verifique a conexão com o navegador" }, "verifyBrowserDesc": { - "message": "Por favor, certifique-se que a impressão digital mostrada é idêntica à impressão digital exibida na extensão do navegador." + "message": "Certifique-se que a frase biométrica mostrada é idêntica à exibida na extensão do navegador." }, "verifyNativeMessagingConnectionTitle": { "message": "$APPID$ quer se conectar ao Bitwarden", @@ -2190,7 +2187,7 @@ } }, "verifyNativeMessagingConnectionDesc": { - "message": "Gostaria de aprovar este pedido?" + "message": "Gostaria de aprovar esta solicitação?" }, "verifyNativeMessagingConnectionWarning": { "message": "Se não iniciou esta solicitação, não a aprove." @@ -2214,19 +2211,19 @@ "message": "Sua senha nova não pode ser a mesma que a sua atual." }, "hintEqualsPassword": { - "message": "Sua dica de senha não pode ser o mesmo que sua senha." + "message": "A dica da sua senha não pode ser a mesma que a sua senha." }, "personalOwnershipPolicyInEffect": { "message": "Uma política de organização está afetando suas opções de propriedade." }, "personalOwnershipPolicyInEffectImports": { - "message": "Uma política da organização bloqueou a importação de itens em seu cofre pessoal." + "message": "Uma política da organização bloqueou a importação de itens em seu cofre individual." }, "personalDetails": { "message": "Detalhes pessoais" }, "identification": { - "message": "Identificação" + "message": "Identidade" }, "contactInfo": { "message": "Informações de contato" @@ -2259,14 +2256,14 @@ "message": "Data de apagamento" }, "deletionDateDesc": { - "message": "O Send será eliminado permanentemente na data e hora especificadas.", + "message": "O Send será apagado para sempre no horário especificado.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "expirationDate": { "message": "Data de validade" }, "expirationDateDesc": { - "message": "Se definido, o acesso a este Send expirará na data e hora especificadas.", + "message": "Se configurado, o acesso a este Send acabará no horário especificado.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "maxAccessCount": { @@ -2274,7 +2271,7 @@ "description": "This text will be displayed after a Send has been accessed the maximum amount of times." }, "maxAccessCountDesc": { - "message": "Se atribuído, usuários não poderão mais acessar este Send assim que o número máximo de acessos for atingido.", + "message": "Se configurado, usuários não poderão mais acessar este Send assim que o número máximo de acessos for atingido.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "currentAccessCount": { @@ -2285,11 +2282,11 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendPasswordDesc": { - "message": "Opcionalmente exigir uma senha para os usuários acessarem este Send.", + "message": "Opcionalmente exija uma senha para os usuários acessarem este Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendNotesDesc": { - "message": "Notas privadas sobre este Send.", + "message": "Anotações privadas sobre este Send.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendLink": { @@ -2349,7 +2346,7 @@ "message": "Personalizado" }, "deleteSendConfirmation": { - "message": "Você tem certeza que deseja excluir este Send?", + "message": "Tem certeza que quer apagar este Send?", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copySendLinkToClipboard": { @@ -2364,7 +2361,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { - "message": "Devido a uma política corporativa, você só pode excluir um Send existente.", + "message": "Devido a uma política corporativa, você só pode apagar um Send existente.", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "copyLink": { @@ -2386,10 +2383,10 @@ "message": "Número máximo de acessos atingido" }, "expired": { - "message": "Expirado" + "message": "Vencido" }, "pendingDeletion": { - "message": "Exclusão pendente" + "message": "Apagamento pendente" }, "webAuthnAuthenticate": { "message": "Autenticar WebAuthn" @@ -2416,16 +2413,16 @@ "message": "Você precisa verificar o seu e-mail para usar este recurso." }, "passwordPrompt": { - "message": "Solicitação nova de senha principal" + "message": "Resolicitar senha principal" }, "passwordConfirmation": { "message": "Confirmação de senha principal" }, "passwordConfirmationDesc": { - "message": "Esta ação está protegida. Para continuar, por favor, reinsira a sua senha principal para verificar sua identidade." + "message": "Esta ação está protegida. Redigite a sua senha principal para verificar sua identidade." }, "masterPasswordSuccessfullySet": { - "message": "Senha principal definida com sucesso" + "message": "Senha principal configurada com sucesso" }, "updatedMasterPassword": { "message": "Senha principal atualizada" @@ -2434,31 +2431,31 @@ "message": "Atualizar senha principal" }, "updateMasterPasswordWarning": { - "message": "Sua senha principal foi alterada recentemente por um administrador de sua organização. Para acessar o cofre, você precisa atualizá-la agora. O processo desconectará você da sessão atual, exigindo que você entre novamente. Sessões ativas em outros dispositivos podem continuar ativas por até uma hora." + "message": "Sua senha principal foi alterada recentemente por um administrador de sua organização. Para acessar o cofre, você precisa atualizá-la agora. O processo desconectará você da sessão atual, exigindo que você conecte-se novamente. Sessões ativas em outros dispositivos podem continuar ativas por até uma hora." }, "updateWeakMasterPasswordWarning": { - "message": "A sua senha principal não atende a uma ou mais das políticas da sua organização. Para acessar o cofre, você deve atualizar a sua senha principal agora. O processo desconectará você da sessão atual, exigindo que você inicie a sessão novamente. Sessões ativas em outros dispositivos podem continuar ativas por até uma hora." + "message": "A sua senha principal não atende a uma ou mais das políticas da sua organização. Para acessar o cofre, você deve atualizar a sua senha principal agora. O processo desconectará você da sessão atual, exigindo que você se conecte novamente. Sessões ativas em outros dispositivos podem continuar ativas por até uma hora." }, "changePasswordWarning": { - "message": "Após mudar a sua senha, será necessário entrar novamente com a sua nova senha. Sessões ativas em outros dispositivos serão encerradas em até uma hora." + "message": "Após mudar a sua senha, será necessário conectar-se novamente com a sua nova senha. Sessões ativas em outros dispositivos serão desconectadas em até uma hora." }, "accountRecoveryUpdateMasterPasswordSubtitle": { - "message": "Mude a sua senha principal para completar a recuperação de conta." + "message": "Altere a sua senha principal para concluir a recuperação da conta." }, "updateMasterPasswordSubtitle": { "message": "Sua senha principal não corresponde aos requisitos da organização. Mude a sua senha principal para continuar." }, "tdeDisabledMasterPasswordRequired": { - "message": "Sua organização desativou a criptografia confiável do dispositivo. Defina uma senha principal para acessar o seu cofre." + "message": "Sua organização desativou a criptografia de dispositivo confiado. Configure uma senha principal para acessar o seu cofre." }, "tryAgain": { - "message": "Repetir" + "message": "Tentar novamente" }, "verificationRequiredForActionSetPinToContinue": { - "message": "Verificação necessária para esta ação. Defina um PIN para continuar." + "message": "Verificação necessária para esta ação. Configure um PIN para continuar." }, "setPin": { - "message": "Definir PIN" + "message": "Configurar PIN" }, "verifyWithBiometrics": { "message": "Verificar com biometria" @@ -2467,13 +2464,13 @@ "message": "Aguardando confirmação" }, "couldNotCompleteBiometrics": { - "message": "Não foi possível completar a biometria." + "message": "Não foi possível concluir a biometria." }, "needADifferentMethod": { "message": "Precisa de um método diferente?" }, "useMasterPassword": { - "message": "Usar a senha principal" + "message": "Usar senha principal" }, "usePin": { "message": "Usar PIN" @@ -2482,7 +2479,7 @@ "message": "Usar biometria" }, "enterVerificationCodeSentToEmail": { - "message": "Digite o código de verificação que foi enviado para o seu e-mail." + "message": "Digite o código de verificação enviado para o seu e-mail." }, "resendCode": { "message": "Reenviar código" @@ -2507,7 +2504,7 @@ } }, "vaultTimeoutPolicyWithActionInEffect": { - "message": "As políticas da sua organização estão afetando o tempo limite do seu cofre. O tempo limite máximo permitido para cofre é $HOURS$ hora(s) e $MINUTES$ minuto(s). A ação de tempo limite do seu cofre é definida como $ACTION$.", + "message": "As políticas da sua organização estão afetando o tempo limite do seu cofre. O máximo permitido do tempo limite do cofre é $HOURS$ hora(s) e $MINUTES$ minuto(s). A ação de tempo limite do seu cofre está configurada para $ACTION$.", "placeholders": { "hours": { "content": "$1", @@ -2524,7 +2521,7 @@ } }, "vaultTimeoutActionPolicyInEffect": { - "message": "As políticas da sua organização definiram a ação do tempo limite do seu cofre para $ACTION$.", + "message": "As políticas da sua organização configuraram a ação do tempo limite do seu cofre para $ACTION$.", "placeholders": { "action": { "content": "$1", @@ -2536,10 +2533,10 @@ "message": "O tempo limite do seu cofre excede as restrições definidas por sua organização." }, "vaultTimeoutPolicyAffectingOptions": { - "message": "Requisitos de políticas corporativas foram adicionadas as suas opções de tempo limite" + "message": "Os requisitos das políticas corporativas foram aplicados às suas opções de tempo limite" }, "vaultTimeoutPolicyInEffect": { - "message": "As políticas da sua organização definiram o seu tempo limite máximo permitido no cofre para $HOURS$ hora(s) e $MINUTES$ minuto(s).", + "message": "As políticas da sua organização configuraram o seu máximo permitido do tempo limite do cofre para $HOURS$ hora(s) e $MINUTES$ minuto(s).", "placeholders": { "hours": { "content": "$1", @@ -2565,7 +2562,7 @@ } }, "vaultCustomTimeoutMinimum": { - "message": "O tempo limite personalizado mínimo é de 1 minuto." + "message": "O mínimo do tempo limite personalizado é de 1 minuto." }, "inviteAccepted": { "message": "Convite aceito" @@ -2580,7 +2577,7 @@ "message": "Exportação de cofre removida" }, "personalVaultExportPolicyInEffect": { - "message": "Uma ou mais políticas da organização impdem que você exporte seu cofre pessoal." + "message": "Uma ou mais políticas da organização impedem que você exporte seu cofre pessoal." }, "addAccount": { "message": "Adicionar conta" @@ -2592,13 +2589,13 @@ "message": "Senha principal removida" }, "removeMasterPasswordForOrganizationUserKeyConnector": { - "message": "Uma senha principal não é mais necessária para membros da seguinte organização. Por favor, confirme o domínio abaixo com o administrador da sua organização." + "message": "Uma senha principal não é mais necessária para membros da seguinte organização. Confirme o domínio abaixo com o administrador da sua organização." }, "organizationName": { "message": "Nome da organização" }, "keyConnectorDomain": { - "message": "Domínio do Conector de Chave" + "message": "Domínio do Key Connector" }, "leaveOrganization": { "message": "Sair da organização" @@ -2610,25 +2607,25 @@ "message": "Você saiu da organização." }, "ssoKeyConnectorError": { - "message": "Erro do conector de chave: certifique-se de que o conector de chave está disponível e funcionando corretamente." + "message": "Erro do Key Connector: certifique-se de que o Key Connector está disponível e funcionando corretamente." }, "lockAllVaults": { "message": "Bloquear todos os cofres" }, "accountLimitReached": { - "message": "Não mais do que 5 contas podem estar logadas ao mesmo tempo." + "message": "Não mais do que 5 contas podem estar conectadas ao mesmo tempo." }, "accountPreferences": { "message": "Preferências" }, "appPreferences": { - "message": "Configurações do Aplicativo (Todas as Contas)" + "message": "Configurações do aplicativo (todas as contas)" }, "accountSwitcherLimitReached": { - "message": "Limite de Contas atingido. Saia de uma conta para adicionar outra." + "message": "Limite de contas atingido. Desconecte uma conta para adicionar outra." }, "settingsTitle": { - "message": "Configurações do Aplicativo para $EMAIL$", + "message": "Configurações do aplicativo para $EMAIL$", "placeholders": { "email": { "content": "$1", @@ -2646,13 +2643,13 @@ "message": "Opções" }, "sessionTimeout": { - "message": "Sua sessão expirou. Volte e tente entrar novamente." + "message": "Sua sessão expirou. Volte e tente se conectar novamente." }, "exportingPersonalVaultTitle": { "message": "Exportando cofre individual" }, "exportingIndividualVaultDescription": { - "message": "Apenas os itens do cofre individual associados com $EMAIL$ serão exportados. Os itens do cofre da organização não serão incluídos. Apenas as informações dos itens do cofre serão exportadas e não incluirão anexos associados.", + "message": "Apenas os itens do cofre individual associados a $EMAIL$ serão exportados. Os itens do cofre de organizações não serão incluídos. Apenas as informações dos itens do cofre serão exportadas e não incluirão anexos associados.", "placeholders": { "email": { "content": "$1", @@ -2661,7 +2658,7 @@ } }, "exportingIndividualVaultWithAttachmentsDescription": { - "message": "Apenas os itens do cofre individual, incluindo anexos associados com $EMAIL$ serão exportados. Os itens do cofre da organização não serão incluídos", + "message": "Apenas os itens do cofre individual, incluindo anexos associados com $EMAIL$ serão exportados. Os itens do cofre de organizações não serão incluídos", "placeholders": { "email": { "content": "$1", @@ -2673,7 +2670,7 @@ "message": "Exportando cofre da organização" }, "exportingOrganizationVaultDesc": { - "message": "Apenas o cofre da organização associado com $ORGANIZATION$ será exportado. Itens do cofre individual e itens de outras organizações não serão incluídos.", + "message": "Apenas o cofre da organização associado a $ORGANIZATION$ será exportado. Itens de cofres individuais e de outras organizações não serão incluídos.", "placeholders": { "organization": { "content": "$1", @@ -2691,7 +2688,7 @@ } }, "exportingOrganizationVaultFromAdminConsoleWithDataOwnershipDesc": { - "message": "Apenas o cofre da organização associado com $ORGANIZATION$ será exportado. Os itens da minhas coleções não serão incluídos.", + "message": "Apenas o cofre da organização associado com $ORGANIZATION$ será exportado. As coleções de itens não serão incluídos.", "placeholders": { "organization": { "content": "$1", @@ -2713,13 +2710,13 @@ "description": "Short for 'credential generator'." }, "whatWouldYouLikeToGenerate": { - "message": "O que você gostaria de gerar?" + "message": "O que gostaria de gerar?" }, "passwordType": { - "message": "Tipo de senha" + "message": "Tipo da senha" }, "regenerateUsername": { - "message": "Regenerar nome de usuário" + "message": "Regerar nome de usuário" }, "generateUsername": { "message": "Gerar nome de usuário" @@ -2740,7 +2737,7 @@ "message": "Senha gerada" }, "passphraseGenerated": { - "message": "Gerador de frase secreta" + "message": "Frase secreta gerada" }, "usernameGenerated": { "message": "Nome de usuário gerado" @@ -2796,19 +2793,19 @@ "message": "E-mail pega-tudo" }, "catchallEmailDesc": { - "message": "Use o catch-all configurado no seu domínio." + "message": "Use a caixa de entrada pega-tudo configurada no seu domínio." }, "useThisEmail": { "message": "Usar este e-mail" }, "useThisPassword": { - "message": "Use esta senha" + "message": "Usar esta senha" }, "useThisPassphrase": { - "message": "Use esta frase secreta" + "message": "Usar esta frase secreta" }, "useThisUsername": { - "message": "Use este nome de usuário" + "message": "Usar este nome de usuário" }, "random": { "message": "Aleatório" @@ -2826,13 +2823,13 @@ "message": "Todos os cofres" }, "searchOrganization": { - "message": "Pesquisar organização" + "message": "Buscar na organização" }, "searchMyVault": { - "message": "Pesquisar meu cofre" + "message": "Buscar no meu cofre" }, "forwardedEmail": { - "message": "Alias de Encaminhamento de E-mail" + "message": "Alias de encaminhamento de e-mail" }, "forwardedEmailDesc": { "message": "Gere um alias de e-mail com um serviço externo de encaminhamento." @@ -2846,7 +2843,7 @@ "description": "Guidance provided for email forwarding services that support multiple email domains." }, "forwarderError": { - "message": "Erro $SERVICENAME$: $ERRORMESSAGE$", + "message": "Erro do $SERVICENAME$: $ERRORMESSAGE$", "description": "Reports an error returned by a forwarding service to the user.", "placeholders": { "servicename": { @@ -2874,7 +2871,7 @@ } }, "forwaderInvalidToken": { - "message": "Token de API $SERVICENAME$ inválido", + "message": "Token de API do $SERVICENAME$ inválido", "description": "Displayed when the user's API token is empty or rejected by the forwarding service.", "placeholders": { "servicename": { @@ -2884,7 +2881,7 @@ } }, "forwaderInvalidTokenWithMessage": { - "message": "Token de API $SERVICENAME$ inválido: $ERRORMESSAGE$", + "message": "Token de API da $SERVICENAME$ inválido: $ERRORMESSAGE$", "description": "Displayed when the user's API token is rejected by the forwarding service with an error message.", "placeholders": { "servicename": { @@ -2922,7 +2919,7 @@ } }, "forwarderNoAccountId": { - "message": "Não foi possível obter o ID da conta de e-mail mascarado $SERVICENAME$.", + "message": "Não é possível obter o ID da conta de e-mail mascarado do $SERVICENAME$.", "description": "Displayed when the forwarding service fails to return an account ID.", "placeholders": { "servicename": { @@ -2932,7 +2929,7 @@ } }, "forwarderNoDomain": { - "message": "Domínio $SERVICENAME$ inválido.", + "message": "Domínio inválido do $SERVICENAME$.", "description": "Displayed when the domain is empty or domain authorization failed at the forwarding service.", "placeholders": { "servicename": { @@ -2942,7 +2939,7 @@ } }, "forwarderNoUrl": { - "message": "URL $SERVICENAME$ inválido.", + "message": "URL inválido do $SERVICENAME$.", "description": "Displayed when the url of the forwarding service wasn't supplied.", "placeholders": { "servicename": { @@ -2952,7 +2949,7 @@ } }, "forwarderUnknownError": { - "message": "Ocorreu um erro $SERVICENAME$ desconhecido.", + "message": "Ocorreu um erro desconhecido do $SERVICENAME$.", "description": "Displayed when the forwarding service failed due to an unknown error.", "placeholders": { "servicename": { @@ -2972,7 +2969,7 @@ } }, "hostname": { - "message": "Servidor", + "message": "Nome do servidor", "description": "Part of a URL." }, "apiAccessToken": { @@ -2997,16 +2994,16 @@ "message": "Cofre" }, "loginWithMasterPassword": { - "message": "Entrar com senha principal" + "message": "Conectar-se com senha principal" }, "rememberEmail": { - "message": "Lembrar e-mail" + "message": "Lembrar do e-mail" }, "newAroundHere": { "message": "Novo por aqui?" }, "loggingInTo": { - "message": "Entrando em $DOMAIN$", + "message": "Conectando-se a $DOMAIN$", "placeholders": { "domain": { "content": "$1", @@ -3015,7 +3012,7 @@ } }, "logInWithAnotherDevice": { - "message": "Entrar com outro dispositivo" + "message": "Conectar-se com outro dispositivo" }, "loginInitiated": { "message": "Autenticação iniciada" @@ -3033,7 +3030,7 @@ "message": "Desbloqueie o Bitwarden no seu dispositivo ou no " }, "notificationSentDeviceAnchor": { - "message": "app web" + "message": "aplicativo web" }, "notificationSentDevicePart2": { "message": "Certifique-se de que a frase biométrica corresponde à frase abaixo antes de aprovar." @@ -3051,7 +3048,7 @@ "message": "Você será notificado assim que a solicitação for aprovada" }, "needAnotherOption": { - "message": "A entrada com dispositivo deve ser ativada nas configurações do aplicativo móvel do Bitwarden. Precisa de outra opção?" + "message": "A autenticação com dispositivo deve ser ativada nas configurações do aplicativo móvel do Bitwarden. Precisa de outra opção?" }, "viewAllLogInOptions": { "message": "Ver todas as opções de autenticação" @@ -3089,10 +3086,10 @@ } }, "youDeniedLoginAttemptFromAnotherDevice": { - "message": "Você negou uma tentativa de autenticação por outro dispositivo. Se foi você, tente entrar com o dispositivo novamente." + "message": "Você negou uma tentativa de autenticação por outro dispositivo. Se foi você, tente conectar-se com o dispositivo novamente." }, "webApp": { - "message": "App web" + "message": "Aplicativo web" }, "mobile": { "message": "Móvel", @@ -3120,7 +3117,7 @@ "message": "Solicitação de autenticação" }, "deviceType": { - "message": "Tipo de dispositivo" + "message": "Tipo do dispositivo" }, "ipAddress": { "message": "Endereço de IP" @@ -3183,7 +3180,7 @@ "message": "Nenhum e-mail?" }, "goBack": { - "message": "Voltar" + "message": "Volte" }, "toEditYourEmailAddress": { "message": "para editar o seu endereço de e-mail." @@ -3213,10 +3210,10 @@ "message": "Acessando" }, "accessTokenUnableToBeDecrypted": { - "message": "Você foi desconectado porque seu token de acesso não pôde ser descriptografado. Por favor, entre novamente para resolver esse problema." + "message": "Você foi desconectado porque seu token de acesso não pôde ser descriptografado. Conecte-se novamente para resolver esse problema." }, "refreshTokenSecureStorageRetrievalFailure": { - "message": "Você foi desconectado porque seu token de recarregamento não pôde ser recuperado. Por favor, entre novamente para resolver esse problema." + "message": "Você foi desconectado porque seu token de recarregamento não pôde ser recuperado. Conecte-se novamente para resolver esse problema." }, "masterPasswordHint": { "message": "A sua senha principal não pode ser recuperada se você esquecê-la!" @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Faça upgrade para o Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Ação do tempo limite" + }, + "sessionTimeoutHeader": { + "message": "Tempo limite da sessão" } } diff --git a/apps/desktop/src/locales/pt_PT/messages.json b/apps/desktop/src/locales/pt_PT/messages.json index d19c0075873..de0427ddab0 100644 --- a/apps/desktop/src/locales/pt_PT/messages.json +++ b/apps/desktop/src/locales/pt_PT/messages.json @@ -70,7 +70,7 @@ } }, "noEditPermissions": { - "message": "You don't have permission to edit this item" + "message": "Não tem permissão para editar este item" }, "welcomeBack": { "message": "Bem-vindo de volta" @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Ocorreu um erro ao ativar a integração do navegador." }, - "browserIntegrationMasOnlyDesc": { - "message": "Infelizmente, a integração do navegador só é suportada na versão da Mac App Store por enquanto." - }, "browserIntegrationWindowsStoreDesc": { "message": "Infelizmente, a integração do navegador não é atualmente suportada na versão da Microsoft Store." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Atualizar para o Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Ação de tempo limite" + }, + "sessionTimeoutHeader": { + "message": "Tempo limite da sessão" } } diff --git a/apps/desktop/src/locales/ro/messages.json b/apps/desktop/src/locales/ro/messages.json index fbb1d018b60..a72ce3547e9 100644 --- a/apps/desktop/src/locales/ro/messages.json +++ b/apps/desktop/src/locales/ro/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Din păcate, integrarea browserului este acceptată numai în versiunea Mac App Store pentru moment." - }, "browserIntegrationWindowsStoreDesc": { "message": "Din păcate, integrarea browserului nu este susținută în prezent în versiunea Microsoft Store." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/ru/messages.json b/apps/desktop/src/locales/ru/messages.json index 0170bd57310..914bb603630 100644 --- a/apps/desktop/src/locales/ru/messages.json +++ b/apps/desktop/src/locales/ru/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Произошла ошибка при включении интеграции с браузером." }, - "browserIntegrationMasOnlyDesc": { - "message": "К сожалению, интеграция браузера пока поддерживается только в версии Mac App Store." - }, "browserIntegrationWindowsStoreDesc": { "message": "К сожалению, интеграция браузера в версии для Microsoft Store в настоящее время не поддерживается." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Обновить до Премиум" + }, + "sessionTimeoutSettingsAction": { + "message": "Тайм-аут действия" + }, + "sessionTimeoutHeader": { + "message": "Тайм-аут сеанса" } } diff --git a/apps/desktop/src/locales/si/messages.json b/apps/desktop/src/locales/si/messages.json index 4193867ead5..a83b2cbf536 100644 --- a/apps/desktop/src/locales/si/messages.json +++ b/apps/desktop/src/locales/si/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index 8fecc5de1b9..e5763d78b9c 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -1856,7 +1856,7 @@ "message": "odomknúť svoj trezor" }, "autoPromptTouchId": { - "message": "Pri spustení požiadať o Touch ID" + "message": "Pri spustení aplikácie požiadať o Touch ID" }, "lockWithMasterPassOnRestart1": { "message": "Pri reštarte zamknúť hlavným heslom" @@ -1904,7 +1904,7 @@ "message": "Musíte vybrať aspoň jednu zbierku." }, "premiumUpdated": { - "message": "Povýšili ste na prémium." + "message": "Upgradovali ste na Prémium." }, "restore": { "message": "Obnoviť" @@ -1964,10 +1964,10 @@ "message": "Naozaj chcete natrvalo odstrániť túto položku?" }, "permanentlyDeletedItem": { - "message": "Položka natrvalo odstránená" + "message": "Položka bola natrvalo odstránená" }, "restoredItem": { - "message": "Obnovená položka" + "message": "Položka bola obnovená" }, "permanentlyDelete": { "message": "Natrvalo odstrániť" @@ -2139,7 +2139,7 @@ "message": "Povoliť integráciu prehliadača DuckDuckGo" }, "enableDuckDuckGoBrowserIntegrationDesc": { - "message": "Používajte svoj trezor Bitwarden pri prehliadaní pomocou DuckDuckGo." + "message": "Používajte svoj trezor v Bitwardene pri prehliadaní pomocou DuckDuckGo." }, "browserIntegrationUnsupportedTitle": { "message": "Integrácia v prehliadači nie je podporovaná" @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Pri povoľovaní integrácie v prehliadači sa vyskytla chyba." }, - "browserIntegrationMasOnlyDesc": { - "message": "Bohužiaľ, integrácia v prehliadači je zatiaľ podporovaná iba vo verzii Mac App Store." - }, "browserIntegrationWindowsStoreDesc": { "message": "Bohužiaľ, integrácia v prehliadači zatiaľ nie je podporovaná, ak je aplikácia nainštalovaná cez Microsoft Store." }, @@ -2181,7 +2178,7 @@ "message": "Uistite sa, že zobrazený odtlačok prsta je identický s odtlačkom prsta zobrazeným v rozšírení prehliadača." }, "verifyNativeMessagingConnectionTitle": { - "message": "$APPID$ sa chce pripojiť k Bitwarden", + "message": "$APPID$ sa chce pripojiť k Bitwardenu", "placeholders": { "appid": { "content": "$1", @@ -2208,7 +2205,7 @@ "message": "Vzhľadom na spôsob inštalácie nebolo možné automaticky povoliť podporu biometrie. Chcete otvoriť dokumentáciu, ako to urobiť manuálne?" }, "personalOwnershipSubmitError": { - "message": "Z dôvodu podnikovej politiky máte obmedzené ukladanie položiek do osobného trezora. Zmeňte možnosť vlastníctvo na organizáciu a vyberte si z dostupných zbierok." + "message": "Z dôvodu podnikových pravidiel máte obmedzené ukladanie položiek do osobného trezora. Zmeňte možnosť vlastníctvo na organizáciu a vyberte si z dostupných zbierok." }, "yourNewPasswordCannotBeTheSameAsYourCurrentPassword": { "message": "Nové heslo nemôže byť rovnaké ako súčasné heslo." @@ -2305,15 +2302,15 @@ "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." }, "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." }, "deletedSend": { - "message": "Send odstránený", + "message": "Send bol odstránený", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "newPassword": { @@ -2324,7 +2321,7 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "createSend": { - "message": "Vytvoriť Send", + "message": "Nový Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendTextDesc": { @@ -2360,7 +2357,7 @@ "message": "Kopírovať odkaz na zdieľanie tohto Sendu do schránky počas ukladania." }, "sendDisabled": { - "message": "Funkcia Send zakázaná", + "message": "Send bol odstránený", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { @@ -2985,10 +2982,10 @@ "message": "Vyžaduje sa predplatné Prémium" }, "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 pozastavenej organizácii nie je možné pristupovať. Požiadajte o pomoc vlastníka organizácie." }, "neverLockWarning": { "message": "Ste si istí, že chcete použiť možnosť \"Nikdy\"? Táto predvoľba ukladá šifrovací kľúč od trezora priamo na zariadení. Ak použijete túto možnosť, mali by ste svoje zariadenie náležite zabezpečiť." @@ -3189,16 +3186,16 @@ "message": "na úpravu e-mailovej adresy." }, "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ť uniknuté 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" @@ -3222,7 +3219,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", @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgradovať na Prémium" + }, + "sessionTimeoutSettingsAction": { + "message": "Akcia pri vypršaní časového limitu" + }, + "sessionTimeoutHeader": { + "message": "Časový limit relácie" } } diff --git a/apps/desktop/src/locales/sl/messages.json b/apps/desktop/src/locales/sl/messages.json index 329403e43fc..353c6858afa 100644 --- a/apps/desktop/src/locales/sl/messages.json +++ b/apps/desktop/src/locales/sl/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/sr/messages.json b/apps/desktop/src/locales/sr/messages.json index b7df1855552..1bc4a0ed016 100644 --- a/apps/desktop/src/locales/sr/messages.json +++ b/apps/desktop/src/locales/sr/messages.json @@ -70,7 +70,7 @@ } }, "noEditPermissions": { - "message": "You don't have permission to edit this item" + "message": "Немате дозволу да уређујете ову ставку" }, "welcomeBack": { "message": "Добродошли назад" @@ -775,7 +775,7 @@ "message": "Употребити једнократну пријаву" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "Ваша организација захтева јединствену пријаву." }, "submit": { "message": "Пошаљи" @@ -1039,7 +1039,7 @@ "message": "Морате додати или основни УРЛ сервера или бар једно прилагођено окружење." }, "selfHostedEnvMustUseHttps": { - "message": "URLs must use HTTPS." + "message": "Везе морају да користе HTTPS." }, "customEnvironment": { "message": "Прилагођено окружење" @@ -1862,10 +1862,10 @@ "message": "Закључајте са главном лозинком при поновном покретању" }, "requireMasterPasswordOrPinOnAppRestart": { - "message": "Require master password or PIN on app restart" + "message": "Потражити главну лозинку или ПИН при поновном покретању" }, "requireMasterPasswordOnAppRestart": { - "message": "Require master password on app restart" + "message": "Потражити главну лозинку при поновном покретању апликације" }, "deleteAccount": { "message": "Брисање налога" @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Дошло је до грешке при омогућавању интеграције прегледача." }, - "browserIntegrationMasOnlyDesc": { - "message": "Нажалост, интеграција прегледача за сада је подржана само у верзији Mac App Store." - }, "browserIntegrationWindowsStoreDesc": { "message": "Нажалост, интеграција прегледача није за сада подржана у Windows Store." }, @@ -4183,7 +4180,7 @@ "message": "Ставка је послата у архиву" }, "itemWasUnarchived": { - "message": "Item was unarchived" + "message": "Ставка враћена из архиве" }, "archiveItem": { "message": "Архивирај ставку" @@ -4192,33 +4189,39 @@ "message": "Архивиране ставке су искључене из општих резултата претраге и предлога за ауто попуњавање. Јесте ли сигурни да желите да архивирате ову ставку?" }, "zipPostalCodeLabel": { - "message": "ZIP / Postal code" + "message": "ZIP/Поштански број" }, "cardNumberLabel": { - "message": "Card number" + "message": "Број картице" }, "upgradeNow": { - "message": "Upgrade now" + "message": "Надогради сада" }, "builtInAuthenticator": { - "message": "Built-in authenticator" + "message": "Уграђени аутентификатор" }, "secureFileStorage": { - "message": "Secure file storage" + "message": "Сигурно складиштење датотека" }, "emergencyAccess": { - "message": "Emergency access" + "message": "Хитан приступ" }, "breachMonitoring": { - "message": "Breach monitoring" + "message": "Праћење повreda безбедности" }, "andMoreFeatures": { - "message": "And more!" + "message": "И још више!" }, "planDescPremium": { - "message": "Complete online security" + "message": "Потпуна онлајн безбедност" }, "upgradeToPremium": { - "message": "Upgrade to Premium" + "message": "Надоградите на Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/sv/messages.json b/apps/desktop/src/locales/sv/messages.json index e06f78efbf6..93d56419ae3 100644 --- a/apps/desktop/src/locales/sv/messages.json +++ b/apps/desktop/src/locales/sv/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Ett fel uppstod vid aktivering av webbläsarintegration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Tyvärr stöds webbläsarintegration för tillfället endast i versionen från Mac App Store." - }, "browserIntegrationWindowsStoreDesc": { "message": "Tyvärr stöds webbläsarintegration för tillfället inte i versionen från Windows Store." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Uppgradera till Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Tidsgränsåtgärd" + }, + "sessionTimeoutHeader": { + "message": "Sessionstidsgräns" } } diff --git a/apps/desktop/src/locales/ta/messages.json b/apps/desktop/src/locales/ta/messages.json index de574204738..2f9d12917d6 100644 --- a/apps/desktop/src/locales/ta/messages.json +++ b/apps/desktop/src/locales/ta/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "உலாவி ஒருங்கிணைப்பை இயக்கும்போது ஒரு பிழை ஏற்பட்டது." }, - "browserIntegrationMasOnlyDesc": { - "message": "துரதிர்ஷ்டவசமாக உலாவி ஒருங்கிணைப்பு தற்போது Mac App Store பதிப்பில் மட்டுமே ஆதரிக்கப்படுகிறது." - }, "browserIntegrationWindowsStoreDesc": { "message": "துரதிர்ஷ்டவசமாக உலாவி ஒருங்கிணைப்பு தற்போது Microsoft Store பதிப்பில் ஆதரிக்கப்படவில்லை." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/te/messages.json b/apps/desktop/src/locales/te/messages.json index 70d4c7cb494..d607bb8d097 100644 --- a/apps/desktop/src/locales/te/messages.json +++ b/apps/desktop/src/locales/te/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/th/messages.json b/apps/desktop/src/locales/th/messages.json index a9b197f946c..d794ace629c 100644 --- a/apps/desktop/src/locales/th/messages.json +++ b/apps/desktop/src/locales/th/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "An error has occurred while enabling browser integration." }, - "browserIntegrationMasOnlyDesc": { - "message": "Unfortunately browser integration is only supported in the Mac App Store version for now." - }, "browserIntegrationWindowsStoreDesc": { "message": "Unfortunately browser integration is currently not supported in the Microsoft Store version." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Upgrade to Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/tr/messages.json b/apps/desktop/src/locales/tr/messages.json index 94f64b31811..ac67b177cbf 100644 --- a/apps/desktop/src/locales/tr/messages.json +++ b/apps/desktop/src/locales/tr/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Tarayıcı entegrasyonu etkinleştirilirken bir hata oluştu." }, - "browserIntegrationMasOnlyDesc": { - "message": "Ne yazık ki tarayıcı entegrasyonu şu anda sadece Mac App Store sürümünde destekleniyor." - }, "browserIntegrationWindowsStoreDesc": { "message": "Maalesef tarayıcı entegrasyonu şimdilik Windows Store sürümünde desteklenmiyor." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Premium'a yükselt" + }, + "sessionTimeoutSettingsAction": { + "message": "Zaman aşımı eylemi" + }, + "sessionTimeoutHeader": { + "message": "Oturum zaman aşımı" } } diff --git a/apps/desktop/src/locales/uk/messages.json b/apps/desktop/src/locales/uk/messages.json index b6e0b18a981..7ed0710ca74 100644 --- a/apps/desktop/src/locales/uk/messages.json +++ b/apps/desktop/src/locales/uk/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Під час увімкнення інтеграції з браузером сталася помилка." }, - "browserIntegrationMasOnlyDesc": { - "message": "На жаль, зараз інтеграція з браузером підтримується лише у версії для Mac з App Store." - }, "browserIntegrationWindowsStoreDesc": { "message": "На жаль, зараз інтеграція з браузером не підтримується у версії з Microsoft Store." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Покращити до Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/vi/messages.json b/apps/desktop/src/locales/vi/messages.json index db945022070..8bf88aba458 100644 --- a/apps/desktop/src/locales/vi/messages.json +++ b/apps/desktop/src/locales/vi/messages.json @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "Đã xảy ra lỗi khi bật tích hợp với trình duyệt." }, - "browserIntegrationMasOnlyDesc": { - "message": "Rất tiếc, tính năng tích hợp trình duyệt hiện chỉ được hỗ trợ trong phiên bản Mac App Store." - }, "browserIntegrationWindowsStoreDesc": { "message": "Rất tiếc, tính năng tích hợp trình duyệt hiện không được hỗ trợ trong phiên bản Microsoft Store." }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "Nâng cấp lên gói Cao cấp" + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" } } diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index 93c907743d9..353fc036f63 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -1298,7 +1298,7 @@ "message": "系统空闲时" }, "onSleep": { - "message": "系统休眠时" + "message": "系统睡眠时" }, "onLocked": { "message": "系统锁定时" @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "启用浏览器集成时出错。" }, - "browserIntegrationMasOnlyDesc": { - "message": "很遗憾,目前仅 Mac App Store 版本支持浏览器集成。" - }, "browserIntegrationWindowsStoreDesc": { "message": "很遗憾,Microsoft Store 版本目前不支持浏览器集成。" }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "升级为高级版" + }, + "sessionTimeoutSettingsAction": { + "message": "超时动作" + }, + "sessionTimeoutHeader": { + "message": "会话超时" } } diff --git a/apps/desktop/src/locales/zh_TW/messages.json b/apps/desktop/src/locales/zh_TW/messages.json index 0dc4d0911fc..61fc00543ed 100644 --- a/apps/desktop/src/locales/zh_TW/messages.json +++ b/apps/desktop/src/locales/zh_TW/messages.json @@ -70,7 +70,7 @@ } }, "noEditPermissions": { - "message": "You don't have permission to edit this item" + "message": "你沒有權限編輯這個項目" }, "welcomeBack": { "message": "歡迎回來" @@ -2150,9 +2150,6 @@ "browserIntegrationErrorDesc": { "message": "啟用瀏覽器整合時發生錯誤。" }, - "browserIntegrationMasOnlyDesc": { - "message": "很遺憾,目前僅 Mac App Store 版本支援瀏覽器整合功能。" - }, "browserIntegrationWindowsStoreDesc": { "message": "很遺憾,Microsoft Store 版本目前尚不支援瀏覽器整合功能。" }, @@ -4220,5 +4217,11 @@ }, "upgradeToPremium": { "message": "升級到 Premium" + }, + "sessionTimeoutSettingsAction": { + "message": "逾時後動作" + }, + "sessionTimeoutHeader": { + "message": "工作階段逾時" } } From 9733ef0a3ef2cfb8ffadab23b6a4b31692cf119c Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Fri, 14 Nov 2025 12:33:26 +0000 Subject: [PATCH 12/75] Autosync the updated translations (#17378) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> Co-authored-by: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> --- apps/web/src/locales/af/messages.json | 108 ++++++++- apps/web/src/locales/ar/messages.json | 108 ++++++++- apps/web/src/locales/az/messages.json | 188 +++++++++++----- apps/web/src/locales/be/messages.json | 108 ++++++++- apps/web/src/locales/bg/messages.json | 112 +++++++++- apps/web/src/locales/bn/messages.json | 108 ++++++++- apps/web/src/locales/bs/messages.json | 108 ++++++++- apps/web/src/locales/ca/messages.json | 108 ++++++++- apps/web/src/locales/cs/messages.json | 110 +++++++++- apps/web/src/locales/cy/messages.json | 108 ++++++++- apps/web/src/locales/da/messages.json | 108 ++++++++- apps/web/src/locales/de/messages.json | 112 +++++++++- apps/web/src/locales/el/messages.json | 108 ++++++++- apps/web/src/locales/en_GB/messages.json | 108 ++++++++- apps/web/src/locales/en_IN/messages.json | 108 ++++++++- apps/web/src/locales/eo/messages.json | 108 ++++++++- apps/web/src/locales/es/messages.json | 108 ++++++++- apps/web/src/locales/et/messages.json | 108 ++++++++- apps/web/src/locales/eu/messages.json | 108 ++++++++- apps/web/src/locales/fa/messages.json | 108 ++++++++- apps/web/src/locales/fi/messages.json | 108 ++++++++- apps/web/src/locales/fil/messages.json | 108 ++++++++- apps/web/src/locales/fr/messages.json | 110 +++++++++- apps/web/src/locales/gl/messages.json | 108 ++++++++- apps/web/src/locales/he/messages.json | 110 +++++++++- apps/web/src/locales/hi/messages.json | 108 ++++++++- apps/web/src/locales/hr/messages.json | 114 ++++++++-- apps/web/src/locales/hu/messages.json | 110 +++++++++- apps/web/src/locales/id/messages.json | 110 +++++++++- apps/web/src/locales/it/messages.json | 108 ++++++++- apps/web/src/locales/ja/messages.json | 108 ++++++++- apps/web/src/locales/ka/messages.json | 108 ++++++++- apps/web/src/locales/km/messages.json | 108 ++++++++- apps/web/src/locales/kn/messages.json | 108 ++++++++- apps/web/src/locales/ko/messages.json | 108 ++++++++- apps/web/src/locales/lv/messages.json | 126 +++++++++-- apps/web/src/locales/ml/messages.json | 108 ++++++++- apps/web/src/locales/mr/messages.json | 108 ++++++++- apps/web/src/locales/my/messages.json | 108 ++++++++- apps/web/src/locales/nb/messages.json | 108 ++++++++- apps/web/src/locales/ne/messages.json | 108 ++++++++- apps/web/src/locales/nl/messages.json | 108 ++++++++- apps/web/src/locales/nn/messages.json | 108 ++++++++- apps/web/src/locales/or/messages.json | 108 ++++++++- apps/web/src/locales/pl/messages.json | 108 ++++++++- apps/web/src/locales/pt_BR/messages.json | 108 ++++++++- apps/web/src/locales/pt_PT/messages.json | 108 ++++++++- apps/web/src/locales/ro/messages.json | 108 ++++++++- apps/web/src/locales/ru/messages.json | 108 ++++++++- apps/web/src/locales/si/messages.json | 108 ++++++++- apps/web/src/locales/sk/messages.json | 142 +++++++++--- apps/web/src/locales/sl/messages.json | 108 ++++++++- apps/web/src/locales/sr_CS/messages.json | 108 ++++++++- apps/web/src/locales/sr_CY/messages.json | 266 +++++++++++++++-------- apps/web/src/locales/sv/messages.json | 108 ++++++++- apps/web/src/locales/ta/messages.json | 108 ++++++++- apps/web/src/locales/te/messages.json | 108 ++++++++- apps/web/src/locales/th/messages.json | 108 ++++++++- apps/web/src/locales/tr/messages.json | 110 +++++++++- apps/web/src/locales/uk/messages.json | 108 ++++++++- apps/web/src/locales/vi/messages.json | 110 +++++++++- apps/web/src/locales/zh_CN/messages.json | 120 ++++++++-- apps/web/src/locales/zh_TW/messages.json | 116 ++++++++-- 63 files changed, 6343 insertions(+), 799 deletions(-) diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index f63a0878540..684e107cd57 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Laai lisensie af" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index 30eddbb1702..fb42b2b5fa2 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "الدفعة القادمة" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "التفاصيل" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "تنزيل الرخصة" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index a09fa41ed90..8dcfa862f03 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -21,7 +21,7 @@ "message": "Parol riski" }, "noEditPermissions": { - "message": "You don't have permission to edit this item" + "message": "Bu elementə düzəliş etmə icazəniz yoxdur" }, "reviewAtRiskPasswords": { "message": "Tətbiqlər arasında riskli (zəif, ifşa olunmuş və ya təkrar istifadə olunmuş) parolları incələyin. İstifadəçilərinizin riskli parollara yönəlmiş təhlükəsizlik tədbirlərinə əhəmiyyət vermələri üçün kritik tətbiqlərinizi seçin." @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "İncələmə gözləyən müraciətlər" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ yeni müraciət", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "Hazırda incələnməli yeni tətbiq yoxdur" }, + "organizationHasItemsSavedForApplications": { + "message": "Təşkilatınızda $COUNT$ tətbiq üçün saxlanılmış elementlər var", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Təşkilatınzıın təhlükəsizliyi üçün ən kritik sayılan elementləri güvəndə saxlamaq üçün tətbiqləri incələyin" + }, + "reviewApplications": { + "message": "Tətbiqləri incələ" + }, "prioritizeCriticalApplications": { "message": "Kritik tətbiqləri prioritetləşdir" }, @@ -784,7 +802,7 @@ } }, "passwordSafe": { - "message": "Bu parol, veri pozuntularında qeydə alınmayıb. Rahatlıqla istifadə edə bilərsiniz." + "message": "Bu parol, veri pozuntularında qeydə alınmayıb. Əmniyyətlə istifadə edə bilərsiniz." }, "save": { "message": "Saxla" @@ -2491,7 +2509,7 @@ "message": "SSO quraşdırması etmisinizsə və ya etmək planınız varsa, İki addımlı giriş, artıq Kimlik Provayderiniz vasitəsilə tətbiq edilmiş ola bilər." }, "twoStepLoginRecoveryWarning": { - "message": "İki addımlı girişi qurmaq, Bitwarden hesabınızı birdəfəlik kilidləyə bilər. Geri qaytarma kodu, normal iki addımlı giriş provayderinizi artıq istifadə edə bilmədiyiniz hallarda (məs. cihazınızı itirəndə) hesabınıza erişməyinizə imkan verir. Hesabınıza erişimi itirsəniz, Bitwarden dəstəyi sizə kömək edə bilməyəcək. Geri qaytarma kodunuzu bir yerə yazmağınızı və ya çap etməyinizi və onu etibarlı bir yerdə saxlamağınızı məsləhət görürük." + "message": "İki addımlı girişi qurmaq, Bitwarden hesabınızı birdəfəlik kilidləyə bilər. Geri qaytarma kodu, normal iki addımlı giriş provayderinizi artıq istifadə edə bilmədiyiniz hallarda (məs. cihazınızı itirəndə) hesabınıza erişməyinizə imkan verir. Hesabınıza erişimi itirsəniz, Bitwarden dəstəyi sizə kömək edə bilməyəcək. Geri qaytarma kodunuzu bir yerə yazmağınızı və ya çap etməyinizi və onu əmniyyətli bir yerdə saxlamağınızı məsləhət görürük." }, "restrictedItemTypePolicy": { "message": "Kart element növünü sil" @@ -2506,7 +2524,7 @@ "message": "1 və ya daha çox təşkilat tərəfindən təyin edilən bir siyasət, kartların seyfinizə köçürülməsini əngəlləyir." }, "yourSingleUseRecoveryCode": { - "message": "İki addımlı giriş provayderinizə erişə bilmədiyiniz halda, iki addımlı girişi söndürmək üçün təkistifadəlik geri qaytarma kodunu istifadə edə bilərsiniz. Bitwarden tövsiyə edir ki, geri qaytarma kodunuzu bir yerə yazıb güvənli bir yerdə saxlayın." + "message": "İki addımlı giriş provayderinizə erişə bilmədiyiniz halda, iki addımlı girişi söndürmək üçün təkistifadəlik geri qaytarma kodunu istifadə edə bilərsiniz. Bitwarden tövsiyə edir ki, geri qaytarma kodunuzu bir yerə yazıb əmniyyətli bir yerdə saxlayın." }, "viewRecoveryCode": { "message": "Geri qaytarma koduna bax" @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Növbəti ödəniş" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Təfsilatlar" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Lisenziyanı endir" }, @@ -3257,7 +3284,7 @@ "message": "Saxlama sahəsi əlavə et" }, "removeStorage": { - "message": "Saxlama sahəsini çıxart" + "message": "Saxlama sahəsini xaric et" }, "subscriptionStorage": { "message": "Abunəliyinizdə cəmi $MAX_STORAGE$ GB şifrələnmiş fayl saxlama sahəsi var. Hazırda $USED_STORAGE$ istifadə edirsiniz.", @@ -3329,7 +3356,7 @@ "message": "GB saxlamaya əlavə et" }, "gbStorageRemove": { - "message": "GB saxlamadan çıxart" + "message": "Anbardan xaric ediləcək GB" }, "storageAddNote": { "message": "Saxlama sahəsi əlavə etmək, fakturanın cəmində dəyişikliklərə səbəb olacaq və dərhal hesab ödəniş metodunuzdan çıxılacaq. İlk çıxılacaq hesab, hazırkı faktura dövrünün qalanı üçün etibarlı olacaq." @@ -3670,10 +3697,10 @@ } }, "removeUserConfirmation": { - "message": "Bu istifadəçini çıxartmaq istədiyinizə əminsiniz?" + "message": "Bu istifadəçini xaric etmək istədiyinizə əminsiniz?" }, "removeOrgUserConfirmation": { - "message": "Bir üzv silindikdə, artıq həmin üzv təşkilat verilərinə erişə bilmir və bu əməliyyatın geri dönüşü yoxdur. Həmin üzvü təşkilata yenidən əlavə etmək üçün, onu dəvət edib üzv olmasını təmin etmək lazımdır." + "message": "Bir üzv xaric edildiyi zaman, artıq həmin üzv təşkilat verilərinə erişə bilmir və bu əməliyyatın geri dönüşü yoxdur. Həmin üzvü təşkilata yenidən əlavə etmək üçün, onu dəvət edib üzv olmasını təmin etmək lazımdır." }, "revokeUserConfirmation": { "message": "Bir üzv ləğv edildikdə, artıq həmin üzv təşkilat verilərinə erişə bilmir. Üzv erişimini daha tez bərpa etmək üçün, Ləğv edilənlər vərəqinə gedin." @@ -4094,7 +4121,7 @@ } }, "removedUserId": { - "message": "$ID$ istifadəçisi çıxarıldı.", + "message": "$ID$ istifadəçisi xaric edildi.", "placeholders": { "id": { "content": "$1", @@ -4103,7 +4130,7 @@ } }, "removeUserIdAccess": { - "message": "$ID$ erişimini sil", + "message": "$ID$ erişimini xaric et", "placeholders": { "id": { "content": "$1", @@ -4232,7 +4259,7 @@ } }, "removedOrganizationId": { - "message": "$ID$ təşkilatı silindi.", + "message": "$ID$ təşkilatı xaric edildi.", "placeholders": { "id": { "content": "$1", @@ -4440,7 +4467,31 @@ "message": "Brauzeri güncəllə" }, "generatingYourAccessIntelligence": { - "message": "Generating your Access Intelligence..." + "message": "Access Intelligence-niz yaradılır..." + }, + "fetchingMemberData": { + "message": "Üzv veriləri alınır..." + }, + "analyzingPasswordHealth": { + "message": "Parol sağlamlığı analiz edirilir..." + }, + "calculatingRiskScores": { + "message": "Risk xalı hesablanır..." + }, + "generatingReportData": { + "message": "Hesabat veriləri yaradılır..." + }, + "savingReport": { + "message": "Hesabat saxlanılır..." + }, + "compilingInsights": { + "message": "Təhlillər şərh edilir..." + }, + "loadingProgress": { + "message": "İrəliləyiş yüklənir" + }, + "thisMightTakeFewMinutes": { + "message": "Bu, bir neçə dəqiqə çəkə bilər." }, "riskInsightsRunReport": { "message": "Hesabatı işə sal" @@ -4716,7 +4767,7 @@ "description": "Seat = User Seat" }, "removeSeats": { - "message": "Yeri götür", + "message": "Yeri xaric et", "description": "Seat = User Seat" }, "subscriptionDesc": { @@ -4825,7 +4876,7 @@ "message": "Əlavə ediləcək yerlər" }, "seatsToRemove": { - "message": "Götürüləcək yerlər" + "message": "Xaric ediləcək yerlər" }, "seatsAddNote": { "message": "İstifadəçi yerləri əlavə etmək, fakturanın cəmində dəyişikliklərə səbəb olacaq və dərhal hesab ödəniş metodunuzdan çıxılacaq. İlk çıxılacaq hesab, hazırkı faktura dövrünün qalanı üçün etibarlı olacaq." @@ -5178,10 +5229,10 @@ "message": "İstifadəçilərin \"iki addımlı giriş\"i qurmasını tələb et." }, "twoStepLoginPolicyWarning": { - "message": "Sahib və ya Administrator olmayan və fərdi hesablarında \"iki addımlı giriş\"i fəallaşdırmayan təşkilat üzvləri təşkilatdan çıxarılacaq və dəyişiklik haqqında onları məlumatlandıran e-poçt göndəriləcək." + "message": "Sahib və ya Administrator olmayan və fərdi hesablarında \"iki addımlı giriş\"i fəallaşdırmayan təşkilat üzvləri təşkilatdan xaric ediləcək və dəyişiklik haqqında onları məlumatlandıran e-poçt göndəriləcək." }, "twoStepLoginPolicyUserWarning": { - "message": "İstifadəçi hesabında \"iki addımlı giriş\"in qurulmasını tələb edən təşkilatın üzvüsünüz. İki addımlı giriş provayderlərinin hamısını söndürsəniz, bu təşkilatdan avtomatik olaraq çıxarılacaqsınız." + "message": "İstifadəçi hesabında \"iki addımlı giriş\"in qurulmasını tələb edən təşkilatın üzvüsünüz. İki addımlı giriş provayderlərinin hamısını söndürsəniz, bu təşkilatdan avtomatik olaraq xaric ediləcəksiniz." }, "passwordGeneratorPolicyDesc": { "message": "Parol yaradıcı üçün tələbləri ayarla." @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "Avtomatik istifadəçi təsdiqi necə işə salınır" }, - "autoConfirmStep1": { - "message": "Bitwarden uzantınızı açın." + "autoConfirmExtension1": { + "message": "Bitwarden uzantınızı açın" }, - "autoConfirmStep2a": { - "message": "Seçin", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension2": { + "message": "Seç:", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " İşə sal.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " İşə sal", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "Bitwarden brauzer uzantısı uğurla açıldı. Artıq avtomatik istifadəçi təsdiqi ayarını aktivləşdirə bilərsiniz." @@ -5851,7 +5902,7 @@ "message": "Bu riskləri və siyasət güncəlləmələrini qəbul edirəm" }, "personalOwnership": { - "message": "Fərdi sahiblik" + "message": "Fərdi seyfi xaric et" }, "personalOwnershipPolicyDesc": { "message": "Fərdi seyf seçimini silərək istifadəçilərin elementləri bir təşkilatda saxlamasını məcburi edin." @@ -5870,7 +5921,7 @@ "description": "This policy will enable Desktop Autotype by default for members on Unlock." }, "disableSend": { - "message": "\"Send\"i sıradan çıxart" + "message": "\"Send\"i sil" }, "disableSendPolicyDesc": { "message": "İstifadəçilərin Bitwarden Send yaratmasına və ya ona düzəliş etməsinə icazə vermə. Mövcud \"Send\"in silinməsinə hələ də icazə verilir.", @@ -5880,7 +5931,7 @@ "message": "Təşkilatın siyasətlərini idarə edə bilən təşkilat istifadəçiləri bu siyasətin tətbiqindən azaddırlar." }, "sendDisabled": { - "message": "Send sıradan çıxarıldı", + "message": "Send silindi", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendDisabledWarning": { @@ -6012,7 +6063,7 @@ "message": "Bir təşkilat siyasəti, elementlərin fərdi seyfinizə köçürülməsini əngəllədi." }, "personalOwnershipCheckboxDesc": { - "message": "Təşkilat istifadəçiləri üçün fərdi sahibliyi sıradan çıxart" + "message": "Təşkilat istifadəçiləri üçün fərdi sahibliyi sil" }, "send": { "message": "Send", @@ -6337,10 +6388,10 @@ "message": "Bu əməliyyat, seçilən istifadəçilərin heç biri üçün etibarlı deyil." }, "removeUsersWarning": { - "message": "Aşağıdakı istifadəçiləri çıxartmaq istədiyinizə əminsiniz? Bu prosesin tamamlanması bir neçə saniyə çəkir, ləğv edilə və ya dayandırıla bilməz." + "message": "Aşağıdakı istifadəçiləri xaric etmək istədiyinizə əminsiniz? Bu prosesin tamamlanması bir neçə saniyə çəkir, ləğv edilə və ya dayandırıla bilməz." }, "removeOrgUsersConfirmation": { - "message": "Üzv(lər) silindikdə, artıq həmin üzv(lər) təşkilat verilərinə erişə bilmir və bu əməliyyatın geri dönüşü yoxdur. Üzvü təşkilata yenidən əlavə etmək üçün, onu dəvət edib üzv olmasını təmin etmək lazımdır. Prosesin tamamlanması bir neçə saniyə çəkə bilər, bu proses dayandırıla və ya ləğv edilə bilməz." + "message": "Üzv(lər) xaric edildiyi zaman, artıq həmin üzv(lər) təşkilat verilərinə erişə bilmir və bu əməliyyatın geri dönüşü yoxdur. Üzvü təşkilata yenidən əlavə etmək üçün, onu dəvət edib üzv olmasını təmin etmək lazımdır. Prosesin tamamlanması bir neçə saniyə çəkə bilər, bu proses dayandırıla və ya ləğv edilə bilməz." }, "revokeUsersWarning": { "message": "Üzv(lər) ləğv edildikə, artıq həmin üzv(lər) təşkilat verilərinə erişə bilmir. Üzv erişimini daha tez bərpa etmək üçün, Ləğv edilənlər vərəqinə gedin. Prosesin tamamlanması bir neçə saniyə çəkə bilər, bu proses dayandırıla və ya ləğv edilə bilməz." @@ -6584,31 +6635,31 @@ "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." }, "sessionTimeoutPolicyTitle": { - "message": "Session timeout" + "message": "Seans vaxt bitməsi" }, "sessionTimeoutPolicyDescription": { - "message": "Set a maximum session timeout for all members except owners." + "message": "Sahiblər istisna olmaqla bütün üzvlər üçün maksimum seans bitmə vaxtını təyin edin." }, "maximumAllowedTimeout": { - "message": "Maximum allowed timeout" + "message": "Maksimum icazə verilən bitmə vaxtı" }, "maximumAllowedTimeoutRequired": { - "message": "Maximum allowed timeout is required." + "message": "İcazə verilən maksimum bitmə vaxtı tələb olunur." }, "sessionTimeoutPolicyInvalidTime": { - "message": "Time is invalid. Change at least one value." + "message": "Vaxt etibarsızdır. Ən azı bir dəyəri dəyişdirin." }, "sessionTimeoutAction": { - "message": "Session timeout action" + "message": "Seans vaxt bitmə əməliyyatı" }, "immediately": { - "message": "Immediately" + "message": "Dərhal" }, "onSystemLock": { - "message": "On system lock" + "message": "Sistem kilidlənəndə" }, "onAppRestart": { - "message": "On app restart" + "message": "Tətbiq yenidən başladılanda" }, "hours": { "message": "Saat" @@ -6617,7 +6668,7 @@ "message": "Dəqiqə" }, "sessionTimeoutConfirmationNeverTitle": { - "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + "message": "Bütün üzvlər üçün maksimum bitmə vaxtını \"Heç vaxt\" olaraq icazə vermək istədiyinizə əminsiniz?" }, "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." @@ -6909,7 +6960,7 @@ "message": "\"Bitwarden Ailələri\"ni istifadə etmək üçün fərdi e-poçtunuzu daxil edin" }, "sponsoredFamiliesLeaveCopy": { - "message": "Bu təşkilatı tərk etsəniz və ya bu təşkilatdan çıxarılsanız, Ailələr planınızın istifadə müddəti faktura dövrünün sonunda başa çatacaq." + "message": "Bu təşkilatı tərk etsəniz və ya bu təşkilatdan xaric edilsəniz, Ailələr planınızın istifadə müddəti faktura dövrünün sonunda başa çatacaq." }, "acceptBitwardenFamiliesHelp": { "message": "Mövcud bir təşkilat üçün bir təklifi qəbul edin və ya yeni bir Ailələr təşkilatını yaradın." @@ -8407,7 +8458,7 @@ "message": "Rol" }, "removeMember": { - "message": "Üzv sil" + "message": "Üzv xaric et" }, "collection": { "message": "Kolleksiya" @@ -8913,7 +8964,7 @@ } }, "removedUserToServiceAccountWithId": { - "message": "$USER_ID$ istifadəçisi, $SERVICE_ACCOUNT_ID$ ID-sinə sahib maşın hesabından çıxarıldı", + "message": "$USER_ID$ istifadəçisi, $SERVICE_ACCOUNT_ID$ ID-sinə sahib maşın hesabından xaric edildi", "placeholders": { "user_id": { "content": "$1", @@ -8926,7 +8977,7 @@ } }, "removedGroupFromServiceAccountWithId": { - "message": "$GROUP_ID$ qrupu, $SERVICE_ACCOUNT_ID$ ID-sinə sahib maşın hesabından çıxarıldı", + "message": "$GROUP_ID$ qrupu, $SERVICE_ACCOUNT_ID$ ID-sinə sahib maşın hesabından xaric edildi", "placeholders": { "group_id": { "content": "$1", @@ -9054,7 +9105,7 @@ "message": "Erişim tokenləri hələ də mövcuddur" }, "saPeopleWarningMessage": { - "message": "İnsanları xidmət hesabından silsəniz belə, yaratdıqları erişim tokenləri silinmir. Ən yaxşı təhlükəsizlik təcrübəsi üçün, xidmət hesabından silinmiş insanlar tərəfindən yaradılan erişim tokenlərinin ləğv edilməsi tövsiyə olunur." + "message": "İnsanları xidmət hesabından xaric etsəniz belə, yaratdıqları erişim tokenləri silinmir. Ən yaxşı təhlükəsizlik təcrübəsi üçün, xidmət hesabından xaric edilmiş insanlar tərəfindən yaradılan erişim tokenlərinin ləğv edilməsi tövsiyə olunur." }, "smAccessRemovalWarningProjectTitle": { "message": "Bu layihəyə erişimi sil" @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Giriş edildi!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Kolleksiya erişimini təyin et" }, @@ -11528,7 +11576,7 @@ "message": "Yeni biznes vahidi" }, "sendsTitleNoItems": { - "message": "Send, həssas məlumatlar təhlükəsizdir", + "message": "Send ilə həssas məlumatlar əmniyyətdədir", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "sendsBodyNoItems": { @@ -11583,7 +11631,7 @@ "message": "Kimliklərinizlə, uzun qeydiyyat və ya əlaqə xanalarını daha tez avtomatik doldurun." }, "newNoteNudgeTitle": { - "message": "Həssas verilərinizi güvənli şəkildə saxlayın" + "message": "Həssas verilərinizi əmniyyətdə saxlayın" }, "newNoteNudgeBody": { "message": "Notlarla, bankçılıq və ya sığorta təfsilatları kimi həssas veriləri təhlükəsiz saxlayın." @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Ödənişsiz Ailələr sınağını başlat" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Seyf vaxt bitmə əməliyyatınızı dəyişdirmək üçün bir kilid açma üsulu qurun." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Müəssisə siyasət tələbləri, vaxt bitmə seçimlərinizə tətbiq edildi" + }, + "vaultTimeoutTooLarge": { + "message": "Seyfin bitmə vaxtı, təşkilatınız tərəfindən ayarlanan məhdudiyyətləri aşır." + }, + "neverLockWarning": { + "message": "\"Heç vaxt\"i seçmək istədiyinizə əminsiniz? Kilid seçimini \"Heç vaxt\" olaraq ayarlasanız, seyfinizin şifrələmə açarı cihazınızda saxlanılacaq. Bu seçimi istifadə etsəniz, cihazınızı daha yaxşı mühafizə etdiyinizə əmin olmalısınız." + }, + "sessionTimeoutSettingsAction": { + "message": "Vaxt bitmə əməliyyatı" + }, + "sessionTimeoutHeader": { + "message": "Sessiya vaxt bitməsi" + }, + "appearance": { + "message": "Görünüş" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Vaxt bitməsinə təyin olunan vaxt, təşkilatınız tərəfindən ayarlanan məhdudiyyəti aşır: Maksimum $HOURS$ saat və $MINUTES$ dəqiqə", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index 21639cddbcc..9fd22d0ccd0 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Наступнае спагнанне" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Падрабязнасці" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Спампаваць ліцэнзію" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Вы ўвайшлі!" }, - "beta": { - "message": "Бэта" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index cc6e298c26e..6f17d4b264b 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -15,7 +15,7 @@ "message": "Няма важни приложения в риск" }, "accessIntelligence": { - "message": "Access Intelligence" + "message": "Анализ на достъпа" }, "passwordRisk": { "message": "Рискова парола" @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Приложения, които имат нужда от преглед" }, + "newApplicationsCardTitle": { + "message": "Преглед на новите приложения" + }, "newApplicationsWithCount": { "message": "$COUNT$ нови приложения", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "В момента няма нови приложения за преглед" }, + "organizationHasItemsSavedForApplications": { + "message": "Вашата организация има запазени елементи за $COUNT$ приложения", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Прегледайте приложенията, за да защитите най-важните за организацията си елементи" + }, + "reviewApplications": { + "message": "Преглед на приложенията" + }, "prioritizeCriticalApplications": { "message": "Даване на приоритет на важните приложения" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Следваща промяна" }, + "nextChargeHeader": { + "message": "Следващо плащане" + }, + "plan": { + "message": "План" + }, "details": { "message": "Детайли" }, + "discount": { + "message": "отстъпка" + }, "downloadLicense": { "message": "Изтегляне на лиценз" }, @@ -4440,7 +4467,31 @@ "message": "Обновяване на браузъра" }, "generatingYourAccessIntelligence": { - "message": "Generating your Access Intelligence..." + "message": "Създаване на Вашия анализ на достъпа…" + }, + "fetchingMemberData": { + "message": "Извличане на данните за членовете…" + }, + "analyzingPasswordHealth": { + "message": "Анализиране на състоянието на паролите…" + }, + "calculatingRiskScores": { + "message": "Изчисляване на оценките на риска…" + }, + "generatingReportData": { + "message": "Създаване на данните за доклада…" + }, + "savingReport": { + "message": "Запазване на доклада…" + }, + "compilingInsights": { + "message": "Събиране на подробности…" + }, + "loadingProgress": { + "message": "Зареждане на напредъка" + }, + "thisMightTakeFewMinutes": { + "message": "Това може да отнеме няколко минути." }, "riskInsightsRunReport": { "message": "Изпълнение на доклада" @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "Как се включва автоматичното потвърждаване на потребителите" }, - "autoConfirmStep1": { - "message": "Отворете добавката на Битуорден." + "autoConfirmExtension1": { + "message": "Отворете добавката на Битуорден" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Изберете", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Включване.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Включване", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "Добавката за браузър на Битуорден е отворена. Сега можете да включите настройката за автоматично потвърждаване на потребителите." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Вписахте се!" }, - "beta": { - "message": "Бета" - }, "assignCollectionAccess": { "message": "Задаване на достъп за колекциите" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Започнете безплатния пробен период на Семейния план" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Задайте метод за отключване, за да може да промените действието при изтичане на времето за достъп до трезора." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Изискванията на политиката за големи компании бяха приложени към настройките на времето за достъп" + }, + "vaultTimeoutTooLarge": { + "message": "Времето за достъп до трезора Ви превишава ограничението, определено от организацията Ви." + }, + "neverLockWarning": { + "message": "Уверени ли сте, че искате да зададете стойност „Никога“? Това води до съхранение на шифриращия ключ за трезора във устройството ви. Ако използвате тази възможност, е много важно да имате надлежна защита на устройството си." + }, + "sessionTimeoutSettingsAction": { + "message": "Действие при изтичането на времето за достъп" + }, + "sessionTimeoutHeader": { + "message": "Изтичане на времето за сесията" + }, + "appearance": { + "message": "Външен вид" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Времето за достъп превишава ограничението, зададено от Вашата организация: максимум $HOURS$ час(а) и $MINUTES$ минута/и", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index ec567fd43e3..4baf480b517 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index c2150c22154..f9b5481cd23 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index 7b0094fdecd..2994137855e 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Càrrec següent" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Detall" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Baixa la llicència" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Connectat!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assigna accés a la col·lecció" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index bb5958b4e59..ed82a38c390 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Aplikace, které vyžadují kontrolu" }, + "newApplicationsCardTitle": { + "message": "Zkontrolovat nové aplikace" + }, "newApplicationsWithCount": { "message": "$COUNT$ nových aplikací", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "Nyní nejsou k dispozici žádné nové kontroly" }, + "organizationHasItemsSavedForApplications": { + "message": "Vaše organizace má položky uložené pro $COUNT$ aplikací", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Zkontrolujte aplikace pro zabezpečení položek nejkritičtějších pro bezpečnost Vaší organizace" + }, + "reviewApplications": { + "message": "Zkontrolovat aplikace" + }, "prioritizeCriticalApplications": { "message": "Upřednostnit kritické aplikace" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Další platba" }, + "nextChargeHeader": { + "message": "Další platba" + }, + "plan": { + "message": "Plán" + }, "details": { "message": "Podrobnosti" }, + "discount": { + "message": "sleva" + }, "downloadLicense": { "message": "Stáhnout licenci" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generování Vaší přístupové inteligence..." }, + "fetchingMemberData": { + "message": "Načítání dat člena..." + }, + "analyzingPasswordHealth": { + "message": "Analyzování zdraví hesla..." + }, + "calculatingRiskScores": { + "message": "Výpočet skóre rizik..." + }, + "generatingReportData": { + "message": "Generování dat hlášení..." + }, + "savingReport": { + "message": "Ukládání hlášení..." + }, + "compilingInsights": { + "message": "Sestavování přehledů..." + }, + "loadingProgress": { + "message": "Průběh načítání" + }, + "thisMightTakeFewMinutes": { + "message": "Může to trvat několik minut." + }, "riskInsightsRunReport": { "message": "Spustit hlášení" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "Jak zapnout automatické potvrzení uživatele" }, - "autoConfirmStep1": { - "message": "Otevřete rozšíření Bitwarden." + "autoConfirmExtension1": { + "message": "Otevřít rozšíření Bitwarden" }, - "autoConfirmStep2a": { - "message": "Vyberte", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension2": { + "message": "Vybrat", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Zapnout.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Zapnout", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "Rozšíření prohlížeče Bitwarden bylo úspěšně otevřeno. Nyní můžete aktivovat automatické potvrzení uživatele." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Přihlášeno!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Přiřadit přístup ke sbírce" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Zahájit bezplatnou zkušební verzi pro rodiny" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Nastavte metodu odemknutí, abyste změnili časový limit Vašeho trezoru." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Na volby časového limitu byly uplatněny požadavky podnikových zásad" + }, + "vaultTimeoutTooLarge": { + "message": "Časový limit Vašeho trezoru překračuje omezení stanovená Vaší organizací." + }, + "neverLockWarning": { + "message": "Opravdu chcete použít volbu \"Nikdy\"? Nastavením volby uzamčení na \"Nikdy\" bude šifrovací klíč k trezoru uložen přímo ve Vašem zařízení. Pokud tuto možnost použijete, měli byste Vaše zařízení řádně zabezpečit a chránit." + }, + "sessionTimeoutSettingsAction": { + "message": "Akce vypršení časového limitu" + }, + "sessionTimeoutHeader": { + "message": "Časový limit relace" + }, + "appearance": { + "message": "Vzhled" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Časový limit překračuje omezení stanovené Vaší organizací: maximálně $HOURS$ hodin a $MINUTES$ minut", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "Nejsou vybrány žádné kritické aplikace" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Opravdu chcete pokračovat?" } } diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index cf5270e3d74..8c62a789fbd 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index 543f846b078..7ddd60a2b75 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Næste betaling" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Detaljer" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download licens" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Indlogget!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Tildel samlingsadgang" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index 57f2a8699f5..162eefe9923 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Anwendungen, die geprüft werden müssen" }, + "newApplicationsCardTitle": { + "message": "Neue Anwendungen überprüfen" + }, "newApplicationsWithCount": { "message": "$COUNT$ neue Anwendungen", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "Derzeit gibt es keine neuen Anwendungen zum Überprüfen" }, + "organizationHasItemsSavedForApplications": { + "message": "Deine Organisation hat Einträge für $COUNT$ Anwendungen gespeichert", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Überprüfe Anwendungen, um die für die Sicherheit deiner Organisation wichtigsten Einträge zu sichern" + }, + "reviewApplications": { + "message": "Anwendungen überprüfen" + }, "prioritizeCriticalApplications": { "message": "Kritische Anwendungen priorisieren" }, @@ -1387,7 +1405,7 @@ "message": "Mit Passkey anmelden" }, "useSingleSignOn": { - "message": "Single Sign-on verwenden" + "message": "Single Sign-On verwenden" }, "yourOrganizationRequiresSingleSignOn": { "message": "Deine Organisation erfordert Single Sign-On." @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Nächste Abbuchung" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Lizenz herunterladen" }, @@ -3640,7 +3667,7 @@ "message": "Richtlinien" }, "singleSignOn": { - "message": "Single Sign-on" + "message": "Single Sign-On" }, "editPolicy": { "message": "Richtlinie bearbeiten" @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Deine Zugangsintelligenz wird generiert..." }, + "fetchingMemberData": { + "message": "Mitgliedsdaten werden abgerufen..." + }, + "analyzingPasswordHealth": { + "message": "Passwortsicherheit wird analysiert..." + }, + "calculatingRiskScores": { + "message": "Risikobewertung wird berechnet..." + }, + "generatingReportData": { + "message": "Berichtsdaten werden generiert..." + }, + "savingReport": { + "message": "Bericht wird gespeichert..." + }, + "compilingInsights": { + "message": "Analyse wird zusammengestellt..." + }, + "loadingProgress": { + "message": "Ladefortschritt" + }, + "thisMightTakeFewMinutes": { + "message": "Dies kann einige Minuten dauern." + }, "riskInsightsRunReport": { "message": "Bericht ausführen" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "So aktivierst du die automatische Benutzerbestätigung" }, - "autoConfirmStep1": { - "message": "Öffne deine Bitwarden-Erweiterung." + "autoConfirmExtension1": { + "message": "Öffne deine Bitwarden-Erweiterung" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Wähle", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Aktivieren.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Einschalten", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "Die Bitwarden Browser-Erweiterung wurde erfolgreich geöffnet. Du kannst nun die automatische Benutzerbestätigung aktivieren." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Angemeldet!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Sammlungszugriff zuweisen" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Kostenlose Families-Testversion starten" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Richte eine Entsperrmethode ein, um deine Aktion bei Tresor-Timeout zu ändern." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Die Unternehmens-Richtlinienanforderungen wurden auf deine Timeout-Optionen angewendet" + }, + "vaultTimeoutTooLarge": { + "message": "Dein Tresor-Timeout überschreitet die von deinem Unternehmen festgelegten Beschränkungen." + }, + "neverLockWarning": { + "message": "Bist du sicher, dass du die Option \"Nie\" verwenden möchtest? Durch das Setzen der Sperroptionen zu \"Nie\" wird der Verschlüsselungsschlüssel deines Tresors auf deinem Gerät gespeichert. Wenn du diese Option verwendest, solltest du sicherstellen, dass dein Gerät ausreichend geschützt ist." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout-Aktion" + }, + "sessionTimeoutHeader": { + "message": "Sitzungs-Timeout" + }, + "appearance": { + "message": "Aussehen" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Das Timeout überschreitet die von deiner Organisation festgelegte Beschränkung: Maximal $HOURS$ Stunde(n) und $MINUTES$ Minute(n)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index 641c789f363..fb066120434 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Επόμενη Χρέωση" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Λεπτομέρειες" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Λήψη Άδειας" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Έχετε συνδεθεί!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Ανάθεση πρόσβασης συλλογής" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index 01b96d1d207..2270d95056c 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organisation has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organisation's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritise critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download licence" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analysing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organisation." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organisation: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index c1da6986bba..99ebcf22413 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organisation has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organisation's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritise critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download licence" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analysing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organisation." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organisation: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index 909ac5df6f6..b1acc78487d 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Sekva Akuzo" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Detaloj" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Elŝuti Permesilon" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Jam salutis!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index adb547b4982..80767d520e8 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Siguiente cobro" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Detalles" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Descargar licencia" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "¡Ha iniciado sesión!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Asignar acceso a colecciones" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index c26994bcf21..0e47bce83ff 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Järgmine makse" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Andmed" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Laadi litsents alla" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index 58282adf95f..95281e381ca 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Hurrengo kobrantza" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Xehetasunak" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Lizentzia deskargatu" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index cf497f9ac86..aec55daad51 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "شارژ بعدی" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "جزئیات" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "دانلود مجوز" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "وارد شده!" }, - "beta": { - "message": "آزمایشی" - }, "assignCollectionAccess": { "message": "اختصاص دسترسی به مجموعه" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index 383572bb5d7..8a1c7550d42 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Seuraava veloitus" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Tiedot" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Lataa lisenssi" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Kirjautuminen onnistui" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Määritä kokoelmien käyttöoikeudet" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index e912c206e60..6b0d5e3e8a5 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Susunod na singil" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Mga Detalye" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Mag-download ng lisensya" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index 14c04bde667..8d51d685d79 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications nécessitant un examen" }, + "newApplicationsCardTitle": { + "message": "Examiner les nouvelles applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ nouvelles applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "Aucune nouvelle application à examiner pour le moment" }, + "organizationHasItemsSavedForApplications": { + "message": "Votre organisation a des éléments enregistrés pour $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Examiner les applications pour sécuriser les éléments les plus critiques pour la sécurité de votre organisation" + }, + "reviewApplications": { + "message": "Examiner les applications" + }, "prioritizeCriticalApplications": { "message": "Prioriser les applications critiques" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Prochain paiement" }, + "nextChargeHeader": { + "message": "Prochain paiement" + }, + "plan": { + "message": "Forfait" + }, "details": { "message": "Détails" }, + "discount": { + "message": "réduction" + }, "downloadLicense": { "message": "Télécharger la licence" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Génération de votre Intelligence d'Accès..." }, + "fetchingMemberData": { + "message": "Récupération des données des membres..." + }, + "analyzingPasswordHealth": { + "message": "Analyse de la santé du mot de passe..." + }, + "calculatingRiskScores": { + "message": "Calcul des niveaux de risque..." + }, + "generatingReportData": { + "message": "Génération des données du rapport..." + }, + "savingReport": { + "message": "Enregistrement du rapport..." + }, + "compilingInsights": { + "message": "Compilation des aperçus..." + }, + "loadingProgress": { + "message": "Chargement de la progression" + }, + "thisMightTakeFewMinutes": { + "message": "Cela peut prendre quelques minutes." + }, "riskInsightsRunReport": { "message": "Exécuter le rapport" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "Comment activer la confirmation automatique de l'utilisateur" }, - "autoConfirmStep1": { - "message": "Ouvrez votre extension Bitwarden." + "autoConfirmExtension1": { + "message": "Ouvrez votre extension Bitwarden" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Sélectionner", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Activer.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Activer", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "Ouverture réussie de l'extension du navigateur Bitwarden. Vous pouvez maintenant activer le paramètre de confirmation automatique de l'utilisateur." @@ -6406,7 +6457,7 @@ "message": "Bitwarden n'a pas pu déchiffrer le(s) élément(s) du coffre listé(s) ci-dessous." }, "contactCSToAvoidDataLossPart1": { - "message": "Contacter Customer Success", + "message": "Contacter Succès Client", "description": "This is part of a larger sentence. The full sentence will read 'Contact customer success to avoid additional data loss.'" }, "contactCSToAvoidDataLossPart2": { @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Connecté !" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assigner l'accès à la collection" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Commencez l'essai gratuit au forfait Familles" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Configurez une méthode de déverrouillage pour changer le délai d'expiration de votre coffre." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Les exigences de la politique de sécurité d'Entreprise ont été appliquées à vos options de délai d'expiration" + }, + "vaultTimeoutTooLarge": { + "message": "Le délai d'expiration de votre coffre dépasse les restrictions définies par votre organisation." + }, + "neverLockWarning": { + "message": "Êtes-vous sûr de vouloir utiliser l'option \"Jamais\" ? Définir le verrouillage sur \"Jamais\" stocke la clé de chiffrement de votre coffre sur votre appareil. Si vous utilisez cette option, vous devez vous assurer de correctement protéger votre appareil." + }, + "sessionTimeoutSettingsAction": { + "message": "Action à l’expiration" + }, + "sessionTimeoutHeader": { + "message": "Expiration de la session" + }, + "appearance": { + "message": "Apparence" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Le délai d'expiration dépasse la restriction définie par votre organisation : $HOURS$ heure(s) et $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index 0a9c69acd9b..e5c8ce9b49f 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index 473380c7e56..b60e76f6a75 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "יישומים צריכים סקירה" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ יישומים חדשים", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "תעדוף יישומים קריטיים" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "החיוב הבא" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "פרטים" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "הורד רישיון" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "הרץ דוח" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "איך להפעיל אישור משתמש אוטומטי" }, - "autoConfirmStep1": { - "message": "פתח את הרחבת Bitwarden שלך." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { - "message": "בחר", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension2": { + "message": "Select", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " הפעל.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "הרחבת Bitwarden לדפדפן נפתחה בהצלחה. כעת ביכולתך להפעיל את הגדרת אישור אוטומטי של משתמשים." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "נכנסת!" }, - "beta": { - "message": "בטא" - }, "assignCollectionAccess": { "message": "הקצה גישה לאוסף" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "התחל ניסיון משפחות בחינם" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index 98d9896dd00..01cf40c3c0d 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index e97c0bdb040..e0f0ce0b28b 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Aplikacije koje je potrebno pregledati" }, + "newApplicationsCardTitle": { + "message": "Pregledaj nove prijave" + }, "newApplicationsWithCount": { "message": "Novih aplikacija: $COUNT$", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "Trenutno nema novih aplikacija za pregled" }, + "organizationHasItemsSavedForApplications": { + "message": "Tvoja organizacija ima spremljene stavke za ovoliko aplikacija: $COUNT$", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Pregledaj aplikacije za zaštitu stavki najvažnijih za sigurnost tvoje organizacije" + }, + "reviewApplications": { + "message": "Pregledaj aplikacije" + }, "prioritizeCriticalApplications": { "message": "Daj prioritet kritičnim aplikacijama" }, @@ -377,10 +395,10 @@ "message": "Odaberi koje su aplikacije najvažnije za tvoju organizaciju, a zatim dodijeli sigurnosne zadatke članovima kako bi se riješili rizici." }, "reviewNewApplications": { - "message": "Review new applications" + "message": "Pregledaj nove prijave" }, "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "message": "Istaknuli smo rizične stavke za nove aplikacije pohranjene u administratorskoj konzoli koje imaju slabe, otkrivene ili ponovno korištene lozinke." }, "clickIconToMarkAppAsCritical": { "message": "Klikni ikonu zvjezdice za označavanje aplikacije kao kritične" @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Sljedeća naplata" }, + "nextChargeHeader": { + "message": "Sljedeća naplata" + }, + "plan": { + "message": "Paket" + }, "details": { "message": "Detalji" }, + "discount": { + "message": "popust" + }, "downloadLicense": { "message": "Preuzmi licencu" }, @@ -4440,7 +4467,31 @@ "message": "Ažuriraj preglednik" }, "generatingYourAccessIntelligence": { - "message": "Generating your Access Intelligence..." + "message": "Generiranje tvoje pristupne inteligencije..." + }, + "fetchingMemberData": { + "message": "Dohvaćanje podataka o članu…" + }, + "analyzingPasswordHealth": { + "message": "Analiziranje zdravlja lozinke…" + }, + "calculatingRiskScores": { + "message": "Izračun ocjene rizika…" + }, + "generatingReportData": { + "message": "Generiranje izvješća…" + }, + "savingReport": { + "message": "Spremanje izvještaja…" + }, + "compilingInsights": { + "message": "Sastavljanje uvida…" + }, + "loadingProgress": { + "message": "Učitavanje napretka" + }, + "thisMightTakeFewMinutes": { + "message": "Ovo može potrajati nekoliko minuta." }, "riskInsightsRunReport": { "message": "Pokreni izvješće" @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "Kako uključiti automatsku potvrdu korisnika" }, - "autoConfirmStep1": { - "message": "Otvori svoje Bitwarden proširenje." + "autoConfirmExtension1": { + "message": "Otvori svoje Bitwarden proširenje" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Odaberi", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Uključi.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Uključi", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "Uspješno otvoreno Bitwarden proširenje za preglednik. Sada možeš aktivirati postavku automatske potvrde korisnika." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Prijava uspješna!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Dodijeli pristup zbirki" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Započni besplatno probno razdoblje za Families" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Postavi metodu otključavanja za promjenu radnje nakon isteka vremenskog ograničenja trezora." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Pravila tvrtke primijenjena su na tvoje mogućnosti vremenskog isteka" + }, + "vaultTimeoutTooLarge": { + "message": "Istek vremenskog ograničenja tvojeg trezora premašuje ograničenje koja je postavila tvoja organizacija." + }, + "neverLockWarning": { + "message": "Sigurni želiš koristiti opciju „Nikad”? Postavljanjem opcije zaključavanja na „Nikad” ključ za šifriranje tvojeg trezora sprema se na tvoj uređaj. Pri korištenju ove opcije osiguraj da je tvoj uređaj pravilno zaštićen." + }, + "sessionTimeoutSettingsAction": { + "message": "Radnja kod isteka" + }, + "sessionTimeoutHeader": { + "message": "Istek sesije" + }, + "appearance": { + "message": "Izgled" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Istek vremenskog ograničenja premašuje ograničenje koje je postavila tvoja organizacija: najviše $HOURS$:$MINUTES$.", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index 0337a402522..d9bbd09b0f6 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Felülvizsgálatot igénylő kérelmek" }, + "newApplicationsCardTitle": { + "message": "Új alkalmazások felülvizsgálata" + }, "newApplicationsWithCount": { "message": "$COUNT$ új alkalmazás", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "Jelenleg nincs új falkalmazás felülvizsgálatra." }, + "organizationHasItemsSavedForApplications": { + "message": "A szervezet $COUNT$ alkalmazáshoz mentett elemeket tartalmaz.", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Tekintsük át az alkalmazásokat, hogy biztosítsuk a szervezet biztonsága szempontjából legkritikusabb elemeket." + }, + "reviewApplications": { + "message": "Alkalmazások áttekintése" + }, "prioritizeCriticalApplications": { "message": "Kritikus alkalmazások rangsorolása" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Következő terhelés" }, + "nextChargeHeader": { + "message": "Következő terhelés" + }, + "plan": { + "message": "Csomag" + }, "details": { "message": "Részletek" }, + "discount": { + "message": "kedvezmény" + }, "downloadLicense": { "message": "Licensz letöltése" }, @@ -4440,7 +4467,31 @@ "message": "Böngésző frissítése" }, "generatingYourAccessIntelligence": { - "message": "Generating your Access Intelligence..." + "message": "Hozzáférési intelligencia generálása..." + }, + "fetchingMemberData": { + "message": "Tagi adatok lekérése..." + }, + "analyzingPasswordHealth": { + "message": "A jelszó állapot elemzése..." + }, + "calculatingRiskScores": { + "message": "Kockázati pontszámok kiszámítása..." + }, + "generatingReportData": { + "message": "Jelentés adatok generálása..." + }, + "savingReport": { + "message": "Jelentés mentése..." + }, + "compilingInsights": { + "message": "Betekintések összeállítása..." + }, + "loadingProgress": { + "message": "Feldolgozás betöltése" + }, + "thisMightTakeFewMinutes": { + "message": "Ez eltarthat pár percig." }, "riskInsightsRunReport": { "message": "Jelentés futtatása" @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "Hogyan kapcsolható be az automatikus felhasználó megerősítés" }, - "autoConfirmStep1": { + "autoConfirmExtension1": { "message": "Nyissuk meg a Bitwarden bővítményt." }, - "autoConfirmStep2a": { - "message": "Kijelölés", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension2": { + "message": "Kiválasztás", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Bekapcsolás.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Bekapcsolás", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "Sikeresen megnyitásra került a Bitwarden böngésző bővítmény. Most már aktiválhatjuk az automatikus felhasználó megerősítés beállítást." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Megtörtént a bejelentkezés." }, - "beta": { - "message": "Béta" - }, "assignCollectionAccess": { "message": "Gyűjtemény elérés hozzárendelése" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Az ingyenes Családok próbaverzió elindítása" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Állítsunk be egy feloldási módot a széf időkifutási műveletének módosításához." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "A vállalkozáspolitikai követelményeket alkalmazásra kerültek az időkifutási opciókra." + }, + "vaultTimeoutTooLarge": { + "message": "A széf időkorlátja túllépi a szervezet által beállított korlátozást." + }, + "neverLockWarning": { + "message": "Biztosan szeretnénk használni a \"Soha\" opciót? A zárolási opciók \"Soha\" értékre állítása a széf titkosítási kulcsát az eszközön tárolja. Ennek az opciónak a használatakor célszerű az eszköz megfelelő védettségét biztosítani." + }, + "sessionTimeoutSettingsAction": { + "message": "Időkifutási művelet" + }, + "sessionTimeoutHeader": { + "message": "Munkamenet időkifutás" + }, + "appearance": { + "message": "Megjelenés" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Az időkifutás meghaladja a szervezet által beállított korlátozást: $HOURS$ óra és $MINUTES$ perc maximum.", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "Nincsenek veszélyben levő kritikus alkalmazások." + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Biztos folytatni szeretnénk?" } } diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index 0cab95e4de5..ad9d2405adb 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Aplikasi yang perlu ditinjau" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ aplikasi baru", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritaskan aplikasi penting" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Tagihan Berikutnya" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Detail" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Unduh Lisensi" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Jalankan laporan" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { - "message": "Pilih", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension2": { + "message": "Select", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Aktifkan.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "Ekstensi peramban Bitwarden berhasil dibuka. Anda sekarang dapat mengaktifkan pengaturan konfirmasi pengguna otomatis." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index 8a9eac612b4..fa3146996d7 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applicazioni in attesa di revisione" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ nuove applicazioni", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Priorità alle applicazioni critiche" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Prossimo addebito" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Dettagli" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Scarica licenza" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Avvia report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Accesso effettuato!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assegna accesso alla raccolta" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index 6d56564c0c2..a70b821a1fc 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "次回の請求" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "詳細" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "ライセンスのダウンロード" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "ログインしました!" }, - "beta": { - "message": "ベータ" - }, "assignCollectionAccess": { "message": "コレクションへのアクセスを割り当て" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index 43e46ef6d6b..973dcc2951b 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index e7051dee661..47df4826851 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index b18ca2a2cc4..17aaa51f261 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "ಮುಂದಿನ ಶುಲ್ಕ" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "ವಿವರಗಳು" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "ಪರವಾನಗಿ ಡೌನ್‌ಲೋಡ್ ಮಾಡಿ" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index dbc9b219d05..f831e76b1eb 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "다음 지불" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "세부사항" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "라이선스 다운로드" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index d85ffcb3784..b35e704eb21 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Lietotnes, kuras ir nepieciešams izskatīt" }, + "newApplicationsCardTitle": { + "message": "Pārskatīt jaunās lietotnes" + }, "newApplicationsWithCount": { "message": "$COUNT$ jaunas lietotnes", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "Pašreiz nav jaunu izskatāmu lietotņu" }, + "organizationHasItemsSavedForApplications": { + "message": "Tavā apvienībā ir saglabāti vienumi $COUNT$ lietotnēm", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Pārskati lietotnes, lai pastiprinātu drošību vienumiem, kuri ir visbūtiskākie apvienības drošībai" + }, + "reviewApplications": { + "message": "Pārskatīt lietotnes" + }, "prioritizeCriticalApplications": { "message": "Paaugstināt būtisko lietotņu svarīgumu" }, @@ -377,10 +395,10 @@ "message": "Jāatlasa, kuras lietontes apvienībai ir visbūtiskākās, tad jāpiešķir drošības uzdevumi dalībniekiem risku novēršanai." }, "reviewNewApplications": { - "message": "Review new applications" + "message": "Pārskatīt jaunās lietotnes" }, "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "message": "Mēs izcēlām riskam pakļautos vienumus jaunām lietotnēm, kas tiek glabātas pārvaldības konsolē un kurās ir vājas, atklātas vai atkārtoti izmantotas paroles." }, "clickIconToMarkAppAsCritical": { "message": "Jāklikšķina uz zvaigznes, lai atzīmētu lietotni kā būtisku" @@ -1218,7 +1236,7 @@ "message": "Pielikums izdzēsts" }, "deleteAttachmentConfirmation": { - "message": "Vai tiešām vēlaties dzēst šo pielikumu?" + "message": "Vai tiešām izdzēst šo pielikumu?" }, "attachmentSaved": { "message": "Pielikums tika saglabāts." @@ -1291,7 +1309,7 @@ "message": "Vienumi pārvietoti" }, "overwritePasswordConfirmation": { - "message": "Vai tiešām vēlaties pārrakstīt pašreizējo paroli?" + "message": "Vai tiešām pārrakstīt pašreizējo paroli?" }, "editedFolder": { "message": "Mape labota" @@ -1300,7 +1318,7 @@ "message": "Mape pievienota" }, "deleteFolderConfirmation": { - "message": "Vai tiešām vēlaties izdzēst šo mapi?" + "message": "Vai tiešām izdzēst šo mapi?" }, "deletedFolder": { "message": "Mape izdzēsta" @@ -1910,7 +1928,7 @@ } }, "deleteSelectedConfirmation": { - "message": "Vai tiešām vēlaties turpināt?" + "message": "Vai tiešām turpināt?" }, "moveSelectedItemsDesc": { "message": "Jāizvelas mape, kurā pievienot $COUNT$ atlasīto(s) vienumu(s).", @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Nākamais maksājums" }, + "nextChargeHeader": { + "message": "Nākamais maksājums" + }, + "plan": { + "message": "Plāns" + }, "details": { "message": "Izklāsts" }, + "discount": { + "message": "atlaide" + }, "downloadLicense": { "message": "Lejupielādēt licenci" }, @@ -3658,10 +3685,10 @@ "message": "Labot kopu" }, "deleteGroupConfirmation": { - "message": "Vai tiešām vēlaties dzēst šo kopu?" + "message": "Vai tiešām izdzēst šo kopu?" }, "deleteMultipleGroupsConfirmation": { - "message": "Vai tiešām vēlaties dzēst šādu $QUANTITY$ grupu(-as)?", + "message": "Vai tiešām izdzēst šo (šīs) $QUANTITY$ kopu(as)?", "placeholders": { "quantity": { "content": "$1", @@ -4440,7 +4467,31 @@ "message": "Atjaunināt pārlūku" }, "generatingYourAccessIntelligence": { - "message": "Generating your Access Intelligence..." + "message": "Izveido informāciju par Tavu piekļuvi…" + }, + "fetchingMemberData": { + "message": "Iegūst dalībnieku datus…" + }, + "analyzingPasswordHealth": { + "message": "Izvērtē paroļu veselību…" + }, + "calculatingRiskScores": { + "message": "Aprēķina risku novērtējumu…" + }, + "generatingReportData": { + "message": "Izveido atskaites datus…" + }, + "savingReport": { + "message": "Saglabā atskaiti…" + }, + "compilingInsights": { + "message": "Apkopo ieskatus…" + }, + "loadingProgress": { + "message": "Ielādē virzību" + }, + "thisMightTakeFewMinutes": { + "message": "Tas var aizņemt dažas minūtes." }, "riskInsightsRunReport": { "message": "Izveidot atskaiti" @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "Kā ieslēgt automātisku lietotōtāju apstiprināšanu" }, - "autoConfirmStep1": { - "message": "Jāatver Bitwarden paplašinājums." + "autoConfirmExtension1": { + "message": "Jāatver Bitwarden paplašinājums" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Jāatlasa", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": "“Ieslēgt”.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " “Ieslēgt”", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "Bitwarden pārlūka paplašīnājums atvērts sekmīgi. Tagad var ieslēgt automātisko lietotāju apstiprināšanas iestatījumu." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Pieteicies." }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Piešķirt krājumu piekļuvi" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Uzsākt ģimenes plāna bezmaksas izmēģinājumu" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Jāuzstāda atslēgšanas veids, lai mainītu glabātavas noildzes darbību." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Noildzes iespējām tika piemērotas uzņēmējdarbības pamatnostādņu prasības" + }, + "vaultTimeoutTooLarge": { + "message": "Glabātavas noildze pārsniedz apvienības uzstādītos ierobežojumus." + }, + "neverLockWarning": { + "message": "Vai tiešām izmantot uzstādījumu \"Nekad\"? Uzstādot aizslēgšanas iespēju uz \"Nekad\", šifrēšanas atslēga tiek glabāta ierīcē. Ja šī iespēja tiek izmantota, jāpārliecinās, ka ierīce tiek pienācīgi aizsargāta." + }, + "sessionTimeoutSettingsAction": { + "message": "Noildzes darbība" + }, + "sessionTimeoutHeader": { + "message": "Sesijas noildze" + }, + "appearance": { + "message": "Izskats" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Noildze pārsniedz apvienības iestatīto ierobežojumu: ne vairāk kā $HOURS$ stunda(s) un $MINUTES$ minūte(s)", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "Neviena būtiska lietotne nav atlasīta" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Vai tiešām turpināt?" } } diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index 1d892bdaa60..624121be5d9 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next Charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "ലൈസൻസ് ഡൌൺലോഡ് ചെയ്യുക" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index 5c65192e0b4..612ff8b8765 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index e7051dee661..47df4826851 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index c3f80105f40..b2fb413be8c 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Neste trekk" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Detaljer" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Last ned lisens" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Innlogget!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index 9da5012921d..8bd88ef4a57 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index 4b68dfe4be6..8ac8cf24ca5 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Aanvragen die herzien moeten worden" }, + "newApplicationsCardTitle": { + "message": "Nieuwe toepassingen beoordelen" + }, "newApplicationsWithCount": { "message": "$COUNT$ nieuwe applicaties", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Je organisatie heeft items opgeslagen voor $COUNT$ applicaties", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Beoordeel applicaties om de items die het meest belangrijk zijn voor de veiligheid van je organisatie te beveiligen" + }, + "reviewApplications": { + "message": "Applicaties beoordelen" + }, "prioritizeCriticalApplications": { "message": "Belangrijke applicaties prioriteren" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Volgende betaling" }, + "nextChargeHeader": { + "message": "Volgende betaling" + }, + "plan": { + "message": "Pakket" + }, "details": { "message": "Details" }, + "discount": { + "message": "korting" + }, "downloadLicense": { "message": "Licentie downloaden" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Je toegangsinlichtingen genereren..." }, + "fetchingMemberData": { + "message": "Ledengegevens ophalen..." + }, + "analyzingPasswordHealth": { + "message": "Wachtwoordgezondheid analyseren..." + }, + "calculatingRiskScores": { + "message": "Risicoscores berekenen..." + }, + "generatingReportData": { + "message": "Rapportgegevens genereren..." + }, + "savingReport": { + "message": "Rapport opslaan..." + }, + "compilingInsights": { + "message": "Inzichten compileren..." + }, + "loadingProgress": { + "message": "Voortgang laden" + }, + "thisMightTakeFewMinutes": { + "message": "Dit kan een paar minuten duren." + }, "riskInsightsRunReport": { "message": "Rapport uitvoeren" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "Automatische bevestiging van gebruikers inschakelen" }, - "autoConfirmStep1": { - "message": "De Bitwarden-extensie openen." + "autoConfirmExtension1": { + "message": "Bitwarden-extensie openen" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Selecteer", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Inschakelen.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Inschakelen", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "De Bitwarden-browserextensie is succesvol geopend. Je kunt nu de automatische bevestiging van de gebruiker activeren." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Ingelogd!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Toegang collectie toewijzen" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start gratis Families-proefperiode" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Stel een ontgrendelingsmethode in om je kluis time-out actie te wijzigen." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Bedrijfsbeleidseisen zijn toegepast op je time-out instellingen" + }, + "vaultTimeoutTooLarge": { + "message": "Je kluis time-out is hoger dan het maximum van jouw organisatie." + }, + "neverLockWarning": { + "message": "Weet je zeker dat je de optie \"Nooit\" wilt gebruiken? De vergrendelingsoptie \"Nooit\" bewaart de sleutel van je kluis op je apparaat. Als je deze optie gebruikt, moet je ervoor zorgen dat je je apparaat naar behoren beschermt." + }, + "sessionTimeoutSettingsAction": { + "message": "Time-out actie" + }, + "sessionTimeoutHeader": { + "message": "Sessietime-out" + }, + "appearance": { + "message": "Uiterlijk" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Time-out overschrijdt de beperking van je organisatie: $HOURS$ uur en $MINUTES$ minu(u) t(en) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "Geen belangrijke applicaties geselecteerd" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Weet je zeker dat je wilt doorgaan?" } } diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index 24845833d22..b6ec66bf695 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index e7051dee661..47df4826851 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index 4d450fa7543..9750a2a7df7 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Następna opłata" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Szczegóły" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Pobierz licencję" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Zalogowano!" }, - "beta": { - "message": "Wersja beta" - }, "assignCollectionAccess": { "message": "Przypisz dostęp do kolekcji" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index 7d4edcecf5f..4ef989dcd75 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Aplicativos que precisam de revisão" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ aplicativos novos", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Priorizar aplicativos críticos" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Próxima cobrança" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Detalhes" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Baixar licença" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Executar relatório" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Conectado!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Atribuir acesso à coleção" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index 09e346b494f..45b511a857f 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Aplicações que necessitam de análise" }, + "newApplicationsCardTitle": { + "message": "Rever novas aplicações" + }, "newApplicationsWithCount": { "message": "$COUNT$ novas aplicações", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "Não há novas aplicações para rever neste momento" }, + "organizationHasItemsSavedForApplications": { + "message": "A sua organização tem itens guardados para $COUNT$ aplicações", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Reveja as aplicações para proteger os itens mais críticos para a segurança da sua organização" + }, + "reviewApplications": { + "message": "Rever aplicações" + }, "prioritizeCriticalApplications": { "message": "Dê prioridade a aplicações críticas" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Próxima cobrança" }, + "nextChargeHeader": { + "message": "Próxima cobrança" + }, + "plan": { + "message": "Plano" + }, "details": { "message": "Detalhes" }, + "discount": { + "message": "desconto" + }, "downloadLicense": { "message": "Transferir licença" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "A gerar a sua Inteligência de Acesso..." }, + "fetchingMemberData": { + "message": "A obter dados dos membros..." + }, + "analyzingPasswordHealth": { + "message": "A analisar a segurança da palavra-passe..." + }, + "calculatingRiskScores": { + "message": "A calcular pontuações de risco..." + }, + "generatingReportData": { + "message": "A gerar dados do relatório..." + }, + "savingReport": { + "message": "A guardar relatório..." + }, + "compilingInsights": { + "message": "A compilar insights..." + }, + "loadingProgress": { + "message": "A carregar progresso" + }, + "thisMightTakeFewMinutes": { + "message": "Isto pode demorar alguns minutos." + }, "riskInsightsRunReport": { "message": "Executar relatório" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "Como ativar a confirmação automática do utilizador" }, - "autoConfirmStep1": { - "message": "Abra a sua extensão Bitwarden." + "autoConfirmExtension1": { + "message": "Abra a sua extensão Bitwarden" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Selecione", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Ativar.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Ativar", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "A extensão do navegador Bitwarden foi aberta com sucesso. Agora pode ativar a configuração de confirmação automática do utilizador." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Sessão iniciada!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Atribuir acesso à coleção" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Comece o teste gratuito do plano Familiar" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Configure um método de desbloqueio para alterar a ação de tempo limite do seu cofre." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Os requisitos da política empresarial foram aplicados às suas opções de tempo limite" + }, + "vaultTimeoutTooLarge": { + "message": "O tempo limite do seu cofre excede as restrições definidas pela sua organização." + }, + "neverLockWarning": { + "message": "Tem a certeza de que deseja utilizar a opção \"Nunca\"? Ao definir as opções de bloqueio para \"Nunca\" armazena a chave de encriptação do seu cofre no seu dispositivo. Se utilizar esta opção deve assegurar-se de que mantém o seu dispositivo devidamente protegido." + }, + "sessionTimeoutSettingsAction": { + "message": "Ação de tempo limite" + }, + "sessionTimeoutHeader": { + "message": "Tempo limite da sessão" + }, + "appearance": { + "message": "Aspeto" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "O tempo limite excede a restrição definida pela sua organização: $HOURS$ hora(s) e $MINUTES$ minuto(s) no máximo", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "Não foram selecionadas aplicações críticas" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Tem a certeza de que deseja continuar?" } } diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index 3a3e8846c66..e683e0a5f10 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Următoarea plată" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Detalii" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Descărcare licență" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index 31a1729cef8..9b5a9399491 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Приложения, требующие проверки" }, + "newApplicationsCardTitle": { + "message": "Обзор новых приложений" + }, "newApplicationsWithCount": { "message": "$COUNT$ новых приложений", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "На данный момент новых приложений для рассмотрения нет" }, + "organizationHasItemsSavedForApplications": { + "message": "В вашей организации есть элементы, сохраненные для $COUNT$ приложений", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Просмотрите приложения, чтобы защитить элементы, наиболее критичные для безопасности вашей организации" + }, + "reviewApplications": { + "message": "Обзор приложений" + }, "prioritizeCriticalApplications": { "message": "Приоритет критичных приложений" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Следующий платеж" }, + "nextChargeHeader": { + "message": "Следующий платеж" + }, + "plan": { + "message": "План" + }, "details": { "message": "Подробности" }, + "discount": { + "message": "скидка" + }, "downloadLicense": { "message": "Загрузить лицензию" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Получение данных о пользователях..." + }, + "analyzingPasswordHealth": { + "message": "Анализ здоровья пароля..." + }, + "calculatingRiskScores": { + "message": "Расчет показателей риска..." + }, + "generatingReportData": { + "message": "Генерация данных отчета..." + }, + "savingReport": { + "message": "Сохранение отчета..." + }, + "compilingInsights": { + "message": "Компиляция информации..." + }, + "loadingProgress": { + "message": "Прогресс загрузки" + }, + "thisMightTakeFewMinutes": { + "message": "Это может занять несколько минут." + }, "riskInsightsRunReport": { "message": "Запустить отчет" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "Как включить автоматическое подтверждение пользователя" }, - "autoConfirmStep1": { - "message": "Откройте свое расширение Bitwarden." + "autoConfirmExtension1": { + "message": "Откройте свое расширение Bitwarden" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Выбрать", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Включить.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Включить", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "Успешно открыто расширение браузера Bitwarden. Теперь вы можете активировать автоматическое подтверждение пользователя." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Выполнен вход!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Назначить доступ к коллекциям" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Начать пробную версию Families" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Настройте способ разблокировки для изменения действия по тайм-ауту хранилища." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "К настройкам тайм-аута были применены требования корпоративной политики" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "Критичные приложения не выбраны" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Вы действительно хотите продолжить?" } } diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index 8d40ec3d93c..0fdc35fb8dc 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index 9dba4eb22b2..fdcaa412db9 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -21,13 +21,13 @@ "message": "Ohrozenie hesla" }, "noEditPermissions": { - "message": "You don't have permission to edit this item" + "message": "Na úpravu tejto položky nemáte oprávnenie" }, "reviewAtRiskPasswords": { "message": "Skontrolujte ohrozené heslá (slabé, odhalené, alebo opätovne použité) naprieč aplikáciami. Vyberte najkritickejšie aplikácie a priorizujte vaším používateľom bezpečnostné opatrenia ohľadom ohrozených hesiel." }, "reviewAtRiskLoginsPrompt": { - "message": "Review at-risk logins" + "message": "Prehľad ohrozených prihlasovacích mien" }, "dataLastUpdated": { "message": "Posledná aktualizácia dát: $DATE$", @@ -242,7 +242,7 @@ "message": "Aplikácie označené ako kritické" }, "criticalApplicationsMarkedSuccess": { - "message": "$COUNT$ applications marked as critical", + "message": "$COUNT$ aplikácií označených ako kritické", "placeholders": { "count": { "content": "$1", @@ -275,10 +275,10 @@ "message": "Členovia s prístupom k ohrozeným položkám kritických aplikácii" }, "membersWithAtRiskPasswords": { - "message": "Members with at-risk passwords" + "message": "Členovia s ohrozenými heslami" }, "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "message": "Členovia dostanú notifikáciu aby vyriešili problémy s ohrozeným heslom prostredníctvom rozšírenia pre prehliadače." }, "membersAtRiskCount": { "message": "$COUNT$ ohrozených členov", @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Aplikácie vyžadujú kontrolu" }, + "newApplicationsCardTitle": { + "message": "Skontrolovať nové aplikácie" + }, "newApplicationsWithCount": { "message": "$COUNT$ nových aplikácií", "placeholders": { @@ -370,11 +373,26 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Uprednostniť kritické aplikácie" }, "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "message": "Vyberte ktoré aplikácie sú pre vašu organizáciu najkritickejšie, potom prideľte členom bezpečnostné úlohy pre vyriešenie ohrozených hesiel." }, "reviewNewApplications": { "message": "Review new applications" @@ -383,7 +401,7 @@ "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." }, "clickIconToMarkAppAsCritical": { - "message": "Click the star icon to mark an app as critical" + "message": "Aplikáciu označíte za kritickú kliknutím na ikonu hviezdičky" }, "markAsCriticalPlaceholder": { "message": "Funkcia označovania za kritické bude implementovaná v budúcej aktualizácii" @@ -863,7 +881,7 @@ "message": "Obľúbené" }, "taskSummary": { - "message": "Task summary" + "message": "Zhrnutie úloh" }, "types": { "message": "Typy" @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Ďalšia platba" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Podrobnosti" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Stiahnuť licenciu" }, @@ -4440,7 +4467,31 @@ "message": "Aktualizovať prehliadač" }, "generatingYourAccessIntelligence": { - "message": "Generating your Access Intelligence..." + "message": "Generuje sa prehľad o prístupe..." + }, + "fetchingMemberData": { + "message": "Sťahujú sa dáta o členoch..." + }, + "analyzingPasswordHealth": { + "message": "Analyzuje sa odolnosť hesiel..." + }, + "calculatingRiskScores": { + "message": "Vypočítava sa úroveň ohrozenia..." + }, + "generatingReportData": { + "message": "Generujú sa dáta reportu..." + }, + "savingReport": { + "message": "Ukladá sa report..." + }, + "compilingInsights": { + "message": "Kompiluje sa prehľad..." + }, + "loadingProgress": { + "message": "Priebeh načítania" + }, + "thisMightTakeFewMinutes": { + "message": "Môže to trvať niekoľko minút." }, "riskInsightsRunReport": { "message": "Generovať report" @@ -5460,7 +5511,7 @@ "description": "Displayed under the limit views field on Send" }, "limitSendViewsCount": { - "message": "Zostáva $ACCESSCOUNT$ zobrazení", + "message": "Zostávajúce zobrazenia: $ACCESSCOUNT$", "description": "Displayed under the limit views field on Send", "placeholders": { "accessCount": { @@ -5503,11 +5554,11 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deletedSend": { - "message": "Send zmazaný", + "message": "Send bol odstránený", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSend": { - "message": "Zmazať Send", + "message": "Odstrániť Send", "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "deleteSendPermanentConfirmation": { @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "Ako zapnúť automatické potvrdzovanie používateľov" }, - "autoConfirmStep1": { - "message": "Otvoriť rozšírenie Bitwarden." + "autoConfirmExtension1": { + "message": "Otvoriť rozšírenie Bitwarden" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Vybrať", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Zapnúť.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Zapnúť", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "Rozšírenie Bitwarden pre prehliadače úspešne otvorene. Teraz môžete v nastaveniach zapnúť automatické potvrdzovanie používateľov." @@ -5836,7 +5887,7 @@ "message": "Vyžaduje sa pravidlo jednej organizácie. " }, "autoConfirmSingleOrgRequiredDesc": { - "message": "All members must only belong to this organization to activate this automation." + "message": "Pre zapnutie tejto automatizácie musia všetci členovia patriť len do tejto organizácie." }, "autoConfirmSingleOrgExemption": { "message": "Pravidlo jednej organizácie sa rozšíri na všetky roly. " @@ -6569,10 +6620,10 @@ "message": "Vaše hlavné heslo nespĺňa jednu alebo viacero podmienok vašej organizácie. Ak chcete získať prístup k trezoru, musíte teraz aktualizovať svoje hlavné heslo. Pokračovaním sa odhlásite z aktuálnej relácie a budete sa musieť znova prihlásiť. Aktívne relácie na iných zariadeniach môžu zostať aktívne až jednu hodinu." }, "automaticAppLoginWithSSO": { - "message": "Automatic login with SSO" + "message": "Automatické prihlásenie prostredníctvom SSO" }, "automaticAppLoginWithSSODesc": { - "message": "Extend SSO security and convenience to unmanaged apps. When users launch an app from your identity provider, their login details are automatically filled and submitted, creating a one-click, secure flow from the identity provider to the app." + "message": "Rozšírte bezpečnosť a pohodlie jednotného prihlásenia (SSO) na nespravované aplikácie. Keď používatelia spustia aplikáciu od vášho poskytovateľa identít, ich prihlasovacie údaje sa automaticky vyplnia a odošlú, čím sa umožní bezpečná cesta jedným kliknutím od poskytovateľa identít do aplikácie." }, "automaticAppLoginIdpHostLabel": { "message": "Identity provider host" @@ -7348,7 +7399,7 @@ "message": "Prístup zamietnutý. Nemáte oprávnenie na zobrazenie tejto stránky." }, "noPageAccess": { - "message": "You do not have access to this page" + "message": "Na túto stránku nemáte prístup" }, "masterPassword": { "message": "Hlavné heslo" @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Prihlásený!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Udeliť prístup k zbierke" }, @@ -9809,7 +9857,7 @@ "message": "Priradiť úlohy" }, "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "message": "Pre riadené riešenie problémov prideľte úlohy členom" }, "assignToCollections": { "message": "Prideliť k zbierkam" @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Začať bezplatnú skúšku pre predplatné Rodiny" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index 771f16e53cf..709baa2e36c 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Podrobnosti" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Prenesi licenco" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index 753ea33c2c1..876669827c8 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Sledeće Plaćanje" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Detalji" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/sr_CY/messages.json b/apps/web/src/locales/sr_CY/messages.json index e17983809ae..dd41032a031 100644 --- a/apps/web/src/locales/sr_CY/messages.json +++ b/apps/web/src/locales/sr_CY/messages.json @@ -21,13 +21,13 @@ "message": "Ризик од лозинке" }, "noEditPermissions": { - "message": "You don't have permission to edit this item" + "message": "Немате дозволу да уређујете ову ставку" }, "reviewAtRiskPasswords": { "message": "Прегледај ризичне лозинке (слабе, изложене или поново коришћене) у апликацијама. Изабери своје најкритичније апликације да би дао приоритет безбедносним радњама како би твоји корисници адресирали ризичне лозинке." }, "reviewAtRiskLoginsPrompt": { - "message": "Review at-risk logins" + "message": "Прегледајте ризичне пријаве" }, "dataLastUpdated": { "message": "Подаци су последњи пут ажурирани: $DATE$", @@ -179,7 +179,7 @@ } }, "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", + "message": "Није пронађена ниједна апликација за $ORG NAME$", "placeholders": { "org name": { "content": "$1", @@ -188,31 +188,31 @@ } }, "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "message": "Увезите податке за пријаву своје организације да бисте почели да надгледате безбедносне ризике акредитива. Када увезете, добијате:" }, "benefit1Title": { - "message": "Prioritize risks" + "message": "Одредите приоритете ризика" }, "benefit1Description": { - "message": "Focus on applications that matter the most" + "message": "Фокусирајте се на апликације које су најважније" }, "benefit2Title": { - "message": "Guide remediation" + "message": "Водич за санацију" }, "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "message": "Доделите вођене задатке члановима изложеним ризику да ротирају акредитиве у ризику" }, "benefit3Title": { - "message": "Monitor progress" + "message": "Праћење напретка" }, "benefit3Description": { - "message": "Track changes over time to show security improvements" + "message": "Пратите промене током времена да бисте показали безбедносна побољшања" }, "noReportRunTitle": { - "message": "Run your first report to see applications" + "message": "Покрените свој први извештај да бисте видели апликације" }, "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:" + "message": "Направите извештај о увиду у ризик да бисте анализирали апликације ваше организације и идентификовали ризичне лозинке на које треба обратити пажњу. Покретање вашег првог извештаја ће:" }, "noCriticalApplicationsTitle": { "message": "Нисте означили ниједну апликацију као критичну" @@ -242,7 +242,7 @@ "message": "Апликације означене као критичне" }, "criticalApplicationsMarkedSuccess": { - "message": "$COUNT$ applications marked as critical", + "message": "Апликације означене као критичне: $COUNT$", "placeholders": { "count": { "content": "$1", @@ -275,10 +275,10 @@ "message": "Чланови са приступом за угрожене ставке критичних апликација" }, "membersWithAtRiskPasswords": { - "message": "Members with at-risk passwords" + "message": "Чланови са ризичним лозинкама" }, "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "message": "Чланови ће добити обавештење за решавање ризичних пријава путем екстензије претраживача." }, "membersAtRiskCount": { "message": "Угрожени чланови: $COUNT$", @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Апликације које треба прегледати" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -365,40 +368,55 @@ "message": "Прегледај сада" }, "allCaughtUp": { - "message": "All caught up!" + "message": "Сви ухваћени!" }, "noNewApplicationsToReviewAtThisTime": { - "message": "No new applications to review at this time" + "message": "Тренутно нема нових апликација за преглед" + }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" }, "prioritizeCriticalApplications": { "message": "Дајте приоритет критичним апликацијама" }, "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "message": "Изаберите које су апликације најкритичније за вашу организацију, а затим доделите безбедносне задатке члановима да бисте решили ризике." }, "reviewNewApplications": { - "message": "Review new applications" + "message": "Прегледајте нове апликације" }, "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "message": "Истакли смо ризичне ставке за нове апликације ускладиштене у Админ конзоли које имају слабе, откривене или поново коришћене лозинке." }, "clickIconToMarkAppAsCritical": { - "message": "Click the star icon to mark an app as critical" + "message": "Кликните на икону звездице да бисте означили апликацију као критичну" }, "markAsCriticalPlaceholder": { "message": "Означи као критичну функционалност ће бити имплементирана у будућем ажурирању" }, "applicationReviewSaved": { - "message": "Application review saved" + "message": "Преглед апликације је сачуван" }, "newApplicationsReviewed": { - "message": "New applications reviewed" + "message": "Нове апликације су прегледане" }, "errorSavingReviewStatus": { - "message": "Error saving review status" + "message": "Грешка при чувању статуса прегледа" }, "pleaseTryAgain": { - "message": "Please try again" + "message": "Покушајте поново" }, "unmarkAsCritical": { "message": "Уклони као критично" @@ -863,7 +881,7 @@ "message": "Омиљени" }, "taskSummary": { - "message": "Task summary" + "message": "Резиме задатка" }, "types": { "message": "Врсте" @@ -1390,7 +1408,7 @@ "message": "Употребити једнократну пријаву" }, "yourOrganizationRequiresSingleSignOn": { - "message": "Your organization requires single sign-on." + "message": "Ваша организација захтева јединствену пријаву." }, "welcomeBack": { "message": "Добродошли назад" @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Следеће пуњење" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Детаљи" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Преузимање лиценце" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Преузимање података о члановима..." + }, + "analyzingPasswordHealth": { + "message": "Анализа здравља лозинки..." + }, + "calculatingRiskScores": { + "message": "Израчунавање резултата ризика..." + }, + "generatingReportData": { + "message": "Генерисање података извештаја..." + }, + "savingReport": { + "message": "Чување извештаја..." + }, + "compilingInsights": { + "message": "Састављање увида..." + }, + "loadingProgress": { + "message": "Учитавање напретка" + }, + "thisMightTakeFewMinutes": { + "message": "Ово може потрајати неколико минута." + }, "riskInsightsRunReport": { "message": "Покрените извештај" }, @@ -5792,63 +5843,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." + "autoConfirmExtension1": { + "message": "Отворити Bitwarden екстензију" }, - "autoConfirmStep2a": { - "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension2": { + "message": "Изабери", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "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": "Смернице за јединствену организацију су потребне. " }, "autoConfirmSingleOrgRequiredDesc": { - "message": "All members must only belong to this organization to activate this automation." + "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": "Лично власништво" @@ -5903,16 +5954,16 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "uriMatchDetectionPolicy": { - "message": "Default URI match detection" + "message": "Стандардно налажење УРЛ" }, "uriMatchDetectionPolicyDesc": { - "message": "Determine when logins are suggested for autofill. Admins and owners are exempt from this policy." + "message": "Одредите када се предлажу пријаве за ауто-попуњавање. Администратори и власници су изузети од ове политике." }, "uriMatchDetectionOptionsLabel": { - "message": "Default URI match detection" + "message": "Стандардно налажење УРЛ" }, "invalidUriMatchDefaultPolicySetting": { - "message": "Please select a valid URI match detection option.", + "message": "Изаберите важећу опцију откривања налажења УРЛ-а.", "description": "Error message displayed when a user attempts to save URI match detection policy settings with an invalid selection." }, "modifiedPolicyId": { @@ -6569,7 +6620,7 @@ "message": "Ваша главна лозинка не испуњава једну или више смерница ваше организације. Да бисте приступили сефу, морате одмах да ажурирате главну лозинку. Ако наставите, одјавићете се са ваше тренутне сесије, што захтева да се поново пријавите. Активне сесије на другим уређајима могу да остану активне до један сат." }, "automaticAppLoginWithSSO": { - "message": "Automatic login with SSO" + "message": "Аутоматско пријављивање са ССО" }, "automaticAppLoginWithSSODesc": { "message": "Extend SSO security and convenience to unmanaged apps. When users launch an app from your identity provider, their login details are automatically filled and submitted, creating a one-click, secure flow from the identity provider to the app." @@ -6584,31 +6635,31 @@ "message": "Ваша организација је ажурирала опције дешифровања. Поставите главну лозинку за приступ вашем сефу." }, "sessionTimeoutPolicyTitle": { - "message": "Session timeout" + "message": "Истек сесије" }, "sessionTimeoutPolicyDescription": { - "message": "Set a maximum session timeout for all members except owners." + "message": "Подесите максимално временско ограничење сесије за све чланове осим власника." }, "maximumAllowedTimeout": { - "message": "Maximum allowed timeout" + "message": "Максимално временско ограничење" }, "maximumAllowedTimeoutRequired": { - "message": "Maximum allowed timeout is required." + "message": "Максимално временско ограничење је обавезно." }, "sessionTimeoutPolicyInvalidTime": { - "message": "Time is invalid. Change at least one value." + "message": "Време је неважеће. Промените бар једну вредност." }, "sessionTimeoutAction": { - "message": "Session timeout action" + "message": "Акција на истек сесије" }, "immediately": { - "message": "Immediately" + "message": "Одмах" }, "onSystemLock": { - "message": "On system lock" + "message": "На закључавање система" }, "onAppRestart": { - "message": "On app restart" + "message": "На поновно покретање" }, "hours": { "message": "Сати/а" @@ -6617,19 +6668,19 @@ "message": "Минути/а" }, "sessionTimeoutConfirmationNeverTitle": { - "message": "Are you certain you want to allow a maximum timeout of \"Never\" for all members?" + "message": "Да ли сте сигурни да желите да дозволите максимално временско ограничење „Никад“ за све чланове?" }, "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." + "message": "Ова опција ће сачувати кључеве за шифровање ваших чланова на њиховим уређајима. Ако одаберете ову опцију, уверите се да су њихови уређаји адекватно заштићени." }, "learnMoreAboutDeviceProtection": { - "message": "Learn more about device protection" + "message": "Сазнајте више о заштити уређаја" }, "sessionTimeoutConfirmationOnSystemLockTitle": { - "message": "\"System lock\" will only apply to the browser and desktop app" + "message": "„Закључавање система“ ће се примењивати само на прегледач и десктоп апликацију" }, "sessionTimeoutConfirmationOnSystemLockDescription": { - "message": "The mobile and web app will use \"on app restart\" as their maximum allowed timeout, since the option is not supported." + "message": "Мобилна и веб апликација ће користити „при поновном покретању апликације“ као максимално дозвољено временско ограничење, пошто опција није подржана." }, "vaultTimeoutPolicyInEffect": { "message": "Полиса ваше организације утиче на време истека сефа. Максимално дозвољено време истека је $HOURS$ сат(и) и $MINUTES$ minut(а)", @@ -7173,7 +7224,7 @@ "message": "Морате додати или основни УРЛ сервера или бар једно прилагођено окружење." }, "selfHostedEnvMustUseHttps": { - "message": "URLs must use HTTPS." + "message": "Везе морају да користе HTTPS." }, "apiUrl": { "message": "УРЛ АПИ Сервера" @@ -7348,7 +7399,7 @@ "message": "Одбијен приступ. Немате дозволу да видите ову страницу." }, "noPageAccess": { - "message": "You do not have access to this page" + "message": "Немате приступ овој страници" }, "masterPassword": { "message": "Главна Лозинка" @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Пријављено!" }, - "beta": { - "message": "Бета" - }, "assignCollectionAccess": { "message": "Додели приступ збирке" }, @@ -9809,7 +9857,7 @@ "message": "Додели задатке" }, "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "message": "Доделите задатке члановима за вођено решавање" }, "assignToCollections": { "message": "Додели колекцијама" @@ -11199,13 +11247,13 @@ "message": "Домен захтеван" }, "itemAddedToFavorites": { - "message": "Item added to favorites" + "message": "Ставка је додата у фаворите" }, "itemRemovedFromFavorites": { - "message": "Item removed from favorites" + "message": "Ставка је уклоњена из фаворите" }, "copyNote": { - "message": "Copy note" + "message": "Копирај белешку" }, "organizationNameMaxLength": { "message": "Име организације не може прећи 50 знакова." @@ -12041,39 +12089,79 @@ "message": "View business plans" }, "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 / Postal code" + "message": "ZIP/Поштански број" }, "cardNumberLabel": { - "message": "Card number" + "message": "Број картице" }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index d8a4d43946c..ca0547d7d95 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applikationer som behöver granskning" }, + "newApplicationsCardTitle": { + "message": "Granska nya applikationer" + }, "newApplicationsWithCount": { "message": "$COUNT$ nya applikationer", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "Inga nya applikationer att granska just nu" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritera kritiska applikationer" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Nästa debitering" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Detaljer" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Hämta licens" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Hämtar medlemsdata..." + }, + "analyzingPasswordHealth": { + "message": "Analyserar lösenordshälsa..." + }, + "calculatingRiskScores": { + "message": "Beräknar riskpoäng..." + }, + "generatingReportData": { + "message": "Genererar rapportdata..." + }, + "savingReport": { + "message": "Sparar rapport..." + }, + "compilingInsights": { + "message": "Sammanställer insikter..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "Detta kan ta några minuter." + }, "riskInsightsRunReport": { "message": "Kör rapport" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "Så här aktiverar du automatisk användarbekräftelse" }, - "autoConfirmStep1": { - "message": "Öppna ditt Bitwarden-tillägg." + "autoConfirmExtension1": { + "message": "Öppna ditt Bitwarden-tillägg" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Välj", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Slå på.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Inloggad!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Tilldela samlingsåtkomst" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Starta gratis testperiod för Families" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Konfigurera en upplåsningsmetod för att ändra tidsgränsåtgärden för valvet." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Företagets policykrav har tillämpats på dina tidsgränsalternativ" + }, + "vaultTimeoutTooLarge": { + "message": "Ditt valvs tidsgräns överskrider de begränsningar som fastställts av din organisation." + }, + "neverLockWarning": { + "message": "Är du säker på att du vill använda alternativet ”Aldrig”? Att ställa in låsningsalternativet till ”Aldrig” lagrar valvets krypteringsnyckel på din enhet. Om du använder det här alternativet bör du se till att du håller enheten ordentligt skyddad." + }, + "sessionTimeoutSettingsAction": { + "message": "Tidsgränsåtgärd" + }, + "sessionTimeoutHeader": { + "message": "Sessionstidsgräns" + }, + "appearance": { + "message": "Utseende" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Tidsgränsen överskrider den begränsning som din organisation har ställt in: $HOURS$ timmar och $MINUTES$ minut(er) maximalt", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/ta/messages.json b/apps/web/src/locales/ta/messages.json index a5acacede66..bea4f3365bc 100644 --- a/apps/web/src/locales/ta/messages.json +++ b/apps/web/src/locales/ta/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "அடுத்த கட்டணம்" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "விவரங்கள்" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "லைசன்ஸ் கோப்பைப் பதிவிறக்கவும்" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "உள்நுழைந்துவிட்டீர்கள்!" }, - "beta": { - "message": "பீட்டா" - }, "assignCollectionAccess": { "message": "சேகரிப்பு அணுகலை ஒதுக்கவும்" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index e7051dee661..47df4826851 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index fd74c2d12a8..3e4797eb03f 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Next charge" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Details" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Download license" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Logged in!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Assign collection access" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index dc64a6a0d8c..bb52d98e1e4 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Sonraki ödeme" }, + "nextChargeHeader": { + "message": "Sonraki ödeme" + }, + "plan": { + "message": "Paket" + }, "details": { "message": "Ayrıntılar" }, + "discount": { + "message": "indirim" + }, "downloadLicense": { "message": "Lisansı indir" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Bitwarden uzantınızı açın" }, - "autoConfirmStep2a": { - "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension2": { + "message": "Etkinleştir", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " seçeneğini seçin", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Giriş yapıldı!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "Koleksiyon erişimi ata" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Kasa zaman aşımı eyleminizi değiştirmek için kilit açma yönteminizi ayarlayın." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Zaman aşımı ayarlarınıza kurumsal ilke gereksinimleri uygulandı" + }, + "vaultTimeoutTooLarge": { + "message": "Kasa zaman aşımınız, kuruluşunuz tarafından belirlenen kısıtlamaları aşıyor." + }, + "neverLockWarning": { + "message": "\"Asla\" seçeneğini kullanmak istediğinizden emin misiniz? Kilit seçeneklerinizi \"Asla\" olarak ayarlarsanız kasanızın şifreleme anahtarı cihazınızda saklanacaktır. Bu seçeneği kullanırsanız cihazınızı çok iyi korumalısınız." + }, + "sessionTimeoutSettingsAction": { + "message": "Zaman aşımı eylemi" + }, + "sessionTimeoutHeader": { + "message": "Oturum zaman aşımı" + }, + "appearance": { + "message": "Görünüm" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Zaman aşımınız kuruluşunuzun belirlediği maksimum süreyi aşıyor: Maksimum $HOURS$ saat $MINUTES$ dakika", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index e1a00b3dde4..6fa971b98a5 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Applications needing review" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ new applications", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Наступна оплата" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Подробиці" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Завантажити ліцензію" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Run report" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "How to turn on automatic user confirmation" }, - "autoConfirmStep1": { - "message": "Open your Bitwarden extension." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "Select", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Turn on.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Ви увійшли!" }, - "beta": { - "message": "Бета" - }, "assignCollectionAccess": { "message": "Призначити доступ до збірки" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Start free Families trial" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index 08df68b6d33..6e6ba52daad 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "Ứng dụng cần xem lại" }, + "newApplicationsCardTitle": { + "message": "Review new applications" + }, "newApplicationsWithCount": { "message": "$COUNT$ ứng dụng mới", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "Hiện tại không có ứng dụng mới nào để đánh giá" }, + "organizationHasItemsSavedForApplications": { + "message": "Your organization has items saved for $COUNT$ applications", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "Review applications to secure the items most critical to your organization's security" + }, + "reviewApplications": { + "message": "Review applications" + }, "prioritizeCriticalApplications": { "message": "Ưu tiên các ứng dụng quan trọng" }, @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "Lần thanh toán tiếp theo" }, + "nextChargeHeader": { + "message": "Next Charge" + }, + "plan": { + "message": "Plan" + }, "details": { "message": "Chi tiết" }, + "discount": { + "message": "discount" + }, "downloadLicense": { "message": "Tải về tệp giấy phép" }, @@ -4442,6 +4469,30 @@ "generatingYourAccessIntelligence": { "message": "Generating your Access Intelligence..." }, + "fetchingMemberData": { + "message": "Fetching member data..." + }, + "analyzingPasswordHealth": { + "message": "Analyzing password health..." + }, + "calculatingRiskScores": { + "message": "Calculating risk scores..." + }, + "generatingReportData": { + "message": "Generating report data..." + }, + "savingReport": { + "message": "Saving report..." + }, + "compilingInsights": { + "message": "Compiling insights..." + }, + "loadingProgress": { + "message": "Loading progress" + }, + "thisMightTakeFewMinutes": { + "message": "This might take a few minutes." + }, "riskInsightsRunReport": { "message": "Chạy báo cáo" }, @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "Cách bật xác nhận người dùng tự động" }, - "autoConfirmStep1": { - "message": "Mở tiện ích mở rộng Bitwarden của bạn." + "autoConfirmExtension1": { + "message": "Open your Bitwarden extension" }, - "autoConfirmStep2a": { - "message": "Chọn", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension2": { + "message": "Select", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " Bật.", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " Turn on", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "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." @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "Đã đăng nhập!" }, - "beta": { - "message": "Phiên bản Beta" - }, "assignCollectionAccess": { "message": "Gán quyền truy cập bộ sưu tập" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "Bắt đầu dùng thử Gói Gia đình miễn phí" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "Set up an unlock method to change your vault timeout action." + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "Enterprise policy requirements have been applied to your timeout options" + }, + "vaultTimeoutTooLarge": { + "message": "Your vault timeout exceeds the restrictions set by your organization." + }, + "neverLockWarning": { + "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + }, + "sessionTimeoutSettingsAction": { + "message": "Timeout action" + }, + "sessionTimeoutHeader": { + "message": "Session timeout" + }, + "appearance": { + "message": "Appearance" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index c8d46b34254..cce4544ac7b 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -188,7 +188,7 @@ } }, "noApplicationsInOrgDescription": { - "message": "导入您组织的登录数据,以开始监控凭据安全风险。导入后您将能够:" + "message": "导入您组织的登录数据,以开始监测凭据安全风险。导入后您将能够:" }, "benefit1Title": { "message": "优先处理风险" @@ -203,7 +203,7 @@ "message": "为存在风险的成员分配引导式任务,以轮换存在风险的凭据" }, "benefit3Title": { - "message": "监控进度" + "message": "监测进度" }, "benefit3Description": { "message": "追踪变化趋势,展示安全改进成效" @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "应用程序需要审查" }, + "newApplicationsCardTitle": { + "message": "审查新应用程序" + }, "newApplicationsWithCount": { "message": "$COUNT$ 个新应用程序", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "目前没有新应用程序需要审查" }, + "organizationHasItemsSavedForApplications": { + "message": "您的组织已为 $COUNT$ 个应用程序保存了项目", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "审查应用程序以保护对您的组织安全最关键的项目" + }, + "reviewApplications": { + "message": "审查应用程序" + }, "prioritizeCriticalApplications": { "message": "优先处理关键应用程序" }, @@ -380,7 +398,7 @@ "message": "审查新应用程序" }, "reviewNewApplicationsDescription": { - "message": "我们突出显示了管理控制台中存储的新应用程序中存在风险的项目,这些项目使用了弱、暴露或重复使用的密码。" + "message": "我们突出显示了存储在管理控制台中的新应用程序的风险项目,这些项目使用了弱、暴露或重复使用的密码。" }, "clickIconToMarkAppAsCritical": { "message": "点击星形图标以将 App 标记为关键" @@ -3230,11 +3248,20 @@ "message": "状态" }, "nextCharge": { - "message": "下一次扣款" + "message": "下一次收费" + }, + "nextChargeHeader": { + "message": "下一次收费" + }, + "plan": { + "message": "方案" }, "details": { "message": "详细信息" }, + "discount": { + "message": "折扣" + }, "downloadLicense": { "message": "下载许可证" }, @@ -3808,7 +3835,7 @@ "message": "加载更多" }, "mobile": { - "message": "手机版应用", + "message": "移动端", "description": "Mobile app" }, "extension": { @@ -4440,7 +4467,31 @@ "message": "更新浏览器" }, "generatingYourAccessIntelligence": { - "message": "Generating your Access Intelligence..." + "message": "正在生成 Access Intelligence..." + }, + "fetchingMemberData": { + "message": "正在获取成员数据..." + }, + "analyzingPasswordHealth": { + "message": "正在分析密码健康度..." + }, + "calculatingRiskScores": { + "message": "正在计算风险评分..." + }, + "generatingReportData": { + "message": "正在生成报告数据..." + }, + "savingReport": { + "message": "正在保存报告..." + }, + "compilingInsights": { + "message": "正在编译洞察..." + }, + "loadingProgress": { + "message": "加载进度" + }, + "thisMightTakeFewMinutes": { + "message": "这可能需要几分钟时间。" }, "riskInsightsRunReport": { "message": "运行报告" @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "如何启用自动用户确认" }, - "autoConfirmStep1": { - "message": "打开您的 Bitwarden 扩展。" + "autoConfirmExtension1": { + "message": "打开您的 Bitwarden 扩展" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "选择", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": "启用。", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": "启用", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "成功打开 Bitwarden 浏览器扩展。您现在可以激活自动用户确认设置。" @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "已登录!" }, - "beta": { - "message": "Beta" - }, "assignCollectionAccess": { "message": "分配集合访问权限" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "开始免费家庭版试用" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "设置一个解锁方式以更改您的密码库超时动作。" + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "企业策略要求已应用到您的超时选项中" + }, + "vaultTimeoutTooLarge": { + "message": "您的密码库超时超出了您组织设置的限制。" + }, + "neverLockWarning": { + "message": "确定要使用「从不」选项吗?将锁定选项设置为「从不」会将密码库的加密密钥存储在您的设备上。如果使用此选项,您必须确保您的设备安全。" + }, + "sessionTimeoutSettingsAction": { + "message": "超时动作" + }, + "sessionTimeoutHeader": { + "message": "会话超时" + }, + "appearance": { + "message": "外观" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "超时超出了您组织设置的限制:最多 $HOURS$ 小时 $MINUTES$ 分钟", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index 4b6f0725cea..784027a735a 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -21,7 +21,7 @@ "message": "密碼風險" }, "noEditPermissions": { - "message": "You don't have permission to edit this item" + "message": "你沒有權限編輯這個項目" }, "reviewAtRiskPasswords": { "message": "檢視全部應用中具有風險的密碼 (弱、被暴露或重複使用)。選擇最重要的應用程式並優先採取安全措施,幫助使用者解決具有風險的密碼。" @@ -349,6 +349,9 @@ "applicationsNeedingReview": { "message": "需要檢視的應用程式" }, + "newApplicationsCardTitle": { + "message": "審查新應用程式" + }, "newApplicationsWithCount": { "message": "$COUNT$ 個新應用程式", "placeholders": { @@ -370,6 +373,21 @@ "noNewApplicationsToReviewAtThisTime": { "message": "目前沒有新的應用程式可供審查" }, + "organizationHasItemsSavedForApplications": { + "message": "您的組織已為 $COUNT$ 個應用程式儲存項目", + "placeholders": { + "count": { + "content": "$1", + "example": "310" + } + } + }, + "reviewApplicationsToSecureItems": { + "message": "審查應用程式以保護對組織安全最重要的項目" + }, + "reviewApplications": { + "message": "審核認領" + }, "prioritizeCriticalApplications": { "message": "優先處理關鍵應用程式" }, @@ -377,10 +395,10 @@ "message": "選擇對組織最關鍵的應用程式,然後將安全任務指派給成員以供解決。" }, "reviewNewApplications": { - "message": "Review new applications" + "message": "審查新應用程式" }, "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "message": "我們已在管理主控台中標示出新應用程式中密碼薄弱、已外洩或重複使用的高風險項目。" }, "clickIconToMarkAppAsCritical": { "message": "點擊星形圖示以將應用程式標記為關鍵" @@ -3232,9 +3250,18 @@ "nextCharge": { "message": "下一次扣款" }, + "nextChargeHeader": { + "message": "下一次收費" + }, + "plan": { + "message": "方案" + }, "details": { "message": "詳細資料" }, + "discount": { + "message": "折扣" + }, "downloadLicense": { "message": "下載授權證" }, @@ -4440,7 +4467,31 @@ "message": "更新瀏覽器" }, "generatingYourAccessIntelligence": { - "message": "Generating your Access Intelligence..." + "message": "正在產生您的存取智慧分析…" + }, + "fetchingMemberData": { + "message": "正在擷取成員資料…" + }, + "analyzingPasswordHealth": { + "message": "正在分析密碼安全狀況…" + }, + "calculatingRiskScores": { + "message": "正在計算風險分數…" + }, + "generatingReportData": { + "message": "正在產生報告資料..." + }, + "savingReport": { + "message": "正在儲存報告..." + }, + "compilingInsights": { + "message": "正在整理洞察結果…" + }, + "loadingProgress": { + "message": "載入進度中" + }, + "thisMightTakeFewMinutes": { + "message": "這可能需要幾分鐘。" }, "riskInsightsRunReport": { "message": "執行報告" @@ -5804,16 +5855,16 @@ "howToTurnOnAutoConfirm": { "message": "如何開啟自動使用者確認" }, - "autoConfirmStep1": { - "message": "開啟你的 Bitwarden 瀏覽器擴充套件。" + "autoConfirmExtension1": { + "message": "開啟你的 Bitwarden 瀏覽器擴充套件" }, - "autoConfirmStep2a": { + "autoConfirmExtension2": { "message": "選擇", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, - "autoConfirmStep2b": { - "message": " 開啟。", - "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on.'" + "autoConfirmExtension3": { + "message": " 開啟", + "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { "message": "已成功開啟 Bitwarden 瀏覽器擴充套件。您現在可以啟用自動使用者確認設定。" @@ -9525,9 +9576,6 @@ "loggedInExclamation": { "message": "已登入!" }, - "beta": { - "message": "Beta 版" - }, "assignCollectionAccess": { "message": "指派分類存取權限" }, @@ -12075,5 +12123,45 @@ }, "startFreeFamiliesTrial": { "message": "開始免費家庭試用" + }, + "unlockMethodNeededToChangeTimeoutActionDesc": { + "message": "設定一個解鎖方式來變更您的密碼庫逾時動作。" + }, + "vaultTimeoutPolicyAffectingOptions": { + "message": "企業政策已套用至您的逾時選項中" + }, + "vaultTimeoutTooLarge": { + "message": "您的密碼庫逾時時間超過組織設定的限制。" + }, + "neverLockWarning": { + "message": "您確定要使用「永不」選項嗎?將鎖定選項設定為「永不」會將密碼庫的加密金鑰儲存在您的裝置上。如果使用此選項,應確保您的裝置是安全的。" + }, + "sessionTimeoutSettingsAction": { + "message": "逾時後動作" + }, + "sessionTimeoutHeader": { + "message": "工作階段逾時" + }, + "appearance": { + "message": "外觀" + }, + "vaultTimeoutPolicyMaximumError": { + "message": "逾時時間超出了您組織設定的此限制:最多 $HOURS$ 小時 $MINUTES$ 分鐘", + "placeholders": { + "hours": { + "content": "$1", + "example": "5" + }, + "minutes": { + "content": "$2", + "example": "5" + } + } + }, + "confirmNoSelectedCriticalApplicationsTitle": { + "message": "No critical applications are selected" + }, + "confirmNoSelectedCriticalApplicationsDesc": { + "message": "Are you sure you want to continue?" } } From 0925f4fa788e1ac52078c6727772f6a13c49cfa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garc=C3=ADa?= <dani-garcia@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:22:31 +0100 Subject: [PATCH 13/75] Bundle windows crates in renovate config (#17365) --- .github/renovate.json5 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index d2f0c75b9f5..6b34998b99b 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -231,6 +231,7 @@ "webpack-node-externals", "widestring", "windows", + "windows-core", "windows-future", "windows-registry", "zbus", @@ -255,6 +256,11 @@ groupName: "zbus", matchPackageNames: ["zbus", "zbus_polkit"], }, + { + // We need to group all windows-related packages together to avoid build errors caused by version incompatibilities. + groupName: "windows", + matchPackageNames: ["windows", "windows-core", "windows-future", "windows-registry"], + }, { // We group all webpack build-related minor and patch updates together to reduce PR noise. // We include patch updates here because we want PRs for webpack patch updates and it's in this group. From 099a4a0f0312c9d75caa7664a885b288d4954ad0 Mon Sep 17 00:00:00 2001 From: Brandon Treston <btreston@bitwarden.com> Date: Fri, 14 Nov 2025 11:43:10 -0500 Subject: [PATCH 14/75] [PM-28216] Add org ability check for one time dialog (#17372) * add org ability check for one time dialog * exclude providers (cautionary step) and add tests --- .../vault/individual-vault/vault.component.ts | 2 +- .../models/domain/organization.spec.ts | 115 ++++++++++++++++++ .../models/domain/organization.ts | 8 ++ 3 files changed, 124 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/vault/individual-vault/vault.component.ts b/apps/web/src/app/vault/individual-vault/vault.component.ts index 07e810a0cbf..3b0a7a6f141 100644 --- a/apps/web/src/app/vault/individual-vault/vault.component.ts +++ b/apps/web/src/app/vault/individual-vault/vault.component.ts @@ -1623,7 +1623,7 @@ export class VaultComponent<C extends CipherViewLike> implements OnInit, OnDestr !policyEnabled && autoConfirmState.showSetupDialog && !!organization && - (organization.canManageUsers || organization.canManagePolicies); + organization.canEnableAutoConfirmPolicy; if (showDialog) { await this.openAutoConfirmFeatureDialog(organization); diff --git a/libs/common/src/admin-console/models/domain/organization.spec.ts b/libs/common/src/admin-console/models/domain/organization.spec.ts index 2ce674dcb36..5765e84dfb2 100644 --- a/libs/common/src/admin-console/models/domain/organization.spec.ts +++ b/libs/common/src/admin-console/models/domain/organization.spec.ts @@ -32,6 +32,7 @@ describe("Organization", () => { useSecretsManager: true, usePasswordManager: true, useActivateAutofillPolicy: false, + useAutomaticUserConfirmation: false, selfHost: false, usersGetPremium: false, seats: 10, @@ -179,4 +180,118 @@ describe("Organization", () => { expect(organization.canManageDeviceApprovals).toBe(true); }); }); + + describe("canEnableAutoConfirmPolicy", () => { + it("should return false when user cannot manage users or policies", () => { + data.type = OrganizationUserType.User; + data.permissions.manageUsers = false; + data.permissions.managePolicies = false; + data.useAutomaticUserConfirmation = true; + + const organization = new Organization(data); + + expect(organization.canEnableAutoConfirmPolicy).toBe(false); + }); + + it("should return false when user can manage users but useAutomaticUserConfirmation is false", () => { + data.type = OrganizationUserType.Admin; + data.useAutomaticUserConfirmation = false; + + const organization = new Organization(data); + + expect(organization.canEnableAutoConfirmPolicy).toBe(false); + }); + + it("should return false when user has manageUsers permission but useAutomaticUserConfirmation is false", () => { + data.type = OrganizationUserType.User; + data.permissions.manageUsers = true; + data.useAutomaticUserConfirmation = false; + + const organization = new Organization(data); + + expect(organization.canEnableAutoConfirmPolicy).toBe(false); + }); + + it("should return false when user can manage policies but useAutomaticUserConfirmation is false", () => { + data.type = OrganizationUserType.Admin; + data.usePolicies = true; + data.useAutomaticUserConfirmation = false; + + const organization = new Organization(data); + + expect(organization.canEnableAutoConfirmPolicy).toBe(false); + }); + + it("should return false when user has managePolicies permission but usePolicies is false", () => { + data.type = OrganizationUserType.User; + data.permissions.managePolicies = true; + data.usePolicies = false; + data.useAutomaticUserConfirmation = true; + + const organization = new Organization(data); + + expect(organization.canEnableAutoConfirmPolicy).toBe(false); + }); + + it("should return true when admin has useAutomaticUserConfirmation enabled", () => { + data.type = OrganizationUserType.Admin; + data.useAutomaticUserConfirmation = true; + + const organization = new Organization(data); + + expect(organization.canEnableAutoConfirmPolicy).toBe(true); + }); + + it("should return true when owner has useAutomaticUserConfirmation enabled", () => { + data.type = OrganizationUserType.Owner; + data.useAutomaticUserConfirmation = true; + + const organization = new Organization(data); + + expect(organization.canEnableAutoConfirmPolicy).toBe(true); + }); + + it("should return true when user has manageUsers permission and useAutomaticUserConfirmation is enabled", () => { + data.type = OrganizationUserType.User; + data.permissions.manageUsers = true; + data.useAutomaticUserConfirmation = true; + + const organization = new Organization(data); + + expect(organization.canEnableAutoConfirmPolicy).toBe(true); + }); + + it("should return true when user has managePolicies permission, usePolicies is true, and useAutomaticUserConfirmation is enabled", () => { + data.type = OrganizationUserType.User; + data.permissions.managePolicies = true; + data.usePolicies = true; + data.useAutomaticUserConfirmation = true; + + const organization = new Organization(data); + + expect(organization.canEnableAutoConfirmPolicy).toBe(true); + }); + + it("should return true when user has both manageUsers and managePolicies permissions with useAutomaticUserConfirmation enabled", () => { + data.type = OrganizationUserType.User; + data.permissions.manageUsers = true; + data.permissions.managePolicies = true; + data.usePolicies = true; + data.useAutomaticUserConfirmation = true; + + const organization = new Organization(data); + + expect(organization.canEnableAutoConfirmPolicy).toBe(true); + }); + + it("should return false when provider user has useAutomaticUserConfirmation enabled", () => { + data.type = OrganizationUserType.Owner; + data.isProviderUser = true; + data.useAutomaticUserConfirmation = true; + + const organization = new Organization(data); + + expect(organization.canEnableAutoConfirmPolicy).toBe(false); + }); + }); }); diff --git a/libs/common/src/admin-console/models/domain/organization.ts b/libs/common/src/admin-console/models/domain/organization.ts index 55682e62357..458ae1e8f0c 100644 --- a/libs/common/src/admin-console/models/domain/organization.ts +++ b/libs/common/src/admin-console/models/domain/organization.ts @@ -310,6 +310,14 @@ export class Organization { return this.isAdmin || this.permissions.manageResetPassword; } + get canEnableAutoConfirmPolicy() { + return ( + (this.canManageUsers || this.canManagePolicies) && + this.useAutomaticUserConfirmation && + !this.isProviderUser + ); + } + get canManageDeviceApprovals() { return ( (this.isAdmin || this.permissions.manageResetPassword) && From 3b97093338d83798fa3cc17f8161aa9e6f2a02b4 Mon Sep 17 00:00:00 2001 From: Addison Beck <github@addisonbeck.com> Date: Fri, 14 Nov 2025 11:54:08 -0500 Subject: [PATCH 15/75] fix(desktop): persist zoom state across vault locks (#17217) * fix(desktop): persist zoom state across vault locks Replace role-based zoom menu items with custom click handlers to fix zoom persistence issue where keyboard shortcuts (Ctrl+/-/0, Cmd+/-/0) weren't saving zoom changes after vault lock. Changes: - Add custom click handlers for zoomIn/zoomOut/resetZoom menu items - Add WindowMain.saveZoomFactor() method for immediate persistence - Pass WindowMain dependency to ViewMenu constructor - Update zoom-changed event comment to clarify coverage - Maintain existing mouse wheel zoom persistence via zoom-changed event Fixes: PM-791 Fixes: https://github.com/bitwarden/clients/issues/4675 * chore: update to macos-15 runners * review: downgrade macos build runner to 14 * review: align step with min zoom level * cleanup from merge --- apps/desktop/src/main/menu/menu.view.ts | 30 +++++++++++++++++++++---- apps/desktop/src/main/menu/menubar.ts | 2 +- apps/desktop/src/main/window.main.ts | 9 +++++++- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/apps/desktop/src/main/menu/menu.view.ts b/apps/desktop/src/main/menu/menu.view.ts index 962c57fdb60..d24128730cc 100644 --- a/apps/desktop/src/main/menu/menu.view.ts +++ b/apps/desktop/src/main/menu/menu.view.ts @@ -6,6 +6,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { isDev } from "../../utils"; +import { WindowMain } from "../window.main"; import { IMenubarMenu } from "./menubar"; @@ -42,11 +43,18 @@ export class ViewMenu implements IMenubarMenu { private readonly _i18nService: I18nService; private readonly _messagingService: MessagingService; private readonly _isLocked: boolean; + private readonly _windowMain: WindowMain; - constructor(i18nService: I18nService, messagingService: MessagingService, isLocked: boolean) { + constructor( + i18nService: I18nService, + messagingService: MessagingService, + isLocked: boolean, + windowMain: WindowMain, + ) { this._i18nService = i18nService; this._messagingService = messagingService; this._isLocked = isLocked; + this._windowMain = windowMain; } private get searchVault(): MenuItemConstructorOptions { @@ -86,7 +94,12 @@ export class ViewMenu implements IMenubarMenu { return { id: "zoomIn", label: this.localize("zoomIn"), - role: "zoomIn", + click: async () => { + const currentZoom = this._windowMain.win.webContents.zoomFactor; + const newZoom = currentZoom + 0.1; + this._windowMain.win.webContents.zoomFactor = newZoom; + await this._windowMain.saveZoomFactor(newZoom); + }, accelerator: "CmdOrCtrl+=", }; } @@ -95,7 +108,12 @@ export class ViewMenu implements IMenubarMenu { return { id: "zoomOut", label: this.localize("zoomOut"), - role: "zoomOut", + click: async () => { + const currentZoom = this._windowMain.win.webContents.zoomFactor; + const newZoom = Math.max(0.2, currentZoom - 0.1); + this._windowMain.win.webContents.zoomFactor = newZoom; + await this._windowMain.saveZoomFactor(newZoom); + }, accelerator: "CmdOrCtrl+-", }; } @@ -104,7 +122,11 @@ export class ViewMenu implements IMenubarMenu { return { id: "resetZoom", label: this.localize("resetZoom"), - role: "resetZoom", + click: async () => { + const newZoom = 1.0; + this._windowMain.win.webContents.zoomFactor = newZoom; + await this._windowMain.saveZoomFactor(newZoom); + }, accelerator: "CmdOrCtrl+0", }; } diff --git a/apps/desktop/src/main/menu/menubar.ts b/apps/desktop/src/main/menu/menubar.ts index 8ac3a084d95..0a00a67b84a 100644 --- a/apps/desktop/src/main/menu/menubar.ts +++ b/apps/desktop/src/main/menu/menubar.ts @@ -86,7 +86,7 @@ export class Menubar { updateRequest?.restrictedCipherTypes, ), new EditMenu(i18nService, messagingService, isLocked), - new ViewMenu(i18nService, messagingService, isLocked), + new ViewMenu(i18nService, messagingService, isLocked, windowMain), new AccountMenu( i18nService, messagingService, diff --git a/apps/desktop/src/main/window.main.ts b/apps/desktop/src/main/window.main.ts index f8ea7551c47..d148a1a35f8 100644 --- a/apps/desktop/src/main/window.main.ts +++ b/apps/desktop/src/main/window.main.ts @@ -303,7 +303,9 @@ export class WindowMain { this.win.webContents.zoomFactor = this.windowStates[mainWindowSizeKey].zoomFactor ?? 1.0; }); - // Persist zoom changes immediately when user zooms in/out or resets zoom + // Persist zoom changes from mouse wheel and programmatic zoom operations + // NOTE: This event does NOT fire for keyboard shortcuts (Ctrl+/-/0, Cmd+/-/0) + // which are handled by custom menu click handlers in ViewMenu // We can't depend on higher level web events (like close) to do this // because locking the vault resets window state. this.win.webContents.on("zoom-changed", async () => { @@ -432,6 +434,11 @@ export class WindowMain { await this.desktopSettingsService.setAlwaysOnTop(this.enableAlwaysOnTop); } + async saveZoomFactor(zoomFactor: number) { + this.windowStates[mainWindowSizeKey].zoomFactor = zoomFactor; + await this.desktopSettingsService.setWindow(this.windowStates[mainWindowSizeKey]); + } + private windowStateChangeHandler(configKey: string, win: BrowserWindow) { global.clearTimeout(this.windowStateChangeTimer); this.windowStateChangeTimer = global.setTimeout(async () => { From fdb2f8b55375e2ba2682e09d71db457e5eec4c93 Mon Sep 17 00:00:00 2001 From: Daniel Riera <driera@livefront.com> Date: Fri, 14 Nov 2025 12:44:32 -0500 Subject: [PATCH 16/75] [PM-4903] - If you back out of autofill flow from locked vault screen, credentials autofilled on normal unlock (#17283) * PM-4903- added a check for auth status and popout tabs, if no popup tab and auth is locked, abandon autofill * add test * clear all notifications if unlock popout closed * add more tests and use tabid for performance optimization --- .../notification.background.spec.ts | 58 +++++++++++++++++++ .../background/notification.background.ts | 44 +++++++++++++- .../src/background/runtime.background.ts | 3 + 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/apps/browser/src/autofill/background/notification.background.spec.ts b/apps/browser/src/autofill/background/notification.background.spec.ts index f9e2e1c534f..8df21bc66ef 100644 --- a/apps/browser/src/autofill/background/notification.background.spec.ts +++ b/apps/browser/src/autofill/background/notification.background.spec.ts @@ -1530,5 +1530,63 @@ describe("NotificationBackground", () => { expect(environmentServiceSpy).toHaveBeenCalled(); }); }); + + describe("handleUnlockPopoutClosed", () => { + let onRemovedListeners: Array<(tabId: number, removeInfo: chrome.tabs.OnRemovedInfo) => void>; + let tabsQuerySpy: jest.SpyInstance; + + beforeEach(() => { + onRemovedListeners = []; + chrome.tabs.onRemoved.addListener = jest.fn((listener) => { + onRemovedListeners.push(listener); + }); + chrome.runtime.getURL = jest.fn().mockReturnValue("chrome-extension://id/popup/index.html"); + notificationBackground.init(); + }); + + const triggerTabRemoved = async (tabId: number) => { + onRemovedListeners[0](tabId, mock<chrome.tabs.OnRemovedInfo>()); + await flushPromises(); + }; + + it("sends abandon message when unlock popout is closed and vault is locked", async () => { + activeAccountStatusMock$.next(AuthenticationStatus.Locked); + tabsQuerySpy = jest.spyOn(BrowserApi, "tabsQuery").mockResolvedValue([]); + + await triggerTabRemoved(1); + + expect(tabsQuerySpy).toHaveBeenCalled(); + expect(messagingService.send).toHaveBeenCalledWith("abandonAutofillPendingNotifications"); + }); + + it("uses tracked tabId for fast lookup when available", async () => { + activeAccountStatusMock$.next(AuthenticationStatus.Locked); + tabsQuerySpy = jest.spyOn(BrowserApi, "tabsQuery").mockResolvedValue([ + { + id: 123, + url: "chrome-extension://id/popup/index.html?singleActionPopout=auth_unlockExtension", + } as chrome.tabs.Tab, + ]); + + await triggerTabRemoved(999); + tabsQuerySpy.mockClear(); + messagingService.send.mockClear(); + + await triggerTabRemoved(123); + + expect(tabsQuerySpy).not.toHaveBeenCalled(); + expect(messagingService.send).toHaveBeenCalledWith("abandonAutofillPendingNotifications"); + }); + + it("returns early when vault is unlocked", async () => { + activeAccountStatusMock$.next(AuthenticationStatus.Unlocked); + tabsQuerySpy = jest.spyOn(BrowserApi, "tabsQuery").mockResolvedValue([]); + + await triggerTabRemoved(1); + + expect(tabsQuerySpy).not.toHaveBeenCalled(); + expect(messagingService.send).not.toHaveBeenCalled(); + }); + }); }); }); diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts index e27b50f13cd..de1514f0342 100644 --- a/apps/browser/src/autofill/background/notification.background.ts +++ b/apps/browser/src/autofill/background/notification.background.ts @@ -45,7 +45,7 @@ import { SecurityTask } from "@bitwarden/common/vault/tasks/models/security-task // FIXME (PM-22628): Popup imports are forbidden in background // eslint-disable-next-line no-restricted-imports -import { openUnlockPopout } from "../../auth/popup/utils/auth-popout-window"; +import { AuthPopoutType, openUnlockPopout } from "../../auth/popup/utils/auth-popout-window"; import { BrowserApi } from "../../platform/browser/browser-api"; // FIXME (PM-22628): Popup imports are forbidden in background // eslint-disable-next-line no-restricted-imports @@ -89,6 +89,7 @@ export default class NotificationBackground { ExtensionCommand.AutofillCard, ExtensionCommand.AutofillIdentity, ]); + private unlockPopoutTabId?: number; private readonly extensionMessageHandlers: NotificationBackgroundExtensionMessageHandlers = { bgAdjustNotificationBar: ({ message, sender }) => this.handleAdjustNotificationBarMessage(message, sender), @@ -146,6 +147,7 @@ export default class NotificationBackground { } this.setupExtensionMessageListener(); + this.setupUnlockPopoutCloseListener(); this.cleanupNotificationQueue(); } @@ -1163,6 +1165,7 @@ export default class NotificationBackground { message: NotificationBackgroundExtensionMessage, sender: chrome.runtime.MessageSender, ): Promise<void> { + this.unlockPopoutTabId = undefined; const messageData = message.data as LockedVaultPendingNotificationsData; const retryCommand = messageData.commandToRetry.message.command as ExtensionCommandType; if (this.allowedRetryCommands.has(retryCommand)) { @@ -1313,4 +1316,43 @@ export default class NotificationBackground { const tabDomain = Utils.getDomain(tab.url); return tabDomain === queueMessage.domain || tabDomain === Utils.getDomain(queueMessage.tab.url); } + + private setupUnlockPopoutCloseListener() { + chrome.tabs.onRemoved.addListener(async (tabId: number) => { + await this.handleUnlockPopoutClosed(tabId); + }); + } + + /** + * If the unlock popout is closed while the vault + * is still locked and there are pending autofill notifications, abandon them. + */ + private async handleUnlockPopoutClosed(removedTabId: number) { + const authStatus = await this.getAuthStatus(); + if (authStatus >= AuthenticationStatus.Unlocked) { + this.unlockPopoutTabId = undefined; + return; + } + + if (this.unlockPopoutTabId === removedTabId) { + this.unlockPopoutTabId = undefined; + this.messagingService.send("abandonAutofillPendingNotifications"); + return; + } + + if (this.unlockPopoutTabId) { + return; + } + + const extensionUrl = chrome.runtime.getURL("popup/index.html"); + const unlockPopoutTabs = (await BrowserApi.tabsQuery({ url: `${extensionUrl}*` })).filter( + (tab) => tab.url?.includes(`singleActionPopout=${AuthPopoutType.unlockExtension}`), + ); + + if (unlockPopoutTabs.length === 0) { + this.messagingService.send("abandonAutofillPendingNotifications"); + } else if (unlockPopoutTabs[0].id) { + this.unlockPopoutTabId = unlockPopoutTabs[0].id; + } + } } diff --git a/apps/browser/src/background/runtime.background.ts b/apps/browser/src/background/runtime.background.ts index de0d79a89db..798a7583f85 100644 --- a/apps/browser/src/background/runtime.background.ts +++ b/apps/browser/src/background/runtime.background.ts @@ -256,6 +256,9 @@ export default class RuntimeBackground { case "addToLockedVaultPendingNotifications": this.lockedVaultPendingNotifications.push(msg.data); break; + case "abandonAutofillPendingNotifications": + this.lockedVaultPendingNotifications = []; + break; case "lockVault": await this.lockService.lock(msg.userId); break; From b56229dd2880448ef212ec891a79d4645639d251 Mon Sep 17 00:00:00 2001 From: Mike Amirault <mamirault@bitwarden.com> Date: Fri, 14 Nov 2025 14:27:40 -0500 Subject: [PATCH 17/75] Remove import page banner when under org policy (#17348) --- libs/importer/src/components/import.component.html | 3 --- 1 file changed, 3 deletions(-) diff --git a/libs/importer/src/components/import.component.html b/libs/importer/src/components/import.component.html index bd4afaf364b..dfee02acf5a 100644 --- a/libs/importer/src/components/import.component.html +++ b/libs/importer/src/components/import.component.html @@ -1,6 +1,3 @@ -<bit-callout type="info" *ngIf="importBlockedByPolicy"> - {{ "personalOwnershipPolicyInEffectImports" | i18n }} -</bit-callout> <bit-callout [title]="'restrictCardTypeImport' | i18n" type="info" From 8a3f1ee1a446cd61ff52b2bfa19334e97ab9c6f6 Mon Sep 17 00:00:00 2001 From: Jason Ng <jng@bitwarden.com> Date: Fri, 14 Nov 2025 16:16:08 -0500 Subject: [PATCH 18/75] [PM-26687] send skeleton (#17333) * adding skeleton to send --- .../popup/send-v2/send-v2.component.html | 11 +++++-- .../tools/popup/send-v2/send-v2.component.ts | 30 +++++++++++++++++-- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.html b/apps/browser/src/tools/popup/send-v2/send-v2.component.html index 997b65e9934..0bcbd47a145 100644 --- a/apps/browser/src/tools/popup/send-v2/send-v2.component.html +++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.html @@ -1,4 +1,4 @@ -<popup-page [loading]="sendsLoading$ | async"> +<popup-page [loading]="showSpinnerLoaders$ | async" [hideOverflow]="showSkeletonsLoaders$ | async"> <popup-header slot="header" [pageTitle]="'send' | i18n"> <ng-container slot="end"> <tools-new-send-dropdown *ngIf="!sendsDisabled"></tools-new-send-dropdown> @@ -6,7 +6,7 @@ <app-current-account></app-current-account> </ng-container> </popup-header> - <ng-container slot="above-scroll-area" *ngIf="!(sendsLoading$ | async)"> + <ng-container slot="above-scroll-area"> <bit-callout *ngIf="sendsDisabled" [title]="'sendDisabled' | i18n"> {{ "sendDisabledWarning" | i18n }} </bit-callout> @@ -34,7 +34,7 @@ </bit-no-items> </div> - <ng-container *ngIf="listState !== sendState.Empty"> + <ng-container *ngIf="listState !== sendState.Empty && !(showSkeletonsLoaders$ | async)"> <div *ngIf="listState === sendState.NoResults" class="tw-flex tw-flex-col tw-justify-center tw-h-auto tw-pt-12" @@ -46,4 +46,9 @@ </div> <app-send-list-items-container [headerText]="title | i18n" [sends]="sends$ | async" /> </ng-container> + @if (showSkeletonsLoaders$ | async) { + <vault-fade-in-skeleton> + <vault-loading-skeleton></vault-loading-skeleton> + </vault-fade-in-skeleton> + } </popup-page> diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts index 1272a86be17..43a1119deca 100644 --- a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts +++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts @@ -1,15 +1,18 @@ import { CommonModule } from "@angular/common"; import { Component, OnDestroy } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; -import { combineLatest, switchMap } from "rxjs"; +import { combineLatest, distinctUntilChanged, map, shareReplay, switchMap } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { NoResults, NoSendsIcon } from "@bitwarden/assets/svg"; +import { VaultLoadingSkeletonComponent } from "@bitwarden/browser/vault/popup/components/vault-loading-skeleton/vault-loading-skeleton.component"; import { BrowserPremiumUpgradePromptService } from "@bitwarden/browser/vault/popup/services/browser-premium-upgrade-prompt.service"; import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { @@ -31,6 +34,7 @@ import { CurrentAccountComponent } from "../../../auth/popup/account-switching/c import { PopOutComponent } from "../../../platform/popup/components/pop-out.component"; import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component"; +import { VaultFadeInOutSkeletonComponent } from "../../../vault/popup/components/vault-fade-in-out-skeleton/vault-fade-in-out-skeleton.component"; // FIXME: update to use a const object instead of a typescript enum // eslint-disable-next-line @bitwarden/platform/no-enums @@ -64,6 +68,8 @@ export enum SendState { SendListFiltersComponent, SendSearchComponent, TypographyModule, + VaultFadeInOutSkeletonComponent, + VaultLoadingSkeletonComponent, ], }) export class SendV2Component implements OnDestroy { @@ -72,7 +78,26 @@ export class SendV2Component implements OnDestroy { protected listState: SendState | null = null; protected sends$ = this.sendItemsService.filteredAndSortedSends$; - protected sendsLoading$ = this.sendItemsService.loading$; + private skeletonFeatureFlag$ = this.configService.getFeatureFlag$( + FeatureFlag.VaultLoadingSkeletons, + ); + protected sendsLoading$ = this.sendItemsService.loading$.pipe( + distinctUntilChanged(), + shareReplay({ bufferSize: 1, refCount: true }), + ); + + /** Spinner Loading State */ + protected showSpinnerLoaders$ = combineLatest([ + this.sendsLoading$, + this.skeletonFeatureFlag$, + ]).pipe(map(([loading, skeletonsEnabled]) => loading && !skeletonsEnabled)); + + /** Skeleton Loading State */ + protected showSkeletonsLoaders$ = combineLatest([ + this.sendsLoading$, + this.skeletonFeatureFlag$, + ]).pipe(map(([loading, skeletonsEnabled]) => loading && skeletonsEnabled)); + protected title: string = "allSends"; protected noItemIcon = NoSendsIcon; protected noResultsIcon = NoResults; @@ -84,6 +109,7 @@ export class SendV2Component implements OnDestroy { protected sendListFiltersService: SendListFiltersService, private policyService: PolicyService, private accountService: AccountService, + private configService: ConfigService, ) { combineLatest([ this.sendItemsService.emptyList$, From 9cd73b8738a09acb52505e1d1befe8a2f6174336 Mon Sep 17 00:00:00 2001 From: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Date: Fri, 14 Nov 2025 16:28:05 -0500 Subject: [PATCH 19/75] Auth/PM-22661 - SendTokenService - improve expired token scenario docs on abstraction (#17371) * PM-22661 - SendTokenService - improve expired token scenario docs on abstraction * PM-22661 - SendTokenService - further clarification --- .../src/auth/send-access/abstractions/send-token.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/src/auth/send-access/abstractions/send-token.service.ts b/libs/common/src/auth/send-access/abstractions/send-token.service.ts index 3ecdc101892..e423713a283 100644 --- a/libs/common/src/auth/send-access/abstractions/send-token.service.ts +++ b/libs/common/src/auth/send-access/abstractions/send-token.service.ts @@ -13,7 +13,7 @@ export abstract class SendTokenService { /** * Attempts to retrieve a {@link SendAccessToken} for the given sendId. * If the access token is found in session storage and is not expired, then it returns the token. - * If the access token is expired, then it returns a {@link TryGetSendAccessTokenError} expired error. + * If the access token found in session storage is expired, then it returns a {@link TryGetSendAccessTokenError} expired error and clears the token from storage so that a subsequent call can attempt to retrieve a new token. * If an access token is not found in storage, then it attempts to retrieve it from the server (will succeed for sends that don't require any credentials to view). * If the access token is successfully retrieved from the server, then it stores the token in session storage and returns it. * If an access token cannot be granted b/c the send requires credentials, then it returns a {@link TryGetSendAccessTokenError} indicating which credentials are required. From a4d773537e3c6c9f7f96c29ce593ead0a6846932 Mon Sep 17 00:00:00 2001 From: Alex Dragovich <46065570+itsadrago@users.noreply.github.com> Date: Fri, 14 Nov 2025 16:07:10 -0800 Subject: [PATCH 20/75] [PM-27465] Fixing cancel button on Send and Vault export (#17138) --- .../tools/popup/send-v2/add-edit/send-add-edit.component.html | 2 +- .../popup/settings/export/export-browser-v2.component.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.html b/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.html index c6ea52aff62..a72847a5bf2 100644 --- a/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.html +++ b/apps/browser/src/tools/popup/send-v2/add-edit/send-add-edit.component.html @@ -16,7 +16,7 @@ <button bitButton type="submit" form="sendForm" buttonType="primary" #submitBtn> {{ "save" | i18n }} </button> - <button bitButton type="button" buttonType="secondary" popupBackAction> + <button bitButton type="button" buttonType="secondary" [popupBackAction]> {{ "cancel" | i18n }} </button> <button diff --git a/apps/browser/src/tools/popup/settings/export/export-browser-v2.component.html b/apps/browser/src/tools/popup/settings/export/export-browser-v2.component.html index 8493fa5fee7..d6bf3a3a253 100644 --- a/apps/browser/src/tools/popup/settings/export/export-browser-v2.component.html +++ b/apps/browser/src/tools/popup/settings/export/export-browser-v2.component.html @@ -23,7 +23,7 @@ > {{ "exportVault" | i18n }} </button> - <button bitButton type="button" buttonType="secondary" popupBackAction> + <button bitButton type="button" buttonType="secondary" [popupBackAction]> {{ "cancel" | i18n }} </button> </popup-footer> From c67715ea29e12ce255b849ba4492afce3eda67af Mon Sep 17 00:00:00 2001 From: Matt Gibson <mgibson@bitwarden.com> Date: Mon, 17 Nov 2025 07:37:36 -0800 Subject: [PATCH 21/75] [PM-28038][PM-28276] Ignore url case for origin matching (#17355) * ignore url case for origin matching * Fixup typo * Inject log services --- apps/browser/src/background/main.background.ts | 2 +- apps/browser/src/platform/browser/browser-api.ts | 4 ++-- .../platform/storage/background-memory-storage.service.ts | 5 +++-- .../storage/memory-storage-service-interactions.spec.ts | 7 ++++++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index cff783942fe..f59b6648486 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -548,7 +548,7 @@ export default class MainBackground { this.memoryStorageForStateProviders = new BrowserMemoryStorageService(); // mv3 stores to storage.session this.memoryStorageService = this.memoryStorageForStateProviders; } else { - this.memoryStorageForStateProviders = new BackgroundMemoryStorageService(); // mv2 stores to memory + this.memoryStorageForStateProviders = new BackgroundMemoryStorageService(this.logService); // mv2 stores to memory this.memoryStorageService = this.memoryStorageForStateProviders; } diff --git a/apps/browser/src/platform/browser/browser-api.ts b/apps/browser/src/platform/browser/browser-api.ts index 76ec18f496f..723df95ef63 100644 --- a/apps/browser/src/platform/browser/browser-api.ts +++ b/apps/browser/src/platform/browser/browser-api.ts @@ -60,8 +60,8 @@ export class BrowserApi { } // Normalize both URLs by removing trailing slashes - const normalizedOrigin = sender.origin.replace(/\/$/, ""); - const normalizedExtensionUrl = extensionUrl.replace(/\/$/, ""); + const normalizedOrigin = sender.origin.replace(/\/$/, "").toLowerCase(); + const normalizedExtensionUrl = extensionUrl.replace(/\/$/, "").toLowerCase(); if (!normalizedOrigin.startsWith(normalizedExtensionUrl)) { logger?.warning( diff --git a/apps/browser/src/platform/storage/background-memory-storage.service.ts b/apps/browser/src/platform/storage/background-memory-storage.service.ts index 5e1bff99c39..e4431c30db6 100644 --- a/apps/browser/src/platform/storage/background-memory-storage.service.ts +++ b/apps/browser/src/platform/storage/background-memory-storage.service.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { LogService } from "@bitwarden/logging"; import { SerializedMemoryStorageService } from "@bitwarden/storage-core"; import { BrowserApi } from "../browser/browser-api"; @@ -11,14 +12,14 @@ import { portName } from "./port-name"; export class BackgroundMemoryStorageService extends SerializedMemoryStorageService { private _ports: chrome.runtime.Port[] = []; - constructor() { + constructor(private readonly logService: LogService) { super(); BrowserApi.addListener(chrome.runtime.onConnect, (port) => { if (port.name !== portName(chrome.storage.session)) { return; } - if (!BrowserApi.senderIsInternal(port.sender)) { + if (!BrowserApi.senderIsInternal(port.sender, this.logService)) { return; } diff --git a/apps/browser/src/platform/storage/memory-storage-service-interactions.spec.ts b/apps/browser/src/platform/storage/memory-storage-service-interactions.spec.ts index 4a8f5d3f2ff..8004559f57c 100644 --- a/apps/browser/src/platform/storage/memory-storage-service-interactions.spec.ts +++ b/apps/browser/src/platform/storage/memory-storage-service-interactions.spec.ts @@ -4,6 +4,9 @@ */ import { trackEmissions } from "@bitwarden/common/../spec/utils"; +import { mock, MockProxy } from "jest-mock-extended"; + +import { LogService } from "@bitwarden/logging"; import { mockPorts } from "../../../spec/mock-port.spec-util"; @@ -14,11 +17,13 @@ import { ForegroundMemoryStorageService } from "./foreground-memory-storage.serv describe.skip("foreground background memory storage interaction", () => { let foreground: ForegroundMemoryStorageService; let background: BackgroundMemoryStorageService; + let logService: MockProxy<LogService>; beforeEach(() => { mockPorts(); + logService = mock(); - background = new BackgroundMemoryStorageService(); + background = new BackgroundMemoryStorageService(logService); foreground = new ForegroundMemoryStorageService(); }); From a2abbd09bf40c06268ef38803b4e7148684607b3 Mon Sep 17 00:00:00 2001 From: neuronull <9162534+neuronull@users.noreply.github.com> Date: Mon, 17 Nov 2025 08:14:50 -0800 Subject: [PATCH 22/75] Desktop Native compile debug builds with debug log level (#17357) * Desktop Native compile debug builds with debug log level * typo in code comment --- .github/workflows/build-desktop.yml | 16 ++++++++-------- apps/desktop/desktop_native/napi/package.json | 2 +- .../desktop_native/napi/scripts/build.js | 14 ++++++++++++++ apps/desktop/desktop_native/napi/src/lib.rs | 17 +++++++++++------ 4 files changed, 34 insertions(+), 15 deletions(-) create mode 100644 apps/desktop/desktop_native/napi/scripts/build.js diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index f651af9dd7d..98192ea9e08 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -251,7 +251,7 @@ jobs: TARGET: musl run: | rustup target add x86_64-unknown-linux-musl - node build.js --target=x86_64-unknown-linux-musl --release + node build.js --target=x86_64-unknown-linux-musl - name: Build application run: npm run dist:lin @@ -414,7 +414,7 @@ jobs: TARGET: musl run: | rustup target add aarch64-unknown-linux-musl - node build.js --target=aarch64-unknown-linux-musl --release + node build.js --target=aarch64-unknown-linux-musl - name: Check index.d.ts generated if: github.event_name == 'pull_request' && steps.cache.outputs.cache-hit != 'true' @@ -995,12 +995,12 @@ jobs: cache: 'npm' cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} - + - name: Set up Python uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: '3.14' - + - name: Set up Node-gyp run: python3 -m pip install setuptools @@ -1232,12 +1232,12 @@ jobs: cache: 'npm' cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} - + - name: Set up Python uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: '3.14' - + - name: Set up Node-gyp run: python3 -m pip install setuptools @@ -1504,12 +1504,12 @@ jobs: cache: 'npm' cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} - + - name: Set up Python uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: '3.14' - + - name: Set up Node-gyp run: python3 -m pip install setuptools diff --git a/apps/desktop/desktop_native/napi/package.json b/apps/desktop/desktop_native/napi/package.json index d557ccfd259..ca17377c9f2 100644 --- a/apps/desktop/desktop_native/napi/package.json +++ b/apps/desktop/desktop_native/napi/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "description": "", "scripts": { - "build": "napi build --platform --js false", + "build": "node scripts/build.js", "test": "cargo test" }, "author": "", diff --git a/apps/desktop/desktop_native/napi/scripts/build.js b/apps/desktop/desktop_native/napi/scripts/build.js new file mode 100644 index 00000000000..a6680f5d311 --- /dev/null +++ b/apps/desktop/desktop_native/napi/scripts/build.js @@ -0,0 +1,14 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +const { execSync } = require('child_process'); + +const args = process.argv.slice(2); +const isRelease = args.includes('--release'); + +if (isRelease) { + console.log('Building release mode.'); +} else { + console.log('Building debug mode.'); + process.env.RUST_LOG = 'debug'; +} + +execSync(`napi build --platform --js false`, { stdio: 'inherit', env: process.env }); diff --git a/apps/desktop/desktop_native/napi/src/lib.rs b/apps/desktop/desktop_native/napi/src/lib.rs index 39e57bd0bb5..01d60ff5f56 100644 --- a/apps/desktop/desktop_native/napi/src/lib.rs +++ b/apps/desktop/desktop_native/napi/src/lib.rs @@ -957,10 +957,7 @@ pub mod logging { use tracing::Level; use tracing_subscriber::fmt::format::{DefaultVisitor, Writer}; use tracing_subscriber::{ - filter::{EnvFilter, LevelFilter}, - layer::SubscriberExt, - util::SubscriberInitExt, - Layer, + filter::EnvFilter, layer::SubscriberExt, util::SubscriberInitExt, Layer, }; struct JsLogger(OnceLock<ThreadsafeFunction<(LogLevel, String), CalleeHandled>>); @@ -1044,9 +1041,17 @@ pub mod logging { pub fn init_napi_log(js_log_fn: ThreadsafeFunction<(LogLevel, String), CalleeHandled>) { let _ = JS_LOGGER.0.set(js_log_fn); + // the log level hierarchy is determined by: + // - if RUST_LOG is detected at runtime + // - if RUST_LOG is provided at compile time + // - default to INFO let filter = EnvFilter::builder() - // set the default log level to INFO. - .with_default_directive(LevelFilter::INFO.into()) + .with_default_directive( + option_env!("RUST_LOG") + .unwrap_or("info") + .parse() + .expect("should provide valid log level at compile time."), + ) // parse directives from the RUST_LOG environment variable, // overriding the default directive for matching targets. .from_env_lossy(); From 16e4eb1dd0e9fb2ba748022e1f9b5a267fc910f9 Mon Sep 17 00:00:00 2001 From: Maximilian Power <mpower@bitwarden.com> Date: Mon, 17 Nov 2025 17:50:39 +0100 Subject: [PATCH 23/75] updates strings (#17422) * updated strings --- apps/web/src/locales/en/messages.json | 72 +++++++++---------- .../password-change-metric.component.html | 2 +- .../activity/all-activity.component.html | 2 +- .../assign-tasks-view.component.html | 2 +- .../new-applications-dialog.component.html | 6 +- .../risk-insights.component.html | 8 +-- .../risk-insights.component.ts | 20 ++---- ...risk-insights-drawer-dialog.component.html | 2 +- 8 files changed, 51 insertions(+), 63 deletions(-) diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 23c430feedd..1b0460e2aa6 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9860,8 +9854,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-cards/password-change-metric.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-cards/password-change-metric.component.html index ab59a36aa6a..fe9880724f3 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-cards/password-change-metric.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/activity-cards/password-change-metric.component.html @@ -12,7 +12,7 @@ </div> <div class="tw-items-baseline tw-gap-2"> - <span bitTypography="body2">{{ "onceYouReviewApps" | i18n }}</span> + <span bitTypography="body2">{{ "onceYouReviewApplications" | i18n }}</span> </div> } 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 43cf936e1a1..ffc67028b77 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 @@ -14,7 +14,7 @@ <dirt-activity-card [title]="'atRiskMembers' | i18n" [cardMetrics]="'membersAtRiskCount' | i18n: totalCriticalAppsAtRiskMemberCount" - [metricDescription]="'membersWithAccessToAtRiskItemsForCriticalApps' | i18n" + [metricDescription]="'membersWithAccessToAtRiskItemsForCriticalApplications' | i18n" actionText="{{ 'viewAtRiskMembers' | i18n }}" [showActionLink]="totalCriticalAppsAtRiskMemberCount > 0" (actionClick)="onViewAtRiskMembers()" diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/application-review-dialog/assign-tasks-view.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/application-review-dialog/assign-tasks-view.component.html index 859bc73905c..572918907f4 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/application-review-dialog/assign-tasks-view.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/application-review-dialog/assign-tasks-view.component.html @@ -71,7 +71,7 @@ <!-- Description Text --> <div bitTypography="helper" class="tw-text-muted"> - {{ "membersWillReceiveNotification" | i18n }} + {{ "membersWillReceiveSecurityTask" | i18n }} </div> </div> </div> diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/application-review-dialog/new-applications-dialog.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/application-review-dialog/new-applications-dialog.component.html index 09fb5cb7ad9..04db5f6d521 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/application-review-dialog/new-applications-dialog.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/activity/application-review-dialog/new-applications-dialog.component.html @@ -5,7 +5,7 @@ ? hasNoCriticalApplications() ? ("prioritizeCriticalApplications" | i18n) : ("reviewNewApplications" | i18n) - : ("assignTasksToMembers" | i18n) + : ("assignSecurityTasksToMembers" | i18n) }} </span> @@ -15,8 +15,8 @@ <p bitTypography="body1" class="tw-mb-5"> {{ hasNoCriticalApplications() - ? ("selectCriticalApplicationsDescription" | i18n) - : ("reviewNewApplicationsDescription" | i18n) + ? ("selectCriticalAppsDescription" | i18n) + : ("reviewNewAppsDescription" | i18n) }} </p> 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 5e00de853ff..19b655a8b23 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 @@ -16,8 +16,8 @@ <!-- Show Empty state when there are no applications (no ciphers to make reports on) --> <empty-state-card [videoSrc]="emptyStateVideoSrc" - [title]="this.i18nService.t('noApplicationsInOrgTitle', organizationName)" - [description]="this.i18nService.t('noApplicationsInOrgDescription')" + [title]="this.i18nService.t('noDataInOrgTitle')" + [description]="this.i18nService.t('noDataInOrgDescription')" [benefits]="emptyStateBenefits" [buttonText]="this.i18nService.t('importData')" [buttonIcon]="IMPORT_ICON" @@ -27,8 +27,8 @@ <!-- Show empty state for no reports run --> <empty-state-card [videoSrc]="emptyStateVideoSrc" - [title]="this.i18nService.t('noReportRunTitle')" - [description]="this.i18nService.t('noReportRunDescription')" + [title]="this.i18nService.t('noReportsRunTitle')" + [description]="this.i18nService.t('noReportsRunDescription')" [benefits]="emptyStateBenefits" [buttonText]="this.i18nService.t('riskInsightsRunReport')" [buttonIcon]="" 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 eddc26cbc77..9e6901572c3 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 @@ -10,7 +10,7 @@ import { } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute, Router } from "@angular/router"; -import { combineLatest, EMPTY, firstValueFrom } from "rxjs"; +import { EMPTY, firstValueFrom } from "rxjs"; import { distinctUntilChanged, map, tap } from "rxjs/operators"; import { JslibModule } from "@bitwarden/angular/jslib.module"; @@ -84,14 +84,11 @@ export class RiskInsightsComponent implements OnInit, OnDestroy { dataLastUpdated: Date | null = null; - // Empty state properties - protected organizationName = ""; - // Empty state computed properties protected emptyStateBenefits: [string, string][] = [ - [this.i18nService.t("benefit1Title"), this.i18nService.t("benefit1Description")], - [this.i18nService.t("benefit2Title"), this.i18nService.t("benefit2Description")], - [this.i18nService.t("benefit3Title"), this.i18nService.t("benefit3Description")], + [this.i18nService.t("feature1Title"), this.i18nService.t("feature1Description")], + [this.i18nService.t("feature2Title"), this.i18nService.t("feature2Description")], + [this.i18nService.t("feature3Title"), this.i18nService.t("feature3Description")], ]; protected emptyStateVideoSrc: string | null = "/videos/risk-insights-mark-as-critical.mp4"; @@ -140,17 +137,14 @@ export class RiskInsightsComponent implements OnInit, OnDestroy { ) .subscribe(); - // Combine report data, vault items check, organization details, and generation state + // Subscribe to report data updates // This declarative pattern ensures proper cleanup and prevents memory leaks - combineLatest([this.dataService.enrichedReportData$, this.dataService.organizationDetails$]) + this.dataService.enrichedReportData$ .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(([report, orgDetails]) => { + .subscribe((report) => { // Update report state this.appsCount = report?.reportData.length ?? 0; this.dataLastUpdated = report?.creationDate ?? null; - - // Update organization name - this.organizationName = orgDetails?.organizationName ?? ""; }); // Subscribe to drawer state changes diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/risk-insights-drawer-dialog.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/risk-insights-drawer-dialog.component.html index 87a8ee00e05..3fa72358f25 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/risk-insights-drawer-dialog.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/risk-insights-drawer-dialog.component.html @@ -8,7 +8,7 @@ <ng-container bitDialogContent> <span bitTypography="body1" class="tw-text-muted tw-text-sm">{{ (drawerDetails.atRiskMemberDetails?.length > 0 - ? "atRiskMembersDescription" + ? "atRiskMemberDescription" : "atRiskMembersDescriptionNone" ) | i18n }}</span> From 1fa10f24f706544227f224a348150cc28c9503cb Mon Sep 17 00:00:00 2001 From: Vince Grassia <593223+vgrassia@users.noreply.github.com> Date: Mon, 17 Nov 2025 12:31:54 -0500 Subject: [PATCH 24/75] Fix beta artifact name (#17425) --- .github/workflows/build-desktop.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 98192ea9e08..102fbdbbdc8 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -871,6 +871,8 @@ jobs: -NewName bitwarden-beta-$env:_PACKAGE_VERSION-x64.nsis.7z Rename-Item -Path .\dist\nsis-web\Bitwarden-Beta-$env:_PACKAGE_VERSION-arm64.nsis.7z ` -NewName bitwarden-beta-$env:_PACKAGE_VERSION-arm64.nsis.7z + Rename-Item -Path .\dist\nsis-web\latest.yml ` + -NewName latest-beta.yml - name: Upload portable exe artifact uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 @@ -963,8 +965,8 @@ jobs: if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: - name: ${{ needs.setup.outputs.release_channel }}-beta.yml - path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release_channel }}.yml + name: latest-beta.yml + path: apps/desktop/dist/nsis-web/latest-beta.yml if-no-files-found: error macos-build: From b296750bcb272dea5197c9824ad68a916ea681c0 Mon Sep 17 00:00:00 2001 From: Github Actions <actions@github.com> Date: Mon, 17 Nov 2025 19:07:49 +0000 Subject: [PATCH 25/75] Bumped client version(s) --- apps/web/package.json | 2 +- package-lock.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index b95d3e6aba5..c2db376e5da 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/web-vault", - "version": "2025.11.2", + "version": "2025.11.3", "scripts": { "build:oss": "webpack", "build:bit": "webpack -c ../../bitwarden_license/bit-web/webpack.config.js", diff --git a/package-lock.json b/package-lock.json index 46b70931f65..bb8355dc632 100644 --- a/package-lock.json +++ b/package-lock.json @@ -294,7 +294,7 @@ }, "apps/web": { "name": "@bitwarden/web-vault", - "version": "2025.11.2" + "version": "2025.11.3" }, "libs/admin-console": { "name": "@bitwarden/admin-console", From 4e2d8988f2aa1950f04b92d646bbbc727f0c1d3f Mon Sep 17 00:00:00 2001 From: Maximilian Power <mpower@bitwarden.com> Date: Mon, 17 Nov 2025 20:08:15 +0100 Subject: [PATCH 26/75] Update at-risk cards with accessibility improvements (#17427) --- .../all-applications.component.html | 111 +++++++++---- .../all-applications.component.ts | 6 +- .../critical-applications.component.html | 148 +++++++++++------- .../critical-applications.component.ts | 13 +- 4 files changed, 186 insertions(+), 92 deletions(-) diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/all-applications.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/all-applications.component.html index 26beaf349a9..b1c2faa4f05 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/all-applications.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/all-applications.component.html @@ -5,42 +5,85 @@ <div class="tw-mt-4 tw-flex tw-flex-col"> <h2 class="tw-mb-6" bitTypography="h2">{{ "allApplications" | i18n }}</h2> <div class="tw-flex tw-gap-6"> - <button - type="button" - class="tw-flex-1" - tabindex="0" - (click)="dataService.setDrawerForOrgAtRiskMembers('allAppsOrgAtRiskMembers')" + <div + role="region" + [attr.aria-label]="'atRiskMembers' | i18n" + class="tw-flex-1 tw-box-border tw-bg-background tw-block tw-text-main tw-border-solid tw-border tw-border-secondary-300 tw-rounded-lg tw-p-4" + [ngClass]="{ + 'tw-bg-primary-100': drawerDetails.invokerId === 'allAppsOrgAtRiskMembers', + }" > - <dirt-card - #allAppsOrgAtRiskMembers - class="tw-w-full" - [ngClass]="{ - 'tw-bg-primary-100': drawerDetails.invokerId === 'allAppsOrgAtRiskMembers', - }" - [title]="'atRiskMembers' | i18n" - [value]="applicationSummary.totalAtRiskMemberCount" - [maxValue]="applicationSummary.totalMemberCount" - > - </dirt-card> - </button> - <button - type="button" - class="tw-flex-1" - tabindex="0" - (click)="dataService.setDrawerForOrgAtRiskApps('allAppsOrgAtRiskApplications')" + <div class="tw-flex tw-flex-col tw-gap-1"> + <span bitTypography="h6" class="tw-flex tw-text-main" id="allAppsOrgAtRiskMembersLabel">{{ + "atRiskMembers" | i18n + }}</span> + <div class="tw-flex tw-items-baseline tw-gap-2" role="status" aria-live="polite"> + <span + bitTypography="h3" + class="!tw-mb-0" + aria-describedby="allAppsOrgAtRiskMembersLabel" + >{{ applicationSummary.totalAtRiskMemberCount }}</span + > + <span bitTypography="body2">{{ + "cardMetrics" | i18n: applicationSummary.totalMemberCount + }}</span> + </div> + <div class="tw-flex tw-items-baseline tw-mt-1 tw-gap-2"> + <p bitTypography="body1" class="tw-mb-0"> + <button + type="button" + bitLink + [attr.aria-label]="('viewAtRiskMembers' | i18n) + ': ' + ('atRiskMembers' | i18n)" + (click)="dataService.setDrawerForOrgAtRiskMembers('allAppsOrgAtRiskMembers')" + > + {{ "viewAtRiskMembers" | i18n }} + </button> + </p> + </div> + </div> + </div> + <div + role="region" + [attr.aria-label]="'atRiskApplications' | i18n" + class="tw-flex-1 tw-box-border tw-bg-background tw-block tw-text-main tw-border-solid tw-border tw-border-secondary-300 tw-rounded-lg tw-p-4" + [ngClass]="{ + 'tw-bg-primary-100': drawerDetails.invokerId === 'allAppsOrgAtRiskApplications', + }" > - <dirt-card - #allAppsOrgAtRiskApplications - class="tw-w-full" - [ngClass]="{ - 'tw-bg-primary-100': drawerDetails.invokerId === 'allAppsOrgAtRiskApplications', - }" - [title]="'atRiskApplications' | i18n" - [value]="applicationSummary.totalAtRiskApplicationCount" - [maxValue]="applicationSummary.totalApplicationCount" - > - </dirt-card> - </button> + <div class="tw-flex tw-flex-col tw-gap-1"> + <span + bitTypography="h6" + class="tw-flex tw-text-main" + id="allAppsOrgAtRiskApplicationsLabel" + >{{ "atRiskApplications" | i18n }}</span + > + <div class="tw-flex tw-items-baseline tw-gap-2" role="status" aria-live="polite"> + <span + bitTypography="h3" + class="!tw-mb-0" + aria-describedby="allAppsOrgAtRiskApplicationsLabel" + >{{ applicationSummary.totalAtRiskApplicationCount }}</span + > + <span bitTypography="body2">{{ + "cardMetrics" | i18n: applicationSummary.totalApplicationCount + }}</span> + </div> + <div class="tw-flex tw-items-baseline tw-mt-1 tw-gap-2"> + <p bitTypography="body1" class="tw-mb-0"> + <button + type="button" + bitLink + [attr.aria-label]=" + ('viewAtRiskApplications' | i18n) + ': ' + ('atRiskApplications' | i18n) + " + (click)="dataService.setDrawerForOrgAtRiskApps('allAppsOrgAtRiskApplications')" + > + {{ "viewAtRiskApplications" | i18n }} + </button> + </p> + </div> + </div> + </div> </div> <div class="tw-flex tw-mt-8 tw-mb-4 tw-gap-4"> <bit-search 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 b4e2bf466b9..acad2901ba4 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 @@ -18,12 +18,13 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { IconButtonModule, + LinkModule, NoItemsModule, SearchModule, TableDataSource, ToastService, + TypographyModule, } from "@bitwarden/components"; -import { CardComponent } from "@bitwarden/dirt-card"; import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module"; import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; @@ -39,13 +40,14 @@ import { ApplicationsLoadingComponent } from "../shared/risk-insights-loading.co imports: [ ApplicationsLoadingComponent, HeaderModule, - CardComponent, + LinkModule, SearchModule, PipesModule, NoItemsModule, SharedModule, AppTableRowScrollableComponent, IconButtonModule, + TypographyModule, ], }) export class AllApplicationsComponent implements OnInit { 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 04c7bd23797..332a91d28c1 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 @@ -1,3 +1,4 @@ +@let drawerDetails = dataService.drawerDetails$ | async; <div class="tw-mt-4 tw-flex tw-flex-col"> <div class="tw-flex tw-justify-between tw-mb-4"> <h2 bitTypography="h2">{{ "criticalApplications" | i18n }}</h2> @@ -16,60 +17,101 @@ }} </button> </div> - @if (dataService.drawerDetails$ | async; as drawerDetails) { - <div class="tw-flex tw-gap-6"> - <button - type="button" - class="tw-flex-1" - tabindex="0" - (click)="dataService.setDrawerForCriticalAtRiskMembers('criticalAppsAtRiskMembers')" - > - <dirt-card - #criticalAppsAtRiskMembers - class="tw-w-full" - [ngClass]="{ - 'tw-bg-primary-100': drawerDetails.invokerId === 'criticalAppsAtRiskMembers', - }" - [title]="'atRiskMembers' | i18n" - [value]="applicationSummary.totalAtRiskMemberCount" - [maxValue]="applicationSummary.totalMemberCount" - > - </dirt-card> - </button> - <button - type="button" - class="tw-flex-1" - tabindex="0" - (click)="dataService.setDrawerForCriticalAtRiskApps('criticalAppsAtRiskApplications')" - > - <dirt-card - #criticalAppsAtRiskApplications - class="tw-w-full" - [ngClass]="{ - 'tw-bg-primary-100': drawerDetails.invokerId === 'criticalAppsAtRiskApplications', - }" - [title]="'atRiskApplications' | i18n" - [value]="applicationSummary.totalAtRiskApplicationCount" - [maxValue]="applicationSummary.totalApplicationCount" - > - </dirt-card> - </button> + <div class="tw-flex tw-gap-6"> + <div + role="region" + [attr.aria-label]="'atRiskMembers' | i18n" + class="tw-flex-1 tw-box-border tw-bg-background tw-block tw-text-main tw-border-solid tw-border tw-border-secondary-300 tw-rounded-lg tw-p-4" + [ngClass]="{ + 'tw-bg-primary-100': drawerDetails.invokerId === 'criticalAppsAtRiskMembers', + }" + > + <div class="tw-flex tw-flex-col tw-gap-1"> + <span bitTypography="h6" class="tw-flex tw-text-main" id="criticalAppsAtRiskMembersLabel">{{ + "atRiskMembers" | i18n + }}</span> + <div class="tw-flex tw-items-baseline tw-gap-2" role="status" aria-live="polite"> + <span + bitTypography="h3" + class="!tw-mb-0" + aria-describedby="criticalAppsAtRiskMembersLabel" + >{{ applicationSummary.totalAtRiskMemberCount }}</span + > + <span bitTypography="body2">{{ + "cardMetrics" | i18n: applicationSummary.totalMemberCount + }}</span> + </div> + <div class="tw-flex tw-items-baseline tw-mt-1 tw-gap-2"> + <p bitTypography="body1" class="tw-mb-0"> + <button + type="button" + bitLink + [attr.aria-label]="('viewAtRiskMembers' | i18n) + ': ' + ('atRiskMembers' | i18n)" + (click)="dataService.setDrawerForCriticalAtRiskMembers('criticalAppsAtRiskMembers')" + > + {{ "viewAtRiskMembers" | i18n }} + </button> + </p> + </div> + </div> </div> - <div class="tw-flex tw-mt-8 tw-mb-4 tw-gap-4"> - <bit-search - [placeholder]="'searchApps' | i18n" - class="tw-grow" - [formControl]="searchControl" - ></bit-search> + <div + role="region" + [attr.aria-label]="'atRiskApplications' | i18n" + class="tw-flex-1 tw-box-border tw-bg-background tw-block tw-text-main tw-border-solid tw-border tw-border-secondary-300 tw-rounded-lg tw-p-4" + [ngClass]="{ + 'tw-bg-primary-100': drawerDetails.invokerId === 'criticalAppsAtRiskApplications', + }" + > + <div class="tw-flex tw-flex-col tw-gap-1"> + <span + bitTypography="h6" + class="tw-flex tw-text-main" + id="criticalAppsAtRiskApplicationsLabel" + >{{ "atRiskApplications" | i18n }}</span + > + <div class="tw-flex tw-items-baseline tw-gap-2" role="status" aria-live="polite"> + <span + bitTypography="h3" + class="!tw-mb-0" + aria-describedby="criticalAppsAtRiskApplicationsLabel" + >{{ applicationSummary.totalAtRiskApplicationCount }}</span + > + <span bitTypography="body2">{{ + "cardMetrics" | i18n: applicationSummary.totalApplicationCount + }}</span> + </div> + <div class="tw-flex tw-items-baseline tw-mt-1 tw-gap-2"> + <p bitTypography="body1" class="tw-mb-0"> + <button + type="button" + bitLink + [attr.aria-label]=" + ('viewAtRiskApplications' | i18n) + ': ' + ('atRiskApplications' | i18n) + " + (click)="dataService.setDrawerForCriticalAtRiskApps('criticalAppsAtRiskApplications')" + > + {{ "viewAtRiskApplications" | i18n }} + </button> + </p> + </div> + </div> </div> + </div> + <div class="tw-flex tw-mt-8 tw-mb-4 tw-gap-4"> + <bit-search + [placeholder]="'searchApps' | i18n" + class="tw-grow" + [formControl]="searchControl" + ></bit-search> + </div> - <app-table-row-scrollable - [dataSource]="dataSource" - [showRowCheckBox]="false" - [showRowMenuForCriticalApps]="true" - [openApplication]="drawerDetails.invokerId || ''" - [showAppAtRiskMembers]="showAppAtRiskMembers" - [unmarkAsCritical]="removeCriticalApplication" - ></app-table-row-scrollable> - } + <app-table-row-scrollable + [dataSource]="dataSource" + [showRowCheckBox]="false" + [showRowMenuForCriticalApps]="true" + [openApplication]="drawerDetails.invokerId || ''" + [showAppAtRiskMembers]="showAppAtRiskMembers" + [unmarkAsCritical]="removeCriticalApplication" + ></app-table-row-scrollable> </div> diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.ts index 7b7ca8c42da..1ea745929db 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.ts @@ -17,8 +17,14 @@ import { createNewSummaryData } from "@bitwarden/bit-common/dirt/reports/risk-in import { OrganizationReportSummary } from "@bitwarden/bit-common/dirt/reports/risk-insights/models/report-models"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { OrganizationId } from "@bitwarden/common/types/guid"; -import { NoItemsModule, SearchModule, TableDataSource, ToastService } from "@bitwarden/components"; -import { CardComponent } from "@bitwarden/dirt-card"; +import { + LinkModule, + NoItemsModule, + SearchModule, + TableDataSource, + ToastService, + TypographyModule, +} from "@bitwarden/components"; import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.module"; import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; @@ -33,13 +39,14 @@ import { AccessIntelligenceSecurityTasksService } from "../shared/security-tasks selector: "dirt-critical-applications", templateUrl: "./critical-applications.component.html", imports: [ - CardComponent, HeaderModule, + LinkModule, SearchModule, NoItemsModule, PipesModule, SharedModule, AppTableRowScrollableComponent, + TypographyModule, ], }) export class CriticalApplicationsComponent implements OnInit { From de17b33dd51236f4b01a0e93dfc4e2f6308ea10f Mon Sep 17 00:00:00 2001 From: SmithThe4th <gsmith@bitwarden.com> Date: Mon, 17 Nov 2025 15:10:50 -0500 Subject: [PATCH 27/75] handle empty strings in identity view for sdk cipher encryption (#17423) --- .../src/vault/models/view/identity.view.ts | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/libs/common/src/vault/models/view/identity.view.ts b/libs/common/src/vault/models/view/identity.view.ts index dca54fa04e8..fadfafb8777 100644 --- a/libs/common/src/vault/models/view/identity.view.ts +++ b/libs/common/src/vault/models/view/identity.view.ts @@ -94,16 +94,16 @@ export class IdentityView extends ItemView implements SdkIdentityView { this.lastName != null ) { let name = ""; - if (this.title != null) { + if (!Utils.isNullOrWhitespace(this.title)) { name += this.title + " "; } - if (this.firstName != null) { + if (!Utils.isNullOrWhitespace(this.firstName)) { name += this.firstName + " "; } - if (this.middleName != null) { + if (!Utils.isNullOrWhitespace(this.middleName)) { name += this.middleName + " "; } - if (this.lastName != null) { + if (!Utils.isNullOrWhitespace(this.lastName)) { name += this.lastName; } return name.trim(); @@ -130,14 +130,20 @@ export class IdentityView extends ItemView implements SdkIdentityView { } get fullAddressPart2(): string | undefined { - if (this.city == null && this.state == null && this.postalCode == null) { + const hasCity = !Utils.isNullOrWhitespace(this.city); + const hasState = !Utils.isNullOrWhitespace(this.state); + const hasPostalCode = !Utils.isNullOrWhitespace(this.postalCode); + + if (!hasCity && !hasState && !hasPostalCode) { return undefined; } - const city = this.city || "-"; + + const city = hasCity ? this.city : "-"; const state = this.state; - const postalCode = this.postalCode || "-"; + const postalCode = hasPostalCode ? this.postalCode : "-"; + let addressPart2 = city; - if (!Utils.isNullOrWhitespace(state)) { + if (hasState) { addressPart2 += ", " + state; } addressPart2 += ", " + postalCode; From 610537535a73c6b250de3c9287c13348b5a7756d Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Mon, 17 Nov 2025 14:14:03 -0600 Subject: [PATCH 28/75] persist archive date when cloning a cipher (#16986) --- .../src/cipher-form/components/cipher-form.component.spec.ts | 4 ++-- .../vault/src/cipher-form/components/cipher-form.component.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts b/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts index a002956b54a..1e60ad91fb1 100644 --- a/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts +++ b/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts @@ -154,13 +154,13 @@ describe("CipherFormComponent", () => { expect(component["updatedCipherView"]?.login.fido2Credentials).toBeNull(); }); - it("clears archiveDate on updatedCipherView", async () => { + it("does not clear archiveDate on updatedCipherView", async () => { cipherView.archivedDate = new Date(); decryptCipher.mockResolvedValue(cipherView); await component.ngOnInit(); - expect(component["updatedCipherView"]?.archivedDate).toBeNull(); + expect(component["updatedCipherView"]?.archivedDate).toBe(cipherView.archivedDate); }); }); diff --git a/libs/vault/src/cipher-form/components/cipher-form.component.ts b/libs/vault/src/cipher-form/components/cipher-form.component.ts index 5e75ea5bc24..f94af25e90a 100644 --- a/libs/vault/src/cipher-form/components/cipher-form.component.ts +++ b/libs/vault/src/cipher-form/components/cipher-form.component.ts @@ -281,7 +281,6 @@ export class CipherFormComponent implements AfterViewInit, OnInit, OnChanges, Ci if (this.config.mode === "clone") { this.updatedCipherView.id = null; - this.updatedCipherView.archivedDate = null; if (this.updatedCipherView.login) { this.updatedCipherView.login.fido2Credentials = null; From 670f3514baabf87ac01497df68ebee920f21b594 Mon Sep 17 00:00:00 2001 From: Jordan Aasen <166539328+jaasen-livefront@users.noreply.github.com> Date: Mon, 17 Nov 2025 12:36:37 -0800 Subject: [PATCH 29/75] [PM-23384] - Browser extension spotlight directing to Premium signup in web (#17343) * premium upgrade nudge * add specs * clean up vault template and specs * fix date comparison. add more specs for date * fix spec * fix specs * make prop private --- apps/browser/src/_locales/en/messages.json | 9 + .../vault-v2/vault-v2.component.html | 9 + .../vault-v2/vault-v2.component.spec.ts | 564 ++++++++++++++++++ .../components/vault-v2/vault-v2.component.ts | 48 ++ .../services/vault-popup-items.service.ts | 5 + .../src/vault/services/nudges.service.ts | 1 + 6 files changed, 636 insertions(+) create mode 100644 apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.spec.ts diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 2384cf8c4ec..f793b24a0e9 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html index 5bca9cddd4f..faaa6a40e98 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html @@ -30,6 +30,15 @@ <!-- Show search & filters outside of the scroll area of the page --> <ng-container slot="above-scroll-area"> + <ng-container *ngIf="showPremiumSpotlight$ | async"> + <bit-spotlight + [title]="'upgradeCompleteSecurity' | i18n" + [subtitle]="'premiumGivesMoreTools' | i18n" + [buttonText]="'explorePremium' | i18n" + (onButtonClick)="showPremiumDialog()" + (onDismiss)="dismissVaultNudgeSpotlight(NudgeType.PremiumUpgrade)" + ></bit-spotlight> + </ng-container> <ng-container *ngIf="vaultState === VaultStateEnum.Empty && showEmptyVaultSpotlight$ | async"> <bit-spotlight [title]="'emptyVaultNudgeTitle' | i18n" diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.spec.ts new file mode 100644 index 00000000000..563ec5f9709 --- /dev/null +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.spec.ts @@ -0,0 +1,564 @@ +import { CdkVirtualScrollableElement } from "@angular/cdk/scrolling"; +import { ChangeDetectionStrategy, Component, input, NO_ERRORS_SCHEMA } from "@angular/core"; +import { TestBed, fakeAsync, flush, tick } from "@angular/core/testing"; +import { By } from "@angular/platform-browser"; +import { ActivatedRoute, Router } from "@angular/router"; +import { RouterTestingModule } from "@angular/router/testing"; +import { mock } from "jest-mock-extended"; +import { BehaviorSubject, Observable, Subject, of } from "rxjs"; + +import { PremiumUpgradeDialogComponent } from "@bitwarden/angular/billing/components"; +import { NudgeType, NudgesService } from "@bitwarden/angular/vault"; +import { VaultProfileService } from "@bitwarden/angular/vault/services/vault-profile.service"; +import { CurrentAccountComponent } from "@bitwarden/browser/auth/popup/account-switching/current-account.component"; +import AutofillService from "@bitwarden/browser/autofill/services/autofill.service"; +import { PopOutComponent } from "@bitwarden/browser/platform/popup/components/pop-out.component"; +import { PopupHeaderComponent } from "@bitwarden/browser/platform/popup/layout/popup-header.component"; +import { PopupRouterCacheService } from "@bitwarden/browser/platform/popup/view-cache/popup-router-cache.service"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service"; +import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; +import { TaskService } from "@bitwarden/common/vault/tasks"; +import { DialogService } from "@bitwarden/components"; +import { StateProvider } from "@bitwarden/state"; +import { DecryptionFailureDialogComponent } from "@bitwarden/vault"; + +import { BrowserApi } from "../../../../platform/browser/browser-api"; +import BrowserPopupUtils from "../../../../platform/browser/browser-popup-utils"; +import { IntroCarouselService } from "../../services/intro-carousel.service"; +import { VaultPopupAutofillService } from "../../services/vault-popup-autofill.service"; +import { VaultPopupCopyButtonsService } from "../../services/vault-popup-copy-buttons.service"; +import { VaultPopupItemsService } from "../../services/vault-popup-items.service"; +import { VaultPopupListFiltersService } from "../../services/vault-popup-list-filters.service"; +import { VaultPopupScrollPositionService } from "../../services/vault-popup-scroll-position.service"; +import { AtRiskPasswordCalloutComponent } from "../at-risk-callout/at-risk-password-callout.component"; + +import { AutofillVaultListItemsComponent } from "./autofill-vault-list-items/autofill-vault-list-items.component"; +import { BlockedInjectionBanner } from "./blocked-injection-banner/blocked-injection-banner.component"; +import { NewItemDropdownV2Component } from "./new-item-dropdown/new-item-dropdown-v2.component"; +import { VaultHeaderV2Component } from "./vault-header/vault-header-v2.component"; +import { VaultListItemsContainerComponent } from "./vault-list-items-container/vault-list-items-container.component"; +import { VaultV2Component } from "./vault-v2.component"; + +@Component({ + selector: "popup-header", + standalone: true, + template: "", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PopupHeaderStubComponent { + readonly pageTitle = input(""); +} + +@Component({ + selector: "app-vault-header-v2", + standalone: true, + template: "", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class VaultHeaderV2StubComponent {} + +@Component({ + selector: "app-current-account", + standalone: true, + template: "", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class CurrentAccountStubComponent {} + +@Component({ + selector: "app-new-item-dropdown", + standalone: true, + template: "", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class NewItemDropdownStubComponent { + readonly initialValues = input(); +} + +@Component({ + selector: "app-pop-out", + standalone: true, + template: "", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class PopOutStubComponent {} + +@Component({ + selector: "blocked-injection-banner", + standalone: true, + template: "", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class BlockedInjectionBannerStubComponent {} + +@Component({ + selector: "vault-at-risk-password-callout", + standalone: true, + template: "", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class VaultAtRiskCalloutStubComponent {} + +@Component({ + selector: "app-autofill-vault-list-items", + standalone: true, + template: "", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class AutofillVaultListItemsStubComponent {} + +@Component({ + selector: "app-vault-list-items-container", + standalone: true, + template: "", + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class VaultListItemsContainerStubComponent { + readonly title = input<string>(); + readonly ciphers = input<any[]>(); + readonly id = input<string>(); + readonly disableSectionMargin = input<boolean>(); + readonly collapsibleKey = input<string>(); +} + +const mockDialogRef = { + close: jest.fn(), + afterClosed: jest.fn().mockReturnValue(of(undefined)), +} as unknown as import("@bitwarden/components").DialogRef<any, any>; + +jest + .spyOn(PremiumUpgradeDialogComponent, "open") + .mockImplementation((_: DialogService) => mockDialogRef as any); + +jest + .spyOn(DecryptionFailureDialogComponent, "open") + .mockImplementation((_: DialogService, _params: any) => mockDialogRef as any); +jest.spyOn(BrowserApi, "isPopupOpen").mockResolvedValue(false); +jest.spyOn(BrowserPopupUtils, "openCurrentPagePopout").mockResolvedValue(); + +describe("VaultV2Component", () => { + let component: VaultV2Component; + + interface FakeAccount { + id: string; + } + + function queryAllSpotlights(fixture: any): HTMLElement[] { + return Array.from(fixture.nativeElement.querySelectorAll("bit-spotlight")) as HTMLElement[]; + } + + const itemsSvc: any = { + emptyVault$: new BehaviorSubject<boolean>(false), + noFilteredResults$: new BehaviorSubject<boolean>(false), + showDeactivatedOrg$: new BehaviorSubject<boolean>(false), + favoriteCiphers$: new BehaviorSubject<any[]>([]), + remainingCiphers$: new BehaviorSubject<any[]>([]), + cipherCount$: new BehaviorSubject<number>(0), + loading$: new BehaviorSubject<boolean>(true), + } as Partial<VaultPopupItemsService>; + + const filtersSvc = { + allFilters$: new Subject<any>(), + filters$: new BehaviorSubject<any>({}), + filterVisibilityState$: new BehaviorSubject<any>({}), + } as Partial<VaultPopupListFiltersService>; + + const accountActive$ = new BehaviorSubject<FakeAccount | null>({ id: "user-1" }); + + const cipherSvc = { + failedToDecryptCiphers$: jest.fn().mockReturnValue(of([])), + } as Partial<CipherService>; + + const nudgesSvc = { + showNudgeSpotlight$: jest.fn().mockImplementation((_type: NudgeType) => of(false)), + dismissNudge: jest.fn().mockResolvedValue(undefined), + } as Partial<NudgesService>; + + const dialogSvc = {} as Partial<DialogService>; + + const introSvc = { + setIntroCarouselDismissed: jest.fn().mockResolvedValue(undefined), + } as Partial<IntroCarouselService>; + + const scrollSvc = { + start: jest.fn(), + stop: jest.fn(), + } as Partial<VaultPopupScrollPositionService>; + + function getObs<T = unknown>(cmp: any, key: string): Observable<T> { + return cmp[key] as Observable<T>; + } + + const hasPremiumFromAnySource$ = new BehaviorSubject<boolean>(false); + + const billingSvc = { + hasPremiumFromAnySource$: (_: string) => hasPremiumFromAnySource$, + }; + + const vaultProfileSvc = { + getProfileCreationDate: jest + .fn() + .mockResolvedValue(new Date(Date.now() - 8 * 24 * 60 * 60 * 1000)), // 8 days ago + }; + + beforeEach(async () => { + jest.clearAllMocks(); + await TestBed.configureTestingModule({ + imports: [VaultV2Component, RouterTestingModule], + providers: [ + { provide: VaultPopupItemsService, useValue: itemsSvc }, + { provide: VaultPopupListFiltersService, useValue: filtersSvc }, + { provide: VaultPopupScrollPositionService, useValue: scrollSvc }, + { + provide: AccountService, + useValue: { activeAccount$: accountActive$ }, + }, + { provide: CipherService, useValue: cipherSvc }, + { provide: DialogService, useValue: dialogSvc }, + { provide: IntroCarouselService, useValue: introSvc }, + { provide: NudgesService, useValue: nudgesSvc }, + { + provide: VaultProfileService, + useValue: vaultProfileSvc, + }, + { + provide: VaultPopupCopyButtonsService, + useValue: { showQuickCopyActions$: new BehaviorSubject<boolean>(false) }, + }, + { + provide: BillingAccountProfileStateService, + useValue: billingSvc, + }, + { + provide: I18nService, + useValue: { translate: (key: string) => key, t: (key: string) => key }, + }, + { provide: PopupRouterCacheService, useValue: mock<PopupRouterCacheService>() }, + { provide: RestrictedItemTypesService, useValue: { restricted$: new BehaviorSubject([]) } }, + { provide: PlatformUtilsService, useValue: mock<PlatformUtilsService>() }, + { provide: AvatarService, useValue: mock<AvatarService>() }, + { provide: ActivatedRoute, useValue: mock<ActivatedRoute>() }, + { provide: AuthService, useValue: mock<AuthService>() }, + { provide: AutofillService, useValue: mock<AutofillService>() }, + { + provide: VaultPopupAutofillService, + useValue: mock<VaultPopupAutofillService>(), + }, + { provide: TaskService, useValue: mock<TaskService>() }, + { provide: StateProvider, useValue: mock<StateProvider>() }, + { + provide: ConfigService, + useValue: { + getFeatureFlag$: (_: string) => of(false), + }, + }, + ], + schemas: [NO_ERRORS_SCHEMA], + }).compileComponents(); + + TestBed.overrideComponent(VaultV2Component, { + remove: { + imports: [ + PopupHeaderComponent, + VaultHeaderV2Component, + CurrentAccountComponent, + NewItemDropdownV2Component, + PopOutComponent, + BlockedInjectionBanner, + AtRiskPasswordCalloutComponent, + AutofillVaultListItemsComponent, + VaultListItemsContainerComponent, + ], + }, + add: { + imports: [ + PopupHeaderStubComponent, + VaultHeaderV2StubComponent, + CurrentAccountStubComponent, + NewItemDropdownStubComponent, + PopOutStubComponent, + BlockedInjectionBannerStubComponent, + VaultAtRiskCalloutStubComponent, + AutofillVaultListItemsStubComponent, + VaultListItemsContainerStubComponent, + ], + }, + }); + + const fixture = TestBed.createComponent(VaultV2Component); + component = fixture.componentInstance; + }); + + describe("vaultState", () => { + type ExpectedKey = "Empty" | "DeactivatedOrg" | "NoResults" | null; + + const cases: [string, boolean, boolean, boolean, ExpectedKey][] = [ + ["null when none true", false, false, false, null], + ["Empty when empty true only", true, false, false, "Empty"], + ["DeactivatedOrg when only deactivated true", false, false, true, "DeactivatedOrg"], + ["NoResults when only noResults true", false, true, false, "NoResults"], + ]; + + it.each(cases)( + "%s", + fakeAsync( + ( + _label: string, + empty: boolean, + noResults: boolean, + deactivated: boolean, + expectedKey: ExpectedKey, + ) => { + const empty$ = itemsSvc.emptyVault$ as BehaviorSubject<boolean>; + const noResults$ = itemsSvc.noFilteredResults$ as BehaviorSubject<boolean>; + const deactivated$ = itemsSvc.showDeactivatedOrg$ as BehaviorSubject<boolean>; + + empty$.next(empty); + noResults$.next(noResults); + deactivated$.next(deactivated); + tick(); + + const expectedValue = + expectedKey === null ? null : (component as any).VaultStateEnum[expectedKey]; + + expect((component as any).vaultState).toBe(expectedValue); + }, + ), + ); + }); + + it("loading$ is true when items loading or filters missing; false when both ready", () => { + const itemsLoading$ = itemsSvc.loading$ as unknown as BehaviorSubject<boolean>; + const allFilters$ = filtersSvc.allFilters$ as unknown as Subject<any>; + + const values: boolean[] = []; + getObs<boolean>(component, "loading$").subscribe((v) => values.push(!!v)); + + itemsLoading$.next(true); + + allFilters$.next({}); + + itemsLoading$.next(false); + + expect(values[values.length - 1]).toBe(false); + }); + + it("ngAfterViewInit waits for allFilters$ then starts scroll position service", fakeAsync(() => { + const allFilters$ = filtersSvc.allFilters$ as unknown as Subject<any>; + + (component as any).virtualScrollElement = {} as CdkVirtualScrollableElement; + + component.ngAfterViewInit(); + expect(scrollSvc.start).not.toHaveBeenCalled(); + + allFilters$.next({ any: true }); + tick(); + + expect(scrollSvc.start).toHaveBeenCalledTimes(1); + expect(scrollSvc.start).toHaveBeenCalledWith((component as any).virtualScrollElement); + + flush(); + })); + + it("showPremiumDialog opens PremiumUpgradeDialogComponent", () => { + component["showPremiumDialog"](); + expect(PremiumUpgradeDialogComponent.open).toHaveBeenCalledTimes(1); + }); + + it("navigateToImport navigates and opens popout if popup is open", fakeAsync(async () => { + (BrowserApi.isPopupOpen as jest.Mock).mockResolvedValueOnce(true); + + const ngRouter = TestBed.inject(Router); + jest.spyOn(ngRouter, "navigate").mockResolvedValue(true as any); + + await component["navigateToImport"](); + + expect(ngRouter.navigate).toHaveBeenCalledWith(["/import"]); + + expect(BrowserPopupUtils.openCurrentPagePopout).toHaveBeenCalled(); + })); + + it("navigateToImport does not popout when popup is not open", fakeAsync(async () => { + (BrowserApi.isPopupOpen as jest.Mock).mockResolvedValueOnce(false); + + const ngRouter = TestBed.inject(Router); + jest.spyOn(ngRouter, "navigate").mockResolvedValue(true as any); + + await component["navigateToImport"](); + + expect(ngRouter.navigate).toHaveBeenCalledWith(["/import"]); + expect(BrowserPopupUtils.openCurrentPagePopout).not.toHaveBeenCalled(); + })); + + it("ngOnInit dismisses intro carousel and opens decryption dialog for non-deleted failures", fakeAsync(() => { + (cipherSvc.failedToDecryptCiphers$ as any).mockReturnValue( + of([ + { id: "a", isDeleted: false }, + { id: "b", isDeleted: true }, + { id: "c", isDeleted: false }, + ]), + ); + + void component.ngOnInit(); + tick(); + + expect(introSvc.setIntroCarouselDismissed).toHaveBeenCalled(); + + expect(DecryptionFailureDialogComponent.open).toHaveBeenCalledWith(expect.any(Object), { + cipherIds: ["a", "c"], + }); + + flush(); + })); + + it("dismissVaultNudgeSpotlight forwards to NudgesService with active user id", fakeAsync(() => { + const spy = jest.spyOn(nudgesSvc, "dismissNudge").mockResolvedValue(undefined); + + accountActive$.next({ id: "user-xyz" }); + + void component.ngOnInit(); + tick(); + + void component["dismissVaultNudgeSpotlight"](NudgeType.HasVaultItems); + tick(); + + expect(spy).toHaveBeenCalledWith(NudgeType.HasVaultItems, "user-xyz"); + })); + + it("accountAgeInDays$ computes integer days since creation", (done) => { + getObs<number | null>(component, "accountAgeInDays$").subscribe((days) => { + if (days !== null) { + expect(days).toBeGreaterThanOrEqual(7); + done(); + } + }); + + void component.ngOnInit(); + }); + + it("renders Premium spotlight when eligible and opens dialog on click", fakeAsync(() => { + itemsSvc.cipherCount$.next(10); + + hasPremiumFromAnySource$.next(false); + + (nudgesSvc.showNudgeSpotlight$ as jest.Mock).mockImplementation((type: NudgeType) => + of(type === NudgeType.PremiumUpgrade), + ); + + const fixture = TestBed.createComponent(VaultV2Component); + const component = fixture.componentInstance; + + void component.ngOnInit(); + + fixture.detectChanges(); + tick(); + + fixture.detectChanges(); + + const spotlights = Array.from( + fixture.nativeElement.querySelectorAll("bit-spotlight"), + ) as HTMLElement[]; + expect(spotlights.length).toBe(1); + + const spotDe = fixture.debugElement.query(By.css("bit-spotlight")); + expect(spotDe).toBeTruthy(); + + spotDe.triggerEventHandler("onButtonClick", undefined); + fixture.detectChanges(); + + expect(PremiumUpgradeDialogComponent.open).toHaveBeenCalledTimes(1); + })); + + it("renders Empty-Vault spotlight when vaultState is Empty and nudge is on", fakeAsync(() => { + itemsSvc.emptyVault$.next(true); + + (nudgesSvc.showNudgeSpotlight$ as jest.Mock).mockImplementation((type: NudgeType) => { + return of(type === NudgeType.EmptyVaultNudge); + }); + + const fixture = TestBed.createComponent(VaultV2Component); + fixture.detectChanges(); + tick(); + + const spotlights = queryAllSpotlights(fixture); + expect(spotlights.length).toBe(1); + + expect(fixture.nativeElement.textContent).toContain("emptyVaultNudgeTitle"); + })); + + it("renders Has-Items spotlight when vault has items and nudge is on", fakeAsync(() => { + itemsSvc.emptyVault$.next(false); + + (nudgesSvc.showNudgeSpotlight$ as jest.Mock).mockImplementation((type: NudgeType) => { + return of(type === NudgeType.HasVaultItems); + }); + + const fixture = TestBed.createComponent(VaultV2Component); + fixture.detectChanges(); + tick(); + + const spotlights = queryAllSpotlights(fixture); + expect(spotlights.length).toBe(1); + + expect(fixture.nativeElement.textContent).toContain("hasItemsVaultNudgeTitle"); + })); + + it("does not render Premium spotlight when account is less than a week old", fakeAsync(() => { + itemsSvc.cipherCount$.next(10); + hasPremiumFromAnySource$.next(false); + + vaultProfileSvc.getProfileCreationDate = jest + .fn() + .mockResolvedValue(new Date(Date.now() - 3 * 24 * 60 * 60 * 1000)); // 3 days ago + + (nudgesSvc.showNudgeSpotlight$ as jest.Mock).mockImplementation((type: NudgeType) => { + return of(type === NudgeType.PremiumUpgrade); + }); + + const fixture = TestBed.createComponent(VaultV2Component); + fixture.detectChanges(); + tick(); + + const spotlights = queryAllSpotlights(fixture); + expect(spotlights.length).toBe(0); + })); + + it("does not render Premium spotlight when vault has less than 5 items", fakeAsync(() => { + itemsSvc.cipherCount$.next(3); + hasPremiumFromAnySource$.next(false); + + (nudgesSvc.showNudgeSpotlight$ as jest.Mock).mockImplementation((type: NudgeType) => { + return of(type === NudgeType.PremiumUpgrade); + }); + + const fixture = TestBed.createComponent(VaultV2Component); + fixture.detectChanges(); + tick(); + + const spotlights = queryAllSpotlights(fixture); + expect(spotlights.length).toBe(0); + })); + + it("does not render Premium spotlight when user already has premium", fakeAsync(() => { + itemsSvc.cipherCount$.next(10); + hasPremiumFromAnySource$.next(true); + + (nudgesSvc.showNudgeSpotlight$ as jest.Mock).mockImplementation((type: NudgeType) => { + return of(type === NudgeType.PremiumUpgrade); + }); + + const fixture = TestBed.createComponent(VaultV2Component); + fixture.detectChanges(); + tick(); + + const spotlights = queryAllSpotlights(fixture); + expect(spotlights.length).toBe(0); + })); +}); diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts index e55a702d350..499e9b76757 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts @@ -9,6 +9,7 @@ import { distinctUntilChanged, filter, firstValueFrom, + from, map, Observable, shareReplay, @@ -17,12 +18,15 @@ import { tap, } from "rxjs"; +import { PremiumUpgradeDialogComponent } from "@bitwarden/angular/billing/components"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { NudgesService, NudgeType } from "@bitwarden/angular/vault"; import { SpotlightComponent } from "@bitwarden/angular/vault/components/spotlight/spotlight.component"; +import { VaultProfileService } from "@bitwarden/angular/vault/services/vault-profile.service"; import { DeactivatedOrg, NoResults, VaultOpen } from "@bitwarden/assets/svg"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; 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"; @@ -124,13 +128,55 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { void this.liveAnnouncer.announce(this.i18nService.translate(key), "polite"); }), ); + private skeletonFeatureFlag$ = this.configService.getFeatureFlag$( FeatureFlag.VaultLoadingSkeletons, ); + private showPremiumNudgeSpotlight$ = this.activeUserId$.pipe( + switchMap((userId) => this.nudgesService.showNudgeSpotlight$(NudgeType.PremiumUpgrade, userId)), + ); + protected favoriteCiphers$ = this.vaultPopupItemsService.favoriteCiphers$; protected remainingCiphers$ = this.vaultPopupItemsService.remainingCiphers$; protected allFilters$ = this.vaultPopupListFiltersService.allFilters$; + protected cipherCount$ = this.vaultPopupItemsService.cipherCount$; + protected hasPremium$ = this.activeUserId$.pipe( + switchMap((userId) => this.billingAccountService.hasPremiumFromAnySource$(userId)), + ); + protected accountAgeInDays$ = this.activeUserId$.pipe( + switchMap((userId) => { + const creationDate$ = from(this.vaultProfileService.getProfileCreationDate(userId)); + return creationDate$.pipe( + map((creationDate) => { + if (!creationDate) { + return 0; + } + const ageInMilliseconds = Date.now() - creationDate.getTime(); + return Math.floor(ageInMilliseconds / (1000 * 60 * 60 * 24)); + }), + ); + }), + ); + + protected showPremiumSpotlight$ = combineLatest([ + this.showPremiumNudgeSpotlight$, + this.showEmptyVaultSpotlight$, + this.showHasItemsVaultSpotlight$, + this.hasPremium$, + this.cipherCount$, + this.accountAgeInDays$, + ]).pipe( + map( + ([showNudge, emptyVault, hasItems, hasPremium, count, age]) => + showNudge && !emptyVault && !hasItems && !hasPremium && count >= 5 && age >= 7, + ), + shareReplay({ bufferSize: 1, refCount: true }), + ); + + showPremiumDialog() { + PremiumUpgradeDialogComponent.open(this.dialogService); + } /** When true, show spinner loading state */ protected showSpinnerLoaders$ = combineLatest([this.loading$, this.skeletonFeatureFlag$]).pipe( @@ -177,6 +223,8 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { private introCarouselService: IntroCarouselService, private nudgesService: NudgesService, private router: Router, + private vaultProfileService: VaultProfileService, + private billingAccountService: BillingAccountProfileStateService, private liveAnnouncer: LiveAnnouncer, private i18nService: I18nService, private configService: ConfigService, 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 afe9d61d5af..321d7936806 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 @@ -288,6 +288,11 @@ export class VaultPopupItemsService { map((ciphers) => !ciphers.length), ); + /** + * Observable that contains the count of ciphers in the active filtered list. + */ + cipherCount$: Observable<number> = this._activeCipherList$.pipe(map((ciphers) => ciphers.length)); + /** * Observable that indicates whether there are no ciphers to show with the current filter. */ diff --git a/libs/angular/src/vault/services/nudges.service.ts b/libs/angular/src/vault/services/nudges.service.ts index 4c77ff38bf6..05d565eb499 100644 --- a/libs/angular/src/vault/services/nudges.service.ts +++ b/libs/angular/src/vault/services/nudges.service.ts @@ -37,6 +37,7 @@ export const NudgeType = { NewNoteItemStatus: "new-note-item-status", NewSshItemStatus: "new-ssh-item-status", GeneratorNudgeStatus: "generator-nudge-status", + PremiumUpgrade: "premium-upgrade", } as const; export type NudgeType = UnionOfValues<typeof NudgeType>; From 413a024e61935b49e2b272ff8789ce2f7029dd51 Mon Sep 17 00:00:00 2001 From: aj-bw <81774843+aj-bw@users.noreply.github.com> Date: Mon, 17 Nov 2025 16:33:12 -0500 Subject: [PATCH 30/75] removal of freebsd build, upload, release and other references (#17354) --- .github/workflows/build-desktop.yml | 7 ------- .github/workflows/release-desktop.yml | 1 - apps/desktop/desktop_native/napi/index.js | 6 ------ apps/desktop/electron-builder.json | 5 +---- 4 files changed, 1 insertion(+), 18 deletions(-) diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 102fbdbbdc8..27a7bfe4124 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -270,13 +270,6 @@ jobs: path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm if-no-files-found: error - - name: Upload .freebsd artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 - with: - name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd - path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd - if-no-files-found: error - - name: Upload .snap artifact uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 with: diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 53132d8647c..10a0f581faa 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -107,7 +107,6 @@ jobs: with: artifacts: "apps/desktop/artifacts/Bitwarden-${{ env.PKG_VERSION }}-amd64.deb, apps/desktop/artifacts/Bitwarden-${{ env.PKG_VERSION }}-x86_64.rpm, - apps/desktop/artifacts/Bitwarden-${{ env.PKG_VERSION }}-x64.freebsd, apps/desktop/artifacts/bitwarden_${{ env.PKG_VERSION }}_amd64.snap, apps/desktop/artifacts/bitwarden_${{ env.PKG_VERSION }}_arm64.snap, apps/desktop/artifacts/bitwarden_${{ env.PKG_VERSION }}_arm64.tar.gz, diff --git a/apps/desktop/desktop_native/napi/index.js b/apps/desktop/desktop_native/napi/index.js index acfd0dffb89..64819be4405 100644 --- a/apps/desktop/desktop_native/napi/index.js +++ b/apps/desktop/desktop_native/napi/index.js @@ -78,12 +78,6 @@ switch (platform) { throw new Error(`Unsupported architecture on macOS: ${arch}`); } break; - case "freebsd": - nativeBinding = loadFirstAvailable( - ["desktop_napi.freebsd-x64.node"], - "@bitwarden/desktop-napi-freebsd-x64", - ); - break; case "linux": switch (arch) { case "x64": diff --git a/apps/desktop/electron-builder.json b/apps/desktop/electron-builder.json index eaa299db508..6e89799e9c4 100644 --- a/apps/desktop/electron-builder.json +++ b/apps/desktop/electron-builder.json @@ -116,7 +116,7 @@ "to": "libprocess_isolation.so" } ], - "target": ["deb", "freebsd", "rpm", "AppImage", "snap"], + "target": ["deb", "rpm", "AppImage", "snap"], "desktop": { "entry": { "Name": "Bitwarden", @@ -252,9 +252,6 @@ "artifactName": "${productName}-${version}-${arch}.${ext}", "fpm": ["--rpm-rpmbuild-define", "_build_id_links none"] }, - "freebsd": { - "artifactName": "${productName}-${version}-${arch}.${ext}" - }, "snap": { "summary": "Bitwarden is a secure and free password manager for all of your devices.", "description": "Password Manager\n**Installation**\nBitwarden requires access to the `password-manager-service`. Please enable it through permissions or by running `sudo snap connect bitwarden:password-manager-service` after installation. See https://btwrdn.com/install-snap for details.", From 8f04f258180a3ccf4dad13d480c35345adbec1d8 Mon Sep 17 00:00:00 2001 From: Maximilian Power <mpower@bitwarden.com> Date: Tue, 18 Nov 2025 09:37:31 +0100 Subject: [PATCH 31/75] Fix Firefox phishing blocker continue button by awaiting tab navigation promises (#17436) --- apps/browser/src/platform/browser/browser-api.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/browser/src/platform/browser/browser-api.ts b/apps/browser/src/platform/browser/browser-api.ts index 723df95ef63..cfc39fa18a1 100644 --- a/apps/browser/src/platform/browser/browser-api.ts +++ b/apps/browser/src/platform/browser/browser-api.ts @@ -268,11 +268,11 @@ export class BrowserApi { static async closeTab(tabId: number): Promise<void> { if (tabId) { if (BrowserApi.isWebExtensionsApi) { - browser.tabs.remove(tabId).catch((error) => { + await browser.tabs.remove(tabId).catch((error) => { throw new Error("[BrowserApi] Failed to remove current tab: " + error.message); }); } else if (BrowserApi.isChromeApi) { - chrome.tabs.remove(tabId).catch((error) => { + await chrome.tabs.remove(tabId).catch((error) => { throw new Error("[BrowserApi] Failed to remove current tab: " + error.message); }); } @@ -288,7 +288,7 @@ export class BrowserApi { static async navigateTabToUrl(tabId: number, url: URL): Promise<void> { if (tabId) { if (BrowserApi.isWebExtensionsApi) { - browser.tabs.update(tabId, { url: url.href }).catch((error) => { + await browser.tabs.update(tabId, { url: url.href }).catch((error) => { throw new Error("Failed to navigate tab to URL: " + error.message); }); } else if (BrowserApi.isChromeApi) { From 9efc31534ba36c627d699661609a84e8a783875c Mon Sep 17 00:00:00 2001 From: Oscar Hinton <Hinton@users.noreply.github.com> Date: Tue, 18 Nov 2025 13:26:38 +0100 Subject: [PATCH 32/75] [PM-28231] Enable component-class-suffix (#17384) * Enable component-class-suffix * Rename file --- .../pages/phishing-warning.component.ts | 2 ++ .../at-risk-passwords.component.spec.ts | 2 ++ .../assign-collections.component.ts | 2 ++ .../blocked-injection-banner.component.ts | 2 ++ .../vault-generator-dialog.component.spec.ts | 2 ++ .../adjust-subscription.component.ts | 2 ++ .../vertical-step.component.ts | 2 ++ .../app/layouts/header/web-header.stories.ts | 8 ++++---- .../web-generator-dialog.component.spec.ts | 2 ++ .../individual-vault/add-edit-v2.component.ts | 2 ++ eslint.config.mjs | 2 +- .../src/button/button.component.spec.ts | 10 +++++----- .../chip-select/chip-select.component.spec.ts | 8 ++++---- .../src/chip-select/chip-select.component.ts | 4 ++-- .../src/dialog/dialog.service.stories.ts | 4 ++-- .../simple-dialog.service.stories.ts | 12 ++++++------ .../src/form-control/form-control.module.ts | 6 +++--- .../src/form-control/label.component.ts | 2 +- .../src/form-field/error-summary.component.ts | 2 +- .../src/form-field/form-field.component.ts | 4 ++-- .../src/form-field/form-field.module.ts | 6 +++--- libs/components/src/menu/index.ts | 2 +- ...em.directive.ts => menu-item.component.ts} | 2 +- .../src/menu/menu.component.spec.ts | 8 ++++---- libs/components/src/menu/menu.component.ts | 6 +++--- libs/components/src/menu/menu.module.ts | 6 +++--- .../radio-group.component.spec.ts | 10 +++++----- .../src/radio-button/radio-group.component.ts | 4 ++-- .../components/kitchen-sink-form.component.ts | 2 +- .../components/kitchen-sink-main.component.ts | 19 ++++++++++++------- .../kitchen-sink-table.component.ts | 2 +- .../kitchen-sink-toggle-list.component.ts | 2 +- .../kitchen-sink/kitchen-sink.stories.ts | 12 ++++++------ .../src/switch/switch.component.spec.ts | 6 +++--- .../components/src/switch/switch.component.ts | 4 ++-- .../toggle-group.component.spec.ts | 10 +++++----- .../copy-cipher-field.directive.spec.ts | 8 ++++---- .../components/copy-cipher-field.directive.ts | 10 +++++----- 38 files changed, 111 insertions(+), 88 deletions(-) rename libs/components/src/menu/{menu-item.directive.ts => menu-item.component.ts} (96%) diff --git a/apps/browser/src/dirt/phishing-detection/pages/phishing-warning.component.ts b/apps/browser/src/dirt/phishing-detection/pages/phishing-warning.component.ts index 2b91a28122c..589b880b206 100644 --- a/apps/browser/src/dirt/phishing-detection/pages/phishing-warning.component.ts +++ b/apps/browser/src/dirt/phishing-detection/pages/phishing-warning.component.ts @@ -47,6 +47,8 @@ import { TypographyModule, ], }) +// FIXME(https://bitwarden.atlassian.net/browse/PM-28231): Use Component suffix +// eslint-disable-next-line @angular-eslint/component-class-suffix export class PhishingWarning { private activatedRoute = inject(ActivatedRoute); private messageSender = inject(MessageSender); diff --git a/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.spec.ts b/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.spec.ts index 96c597113a5..9b972fd0f3e 100644 --- a/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.spec.ts +++ b/apps/browser/src/vault/popup/components/at-risk-passwords/at-risk-passwords.component.spec.ts @@ -61,6 +61,8 @@ class MockPopupPageComponent { template: `<ng-content></ng-content>`, changeDetection: ChangeDetectionStrategy.OnPush, }) +// FIXME(https://bitwarden.atlassian.net/browse/PM-28231): Use Component suffix +// eslint-disable-next-line @angular-eslint/component-class-suffix class MockAppIcon { readonly cipher = input<CipherView | undefined>(undefined); } diff --git a/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.ts b/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.ts index b314c48fecd..546a352111e 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.ts @@ -49,6 +49,8 @@ import { PopupPageComponent } from "../../../../../platform/popup/layout/popup-p PopOutComponent, ], }) +// FIXME(https://bitwarden.atlassian.net/browse/PM-28231): Use Component suffix +// eslint-disable-next-line @angular-eslint/component-class-suffix export class AssignCollections { /** Params needed to populate the assign collections component */ params: CollectionAssignmentParams; diff --git a/apps/browser/src/vault/popup/components/vault-v2/blocked-injection-banner/blocked-injection-banner.component.ts b/apps/browser/src/vault/popup/components/vault-v2/blocked-injection-banner/blocked-injection-banner.component.ts index 2125af289a2..44a033137de 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/blocked-injection-banner/blocked-injection-banner.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/blocked-injection-banner/blocked-injection-banner.component.ts @@ -30,6 +30,8 @@ const blockedURISettingsRoute = "/blocked-domains"; selector: "blocked-injection-banner", templateUrl: "blocked-injection-banner.component.html", }) +// FIXME(https://bitwarden.atlassian.net/browse/PM-28231): Use Component suffix +// eslint-disable-next-line @angular-eslint/component-class-suffix export class BlockedInjectionBanner implements OnInit { /** * Flag indicating that the banner should be shown diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.spec.ts index 2139b6d9a4f..8fa48dc5d79 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-generator-dialog/vault-generator-dialog.component.spec.ts @@ -24,6 +24,8 @@ import { selector: "vault-cipher-form-generator", template: "", }) +// FIXME(https://bitwarden.atlassian.net/browse/PM-28231): Use Component suffix +// eslint-disable-next-line @angular-eslint/component-class-suffix class MockCipherFormGenerator { // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-signals diff --git a/apps/web/src/app/billing/organizations/adjust-subscription.component.ts b/apps/web/src/app/billing/organizations/adjust-subscription.component.ts index 7ee5891e8a9..255e1ef544c 100644 --- a/apps/web/src/app/billing/organizations/adjust-subscription.component.ts +++ b/apps/web/src/app/billing/organizations/adjust-subscription.component.ts @@ -23,6 +23,8 @@ import { ToastService } from "@bitwarden/components"; templateUrl: "adjust-subscription.component.html", standalone: false, }) +// FIXME(https://bitwarden.atlassian.net/browse/PM-28231): Use Component suffix +// eslint-disable-next-line @angular-eslint/component-class-suffix export class AdjustSubscription implements OnInit, OnDestroy { // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-signals diff --git a/apps/web/src/app/billing/trial-initiation/vertical-stepper/vertical-step.component.ts b/apps/web/src/app/billing/trial-initiation/vertical-stepper/vertical-step.component.ts index efd0f68e5d1..f79bb5a4451 100644 --- a/apps/web/src/app/billing/trial-initiation/vertical-stepper/vertical-step.component.ts +++ b/apps/web/src/app/billing/trial-initiation/vertical-stepper/vertical-step.component.ts @@ -9,6 +9,8 @@ import { Component, Input } from "@angular/core"; providers: [{ provide: CdkStep, useExisting: VerticalStep }], standalone: false, }) +// FIXME(https://bitwarden.atlassian.net/browse/PM-28231): Use Component suffix +// eslint-disable-next-line @angular-eslint/component-class-suffix export class VerticalStep extends CdkStep { // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-signals diff --git a/apps/web/src/app/layouts/header/web-header.stories.ts b/apps/web/src/app/layouts/header/web-header.stories.ts index ccc68cf5db9..88c98f01e6c 100644 --- a/apps/web/src/app/layouts/header/web-header.stories.ts +++ b/apps/web/src/app/layouts/header/web-header.stories.ts @@ -53,7 +53,7 @@ class MockStateService { template: `<button type="button" bitIconButton="bwi-filter" label="Switch products"></button>`, standalone: false, }) -class MockProductSwitcher {} +class MockProductSwitcherComponent {} // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @@ -62,7 +62,7 @@ class MockProductSwitcher {} template: `<bit-avatar [text]="name$ | async"></bit-avatar>`, imports: [CommonModule, AvatarModule], }) -class MockDynamicAvatar implements Partial<DynamicAvatarComponent> { +class MockDynamicAvatarComponent implements Partial<DynamicAvatarComponent> { protected name$ = combineLatest([ this.stateService.accounts$, this.stateService.activeAccount$, @@ -100,9 +100,9 @@ export default { TabsModule, TypographyModule, NavigationModule, - MockDynamicAvatar, + MockDynamicAvatarComponent, ], - declarations: [WebHeaderComponent, MockProductSwitcher], + declarations: [WebHeaderComponent, MockProductSwitcherComponent], providers: [ { provide: StateService, useClass: MockStateService }, { diff --git a/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.spec.ts b/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.spec.ts index c2d6c87d865..82eb1180961 100644 --- a/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.spec.ts +++ b/apps/web/src/app/vault/components/web-generator-dialog/web-generator-dialog.component.spec.ts @@ -22,6 +22,8 @@ import { selector: "vault-cipher-form-generator", template: "", }) +// FIXME(https://bitwarden.atlassian.net/browse/PM-28231): Use Component suffix +// eslint-disable-next-line @angular-eslint/component-class-suffix class MockCipherFormGenerator { // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-signals diff --git a/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts b/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts index 41c922cf4fe..5277d4a0aea 100644 --- a/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts +++ b/apps/web/src/app/vault/individual-vault/add-edit-v2.component.ts @@ -78,6 +78,8 @@ export interface AddEditCipherDialogCloseResult { ], providers: [{ provide: CipherFormGenerationService, useClass: WebCipherFormGenerationService }], }) +// FIXME(https://bitwarden.atlassian.net/browse/PM-28231): Use Component suffix +// eslint-disable-next-line @angular-eslint/component-class-suffix export class AddEditComponentV2 implements OnInit { config: CipherFormConfig; headerText: string; diff --git a/eslint.config.mjs b/eslint.config.mjs index bfa8b6ec079..df98b3af424 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -61,7 +61,7 @@ export default tseslint.config( "rxjs/no-exposed-subjects": ["error", { allowProtected: true }], // TODO: Enable these. - "@angular-eslint/component-class-suffix": 0, + "@angular-eslint/component-class-suffix": "error", "@angular-eslint/contextual-lifecycle": "error", "@angular-eslint/directive-class-suffix": 0, "@angular-eslint/no-empty-lifecycle-method": 0, diff --git a/libs/components/src/button/button.component.spec.ts b/libs/components/src/button/button.component.spec.ts index 745be014a0c..4eb92227d22 100644 --- a/libs/components/src/button/button.component.spec.ts +++ b/libs/components/src/button/button.component.spec.ts @@ -5,19 +5,19 @@ import { By } from "@angular/platform-browser"; import { ButtonModule } from "./index"; describe("Button", () => { - let fixture: ComponentFixture<TestApp>; - let testAppComponent: TestApp; + let fixture: ComponentFixture<TestAppComponent>; + let testAppComponent: TestAppComponent; let buttonDebugElement: DebugElement; let disabledButtonDebugElement: DebugElement; let linkDebugElement: DebugElement; beforeEach(async () => { TestBed.configureTestingModule({ - imports: [TestApp], + imports: [TestAppComponent], }); await TestBed.compileComponents(); - fixture = TestBed.createComponent(TestApp); + fixture = TestBed.createComponent(TestAppComponent); testAppComponent = fixture.debugElement.componentInstance; buttonDebugElement = fixture.debugElement.query(By.css("button")); disabledButtonDebugElement = fixture.debugElement.query(By.css("button#disabled")); @@ -86,7 +86,7 @@ describe("Button", () => { `, imports: [ButtonModule], }) -class TestApp { +class TestAppComponent { buttonType?: string; block?: boolean; disabled?: boolean; diff --git a/libs/components/src/chip-select/chip-select.component.spec.ts b/libs/components/src/chip-select/chip-select.component.spec.ts index 2485a5bca7e..3c2f71ef8d7 100644 --- a/libs/components/src/chip-select/chip-select.component.spec.ts +++ b/libs/components/src/chip-select/chip-select.component.spec.ts @@ -27,7 +27,7 @@ const mockI18nService = { describe("ChipSelectComponent", () => { let component: ChipSelectComponent<string>; - let fixture: ComponentFixture<TestApp>; + let fixture: ComponentFixture<TestAppComponent>; const testOptions: ChipSelectOption<string>[] = [ { label: "Option 1", value: "opt1", icon: "bwi-folder" }, @@ -58,11 +58,11 @@ describe("ChipSelectComponent", () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [TestApp, NoopAnimationsModule], + imports: [TestAppComponent, NoopAnimationsModule], providers: [{ provide: I18nService, useValue: mockI18nService }], }).compileComponents(); - fixture = TestBed.createComponent(TestApp); + fixture = TestBed.createComponent(TestAppComponent); fixture.detectChanges(); component = fixture.debugElement.query(By.directive(ChipSelectComponent)).componentInstance; @@ -468,7 +468,7 @@ describe("ChipSelectComponent", () => { imports: [ChipSelectComponent], changeDetection: ChangeDetectionStrategy.OnPush, }) -class TestApp { +class TestAppComponent { readonly options = signal<ChipSelectOption<string>[]>([ { label: "Option 1", value: "opt1", icon: "bwi-folder" }, { label: "Option 2", value: "opt2" }, diff --git a/libs/components/src/chip-select/chip-select.component.ts b/libs/components/src/chip-select/chip-select.component.ts index e0f8b343040..bf6c6fb2aad 100644 --- a/libs/components/src/chip-select/chip-select.component.ts +++ b/libs/components/src/chip-select/chip-select.component.ts @@ -20,7 +20,7 @@ import { compareValues } from "@bitwarden/common/platform/misc/compare-values"; import { ButtonModule } from "../button"; import { IconButtonModule } from "../icon-button"; -import { MenuComponent, MenuItemDirective, MenuModule, MenuTriggerForDirective } from "../menu"; +import { MenuComponent, MenuItemComponent, MenuModule, MenuTriggerForDirective } from "../menu"; import { Option } from "../select/option"; import { SharedModule } from "../shared"; import { TypographyModule } from "../typography"; @@ -51,7 +51,7 @@ export class ChipSelectComponent<T = unknown> implements ControlValueAccessor { private readonly cdr = inject(ChangeDetectorRef); readonly menu = viewChild(MenuComponent); - readonly menuItems = viewChildren(MenuItemDirective); + readonly menuItems = viewChildren(MenuItemComponent); readonly chipSelectButton = viewChild<ElementRef<HTMLButtonElement>>("chipSelectButton"); readonly menuTrigger = viewChild(MenuTriggerForDirective); diff --git a/libs/components/src/dialog/dialog.service.stories.ts b/libs/components/src/dialog/dialog.service.stories.ts index 9bcb704a7fd..0921e9a9def 100644 --- a/libs/components/src/dialog/dialog.service.stories.ts +++ b/libs/components/src/dialog/dialog.service.stories.ts @@ -47,7 +47,7 @@ class StoryDialogComponent { } openDialogNonDismissable() { - this.dialogService.open(NonDismissableContent, { + this.dialogService.open(NonDismissableContentComponent, { data: { animal: "panda", }, @@ -117,7 +117,7 @@ class StoryDialogContentComponent { `, imports: [DialogModule, ButtonModule], }) -class NonDismissableContent { +class NonDismissableContentComponent { constructor( public dialogRef: DialogRef, @Inject(DIALOG_DATA) private data: Animal, diff --git a/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts b/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts index b682b9f772a..7e03ad60ca2 100644 --- a/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts +++ b/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts @@ -33,7 +33,7 @@ class StoryDialogComponent { constructor(public dialogService: DialogService) {} openSimpleDialog() { - this.dialogService.open(SimpleDialogContent, { + this.dialogService.open(SimpleDialogContentComponent, { data: { animal: "panda", }, @@ -42,7 +42,7 @@ class StoryDialogComponent { } openNonDismissableWithPrimaryButtonDialog() { - this.dialogService.open(NonDismissableWithPrimaryButtonContent, { + this.dialogService.open(NonDismissableWithPrimaryButtonContentComponent, { data: { animal: "panda", }, @@ -52,7 +52,7 @@ class StoryDialogComponent { } openNonDismissableWithNoButtonsDialog() { - this.dialogService.open(NonDismissableWithNoButtonsContent, { + this.dialogService.open(NonDismissableWithNoButtonsContentComponent, { data: { animal: "panda", }, @@ -83,7 +83,7 @@ class StoryDialogComponent { `, imports: [ButtonModule, DialogModule], }) -class SimpleDialogContent { +class SimpleDialogContentComponent { constructor( public dialogRef: DialogRef, @Inject(DIALOG_DATA) private data: Animal, @@ -114,7 +114,7 @@ class SimpleDialogContent { `, imports: [ButtonModule, DialogModule], }) -class NonDismissableWithPrimaryButtonContent { +class NonDismissableWithPrimaryButtonContentComponent { constructor( public dialogRef: DialogRef, @Inject(DIALOG_DATA) private data: Animal, @@ -140,7 +140,7 @@ class NonDismissableWithPrimaryButtonContent { `, imports: [ButtonModule, DialogModule], }) -class NonDismissableWithNoButtonsContent { +class NonDismissableWithNoButtonsContentComponent { constructor( public dialogRef: DialogRef, @Inject(DIALOG_DATA) private data: Animal, diff --git a/libs/components/src/form-control/form-control.module.ts b/libs/components/src/form-control/form-control.module.ts index df168d8e98f..2646f36ecd3 100644 --- a/libs/components/src/form-control/form-control.module.ts +++ b/libs/components/src/form-control/form-control.module.ts @@ -2,10 +2,10 @@ import { NgModule } from "@angular/core"; import { FormControlComponent } from "./form-control.component"; import { BitHintComponent } from "./hint.component"; -import { BitLabel } from "./label.component"; +import { BitLabelComponent } from "./label.component"; @NgModule({ - imports: [BitLabel, FormControlComponent, BitHintComponent], - exports: [FormControlComponent, BitLabel, BitHintComponent], + imports: [BitLabelComponent, FormControlComponent, BitHintComponent], + exports: [FormControlComponent, BitLabelComponent, BitHintComponent], }) export class FormControlModule {} diff --git a/libs/components/src/form-control/label.component.ts b/libs/components/src/form-control/label.component.ts index 57f9e338bb6..95133cae99f 100644 --- a/libs/components/src/form-control/label.component.ts +++ b/libs/components/src/form-control/label.component.ts @@ -17,7 +17,7 @@ let nextId = 0; "[id]": "id()", }, }) -export class BitLabel { +export class BitLabelComponent { constructor( private elementRef: ElementRef<HTMLInputElement>, @Optional() private parentFormControl: FormControlComponent, diff --git a/libs/components/src/form-field/error-summary.component.ts b/libs/components/src/form-field/error-summary.component.ts index cf26fd1e21f..cbcd172399a 100644 --- a/libs/components/src/form-field/error-summary.component.ts +++ b/libs/components/src/form-field/error-summary.component.ts @@ -16,7 +16,7 @@ import { I18nPipe } from "@bitwarden/ui-common"; }, imports: [I18nPipe], }) -export class BitErrorSummary { +export class BitErrorSummaryComponent { readonly formGroup = input<UntypedFormGroup>(); get errorCount(): number { diff --git a/libs/components/src/form-field/form-field.component.ts b/libs/components/src/form-field/form-field.component.ts index ccfdcec4132..3d49a58b1fc 100644 --- a/libs/components/src/form-field/form-field.component.ts +++ b/libs/components/src/form-field/form-field.component.ts @@ -16,7 +16,7 @@ import { import { I18nPipe } from "@bitwarden/ui-common"; import { BitHintComponent } from "../form-control/hint.component"; -import { BitLabel } from "../form-control/label.component"; +import { BitLabelComponent } from "../form-control/label.component"; import { inputBorderClasses } from "../input/input.directive"; import { BitErrorComponent } from "./error.component"; @@ -32,7 +32,7 @@ import { BitFormFieldControl } from "./form-field-control"; export class BitFormFieldComponent implements AfterContentChecked { readonly input = contentChild.required(BitFormFieldControl); readonly hint = contentChild(BitHintComponent); - readonly label = contentChild(BitLabel); + readonly label = contentChild(BitLabelComponent); readonly prefixContainer = viewChild<ElementRef<HTMLDivElement>>("prefixContainer"); readonly suffixContainer = viewChild<ElementRef<HTMLDivElement>>("suffixContainer"); diff --git a/libs/components/src/form-field/form-field.module.ts b/libs/components/src/form-field/form-field.module.ts index 88d7ffcc78b..c7529c14fb7 100644 --- a/libs/components/src/form-field/form-field.module.ts +++ b/libs/components/src/form-field/form-field.module.ts @@ -4,7 +4,7 @@ import { FormControlModule } from "../form-control"; import { InputModule } from "../input/input.module"; import { MultiSelectModule } from "../multi-select/multi-select.module"; -import { BitErrorSummary } from "./error-summary.component"; +import { BitErrorSummaryComponent } from "./error-summary.component"; import { BitErrorComponent } from "./error.component"; import { BitFormFieldComponent } from "./form-field.component"; import { BitPasswordInputToggleDirective } from "./password-input-toggle.directive"; @@ -18,7 +18,7 @@ import { BitSuffixDirective } from "./suffix.directive"; MultiSelectModule, BitErrorComponent, - BitErrorSummary, + BitErrorSummaryComponent, BitFormFieldComponent, BitPasswordInputToggleDirective, BitPrefixDirective, @@ -30,7 +30,7 @@ import { BitSuffixDirective } from "./suffix.directive"; MultiSelectModule, BitErrorComponent, - BitErrorSummary, + BitErrorSummaryComponent, BitFormFieldComponent, BitPasswordInputToggleDirective, BitPrefixDirective, diff --git a/libs/components/src/menu/index.ts b/libs/components/src/menu/index.ts index 52c48d2fb8e..69b71b8ee24 100644 --- a/libs/components/src/menu/index.ts +++ b/libs/components/src/menu/index.ts @@ -1,5 +1,5 @@ export * from "./menu.module"; export * from "./menu.component"; export * from "./menu-trigger-for.directive"; -export * from "./menu-item.directive"; +export * from "./menu-item.component"; export * from "./menu-divider.component"; diff --git a/libs/components/src/menu/menu-item.directive.ts b/libs/components/src/menu/menu-item.component.ts similarity index 96% rename from libs/components/src/menu/menu-item.directive.ts rename to libs/components/src/menu/menu-item.component.ts index e19e208f7b0..149fc3ca297 100644 --- a/libs/components/src/menu/menu-item.directive.ts +++ b/libs/components/src/menu/menu-item.component.ts @@ -10,7 +10,7 @@ import { Component, ElementRef, HostBinding, Input } from "@angular/core"; templateUrl: "menu-item.component.html", imports: [NgClass], }) -export class MenuItemDirective implements FocusableOption { +export class MenuItemComponent implements FocusableOption { @HostBinding("class") classList = [ "tw-block", "tw-w-full", diff --git a/libs/components/src/menu/menu.component.spec.ts b/libs/components/src/menu/menu.component.spec.ts index 59ad7c6dbc8..5ec614afade 100644 --- a/libs/components/src/menu/menu.component.spec.ts +++ b/libs/components/src/menu/menu.component.spec.ts @@ -7,7 +7,7 @@ import { MenuTriggerForDirective } from "./menu-trigger-for.directive"; import { MenuModule } from "./index"; describe("Menu", () => { - let fixture: ComponentFixture<TestApp>; + let fixture: ComponentFixture<TestAppComponent>; const getMenuTriggerDirective = () => { const buttonDebugElement = fixture.debugElement.query(By.directive(MenuTriggerForDirective)); return buttonDebugElement.injector.get(MenuTriggerForDirective); @@ -18,12 +18,12 @@ describe("Menu", () => { beforeEach(async () => { TestBed.configureTestingModule({ - imports: [TestApp], + imports: [TestAppComponent], }); await TestBed.compileComponents(); - fixture = TestBed.createComponent(TestApp); + fixture = TestBed.createComponent(TestAppComponent); fixture.detectChanges(); }); @@ -82,4 +82,4 @@ describe("Menu", () => { `, imports: [MenuModule], }) -class TestApp {} +class TestAppComponent {} diff --git a/libs/components/src/menu/menu.component.ts b/libs/components/src/menu/menu.component.ts index f32a790ef69..fc7a4673fea 100644 --- a/libs/components/src/menu/menu.component.ts +++ b/libs/components/src/menu/menu.component.ts @@ -10,7 +10,7 @@ import { contentChildren, } from "@angular/core"; -import { MenuItemDirective } from "./menu-item.directive"; +import { MenuItemComponent } from "./menu-item.component"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @@ -25,8 +25,8 @@ export class MenuComponent implements AfterContentInit { // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() closed = new EventEmitter<void>(); - readonly menuItems = contentChildren(MenuItemDirective, { descendants: true }); - keyManager?: FocusKeyManager<MenuItemDirective>; + readonly menuItems = contentChildren(MenuItemComponent, { descendants: true }); + keyManager?: FocusKeyManager<MenuItemComponent>; readonly ariaRole = input<"menu" | "dialog">("menu"); diff --git a/libs/components/src/menu/menu.module.ts b/libs/components/src/menu/menu.module.ts index 117460df559..a9a5e64dd9f 100644 --- a/libs/components/src/menu/menu.module.ts +++ b/libs/components/src/menu/menu.module.ts @@ -1,12 +1,12 @@ import { NgModule } from "@angular/core"; import { MenuDividerComponent } from "./menu-divider.component"; -import { MenuItemDirective } from "./menu-item.directive"; +import { MenuItemComponent } from "./menu-item.component"; import { MenuTriggerForDirective } from "./menu-trigger-for.directive"; import { MenuComponent } from "./menu.component"; @NgModule({ - imports: [MenuComponent, MenuTriggerForDirective, MenuItemDirective, MenuDividerComponent], - exports: [MenuComponent, MenuTriggerForDirective, MenuItemDirective, MenuDividerComponent], + imports: [MenuComponent, MenuTriggerForDirective, MenuItemComponent, MenuDividerComponent], + exports: [MenuComponent, MenuTriggerForDirective, MenuItemComponent, MenuDividerComponent], }) export class MenuModule {} diff --git a/libs/components/src/radio-button/radio-group.component.spec.ts b/libs/components/src/radio-button/radio-group.component.spec.ts index 94cc7bc25cb..8d70dde9a56 100644 --- a/libs/components/src/radio-button/radio-group.component.spec.ts +++ b/libs/components/src/radio-button/radio-group.component.spec.ts @@ -11,19 +11,19 @@ import { RadioButtonComponent } from "./radio-button.component"; import { RadioButtonModule } from "./radio-button.module"; describe("RadioGroupComponent", () => { - let fixture: ComponentFixture<TestApp>; - let testAppComponent: TestApp; + let fixture: ComponentFixture<TestAppComponent>; + let testAppComponent: TestAppComponent; let buttonElements: RadioButtonComponent[]; let radioButtons: HTMLInputElement[]; beforeEach(async () => { TestBed.configureTestingModule({ - imports: [TestApp], + imports: [TestAppComponent], providers: [{ provide: I18nService, useValue: new I18nMockService({}) }], }); await TestBed.compileComponents(); - fixture = TestBed.createComponent(TestApp); + fixture = TestBed.createComponent(TestAppComponent); fixture.detectChanges(); testAppComponent = fixture.debugElement.componentInstance; buttonElements = fixture.debugElement @@ -76,6 +76,6 @@ describe("RadioGroupComponent", () => { `, imports: [FormsModule, RadioButtonModule], }) -class TestApp { +class TestAppComponent { selected?: string; } diff --git a/libs/components/src/radio-button/radio-group.component.ts b/libs/components/src/radio-button/radio-group.component.ts index dd44623b313..7a1288b71e5 100644 --- a/libs/components/src/radio-button/radio-group.component.ts +++ b/libs/components/src/radio-button/radio-group.component.ts @@ -4,7 +4,7 @@ import { ControlValueAccessor, NgControl, Validators } from "@angular/forms"; import { I18nPipe } from "@bitwarden/ui-common"; -import { BitLabel } from "../form-control/label.component"; +import { BitLabelComponent } from "../form-control/label.component"; let nextId = 0; @@ -32,7 +32,7 @@ export class RadioGroupComponent implements ControlValueAccessor { readonly id = input(`bit-radio-group-${nextId++}`); @HostBinding("class") classList = ["tw-block", "tw-mb-4"]; - protected readonly label = contentChild(BitLabel); + protected readonly label = contentChild(BitLabelComponent); constructor(@Optional() @Self() private ngControl?: NgControl) { if (ngControl != null) { diff --git a/libs/components/src/stories/kitchen-sink/components/kitchen-sink-form.component.ts b/libs/components/src/stories/kitchen-sink/components/kitchen-sink-form.component.ts index 8f49415e0dd..0a9581fb375 100644 --- a/libs/components/src/stories/kitchen-sink/components/kitchen-sink-form.component.ts +++ b/libs/components/src/stories/kitchen-sink/components/kitchen-sink-form.component.ts @@ -134,7 +134,7 @@ import { KitchenSinkSharedModule } from "../kitchen-sink-shared.module"; </form> `, }) -export class KitchenSinkForm { +export class KitchenSinkFormComponent { constructor( public dialogService: DialogService, public formBuilder: FormBuilder, diff --git a/libs/components/src/stories/kitchen-sink/components/kitchen-sink-main.component.ts b/libs/components/src/stories/kitchen-sink/components/kitchen-sink-main.component.ts index f96217ffb63..0819ae3f06b 100644 --- a/libs/components/src/stories/kitchen-sink/components/kitchen-sink-main.component.ts +++ b/libs/components/src/stories/kitchen-sink/components/kitchen-sink-main.component.ts @@ -4,9 +4,9 @@ import { Component, signal } from "@angular/core"; import { DialogService } from "../../../dialog"; import { KitchenSinkSharedModule } from "../kitchen-sink-shared.module"; -import { KitchenSinkForm } from "./kitchen-sink-form.component"; -import { KitchenSinkTable } from "./kitchen-sink-table.component"; -import { KitchenSinkToggleList } from "./kitchen-sink-toggle-list.component"; +import { KitchenSinkFormComponent } from "./kitchen-sink-form.component"; +import { KitchenSinkTableComponent } from "./kitchen-sink-table.component"; +import { KitchenSinkToggleListComponent } from "./kitchen-sink-toggle-list.component"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @@ -83,7 +83,7 @@ import { KitchenSinkToggleList } from "./kitchen-sink-toggle-list.component"; </bit-dialog> `, }) -class KitchenSinkDialog { +class KitchenSinkDialogComponent { constructor(public dialogRef: DialogRef) {} } @@ -91,7 +91,12 @@ class KitchenSinkDialog { // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "bit-tab-main", - imports: [KitchenSinkSharedModule, KitchenSinkTable, KitchenSinkToggleList, KitchenSinkForm], + imports: [ + KitchenSinkSharedModule, + KitchenSinkTableComponent, + KitchenSinkToggleListComponent, + KitchenSinkFormComponent, + ], template: ` <bit-banner bannerType="info"> Kitchen Sink test zone </bit-banner> @@ -182,11 +187,11 @@ export class KitchenSinkMainComponent { protected readonly drawerOpen = signal(false); openDialog() { - this.dialogService.open(KitchenSinkDialog); + this.dialogService.open(KitchenSinkDialogComponent); } openDrawer() { - this.dialogService.openDrawer(KitchenSinkDialog); + this.dialogService.openDrawer(KitchenSinkDialogComponent); } navItems = [ diff --git a/libs/components/src/stories/kitchen-sink/components/kitchen-sink-table.component.ts b/libs/components/src/stories/kitchen-sink/components/kitchen-sink-table.component.ts index d8d2e20f9ae..69602100eae 100644 --- a/libs/components/src/stories/kitchen-sink/components/kitchen-sink-table.component.ts +++ b/libs/components/src/stories/kitchen-sink/components/kitchen-sink-table.component.ts @@ -57,4 +57,4 @@ import { KitchenSinkSharedModule } from "../kitchen-sink-shared.module"; </bit-table> `, }) -export class KitchenSinkTable {} +export class KitchenSinkTableComponent {} diff --git a/libs/components/src/stories/kitchen-sink/components/kitchen-sink-toggle-list.component.ts b/libs/components/src/stories/kitchen-sink/components/kitchen-sink-toggle-list.component.ts index 18846ce2831..f914c49cca5 100644 --- a/libs/components/src/stories/kitchen-sink/components/kitchen-sink-toggle-list.component.ts +++ b/libs/components/src/stories/kitchen-sink/components/kitchen-sink-toggle-list.component.ts @@ -28,7 +28,7 @@ import { KitchenSinkSharedModule } from "../kitchen-sink-shared.module"; } `, }) -export class KitchenSinkToggleList { +export class KitchenSinkToggleListComponent { selectedToggle: "all" | "large" | "small" = "all"; companyList = [ diff --git a/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts b/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts index a0bcbbffe04..c25eb778d11 100644 --- a/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts +++ b/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts @@ -19,10 +19,10 @@ import { I18nMockService } from "../../utils/i18n-mock.service"; import { positionFixedWrapperDecorator } from "../storybook-decorators"; import { DialogVirtualScrollBlockComponent } from "./components/dialog-virtual-scroll-block.component"; -import { KitchenSinkForm } from "./components/kitchen-sink-form.component"; +import { KitchenSinkFormComponent } from "./components/kitchen-sink-form.component"; import { KitchenSinkMainComponent } from "./components/kitchen-sink-main.component"; -import { KitchenSinkTable } from "./components/kitchen-sink-table.component"; -import { KitchenSinkToggleList } from "./components/kitchen-sink-toggle-list.component"; +import { KitchenSinkTableComponent } from "./components/kitchen-sink-table.component"; +import { KitchenSinkToggleListComponent } from "./components/kitchen-sink-toggle-list.component"; import { KitchenSinkSharedModule } from "./kitchen-sink-shared.module"; export default { @@ -33,10 +33,10 @@ export default { moduleMetadata({ imports: [ KitchenSinkSharedModule, - KitchenSinkForm, + KitchenSinkFormComponent, KitchenSinkMainComponent, - KitchenSinkTable, - KitchenSinkToggleList, + KitchenSinkTableComponent, + KitchenSinkToggleListComponent, ], }), applicationConfig({ diff --git a/libs/components/src/switch/switch.component.spec.ts b/libs/components/src/switch/switch.component.spec.ts index 10574c8084e..6a53316a1b4 100644 --- a/libs/components/src/switch/switch.component.spec.ts +++ b/libs/components/src/switch/switch.component.spec.ts @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from "@angular/core/testing"; import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from "@angular/forms"; import { By } from "@angular/platform-browser"; -import { BitLabel } from "../form-control/label.component"; +import { BitLabelComponent } from "../form-control/label.component"; import { SwitchComponent } from "./switch.component"; @@ -16,7 +16,7 @@ describe("SwitchComponent", () => { // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "test-host", - imports: [FormsModule, BitLabel, ReactiveFormsModule, SwitchComponent], + imports: [FormsModule, BitLabelComponent, ReactiveFormsModule, SwitchComponent], template: ` <form [formGroup]="formObj"> <bit-switch formControlName="switch"> @@ -77,7 +77,7 @@ describe("SwitchComponent", () => { selector: "test-selected-host", template: `<bit-switch [selected]="checked"><bit-label>Element</bit-label></bit-switch>`, standalone: true, - imports: [SwitchComponent, BitLabel], + imports: [SwitchComponent, BitLabelComponent], }) class TestSelectedHostComponent { checked = false; diff --git a/libs/components/src/switch/switch.component.ts b/libs/components/src/switch/switch.component.ts index 456fe5671b1..30e1ac59d48 100644 --- a/libs/components/src/switch/switch.component.ts +++ b/libs/components/src/switch/switch.component.ts @@ -14,7 +14,7 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; import { AriaDisableDirective } from "../a11y"; import { FormControlModule } from "../form-control/form-control.module"; import { BitHintComponent } from "../form-control/hint.component"; -import { BitLabel } from "../form-control/label.component"; +import { BitLabelComponent } from "../form-control/label.component"; let nextId = 0; @@ -43,7 +43,7 @@ let nextId = 0; }) export class SwitchComponent implements ControlValueAccessor, AfterViewInit { private el = inject(ElementRef<HTMLButtonElement>); - private readonly label = contentChild.required(BitLabel); + private readonly label = contentChild.required(BitLabelComponent); /** * Model signal for selected state binding when used outside of a form diff --git a/libs/components/src/toggle-group/toggle-group.component.spec.ts b/libs/components/src/toggle-group/toggle-group.component.spec.ts index 832741ae79c..3a9d35b862b 100644 --- a/libs/components/src/toggle-group/toggle-group.component.spec.ts +++ b/libs/components/src/toggle-group/toggle-group.component.spec.ts @@ -6,18 +6,18 @@ import { ToggleGroupModule } from "./toggle-group.module"; import { ToggleComponent } from "./toggle.component"; describe("Button", () => { - let fixture: ComponentFixture<TestApp>; - let testAppComponent: TestApp; + let fixture: ComponentFixture<TestAppComponent>; + let testAppComponent: TestAppComponent; let buttonElements: ToggleComponent<unknown>[]; let radioButtons: HTMLInputElement[]; beforeEach(async () => { TestBed.configureTestingModule({ - imports: [TestApp], + imports: [TestAppComponent], }); await TestBed.compileComponents(); - fixture = TestBed.createComponent(TestApp); + fixture = TestBed.createComponent(TestAppComponent); testAppComponent = fixture.debugElement.componentInstance; buttonElements = fixture.debugElement .queryAll(By.css("bit-toggle")) @@ -66,6 +66,6 @@ describe("Button", () => { `, imports: [ToggleGroupModule], }) -class TestApp { +class TestAppComponent { selected?: string; } diff --git a/libs/vault/src/components/copy-cipher-field.directive.spec.ts b/libs/vault/src/components/copy-cipher-field.directive.spec.ts index a3650c68c9b..e20fccd053f 100644 --- a/libs/vault/src/components/copy-cipher-field.directive.spec.ts +++ b/libs/vault/src/components/copy-cipher-field.directive.spec.ts @@ -5,7 +5,7 @@ import { Account, AccountService } from "@bitwarden/common/auth/abstractions/acc import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; -import { BitIconButtonComponent, MenuItemDirective } from "@bitwarden/components"; +import { BitIconButtonComponent, MenuItemComponent } from "@bitwarden/components"; import { CopyCipherFieldService } from "@bitwarden/vault"; import { CopyCipherFieldDirective } from "./copy-cipher-field.directive"; @@ -83,7 +83,7 @@ describe("CopyCipherFieldDirective", () => { }); it("updates menuItemDirective disabled state", async () => { - const menuItemDirective = { + const menuItemComponent = { disabled: false, }; @@ -91,14 +91,14 @@ describe("CopyCipherFieldDirective", () => { copyFieldService as unknown as CopyCipherFieldService, mockAccountService, mockCipherService, - menuItemDirective as unknown as MenuItemDirective, + menuItemComponent as unknown as MenuItemComponent, ); copyCipherFieldDirective.action = "totp"; await copyCipherFieldDirective.ngOnChanges(); - expect(menuItemDirective.disabled).toBe(true); + expect(menuItemComponent.disabled).toBe(true); }); }); diff --git a/libs/vault/src/components/copy-cipher-field.directive.ts b/libs/vault/src/components/copy-cipher-field.directive.ts index 7e8ca334f9e..b4b1941273f 100644 --- a/libs/vault/src/components/copy-cipher-field.directive.ts +++ b/libs/vault/src/components/copy-cipher-field.directive.ts @@ -10,7 +10,7 @@ import { CipherViewLike, CipherViewLikeUtils, } from "@bitwarden/common/vault/utils/cipher-view-like-utils"; -import { MenuItemDirective, BitIconButtonComponent } from "@bitwarden/components"; +import { MenuItemComponent, BitIconButtonComponent } from "@bitwarden/components"; import { CopyAction, CopyCipherFieldService } from "@bitwarden/vault"; /** @@ -47,7 +47,7 @@ export class CopyCipherFieldDirective implements OnChanges { private copyCipherFieldService: CopyCipherFieldService, private accountService: AccountService, private cipherService: CipherService, - @Optional() private menuItemDirective?: MenuItemDirective, + @Optional() private menuItemComponent?: MenuItemComponent, @Optional() private iconButtonComponent?: BitIconButtonComponent, ) {} @@ -60,7 +60,7 @@ export class CopyCipherFieldDirective implements OnChanges { */ @HostBinding("class.tw-hidden") private get hidden() { - return this.disabled && this.menuItemDirective; + return this.disabled && this.menuItemComponent; } @HostListener("click") @@ -87,8 +87,8 @@ export class CopyCipherFieldDirective implements OnChanges { } // If the directive is used on a menu item, update the menu item to prevent keyboard navigation - if (this.menuItemDirective) { - this.menuItemDirective.disabled = this.disabled ?? false; + if (this.menuItemComponent) { + this.menuItemComponent.disabled = this.disabled ?? false; } } From 2bf734bd433701d8eeeedf60ca9328ef31d1deb3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 08:48:29 -0600 Subject: [PATCH 33/75] [deps] Platform: Update @types/node to v22.19.1 (#17448) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .../native-messaging-test-runner/package-lock.json | 8 ++++---- apps/desktop/native-messaging-test-runner/package.json | 2 +- package-lock.json | 8 ++++---- package.json | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/desktop/native-messaging-test-runner/package-lock.json b/apps/desktop/native-messaging-test-runner/package-lock.json index a4286aabed9..9ad1ffb3ec0 100644 --- a/apps/desktop/native-messaging-test-runner/package-lock.json +++ b/apps/desktop/native-messaging-test-runner/package-lock.json @@ -19,7 +19,7 @@ "yargs": "18.0.0" }, "devDependencies": { - "@types/node": "22.19.0", + "@types/node": "22.19.1", "typescript": "5.4.2" } }, @@ -117,9 +117,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.19.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.0.tgz", - "integrity": "sha512-xpr/lmLPQEj+TUnHmR+Ab91/glhJvsqcjB+yY0Ix9GO70H6Lb4FHH5GeqdOE5btAx7eIMwuHkp4H2MSkLcqWbA==", + "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "license": "MIT", "peer": true, "dependencies": { diff --git a/apps/desktop/native-messaging-test-runner/package.json b/apps/desktop/native-messaging-test-runner/package.json index 55699af47dd..21a6ba3626a 100644 --- a/apps/desktop/native-messaging-test-runner/package.json +++ b/apps/desktop/native-messaging-test-runner/package.json @@ -24,7 +24,7 @@ "yargs": "18.0.0" }, "devDependencies": { - "@types/node": "22.19.0", + "@types/node": "22.19.1", "typescript": "5.4.2" }, "_moduleAliases": { diff --git a/package-lock.json b/package-lock.json index bb8355dc632..82d05df83d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -112,7 +112,7 @@ "@types/koa-json": "2.0.23", "@types/lowdb": "1.0.15", "@types/lunr": "2.3.7", - "@types/node": "22.19.0", + "@types/node": "22.19.1", "@types/node-fetch": "2.6.4", "@types/node-forge": "1.3.11", "@types/papaparse": "5.3.16", @@ -14391,9 +14391,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.19.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.0.tgz", - "integrity": "sha512-xpr/lmLPQEj+TUnHmR+Ab91/glhJvsqcjB+yY0Ix9GO70H6Lb4FHH5GeqdOE5btAx7eIMwuHkp4H2MSkLcqWbA==", + "version": "22.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.1.tgz", + "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" diff --git a/package.json b/package.json index f9757aa1e68..53fcb74ce80 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "@types/koa-json": "2.0.23", "@types/lowdb": "1.0.15", "@types/lunr": "2.3.7", - "@types/node": "22.19.0", + "@types/node": "22.19.1", "@types/node-fetch": "2.6.4", "@types/node-forge": "1.3.11", "@types/papaparse": "5.3.16", From bd2f6e75666f1604fbb3554d14c13496e4cb0de4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 15:49:45 +0100 Subject: [PATCH 34/75] [deps]: Update anchore/scan-action action to v7 (#16635) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build-web.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 719063958f7..89dd684c5f0 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -334,7 +334,7 @@ jobs: - name: Scan Docker image if: ${{ needs.setup.outputs.has_secrets == 'true' }} id: container-scan - uses: anchore/scan-action@1638637db639e0ade3258b51db49a9a137574c3e # v6.5.1 + uses: anchore/scan-action@568b89d27fc18c60e56937bff480c91c772cd993 # v7.1.0 with: image: ${{ steps.image-name.outputs.name }} fail-build: false From 20d44b5136f20d061bb3756b9f114757e74fad03 Mon Sep 17 00:00:00 2001 From: Leslie Tilton <23057410+Banrion@users.noreply.github.com> Date: Tue, 18 Nov 2025 09:03:05 -0600 Subject: [PATCH 35/75] [PM-27739] Fix bug for icons not showing on application tables (#17373) * Added function to get a cipher icon for application tables. Update all application component to use signal properties * Fix type * Handle no ciphers on application --- .../risk-insights-orchestrator.service.ts | 18 ++++ .../view/risk-insights-data.service.ts | 4 +- .../all-applications.component.html | 16 ++-- .../all-applications.component.ts | 87 ++++++++++++------- .../critical-applications.component.ts | 34 ++++++-- .../app-table-row-scrollable.component.html | 5 +- .../app-table-row-scrollable.component.ts | 7 +- 7 files changed, 117 insertions(+), 54 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 38e12373182..51d35570cd2 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 @@ -35,6 +35,7 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { CipherId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { CipherViewLike } from "@bitwarden/common/vault/utils/cipher-view-like-utils"; import { LogService } from "@bitwarden/logging"; import { @@ -191,6 +192,23 @@ export class RiskInsightsOrchestratorService { this._generateReportTriggerSubject.next(true); } + /** + * Gets the cipher icon for a given cipher ID + * + * @param cipherId The ID of the cipher to get the icon for + * @returns A CipherViewLike if found, otherwise undefined + */ + getCipherIcon(cipherId: string): CipherViewLike | undefined { + const currentCiphers = this._ciphersSubject.value; + if (!currentCiphers) { + return undefined; + } + + const foundCipher = currentCiphers.find((c) => c.id === cipherId); + + return foundCipher; + } + /** * Initializes the service context for a specific organization * 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 7b9255ca821..d426a6b09c1 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 @@ -87,7 +87,9 @@ export class RiskInsightsDataService { this._destroy$.complete(); } - // ----- UI-triggered methods (delegate to orchestrator) ----- + getCipherIcon(cipherId: string) { + return this.orchestrator.getCipherIcon(cipherId); + } initializeForOrganization(organizationId: OrganizationId) { this.orchestrator.initializeForOrganization(organizationId); } diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/all-applications.component.html b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/all-applications.component.html index b1c2faa4f05..73ded40388d 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/all-applications.component.html +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/all-applications/all-applications.component.html @@ -22,10 +22,10 @@ bitTypography="h3" class="!tw-mb-0" aria-describedby="allAppsOrgAtRiskMembersLabel" - >{{ applicationSummary.totalAtRiskMemberCount }}</span + >{{ applicationSummary().totalAtRiskMemberCount }}</span > <span bitTypography="body2">{{ - "cardMetrics" | i18n: applicationSummary.totalMemberCount + "cardMetrics" | i18n: applicationSummary().totalMemberCount }}</span> </div> <div class="tw-flex tw-items-baseline tw-mt-1 tw-gap-2"> @@ -62,10 +62,10 @@ bitTypography="h3" class="!tw-mb-0" aria-describedby="allAppsOrgAtRiskApplicationsLabel" - >{{ applicationSummary.totalAtRiskApplicationCount }}</span + >{{ applicationSummary().totalAtRiskApplicationCount }}</span > <span bitTypography="body2">{{ - "cardMetrics" | i18n: applicationSummary.totalApplicationCount + "cardMetrics" | i18n: applicationSummary().totalApplicationCount }}</span> </div> <div class="tw-flex tw-items-baseline tw-mt-1 tw-gap-2"> @@ -95,11 +95,11 @@ type="button" [buttonType]="'primary'" bitButton - [disabled]="!selectedUrls.size" - [loading]="markingAsCritical" + [disabled]="!selectedUrls().size" + [loading]="markingAsCritical()" (click)="markAppsAsCritical()" > - <i class="bwi tw-mr-2" [ngClass]="selectedUrls.size ? 'bwi-star-f' : 'bwi-star'"></i> + <i class="bwi tw-mr-2" [ngClass]="selectedUrls().size ? 'bwi-star-f' : 'bwi-star'"></i> {{ "markAppAsCritical" | i18n }} </button> </div> @@ -108,7 +108,7 @@ [dataSource]="dataSource" [showRowCheckBox]="true" [showRowMenuForCriticalApps]="false" - [selectedUrls]="selectedUrls" + [selectedUrls]="selectedUrls()" [openApplication]="drawerDetails.invokerId || ''" [checkboxChange]="onCheckboxChange" [showAppAtRiskMembers]="showAppAtRiskMembers" 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 acad2901ba4..3a9159ad68c 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,20 +1,23 @@ -import { Component, DestroyRef, inject, OnInit } from "@angular/core"; +import { + Component, + DestroyRef, + inject, + OnInit, + ChangeDetectionStrategy, + signal, +} from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormControl } from "@angular/forms"; -import { ActivatedRoute, Router } from "@angular/router"; +import { ActivatedRoute } from "@angular/router"; import { debounceTime } from "rxjs"; import { Security } from "@bitwarden/assets/svg"; -import { - ApplicationHealthReportDetailEnriched, - RiskInsightsDataService, -} from "@bitwarden/bit-common/dirt/reports/risk-insights"; +import { RiskInsightsDataService } from "@bitwarden/bit-common/dirt/reports/risk-insights"; import { createNewSummaryData } from "@bitwarden/bit-common/dirt/reports/risk-insights/helpers"; import { OrganizationReportSummary, ReportStatus, } from "@bitwarden/bit-common/dirt/reports/risk-insights/models/report-models"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { IconButtonModule, @@ -29,12 +32,14 @@ import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.mod import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; -import { AppTableRowScrollableComponent } from "../shared/app-table-row-scrollable.component"; +import { + ApplicationTableDataSource, + AppTableRowScrollableComponent, +} from "../shared/app-table-row-scrollable.component"; import { ApplicationsLoadingComponent } from "../shared/risk-insights-loading.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({ + changeDetection: ChangeDetectionStrategy.OnPush, selector: "dirt-all-applications", templateUrl: "./all-applications.component.html", imports: [ @@ -51,24 +56,25 @@ import { ApplicationsLoadingComponent } from "../shared/risk-insights-loading.co ], }) export class AllApplicationsComponent implements OnInit { - protected dataSource = new TableDataSource<ApplicationHealthReportDetailEnriched>(); - protected selectedUrls: Set<string> = new Set<string>(); - protected searchControl = new FormControl("", { nonNullable: true }); - protected organization = new Organization(); - noItemsIcon = Security; - protected markingAsCritical = false; - protected applicationSummary: OrganizationReportSummary = createNewSummaryData(); - protected ReportStatusEnum = ReportStatus; - destroyRef = inject(DestroyRef); + protected ReportStatusEnum = ReportStatus; + protected noItemsIcon = Security; + + // Standard properties + protected readonly dataSource = new TableDataSource<ApplicationTableDataSource>(); + protected readonly searchControl = new FormControl("", { nonNullable: true }); + + // Template driven properties + protected readonly selectedUrls = signal(new Set<string>()); + protected readonly markingAsCritical = signal(false); + protected readonly applicationSummary = signal<OrganizationReportSummary>(createNewSummaryData()); + constructor( protected i18nService: I18nService, protected activatedRoute: ActivatedRoute, protected toastService: ToastService, protected dataService: RiskInsightsDataService, - private router: Router, - // protected allActivitiesService: AllActivitiesService, ) { this.searchControl.valueChanges .pipe(debounceTime(200), takeUntilDestroyed()) @@ -78,8 +84,21 @@ export class AllApplicationsComponent implements OnInit { async ngOnInit() { this.dataService.enrichedReportData$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({ next: (report) => { - this.applicationSummary = report?.summaryData ?? createNewSummaryData(); - this.dataSource.data = report?.reportData ?? []; + if (report != null) { + this.applicationSummary.set(report.summaryData); + + // Map the report data to include the iconCipher for each application + const tableDataWithIcon = report.reportData.map((app) => ({ + ...app, + iconCipher: + app.cipherIds.length > 0 + ? this.dataService.getCipherIcon(app.cipherIds[0]) + : undefined, + })); + this.dataSource.data = tableDataWithIcon; + } else { + this.dataSource.data = []; + } }, error: () => { this.dataSource.data = []; @@ -88,15 +107,15 @@ export class AllApplicationsComponent implements OnInit { } isMarkedAsCriticalItem(applicationName: string) { - return this.selectedUrls.has(applicationName); + return this.selectedUrls().has(applicationName); } markAppsAsCritical = async () => { - this.markingAsCritical = true; - const count = this.selectedUrls.size; + this.markingAsCritical.set(true); + const count = this.selectedUrls().size; this.dataService - .saveCriticalApplications(Array.from(this.selectedUrls)) + .saveCriticalApplications(Array.from(this.selectedUrls())) .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: () => { @@ -105,8 +124,8 @@ export class AllApplicationsComponent implements OnInit { title: "", message: this.i18nService.t("criticalApplicationsMarkedSuccess", count.toString()), }); - this.selectedUrls.clear(); - this.markingAsCritical = false; + this.selectedUrls.set(new Set<string>()); + this.markingAsCritical.set(false); }, error: () => { this.toastService.showToast({ @@ -125,9 +144,15 @@ export class AllApplicationsComponent implements OnInit { onCheckboxChange = (applicationName: string, event: Event) => { const isChecked = (event.target as HTMLInputElement).checked; if (isChecked) { - this.selectedUrls.add(applicationName); + this.selectedUrls.update((selectedUrls) => { + selectedUrls.add(applicationName); + return selectedUrls; + }); } else { - this.selectedUrls.delete(applicationName); + this.selectedUrls.update((selectedUrls) => { + selectedUrls.delete(applicationName); + return selectedUrls; + }); } }; } diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.ts index 1ea745929db..b61190df660 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/critical-applications/critical-applications.component.ts @@ -1,6 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Component, DestroyRef, inject, OnInit } from "@angular/core"; +import { Component, DestroyRef, inject, OnInit, ChangeDetectionStrategy } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormControl } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; @@ -8,7 +8,6 @@ import { debounceTime, EMPTY, from, map, switchMap, take } from "rxjs"; import { Security } from "@bitwarden/assets/svg"; import { - ApplicationHealthReportDetailEnriched, CriticalAppsService, RiskInsightsDataService, RiskInsightsReportService, @@ -30,12 +29,14 @@ import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; import { RiskInsightsTabType } from "../models/risk-insights.models"; -import { AppTableRowScrollableComponent } from "../shared/app-table-row-scrollable.component"; +import { + ApplicationTableDataSource, + AppTableRowScrollableComponent, +} from "../shared/app-table-row-scrollable.component"; import { AccessIntelligenceSecurityTasksService } from "../shared/security-tasks.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({ + changeDetection: ChangeDetectionStrategy.OnPush, selector: "dirt-critical-applications", templateUrl: "./critical-applications.component.html", imports: [ @@ -55,7 +56,7 @@ export class CriticalApplicationsComponent implements OnInit { protected organizationId: OrganizationId; noItemsIcon = Security; - protected dataSource = new TableDataSource<ApplicationHealthReportDetailEnriched>(); + protected dataSource = new TableDataSource<ApplicationTableDataSource>(); protected applicationSummary = {} as OrganizationReportSummary; protected selectedIds: Set<number> = new Set<number>(); @@ -79,9 +80,24 @@ export class CriticalApplicationsComponent implements OnInit { async ngOnInit() { this.dataService.criticalReportResults$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({ next: (criticalReport) => { - this.dataSource.data = criticalReport?.reportData ?? []; - this.applicationSummary = criticalReport?.summaryData ?? createNewSummaryData(); - this.enableRequestPasswordChange = criticalReport?.summaryData?.totalAtRiskMemberCount > 0; + if (criticalReport != null) { + // Map the report data to include the iconCipher for each application + const tableDataWithIcon = criticalReport.reportData.map((app) => ({ + ...app, + iconCipher: + app.cipherIds.length > 0 + ? this.dataService.getCipherIcon(app.cipherIds[0]) + : undefined, + })); + this.dataSource.data = tableDataWithIcon; + + this.applicationSummary = criticalReport.summaryData; + this.enableRequestPasswordChange = criticalReport.summaryData.totalAtRiskMemberCount > 0; + } else { + this.dataSource.data = []; + this.applicationSummary = createNewSummaryData(); + this.enableRequestPasswordChange = false; + } }, error: () => { this.dataSource.data = []; 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 79af3869d99..edd90eaf97d 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 @@ -46,10 +46,7 @@ [attr.aria-label]="'viewItem' | i18n" > <!-- Passing the first cipher of the application for app-vault-icon cipher input requirement --> - <app-vault-icon - *ngIf="row.cipherIds.length > 0" - [cipher]="row.cipherIds[0]" - ></app-vault-icon> + <app-vault-icon *ngIf="row.iconCipher" [cipher]="row.iconCipher"></app-vault-icon> </td> <td class="tw-cursor-pointer" diff --git a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/app-table-row-scrollable.component.ts b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/app-table-row-scrollable.component.ts index f2ecff75847..87e58ace898 100644 --- a/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/app-table-row-scrollable.component.ts +++ b/bitwarden_license/bit-web/src/app/dirt/access-intelligence/shared/app-table-row-scrollable.component.ts @@ -3,10 +3,15 @@ import { Component, Input } from "@angular/core"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { ApplicationHealthReportDetailEnriched } from "@bitwarden/bit-common/dirt/reports/risk-insights"; +import { CipherViewLike } from "@bitwarden/common/vault/utils/cipher-view-like-utils"; import { MenuModule, TableDataSource, TableModule } from "@bitwarden/components"; import { SharedModule } from "@bitwarden/web-vault/app/shared"; import { PipesModule } from "@bitwarden/web-vault/app/vault/individual-vault/pipes/pipes.module"; +export type ApplicationTableDataSource = ApplicationHealthReportDetailEnriched & { + iconCipher: CipherViewLike | undefined; +}; + // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ @@ -18,7 +23,7 @@ export class AppTableRowScrollableComponent { // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-signals @Input() - dataSource!: TableDataSource<ApplicationHealthReportDetailEnriched>; + dataSource!: TableDataSource<ApplicationTableDataSource>; // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-signals @Input() showRowMenuForCriticalApps: boolean = false; From 96ffb567711f8f5cdab41e4d3a6d9bc36f1af971 Mon Sep 17 00:00:00 2001 From: Vicki League <vleague@bitwarden.com> Date: Tue, 18 Nov 2025 11:25:48 -0500 Subject: [PATCH 36/75] [CL-928] Set link component to semibold weight (#17395) --- libs/components/src/link/link.directive.ts | 2 +- libs/components/src/typography/typography.stories.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libs/components/src/link/link.directive.ts b/libs/components/src/link/link.directive.ts index df124a6811d..e6de8ac8402 100644 --- a/libs/components/src/link/link.directive.ts +++ b/libs/components/src/link/link.directive.ts @@ -25,7 +25,7 @@ const commonStyles = [ "tw-leading-none", "tw-px-0", "tw-py-0.5", - "tw-font-medium", + "tw-font-semibold", "tw-bg-transparent", "tw-border-0", "tw-border-none", diff --git a/libs/components/src/typography/typography.stories.ts b/libs/components/src/typography/typography.stories.ts index bb055be79a0..f9c57ae2637 100644 --- a/libs/components/src/typography/typography.stories.ts +++ b/libs/components/src/typography/typography.stories.ts @@ -27,42 +27,42 @@ const typographyProps: TypographyData[] = [ { id: "h1", typography: "h1", - weight: "Regular", + weight: "Medium", size: 30, lineHeight: "150%", }, { id: "h2", typography: "h2", - weight: "Regular", + weight: "Medium", size: 24, lineHeight: "150%", }, { id: "h3", typography: "h3", - weight: "Regular", + weight: "Medium", size: 20, lineHeight: "150%", }, { id: "h4", typography: "h4", - weight: "Regular", + weight: "Medium", size: 18, lineHeight: "150%", }, { id: "h5", typography: "h5", - weight: "Regular", + weight: "Medium", size: 16, lineHeight: "150%", }, { id: "h6", typography: "h6", - weight: "Regular", + weight: "Medium", size: 14, lineHeight: "150%", }, From 9d15ae80ca4910e60406b3252e2cb8c28bcc6619 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 10:30:27 -0600 Subject: [PATCH 37/75] [deps] Tools: Update @types/papaparse to v5.5.0 (#17454) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 82d05df83d2..5d9d43419f2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -115,7 +115,7 @@ "@types/node": "22.19.1", "@types/node-fetch": "2.6.4", "@types/node-forge": "1.3.11", - "@types/papaparse": "5.3.16", + "@types/papaparse": "5.5.0", "@types/proper-lockfile": "4.1.4", "@types/retry": "0.12.5", "@types/zxcvbn": "4.4.5", @@ -14459,9 +14459,9 @@ } }, "node_modules/@types/papaparse": { - "version": "5.3.16", - "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.16.tgz", - "integrity": "sha512-T3VuKMC2H0lgsjI9buTB3uuKj3EMD2eap1MOuEQuBQ44EnDx/IkGhU6EwiTf9zG3za4SKlmwKAImdDKdNnCsXg==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.5.0.tgz", + "integrity": "sha512-GVs5iMQmUr54BAZYYkByv8zPofFxmyxUpISPb2oh8sayR3+1zbxasrOvoKiHJ/nnoq/uULuPsu1Lze1EkagVFg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 53fcb74ce80..3e5a2581e8f 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "@types/node": "22.19.1", "@types/node-fetch": "2.6.4", "@types/node-forge": "1.3.11", - "@types/papaparse": "5.3.16", + "@types/papaparse": "5.5.0", "@types/proper-lockfile": "4.1.4", "@types/retry": "0.12.5", "@types/zxcvbn": "4.4.5", From 60977b02771c4393699be8ae3ef59b85db8e12c5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 17:46:55 +0100 Subject: [PATCH 38/75] [deps] Platform: Update electron-log to v5.4.3 (#17455) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5d9d43419f2..9c3c7e2226c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -136,7 +136,7 @@ "css-loader": "7.1.2", "electron": "37.7.0", "electron-builder": "26.0.12", - "electron-log": "5.4.0", + "electron-log": "5.4.3", "electron-reload": "2.0.0-alpha.1", "electron-store": "8.2.0", "electron-updater": "6.6.4", @@ -20938,9 +20938,9 @@ } }, "node_modules/electron-log": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-5.4.0.tgz", - "integrity": "sha512-AXI5OVppskrWxEAmCxuv8ovX+s2Br39CpCAgkGMNHQtjYT3IiVbSQTncEjFVGPgoH35ZygRm/mvUMBDWwhRxgg==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-5.4.3.tgz", + "integrity": "sha512-sOUsM3LjZdugatazSQ/XTyNcw8dfvH1SYhXWiJyfYodAAKOZdHs0txPiLDXFzOZbhXgAgshQkshH2ccq0feyLQ==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 3e5a2581e8f..d97b7755de8 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "css-loader": "7.1.2", "electron": "37.7.0", "electron-builder": "26.0.12", - "electron-log": "5.4.0", + "electron-log": "5.4.3", "electron-reload": "2.0.0-alpha.1", "electron-store": "8.2.0", "electron-updater": "6.6.4", From 82a0b3aa70be774cd48e938d00cddfb400256e2f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 17:48:52 +0100 Subject: [PATCH 39/75] [deps] Platform: Update semver to v7.7.3 (#17457) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> --- apps/cli/package.json | 2 +- package-lock.json | 36 ++++++++++++++++++------------------ package.json | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/apps/cli/package.json b/apps/cli/package.json index 26e1183004a..00686959ef0 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -87,7 +87,7 @@ "papaparse": "5.5.3", "proper-lockfile": "4.1.2", "rxjs": "7.8.1", - "semver": "7.7.2", + "semver": "7.7.3", "tldts": "7.0.1", "zxcvbn": "4.4.2" } diff --git a/package-lock.json b/package-lock.json index 9c3c7e2226c..f673947be92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,7 +66,7 @@ "qrcode-parser": "2.1.3", "qrious": "4.0.2", "rxjs": "7.8.1", - "semver": "7.7.2", + "semver": "7.7.3", "tabbable": "6.3.0", "tldts": "7.0.1", "ts-node": "10.9.2", @@ -225,7 +225,7 @@ "papaparse": "5.5.3", "proper-lockfile": "4.1.2", "rxjs": "7.8.1", - "semver": "7.7.2", + "semver": "7.7.3", "tldts": "7.0.1", "zxcvbn": "4.4.2" }, @@ -1936,6 +1936,19 @@ "strip-json-comments": "3.1.1" } }, + "node_modules/@angular-eslint/schematics/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@angular-eslint/template-parser": { "version": "19.6.0", "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-19.6.0.tgz", @@ -36903,9 +36916,9 @@ } }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -39354,19 +39367,6 @@ } } }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/ts-jest/node_modules/type-fest": { "version": "4.41.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", diff --git a/package.json b/package.json index d97b7755de8..42162a6189b 100644 --- a/package.json +++ b/package.json @@ -203,7 +203,7 @@ "qrcode-parser": "2.1.3", "qrious": "4.0.2", "rxjs": "7.8.1", - "semver": "7.7.2", + "semver": "7.7.3", "tabbable": "6.3.0", "tldts": "7.0.1", "ts-node": "10.9.2", From 3b84da60cafa79820d5ff89cf7c5336e9aa3775b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 17:01:53 +0000 Subject: [PATCH 40/75] [deps] Platform: Update @types/chrome to v0.1.27 (#17453) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index f673947be92..b7b625fdde2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -100,7 +100,7 @@ "@storybook/theming": "8.6.12", "@storybook/web-components-webpack5": "8.6.12", "@tailwindcss/container-queries": "0.1.1", - "@types/chrome": "0.1.12", + "@types/chrome": "0.1.27", "@types/firefox-webext-browser": "120.0.4", "@types/inquirer": "8.2.10", "@types/jest": "29.5.14", @@ -13957,9 +13957,9 @@ } }, "node_modules/@types/chrome": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.1.12.tgz", - "integrity": "sha512-jEkxs9GPQHx7g49WjkA8QDNcqODbMGDuBbWQOtjiS/Wf9AiEcDmQMIAgJvC/Xi36WoCVNx584g0Dd9ThJQCAiw==", + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.1.27.tgz", + "integrity": "sha512-pkkCb0Ft8X+Igi751POzT+YqchSxUCtB6s4Gs6ttgSj8qzJga/qlJMgSW1mKxuQTW4i0sTqQbqVtzXDS5AU+4A==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 42162a6189b..52a7d1c60a2 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@storybook/theming": "8.6.12", "@storybook/web-components-webpack5": "8.6.12", "@tailwindcss/container-queries": "0.1.1", - "@types/chrome": "0.1.12", + "@types/chrome": "0.1.27", "@types/firefox-webext-browser": "120.0.4", "@types/inquirer": "8.2.10", "@types/jest": "29.5.14", From b1acff7f5c09a82b919a40544b413824242f7fa6 Mon Sep 17 00:00:00 2001 From: Daniel Riera <driera@livefront.com> Date: Tue, 18 Nov 2025 12:22:13 -0500 Subject: [PATCH 41/75] Pm 27900 add additional hardening in extension frame validation (#17265) * PM-27900 harden iframe, origin route tightening and test updates * reduce comments to make more legible * Removes referrer check in favor of PM-27822 #17313 bitwarden/clients@4206447cfece36766ed413791821ac6156e12ba8 * nake token optional since it is later set * whitelist -> allowlist * improve notes on unsafe * improve content handler notes * order allowlist * improve jsdoc on ismessagefromextension method * cover additional test cases * rename verifytoken and document more clear, update referrer --------- Co-authored-by: Miles Blackwood <mrobinson@bitwarden.com> --- .../content/content-message-handler.ts | 19 ++- .../autofill-inline-menu-container.ts | 1 + .../autofill-inline-menu-container.spec.ts | 71 +++++++- .../autofill-inline-menu-container.ts | 156 +++++++++++++++--- .../autofill-inline-menu-page-element.ts | 20 ++- 5 files changed, 233 insertions(+), 34 deletions(-) diff --git a/apps/browser/src/autofill/content/content-message-handler.ts b/apps/browser/src/autofill/content/content-message-handler.ts index c57b2d959f3..63afc215923 100644 --- a/apps/browser/src/autofill/content/content-message-handler.ts +++ b/apps/browser/src/autofill/content/content-message-handler.ts @@ -86,17 +86,30 @@ function handleOpenBrowserExtensionToUrlMessage({ url }: { url?: ExtensionPageUr } /** - * Handles the window message event. + * Handles window message events, validating source and extracting referrer for security. * * @param event - The window message event */ function handleWindowMessageEvent(event: MessageEvent) { - const { source, data } = event; + const { source, data, origin } = event; if (source !== window || !data?.command) { return; } - const referrer = source.location.hostname; + // Extract hostname from event.origin for secure referrer validation in background script + let referrer: string; + // Sandboxed iframe or opaque origin support + if (origin === "null") { + referrer = "null"; + } else { + try { + const originUrl = new URL(origin); + referrer = originUrl.hostname; + } catch { + return; + } + } + const handler = windowMessageHandlers[data.command]; if (handler) { handler({ data, referrer }); diff --git a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-container.ts b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-container.ts index a147e0ba165..af60d1de77d 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-container.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/abstractions/autofill-inline-menu-container.ts @@ -5,6 +5,7 @@ import { InlineMenuCipherData } from "../../../background/abstractions/overlay.b export type AutofillInlineMenuContainerMessage = { command: string; portKey: string; + token?: string; }; export type InitAutofillInlineMenuElementMessage = AutofillInlineMenuContainerMessage & { diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.spec.ts index f7a5727e47f..d7a61bec61f 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.spec.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.spec.ts @@ -6,11 +6,13 @@ import { AutofillInlineMenuContainer } from "./autofill-inline-menu-container"; describe("AutofillInlineMenuContainer", () => { const portKey = "testPortKey"; - const iframeUrl = "https://example.com"; + const extensionOrigin = "chrome-extension://test-extension-id"; + const iframeUrl = `${extensionOrigin}/overlay/menu-list.html`; const pageTitle = "Example"; let autofillInlineMenuContainer: AutofillInlineMenuContainer; beforeEach(() => { + jest.spyOn(chrome.runtime, "getURL").mockReturnValue(`${extensionOrigin}/`); autofillInlineMenuContainer = new AutofillInlineMenuContainer(); }); @@ -28,7 +30,7 @@ describe("AutofillInlineMenuContainer", () => { portName: AutofillOverlayPort.List, }; - postWindowMessage(message); + postWindowMessage(message, extensionOrigin); expect(autofillInlineMenuContainer["defaultIframeAttributes"].src).toBe(message.iframeUrl); expect(autofillInlineMenuContainer["defaultIframeAttributes"].title).toBe(message.pageTitle); @@ -44,15 +46,48 @@ describe("AutofillInlineMenuContainer", () => { portName: AutofillOverlayPort.Button, }; - postWindowMessage(message); + postWindowMessage(message, extensionOrigin); jest.spyOn(autofillInlineMenuContainer["inlineMenuPageIframe"].contentWindow, "postMessage"); autofillInlineMenuContainer["inlineMenuPageIframe"].dispatchEvent(new Event("load")); expect(chrome.runtime.connect).toHaveBeenCalledWith({ name: message.portName }); + const expectedMessage = expect.objectContaining({ + ...message, + token: expect.any(String), + }); expect( autofillInlineMenuContainer["inlineMenuPageIframe"].contentWindow.postMessage, - ).toHaveBeenCalledWith(message, "*"); + ).toHaveBeenCalledWith(expectedMessage, "*"); + }); + + it("ignores initialization when URLs are not from extension origin", () => { + const invalidIframeUrlMessage = { + command: "initAutofillInlineMenuList", + iframeUrl: "https://malicious.com/overlay/menu-list.html", + pageTitle, + portKey, + portName: AutofillOverlayPort.List, + }; + + postWindowMessage(invalidIframeUrlMessage, extensionOrigin); + expect(autofillInlineMenuContainer["inlineMenuPageIframe"]).toBeUndefined(); + expect(autofillInlineMenuContainer["isInitialized"]).toBe(false); + + autofillInlineMenuContainer = new AutofillInlineMenuContainer(); + + const invalidStyleSheetUrlMessage = { + command: "initAutofillInlineMenuList", + iframeUrl, + pageTitle, + portKey, + portName: AutofillOverlayPort.List, + styleSheetUrl: "https://malicious.com/styles.css", + }; + + postWindowMessage(invalidStyleSheetUrlMessage, extensionOrigin); + expect(autofillInlineMenuContainer["inlineMenuPageIframe"]).toBeUndefined(); + expect(autofillInlineMenuContainer["isInitialized"]).toBe(false); }); }); @@ -69,7 +104,7 @@ describe("AutofillInlineMenuContainer", () => { portName: AutofillOverlayPort.Button, }; - postWindowMessage(message); + postWindowMessage(message, extensionOrigin); iframe = autofillInlineMenuContainer["inlineMenuPageIframe"]; jest.spyOn(iframe.contentWindow, "postMessage"); @@ -112,7 +147,8 @@ describe("AutofillInlineMenuContainer", () => { }); it("posts a message to the background from the inline menu iframe", () => { - const message = { command: "checkInlineMenuButtonFocused", portKey }; + const token = autofillInlineMenuContainer["token"]; + const message = { command: "checkInlineMenuButtonFocused", portKey, token }; postWindowMessage(message, "null", iframe.contentWindow as any); @@ -124,7 +160,28 @@ describe("AutofillInlineMenuContainer", () => { postWindowMessage(message); - expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(message, "*"); + const expectedMessage = expect.objectContaining({ + ...message, + token: expect.any(String), + }); + expect(iframe.contentWindow.postMessage).toHaveBeenCalledWith(expectedMessage, "*"); + }); + + it("ignores messages from iframe with invalid token", () => { + const message = { command: "checkInlineMenuButtonFocused", portKey, token: "invalid-token" }; + + postWindowMessage(message, "null", iframe.contentWindow as any); + + expect(port.postMessage).not.toHaveBeenCalled(); + }); + + it("ignores messages from iframe with commands not in the allowlist", () => { + const token = autofillInlineMenuContainer["token"]; + const message = { command: "maliciousCommand", portKey, token }; + + postWindowMessage(message, "null", iframe.contentWindow as any); + + expect(port.postMessage).not.toHaveBeenCalled(); }); }); }); diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts index 6d85982a1ac..ad0b11f0bc6 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/menu-container/autofill-inline-menu-container.ts @@ -1,6 +1,6 @@ import { EVENTS } from "@bitwarden/common/autofill/constants"; -import { setElementStyles } from "../../../../utils"; +import { generateRandomChars, setElementStyles } from "../../../../utils"; import { InitAutofillInlineMenuElementMessage, AutofillInlineMenuContainerWindowMessageHandlers, @@ -8,14 +8,37 @@ import { AutofillInlineMenuContainerPortMessage, } from "../../abstractions/autofill-inline-menu-container"; +/** + * Allowlist of commands that can be sent to the background script. + */ +const ALLOWED_BG_COMMANDS = new Set<string>([ + "addNewVaultItem", + "autofillInlineMenuBlurred", + "autofillInlineMenuButtonClicked", + "checkAutofillInlineMenuButtonFocused", + "checkInlineMenuButtonFocused", + "fillAutofillInlineMenuCipher", + "fillGeneratedPassword", + "redirectAutofillInlineMenuFocusOut", + "refreshGeneratedPassword", + "refreshOverlayCiphers", + "triggerDelayedAutofillInlineMenuClosure", + "updateAutofillInlineMenuColorScheme", + "updateAutofillInlineMenuListHeight", + "unlockVault", + "viewSelectedCipher", +]); + export class AutofillInlineMenuContainer { private readonly setElementStyles = setElementStyles; - private readonly extensionOriginsSet: Set<string>; private port: chrome.runtime.Port | null = null; /** Non-null asserted. */ private portName!: string; /** Non-null asserted. */ private inlineMenuPageIframe!: HTMLIFrameElement; + private token: string; + private isInitialized: boolean = false; + private readonly extensionOrigin: string; private readonly iframeStyles: Partial<CSSStyleDeclaration> = { all: "initial", position: "fixed", @@ -49,11 +72,8 @@ export class AutofillInlineMenuContainer { }; constructor() { - this.extensionOriginsSet = new Set([ - chrome.runtime.getURL("").slice(0, -1).toLowerCase(), // Remove the trailing slash and normalize the extension url to lowercase - "null", - ]); - + this.token = generateRandomChars(32); + this.extensionOrigin = chrome.runtime.getURL("").slice(0, -1); globalThis.addEventListener("message", this.handleWindowMessage); } @@ -63,9 +83,22 @@ export class AutofillInlineMenuContainer { * @param message - The message containing the iframe url and page title. */ private handleInitInlineMenuIframe(message: InitAutofillInlineMenuElementMessage) { + if (this.isInitialized) { + return; + } + + if (!this.isExtensionUrl(message.iframeUrl)) { + return; + } + + if (message.styleSheetUrl && !this.isExtensionUrl(message.styleSheetUrl)) { + return; + } + this.defaultIframeAttributes.src = message.iframeUrl; this.defaultIframeAttributes.title = message.pageTitle; this.portName = message.portName; + this.isInitialized = true; this.inlineMenuPageIframe = globalThis.document.createElement("iframe"); this.setElementStyles(this.inlineMenuPageIframe, this.iframeStyles, true); @@ -81,6 +114,26 @@ export class AutofillInlineMenuContainer { globalThis.document.body.appendChild(this.inlineMenuPageIframe); } + /** + * validates that a URL is from the extension origin. + * prevents loading arbitrary URLs in the iframe. + * + * @param url - The URL to validate. + */ + private isExtensionUrl(url: string): boolean { + if (!url) { + return false; + } + try { + const urlObj = new URL(url); + return ( + urlObj.origin === this.extensionOrigin || urlObj.href.startsWith(this.extensionOrigin + "/") + ); + } catch { + return false; + } + } + /** * Sets up the port message listener for the inline menu page. * @@ -88,7 +141,8 @@ export class AutofillInlineMenuContainer { */ private setupPortMessageListener = (message: InitAutofillInlineMenuElementMessage) => { this.port = chrome.runtime.connect({ name: this.portName }); - this.postMessageToInlineMenuPage(message); + const initMessage = { ...message, token: this.token }; + this.postMessageToInlineMenuPageUnsafe(initMessage); }; /** @@ -97,6 +151,22 @@ export class AutofillInlineMenuContainer { * @param message - The message to post. */ private postMessageToInlineMenuPage(message: AutofillInlineMenuContainerWindowMessage) { + if (this.inlineMenuPageIframe?.contentWindow) { + const messageWithToken = { ...message, token: this.token }; + this.postMessageToInlineMenuPageUnsafe(messageWithToken); + } + } + + /** + * Posts a message to the inline menu page iframe without token validation. + * + * UNSAFE: Bypasses token authentication and sends raw messages. Only use internally + * when sending trusted messages (e.g., initialization) or when token validation + * would create circular dependencies. External callers should use postMessageToInlineMenuPage(). + * + * @param message - The message to post. + */ + private postMessageToInlineMenuPageUnsafe(message: Record<string, unknown>) { if (this.inlineMenuPageIframe?.contentWindow) { this.inlineMenuPageIframe.contentWindow.postMessage(message, "*"); } @@ -108,9 +178,15 @@ export class AutofillInlineMenuContainer { * @param message - The message to post. */ private postMessageToBackground(message: AutofillInlineMenuContainerPortMessage) { - if (this.port) { - this.port.postMessage(message); + if (!this.port) { + return; } + + if (message.command && !ALLOWED_BG_COMMANDS.has(message.command)) { + return; + } + + this.port.postMessage(message); } /** @@ -124,23 +200,33 @@ export class AutofillInlineMenuContainer { return; } - if ( - this.windowMessageHandlers[ - message.command as keyof AutofillInlineMenuContainerWindowMessageHandlers - ] - ) { - this.windowMessageHandlers[ - message.command as keyof AutofillInlineMenuContainerWindowMessageHandlers - ](message); + if (this.windowMessageHandlers[message.command]) { + // only accept init messages from extension origin or parent window + if ( + (message.command === "initAutofillInlineMenuButton" || + message.command === "initAutofillInlineMenuList") && + !this.isMessageFromExtensionOrigin(event) && + !this.isMessageFromParentWindow(event) + ) { + return; + } + this.windowMessageHandlers[message.command](message); return; } if (this.isMessageFromParentWindow(event)) { + // messages from parent window are trusted and forwarded to iframe this.postMessageToInlineMenuPage(message); return; } - this.postMessageToBackground(message); + // messages from iframe to background require object identity verification with a contentWindow check and token auth + if (this.isMessageFromInlineMenuPageIframe(event)) { + if (this.isValidSessionToken(message)) { + this.postMessageToBackground(message); + } + return; + } }; /** @@ -184,10 +270,34 @@ export class AutofillInlineMenuContainer { if (!this.inlineMenuPageIframe) { return false; } + // only trust the specific iframe we created + return this.inlineMenuPageIframe.contentWindow === event.source; + } - return ( - this.inlineMenuPageIframe.contentWindow === event.source && - this.extensionOriginsSet.has(event.origin.toLowerCase()) - ); + /** + * Validates that the message contains a valid session token. + * The session token is generated when the container is created and is refreshed + * every time the inline menu container is recreated. + * + */ + private isValidSessionToken(message: { token?: string }): boolean { + return message.token === this.token; + } + + /** + * Validates that a message event originates from the extension. + * + * @param event - The message event to validate. + * @returns True if the message is from the extension origin. + */ + private isMessageFromExtensionOrigin(event: MessageEvent): boolean { + try { + if (event.origin === "null") { + return false; + } + return event.origin === this.extensionOrigin; + } catch { + return false; + } } } diff --git a/apps/browser/src/autofill/overlay/inline-menu/pages/shared/autofill-inline-menu-page-element.ts b/apps/browser/src/autofill/overlay/inline-menu/pages/shared/autofill-inline-menu-page-element.ts index 89f44a6a80d..ea77e3e434d 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/pages/shared/autofill-inline-menu-page-element.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/pages/shared/autofill-inline-menu-page-element.ts @@ -16,6 +16,7 @@ export class AutofillInlineMenuPageElement extends HTMLElement { private portKey!: string; /** Non-null asserted. */ protected windowMessageHandlers!: AutofillInlineMenuPageElementWindowMessageHandlers; + private token?: string; constructor() { super(); @@ -37,8 +38,12 @@ export class AutofillInlineMenuPageElement extends HTMLElement { styleSheetUrl: string, translations: Record<string, string>, portKey: string, + token?: string, ): Promise<HTMLLinkElement> { this.portKey = portKey; + if (token) { + this.token = token; + } this.translations = translations; globalThis.document.documentElement.setAttribute("lang", this.getTranslation("locale")); @@ -58,7 +63,11 @@ export class AutofillInlineMenuPageElement extends HTMLElement { * @param message - The message to post */ protected postMessageToParent(message: AutofillInlineMenuPageElementWindowMessage) { - globalThis.parent.postMessage({ portKey: this.portKey, ...message }, "*"); + const messageWithAuth: Record<string, unknown> = { portKey: this.portKey, ...message }; + if (this.token) { + messageWithAuth.token = this.token; + } + globalThis.parent.postMessage(messageWithAuth, "*"); } /** @@ -105,6 +114,15 @@ export class AutofillInlineMenuPageElement extends HTMLElement { } const message = event?.data; + + if ( + message?.token && + (message?.command === "initAutofillInlineMenuButton" || + message?.command === "initAutofillInlineMenuList") + ) { + this.token = message.token; + } + const handler = this.windowMessageHandlers[message?.command]; if (!handler) { return; From fa563641b20bb0f66ac039d140510e1b6f2850b5 Mon Sep 17 00:00:00 2001 From: Vince Grassia <593223+vgrassia@users.noreply.github.com> Date: Tue, 18 Nov 2025 12:23:39 -0500 Subject: [PATCH 42/75] BRE-1355 - Rename Bitwarden Unified to Bitwarden Lite (#17456) --- .github/workflows/publish-web.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish-web.yml b/.github/workflows/publish-web.yml index 6bf2b282b38..4f41898a9b2 100644 --- a/.github/workflows/publish-web.yml +++ b/.github/workflows/publish-web.yml @@ -157,11 +157,10 @@ jobs: - name: Log out of Docker run: docker logout - self-host-unified-build: - name: Trigger self-host unified build + bitwarden-lite-build: + name: Trigger Bitwarden Lite build runs-on: ubuntu-22.04 - needs: - - setup + needs: setup permissions: id-token: write steps: @@ -182,7 +181,7 @@ jobs: - name: Log out from Azure uses: bitwarden/gh-actions/azure-logout@main - - name: Trigger self-host build + - name: Trigger Bitwarden Lite build uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: github-token: ${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }} @@ -190,7 +189,7 @@ jobs: await github.rest.actions.createWorkflowDispatch({ owner: 'bitwarden', repo: 'self-host', - workflow_id: 'build-unified.yml', + workflow_id: 'build-bitwarden-lite.yml', ref: 'main', inputs: { use_latest_core_version: true From 0d14060e9d2cd317aee2a4b07a93f16e742352ec Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 18:33:12 +0100 Subject: [PATCH 43/75] Autosync the updated translations (#17460) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/desktop/src/locales/he/messages.json | 20 ++++++++++---------- apps/desktop/src/locales/sk/messages.json | 18 +++++++++--------- apps/desktop/src/locales/zh_CN/messages.json | 4 ++-- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/apps/desktop/src/locales/he/messages.json b/apps/desktop/src/locales/he/messages.json index 87fac938a34..868cd9ccbc5 100644 --- a/apps/desktop/src/locales/he/messages.json +++ b/apps/desktop/src/locales/he/messages.json @@ -4195,33 +4195,33 @@ "message": "מספר כרטיס" }, "upgradeNow": { - "message": "Upgrade now" + "message": "שדרג עכשיו" }, "builtInAuthenticator": { - "message": "Built-in authenticator" + "message": "מאמת מובנה" }, "secureFileStorage": { - "message": "Secure file storage" + "message": "אחסון קבצים מאובטח" }, "emergencyAccess": { - "message": "Emergency access" + "message": "גישת חירום" }, "breachMonitoring": { - "message": "Breach monitoring" + "message": "ניטור פרצות" }, "andMoreFeatures": { - "message": "And more!" + "message": "ועוד!" }, "planDescPremium": { - "message": "Complete online security" + "message": "השלם אבטחה מקוונת" }, "upgradeToPremium": { - "message": "Upgrade to Premium" + "message": "שדרג לפרימיום" }, "sessionTimeoutSettingsAction": { - "message": "Timeout action" + "message": "פעולת פסק זמן" }, "sessionTimeoutHeader": { - "message": "Session timeout" + "message": "פסק זמן להפעלה" } } diff --git a/apps/desktop/src/locales/sk/messages.json b/apps/desktop/src/locales/sk/messages.json index e5763d78b9c..0b14b961bbb 100644 --- a/apps/desktop/src/locales/sk/messages.json +++ b/apps/desktop/src/locales/sk/messages.json @@ -1692,7 +1692,7 @@ "description": "Default URI match detection for auto-fill." }, "toggleOptions": { - "message": "Voľby prepínača" + "message": "Zobraziť/skryť možnosti" }, "organization": { "message": "Organizácia", @@ -2425,7 +2425,7 @@ "message": "Hlavné heslo bolo úspešne nastavené" }, "updatedMasterPassword": { - "message": "Hlavné heslo aktualizované" + "message": "Hlavné heslo bolo aktualizované" }, "updateMasterPassword": { "message": "Aktualizovať hlavné heslo" @@ -2476,7 +2476,7 @@ "message": "Použiť PIN kód" }, "useBiometrics": { - "message": "Použiť biometrické údaje" + "message": "Použiť biometriu" }, "enterVerificationCodeSentToEmail": { "message": "Zadajte overovací kód, ktorý vám bol zaslaný na e-mail." @@ -2574,7 +2574,7 @@ "message": "Táto organizácia má podnikovú politiku, ktorá vás automaticky zaregistruje na obnovenie hesla. Registrácia umožní správcom organizácie zmeniť vaše hlavné heslo." }, "vaultExportDisabled": { - "message": "Export trezoru je zakázaný" + "message": "Export trezoru bol odstránený" }, "personalVaultExportPolicyInEffect": { "message": "Jedna alebo viacero zásad organizácie vám bráni exportovať váš osobný trezor." @@ -2619,7 +2619,7 @@ "message": "Predvoľby" }, "appPreferences": { - "message": "Nastavenia aplikácie (Všetky účty)" + "message": "Nastavenia aplikácie (všetky účty)" }, "accountSwitcherLimitReached": { "message": "Dosiahnutý limit počtu účtov. Odhláste sa z účtu aby ste mohli pridať ďalší." @@ -2790,7 +2790,7 @@ "message": "Použiť možnosti subadresovania svojho poskytovateľa e-mailu." }, "catchallEmail": { - "message": "Catch-all Email" + "message": "E-mail Catch-all" }, "catchallEmailDesc": { "message": "Použiť doručenú poštu typu catch-all nastavenú na doméne." @@ -2843,7 +2843,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": { @@ -2959,7 +2959,7 @@ } }, "forwarderUnknownForwarder": { - "message": "Nepodporovaná služba: '$SERVICENAME$'.", + "message": "Neznáme presmerovanie: '$SERVICENAME$'.", "description": "Displayed when the forwarding service is not supported.", "placeholders": { "servicename": { @@ -2976,7 +2976,7 @@ "message": "Prístupový token API" }, "apiKey": { - "message": "API kľúč" + "message": "Kľúč API" }, "premiumSubcriptionRequired": { "message": "Vyžaduje sa predplatné Prémium" diff --git a/apps/desktop/src/locales/zh_CN/messages.json b/apps/desktop/src/locales/zh_CN/messages.json index 353fc036f63..b5e68b83bde 100644 --- a/apps/desktop/src/locales/zh_CN/messages.json +++ b/apps/desktop/src/locales/zh_CN/messages.json @@ -1503,7 +1503,7 @@ "message": "优先客户支持。" }, "premiumSignUpFuture": { - "message": "所有未来的高级功能。即将推出!" + "message": "未来的更多高级版功能。敬请期待!" }, "premiumPurchase": { "message": "购买高级版" @@ -2029,7 +2029,7 @@ "message": "Bitwarden 可以存储并填充两步验证码。选择相机图标来拍摄此网站的验证器二维码,或将密钥复制并粘贴到此字段。" }, "premium": { - "message": "高级会员", + "message": "高级版", "description": "Premium membership" }, "freeOrgsCannotUseAttachments": { From bbb42d9b17a3fd4666b2b887993687b59c8a90da Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 18:36:24 +0100 Subject: [PATCH 44/75] Autosync the updated translations (#17461) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/browser/src/_locales/ar/messages.json | 9 +++++ apps/browser/src/_locales/az/messages.json | 11 +++++- apps/browser/src/_locales/be/messages.json | 9 +++++ apps/browser/src/_locales/bg/messages.json | 11 +++++- apps/browser/src/_locales/bn/messages.json | 9 +++++ apps/browser/src/_locales/bs/messages.json | 9 +++++ apps/browser/src/_locales/ca/messages.json | 9 +++++ apps/browser/src/_locales/cs/messages.json | 9 +++++ apps/browser/src/_locales/cy/messages.json | 9 +++++ apps/browser/src/_locales/da/messages.json | 9 +++++ apps/browser/src/_locales/de/messages.json | 11 +++++- apps/browser/src/_locales/el/messages.json | 9 +++++ apps/browser/src/_locales/en_GB/messages.json | 9 +++++ apps/browser/src/_locales/en_IN/messages.json | 9 +++++ apps/browser/src/_locales/es/messages.json | 9 +++++ apps/browser/src/_locales/et/messages.json | 9 +++++ apps/browser/src/_locales/eu/messages.json | 9 +++++ apps/browser/src/_locales/fa/messages.json | 9 +++++ apps/browser/src/_locales/fi/messages.json | 9 +++++ apps/browser/src/_locales/fil/messages.json | 9 +++++ apps/browser/src/_locales/fr/messages.json | 9 +++++ apps/browser/src/_locales/gl/messages.json | 9 +++++ apps/browser/src/_locales/he/messages.json | 39 ++++++++++++------- apps/browser/src/_locales/hi/messages.json | 9 +++++ apps/browser/src/_locales/hr/messages.json | 9 +++++ apps/browser/src/_locales/hu/messages.json | 9 +++++ apps/browser/src/_locales/id/messages.json | 9 +++++ apps/browser/src/_locales/it/messages.json | 9 +++++ apps/browser/src/_locales/ja/messages.json | 9 +++++ apps/browser/src/_locales/ka/messages.json | 9 +++++ apps/browser/src/_locales/km/messages.json | 9 +++++ apps/browser/src/_locales/kn/messages.json | 9 +++++ apps/browser/src/_locales/ko/messages.json | 9 +++++ apps/browser/src/_locales/lt/messages.json | 9 +++++ apps/browser/src/_locales/lv/messages.json | 9 +++++ apps/browser/src/_locales/ml/messages.json | 9 +++++ apps/browser/src/_locales/mr/messages.json | 9 +++++ apps/browser/src/_locales/my/messages.json | 9 +++++ apps/browser/src/_locales/nb/messages.json | 9 +++++ apps/browser/src/_locales/ne/messages.json | 9 +++++ apps/browser/src/_locales/nl/messages.json | 9 +++++ apps/browser/src/_locales/nn/messages.json | 9 +++++ apps/browser/src/_locales/or/messages.json | 9 +++++ apps/browser/src/_locales/pl/messages.json | 9 +++++ apps/browser/src/_locales/pt_BR/messages.json | 9 +++++ apps/browser/src/_locales/pt_PT/messages.json | 9 +++++ apps/browser/src/_locales/ro/messages.json | 9 +++++ apps/browser/src/_locales/ru/messages.json | 9 +++++ apps/browser/src/_locales/si/messages.json | 9 +++++ apps/browser/src/_locales/sk/messages.json | 11 +++++- apps/browser/src/_locales/sl/messages.json | 9 +++++ apps/browser/src/_locales/sr/messages.json | 9 +++++ apps/browser/src/_locales/sv/messages.json | 11 +++++- apps/browser/src/_locales/ta/messages.json | 9 +++++ apps/browser/src/_locales/te/messages.json | 9 +++++ apps/browser/src/_locales/th/messages.json | 9 +++++ apps/browser/src/_locales/tr/messages.json | 11 +++++- apps/browser/src/_locales/uk/messages.json | 9 +++++ apps/browser/src/_locales/vi/messages.json | 9 +++++ apps/browser/src/_locales/zh_CN/messages.json | 15 +++++-- apps/browser/src/_locales/zh_TW/messages.json | 11 +++++- 61 files changed, 574 insertions(+), 25 deletions(-) diff --git a/apps/browser/src/_locales/ar/messages.json b/apps/browser/src/_locales/ar/messages.json index 79d54193b59..20eb31a5453 100644 --- a/apps/browser/src/_locales/ar/messages.json +++ b/apps/browser/src/_locales/ar/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/az/messages.json b/apps/browser/src/_locales/az/messages.json index b67d5ace0d4..d8d8589f47e 100644 --- a/apps/browser/src/_locales/az/messages.json +++ b/apps/browser/src/_locales/az/messages.json @@ -595,7 +595,7 @@ "message": "Hamısına bax" }, "viewLess": { - "message": "View less" + "message": "Daha azına bax" }, "viewLogin": { "message": "Girişə bax" @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "\"Premium\"a yüksəlt" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Seyf yüklənir" }, diff --git a/apps/browser/src/_locales/be/messages.json b/apps/browser/src/_locales/be/messages.json index 450fb6e3df5..47bafb7efe3 100644 --- a/apps/browser/src/_locales/be/messages.json +++ b/apps/browser/src/_locales/be/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/bg/messages.json b/apps/browser/src/_locales/bg/messages.json index 0a71e453c21..98da9bf033e 100644 --- a/apps/browser/src/_locales/bg/messages.json +++ b/apps/browser/src/_locales/bg/messages.json @@ -595,7 +595,7 @@ "message": "Показване на всички" }, "viewLess": { - "message": "View less" + "message": "Преглед на по-малко" }, "viewLogin": { "message": "Преглед на елемента за вписване" @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Надградете до Платения план" }, + "upgradeCompleteSecurity": { + "message": "Надградете, за да се възползвате от пълна защита" + }, + "premiumGivesMoreTools": { + "message": "Платеният план предоставя повече инструменти за защита, ефективна работа и контрол." + }, + "explorePremium": { + "message": "Разгледайте платения план" + }, "loadingVault": { "message": "Зареждане на трезора" }, diff --git a/apps/browser/src/_locales/bn/messages.json b/apps/browser/src/_locales/bn/messages.json index f43e3fdad29..794e380d012 100644 --- a/apps/browser/src/_locales/bn/messages.json +++ b/apps/browser/src/_locales/bn/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/bs/messages.json b/apps/browser/src/_locales/bs/messages.json index 4fbcccd9aae..611cb4f5f55 100644 --- a/apps/browser/src/_locales/bs/messages.json +++ b/apps/browser/src/_locales/bs/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/ca/messages.json b/apps/browser/src/_locales/ca/messages.json index 15a309fa8fd..01611cc0764 100644 --- a/apps/browser/src/_locales/ca/messages.json +++ b/apps/browser/src/_locales/ca/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/cs/messages.json b/apps/browser/src/_locales/cs/messages.json index f9f572d87d8..5c0c8fbf524 100644 --- a/apps/browser/src/_locales/cs/messages.json +++ b/apps/browser/src/_locales/cs/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Aktualizovat na Premium" }, + "upgradeCompleteSecurity": { + "message": "Aktualizujte pro úplné zabezpečení" + }, + "premiumGivesMoreTools": { + "message": "Verze Premium Vám poskytne více nástrojů k zabezpečení, efektivní práci a udržení kontroly." + }, + "explorePremium": { + "message": "Objevit Premium" + }, "loadingVault": { "message": "Načítání trezoru" }, diff --git a/apps/browser/src/_locales/cy/messages.json b/apps/browser/src/_locales/cy/messages.json index 33c68b338a0..3bd81cc8039 100644 --- a/apps/browser/src/_locales/cy/messages.json +++ b/apps/browser/src/_locales/cy/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/da/messages.json b/apps/browser/src/_locales/da/messages.json index a5999945692..9a12f808f22 100644 --- a/apps/browser/src/_locales/da/messages.json +++ b/apps/browser/src/_locales/da/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/de/messages.json b/apps/browser/src/_locales/de/messages.json index b72cc2decb4..517dc638aff 100644 --- a/apps/browser/src/_locales/de/messages.json +++ b/apps/browser/src/_locales/de/messages.json @@ -595,7 +595,7 @@ "message": "Alles anzeigen" }, "viewLess": { - "message": "View less" + "message": "Weniger anzeigen" }, "viewLogin": { "message": "Zugangsdaten anzeigen" @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade auf Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade für umfassende Sicherheit" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Tresor wird geladen" }, diff --git a/apps/browser/src/_locales/el/messages.json b/apps/browser/src/_locales/el/messages.json index e0de7e5e9e0..ae685fff651 100644 --- a/apps/browser/src/_locales/el/messages.json +++ b/apps/browser/src/_locales/el/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/en_GB/messages.json b/apps/browser/src/_locales/en_GB/messages.json index a9c57e157e6..5079e1e6689 100644 --- a/apps/browser/src/_locales/en_GB/messages.json +++ b/apps/browser/src/_locales/en_GB/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/en_IN/messages.json b/apps/browser/src/_locales/en_IN/messages.json index cd8c91f8437..2a34400ea58 100644 --- a/apps/browser/src/_locales/en_IN/messages.json +++ b/apps/browser/src/_locales/en_IN/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/es/messages.json b/apps/browser/src/_locales/es/messages.json index 470cf2ab35a..8100fa80435 100644 --- a/apps/browser/src/_locales/es/messages.json +++ b/apps/browser/src/_locales/es/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/et/messages.json b/apps/browser/src/_locales/et/messages.json index 9220d61e466..0f69561df57 100644 --- a/apps/browser/src/_locales/et/messages.json +++ b/apps/browser/src/_locales/et/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/eu/messages.json b/apps/browser/src/_locales/eu/messages.json index c360bed28e0..c60ce5e4da7 100644 --- a/apps/browser/src/_locales/eu/messages.json +++ b/apps/browser/src/_locales/eu/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/fa/messages.json b/apps/browser/src/_locales/fa/messages.json index 774a02f50d3..5a03f8427fa 100644 --- a/apps/browser/src/_locales/fa/messages.json +++ b/apps/browser/src/_locales/fa/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/fi/messages.json b/apps/browser/src/_locales/fi/messages.json index 8766632a91e..db4649c33a7 100644 --- a/apps/browser/src/_locales/fi/messages.json +++ b/apps/browser/src/_locales/fi/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/fil/messages.json b/apps/browser/src/_locales/fil/messages.json index 6c7154a1ba5..6239e9d4f97 100644 --- a/apps/browser/src/_locales/fil/messages.json +++ b/apps/browser/src/_locales/fil/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/fr/messages.json b/apps/browser/src/_locales/fr/messages.json index 0e701750c5b..1cc04809a1b 100644 --- a/apps/browser/src/_locales/fr/messages.json +++ b/apps/browser/src/_locales/fr/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/gl/messages.json b/apps/browser/src/_locales/gl/messages.json index c61325ef8de..090bc6b1493 100644 --- a/apps/browser/src/_locales/gl/messages.json +++ b/apps/browser/src/_locales/gl/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/he/messages.json b/apps/browser/src/_locales/he/messages.json index 81ac1e176f0..2934d345600 100644 --- a/apps/browser/src/_locales/he/messages.json +++ b/apps/browser/src/_locales/he/messages.json @@ -595,7 +595,7 @@ "message": "הצג הכל" }, "viewLess": { - "message": "View less" + "message": "הצג פחות" }, "viewLogin": { "message": "הצג כניסה" @@ -800,10 +800,10 @@ "message": "בנעילת המערכת" }, "onIdle": { - "message": "On system idle" + "message": "כשהמערכת מזהה חוסר פעילות" }, "onSleep": { - "message": "On system sleep" + "message": "כשהמערכת נכנסת למצב שינה" }, "onRestart": { "message": "בהפעלת הדפדפן מחדש" @@ -4984,7 +4984,7 @@ } }, "defaultLabelWithValue": { - "message": "Default ( $VALUE$ )", + "message": "ברירת מחדל ( $VALUE$ )", "description": "A label that indicates the default value for a field with the current default value in parentheses.", "placeholders": { "value": { @@ -5786,34 +5786,43 @@ "message": "עבודה נהדרת באבטחת הכניסות בסיכון שלך!" }, "upgradeNow": { - "message": "Upgrade now" + "message": "שדרג עכשיו" }, "builtInAuthenticator": { - "message": "Built-in authenticator" + "message": "מאמת מובנה" }, "secureFileStorage": { - "message": "Secure file storage" + "message": "אחסון קבצים מאובטח" }, "emergencyAccess": { - "message": "Emergency access" + "message": "גישת חירום" }, "breachMonitoring": { - "message": "Breach monitoring" + "message": "ניטור פרצות" }, "andMoreFeatures": { - "message": "And more!" + "message": "ועוד!" }, "planDescPremium": { - "message": "Complete online security" + "message": "השלם אבטחה מקוונת" }, "upgradeToPremium": { - "message": "Upgrade to Premium" + "message": "שדרג לפרימיום" + }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" }, "loadingVault": { - "message": "Loading vault" + "message": "טוען כספת" }, "vaultLoaded": { - "message": "Vault loaded" + "message": "הכספת נטענה" }, "settingDisabledByPolicy": { "message": "הגדרה זו מושבתת על ידי מדיניות של הארגון שלך.", @@ -5826,6 +5835,6 @@ "message": "מספר כרטיס" }, "sessionTimeoutSettingsAction": { - "message": "Timeout action" + "message": "פעולת פסק זמן" } } diff --git a/apps/browser/src/_locales/hi/messages.json b/apps/browser/src/_locales/hi/messages.json index ff24818f821..75011ebf8e5 100644 --- a/apps/browser/src/_locales/hi/messages.json +++ b/apps/browser/src/_locales/hi/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/hr/messages.json b/apps/browser/src/_locales/hr/messages.json index 052fae33683..fb89e8940e3 100644 --- a/apps/browser/src/_locales/hr/messages.json +++ b/apps/browser/src/_locales/hr/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": " Nadogradi na Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Učitavanje trezora" }, diff --git a/apps/browser/src/_locales/hu/messages.json b/apps/browser/src/_locales/hu/messages.json index fb94c4f4665..f9a2f9ff009 100644 --- a/apps/browser/src/_locales/hu/messages.json +++ b/apps/browser/src/_locales/hu/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Áttérés Prémium csomagra" }, + "upgradeCompleteSecurity": { + "message": "Áttérés a teljes biztonságért" + }, + "premiumGivesMoreTools": { + "message": "A Premium több eszközt ad a biztonság megőrzéséhez, a hatékony munkavégzéshez és az irányítás megőrzéséhez." + }, + "explorePremium": { + "message": "Premium felfedezése" + }, "loadingVault": { "message": "Széf betöltése" }, diff --git a/apps/browser/src/_locales/id/messages.json b/apps/browser/src/_locales/id/messages.json index 1cb79804923..a88e3cc2d37 100644 --- a/apps/browser/src/_locales/id/messages.json +++ b/apps/browser/src/_locales/id/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/it/messages.json b/apps/browser/src/_locales/it/messages.json index dc69cb13cb3..f0e63d99ecf 100644 --- a/apps/browser/src/_locales/it/messages.json +++ b/apps/browser/src/_locales/it/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/ja/messages.json b/apps/browser/src/_locales/ja/messages.json index 8b3b0e2cc6d..98ac152a08f 100644 --- a/apps/browser/src/_locales/ja/messages.json +++ b/apps/browser/src/_locales/ja/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "プレミアムにアップグレード" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/ka/messages.json b/apps/browser/src/_locales/ka/messages.json index 5c7a8da23a7..e6e8becd50a 100644 --- a/apps/browser/src/_locales/ka/messages.json +++ b/apps/browser/src/_locales/ka/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/km/messages.json b/apps/browser/src/_locales/km/messages.json index 13e74f8d807..cb1a5c089fe 100644 --- a/apps/browser/src/_locales/km/messages.json +++ b/apps/browser/src/_locales/km/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/kn/messages.json b/apps/browser/src/_locales/kn/messages.json index 3e929bc6533..c83d63762d6 100644 --- a/apps/browser/src/_locales/kn/messages.json +++ b/apps/browser/src/_locales/kn/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/ko/messages.json b/apps/browser/src/_locales/ko/messages.json index 5a21928c233..50f49833d50 100644 --- a/apps/browser/src/_locales/ko/messages.json +++ b/apps/browser/src/_locales/ko/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/lt/messages.json b/apps/browser/src/_locales/lt/messages.json index ac598394a8c..21cd9ca401c 100644 --- a/apps/browser/src/_locales/lt/messages.json +++ b/apps/browser/src/_locales/lt/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/lv/messages.json b/apps/browser/src/_locales/lv/messages.json index 70f46e0f068..088d671a2b5 100644 --- a/apps/browser/src/_locales/lv/messages.json +++ b/apps/browser/src/_locales/lv/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Uzlabot uz Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Ielādē glabātavu" }, diff --git a/apps/browser/src/_locales/ml/messages.json b/apps/browser/src/_locales/ml/messages.json index d139531315b..d76f579fac4 100644 --- a/apps/browser/src/_locales/ml/messages.json +++ b/apps/browser/src/_locales/ml/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/mr/messages.json b/apps/browser/src/_locales/mr/messages.json index 438cc750557..acbbc97ceac 100644 --- a/apps/browser/src/_locales/mr/messages.json +++ b/apps/browser/src/_locales/mr/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/my/messages.json b/apps/browser/src/_locales/my/messages.json index 13e74f8d807..cb1a5c089fe 100644 --- a/apps/browser/src/_locales/my/messages.json +++ b/apps/browser/src/_locales/my/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/nb/messages.json b/apps/browser/src/_locales/nb/messages.json index 4ae8a01a12f..3933f66c541 100644 --- a/apps/browser/src/_locales/nb/messages.json +++ b/apps/browser/src/_locales/nb/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/ne/messages.json b/apps/browser/src/_locales/ne/messages.json index 13e74f8d807..cb1a5c089fe 100644 --- a/apps/browser/src/_locales/ne/messages.json +++ b/apps/browser/src/_locales/ne/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/nl/messages.json b/apps/browser/src/_locales/nl/messages.json index b3463c9f1b3..a4d38f6c9ba 100644 --- a/apps/browser/src/_locales/nl/messages.json +++ b/apps/browser/src/_locales/nl/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Opwaarderen naar Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade voor volledige beveiliging" + }, + "premiumGivesMoreTools": { + "message": "Premium geeft je meer tools om veilig te blijven, efficiënt te werken en in controle te blijven." + }, + "explorePremium": { + "message": "Premium verkennen" + }, "loadingVault": { "message": "Kluis laden" }, diff --git a/apps/browser/src/_locales/nn/messages.json b/apps/browser/src/_locales/nn/messages.json index 13e74f8d807..cb1a5c089fe 100644 --- a/apps/browser/src/_locales/nn/messages.json +++ b/apps/browser/src/_locales/nn/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/or/messages.json b/apps/browser/src/_locales/or/messages.json index 13e74f8d807..cb1a5c089fe 100644 --- a/apps/browser/src/_locales/or/messages.json +++ b/apps/browser/src/_locales/or/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/pl/messages.json b/apps/browser/src/_locales/pl/messages.json index 77b6cc436d7..0847126f33e 100644 --- a/apps/browser/src/_locales/pl/messages.json +++ b/apps/browser/src/_locales/pl/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/pt_BR/messages.json b/apps/browser/src/_locales/pt_BR/messages.json index c3d96145944..dbd7229db2a 100644 --- a/apps/browser/src/_locales/pt_BR/messages.json +++ b/apps/browser/src/_locales/pt_BR/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Faça upgrade para o Premium" }, + "upgradeCompleteSecurity": { + "message": "Faça upgrade para segurança completa" + }, + "premiumGivesMoreTools": { + "message": "O Premium te oferece mais ferramentas para se permanecer seguro, trabalhar eficientemente, e manter o controle." + }, + "explorePremium": { + "message": "Explorar o Premium" + }, "loadingVault": { "message": "Carregando cofre" }, diff --git a/apps/browser/src/_locales/pt_PT/messages.json b/apps/browser/src/_locales/pt_PT/messages.json index 10fbc3db004..55065838ec8 100644 --- a/apps/browser/src/_locales/pt_PT/messages.json +++ b/apps/browser/src/_locales/pt_PT/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Atualizar para o Premium" }, + "upgradeCompleteSecurity": { + "message": "Atualize para obter segurança total" + }, + "premiumGivesMoreTools": { + "message": "O Premium oferece mais ferramentas para manter a segurança, trabalhar com eficiência e manter o controlo." + }, + "explorePremium": { + "message": "Explorar o Premium" + }, "loadingVault": { "message": "A carregar o cofre" }, diff --git a/apps/browser/src/_locales/ro/messages.json b/apps/browser/src/_locales/ro/messages.json index 5fe7c61f9cc..84a1937bf14 100644 --- a/apps/browser/src/_locales/ro/messages.json +++ b/apps/browser/src/_locales/ro/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/ru/messages.json b/apps/browser/src/_locales/ru/messages.json index 349e68c5194..22e9052fe43 100644 --- a/apps/browser/src/_locales/ru/messages.json +++ b/apps/browser/src/_locales/ru/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Обновить до Премиум" }, + "upgradeCompleteSecurity": { + "message": "Перейти для полной защищенности" + }, + "premiumGivesMoreTools": { + "message": "Премиум предоставит вам больше инструментов для обеспечения безопасности, эффективной работы и контроля над ситуацией." + }, + "explorePremium": { + "message": "Познакомиться с Премиум" + }, "loadingVault": { "message": "Загрузка хранилища" }, diff --git a/apps/browser/src/_locales/si/messages.json b/apps/browser/src/_locales/si/messages.json index 9b36684dc5a..f4c00449a2b 100644 --- a/apps/browser/src/_locales/si/messages.json +++ b/apps/browser/src/_locales/si/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/sk/messages.json b/apps/browser/src/_locales/sk/messages.json index a269756a414..b8b820def35 100644 --- a/apps/browser/src/_locales/sk/messages.json +++ b/apps/browser/src/_locales/sk/messages.json @@ -2195,7 +2195,7 @@ "description": "Default URI match detection for autofill." }, "toggleOptions": { - "message": "Voľby prepínača" + "message": "Zobraziť/skryť možnosti" }, "toggleCurrentUris": { "message": "Prepnúť zobrazenie aktuálnej URI", @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgradovať na Prémium" }, + "upgradeCompleteSecurity": { + "message": "Upgradovať pre úplné zabezpečenie" + }, + "premiumGivesMoreTools": { + "message": "Predplatné Prémium vám poskytuje viac nástrojov na zabezpečenie, efektívnu prácu a kontrolu." + }, + "explorePremium": { + "message": "Preskúmať Prémium" + }, "loadingVault": { "message": "Načítava sa trezor" }, diff --git a/apps/browser/src/_locales/sl/messages.json b/apps/browser/src/_locales/sl/messages.json index 3cbd9a11342..70d9c5f70c2 100644 --- a/apps/browser/src/_locales/sl/messages.json +++ b/apps/browser/src/_locales/sl/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/sr/messages.json b/apps/browser/src/_locales/sr/messages.json index d13939f8656..97fcb37fb58 100644 --- a/apps/browser/src/_locales/sr/messages.json +++ b/apps/browser/src/_locales/sr/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Надоградите на Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/sv/messages.json b/apps/browser/src/_locales/sv/messages.json index 9f84e9d714c..ea7a4d80359 100644 --- a/apps/browser/src/_locales/sv/messages.json +++ b/apps/browser/src/_locales/sv/messages.json @@ -595,7 +595,7 @@ "message": "Visa alla" }, "viewLess": { - "message": "View less" + "message": "Visa mindre" }, "viewLogin": { "message": "Visa inloggning" @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Uppgradera till Premium" }, + "upgradeCompleteSecurity": { + "message": "Uppgradera för fullständig säkerhet" + }, + "premiumGivesMoreTools": { + "message": "Premium ger dig fler verktyg för att hålla dig säker, arbeta effektivt och ha kontroll." + }, + "explorePremium": { + "message": "Utforska Premium" + }, "loadingVault": { "message": "Läser in valv" }, diff --git a/apps/browser/src/_locales/ta/messages.json b/apps/browser/src/_locales/ta/messages.json index cbefd26424c..a95da4d2059 100644 --- a/apps/browser/src/_locales/ta/messages.json +++ b/apps/browser/src/_locales/ta/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/te/messages.json b/apps/browser/src/_locales/te/messages.json index 13e74f8d807..cb1a5c089fe 100644 --- a/apps/browser/src/_locales/te/messages.json +++ b/apps/browser/src/_locales/te/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/th/messages.json b/apps/browser/src/_locales/th/messages.json index 594bc6d7a94..9096067ce3b 100644 --- a/apps/browser/src/_locales/th/messages.json +++ b/apps/browser/src/_locales/th/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Upgrade to Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/tr/messages.json b/apps/browser/src/_locales/tr/messages.json index 7f234b8750a..4c7ca6937a4 100644 --- a/apps/browser/src/_locales/tr/messages.json +++ b/apps/browser/src/_locales/tr/messages.json @@ -595,7 +595,7 @@ "message": "Tümünü göster" }, "viewLess": { - "message": "View less" + "message": "Daha az göster" }, "viewLogin": { "message": "Hesabı göster" @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Premium'a yükselt" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Kasa yükleniyor" }, diff --git a/apps/browser/src/_locales/uk/messages.json b/apps/browser/src/_locales/uk/messages.json index a17033ee6e8..7f8b0f8b13b 100644 --- a/apps/browser/src/_locales/uk/messages.json +++ b/apps/browser/src/_locales/uk/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Покращити до Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/vi/messages.json b/apps/browser/src/_locales/vi/messages.json index 2fdba62adeb..414b4cc2cac 100644 --- a/apps/browser/src/_locales/vi/messages.json +++ b/apps/browser/src/_locales/vi/messages.json @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "Nâng cấp lên gói Cao cấp" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "Loading vault" }, diff --git a/apps/browser/src/_locales/zh_CN/messages.json b/apps/browser/src/_locales/zh_CN/messages.json index 52d8a03b769..548bdd9e178 100644 --- a/apps/browser/src/_locales/zh_CN/messages.json +++ b/apps/browser/src/_locales/zh_CN/messages.json @@ -595,7 +595,7 @@ "message": "查看全部" }, "viewLess": { - "message": "View less" + "message": "查看更少" }, "viewLogin": { "message": "查看登录" @@ -1485,7 +1485,7 @@ "message": "优先客户支持。" }, "ppremiumSignUpFuture": { - "message": "未来的更多高级功能。敬请期待!" + "message": "未来的更多高级版功能。敬请期待!" }, "premiumPurchase": { "message": "购买高级版" @@ -4897,7 +4897,7 @@ "message": "确定要永久删除此附件吗?" }, "premium": { - "message": "高级会员" + "message": "高级版" }, "freeOrgsCannotUseAttachments": { "message": "免费组织无法使用附件" @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "升级为高级版" }, + "upgradeCompleteSecurity": { + "message": "升级以获得全面的安全防护" + }, + "premiumGivesMoreTools": { + "message": "高级版为您提供更多工具,助您保障安全、高效工作并掌控一切。" + }, + "explorePremium": { + "message": "探索高级版" + }, "loadingVault": { "message": "正在加载密码库" }, diff --git a/apps/browser/src/_locales/zh_TW/messages.json b/apps/browser/src/_locales/zh_TW/messages.json index 370c147871b..600447a29f3 100644 --- a/apps/browser/src/_locales/zh_TW/messages.json +++ b/apps/browser/src/_locales/zh_TW/messages.json @@ -595,7 +595,7 @@ "message": "檢視全部" }, "viewLess": { - "message": "View less" + "message": "顯示較少" }, "viewLogin": { "message": "檢視登入" @@ -5809,6 +5809,15 @@ "upgradeToPremium": { "message": "升級到 Premium" }, + "upgradeCompleteSecurity": { + "message": "Upgrade for complete security" + }, + "premiumGivesMoreTools": { + "message": "Premium gives you more tools to stay secure, work efficiently, and stay in control." + }, + "explorePremium": { + "message": "Explore Premium" + }, "loadingVault": { "message": "正在載入密碼庫" }, From fd1155ae581b046fc0e300ef9b04cf7b6d43f426 Mon Sep 17 00:00:00 2001 From: Jason Ng <jng@bitwarden.com> Date: Tue, 18 Nov 2025 12:38:18 -0500 Subject: [PATCH 45/75] [PM-27103] Add URL Check to Send (#17056) * add dangerousPatters check to api service --- libs/common/src/platform/misc/utils.spec.ts | 26 +++++++++++ libs/common/src/platform/misc/utils.ts | 49 +++++++++++++++++++++ libs/common/src/services/api.service.ts | 10 ++++- 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/libs/common/src/platform/misc/utils.spec.ts b/libs/common/src/platform/misc/utils.spec.ts index 9f01db61fa6..664c6e22b3a 100644 --- a/libs/common/src/platform/misc/utils.spec.ts +++ b/libs/common/src/platform/misc/utils.spec.ts @@ -689,6 +689,32 @@ describe("Utils Service", () => { }); }); + describe("invalidUrlPatterns", () => { + it("should return false if no invalid patterns are found", () => { + const urlString = "https://www.example.com/api/my/account/status"; + + const actual = Utils.invalidUrlPatterns(urlString); + + expect(actual).toBe(false); + }); + + it("should return true if an invalid pattern is found", () => { + const urlString = "https://www.example.com/api/%2e%2e/secret"; + + const actual = Utils.invalidUrlPatterns(urlString); + + expect(actual).toBe(true); + }); + + it("should return true if an invalid pattern is found in a param", () => { + const urlString = "https://www.example.com/api/history?someToken=../secret"; + + const actual = Utils.invalidUrlPatterns(urlString); + + expect(actual).toBe(true); + }); + }); + describe("getUrl", () => { it("assumes a http protocol if no protocol is specified", () => { const urlString = "www.exampleapp.com.au:4000"; diff --git a/libs/common/src/platform/misc/utils.ts b/libs/common/src/platform/misc/utils.ts index 5f977da3979..136b0ac394f 100644 --- a/libs/common/src/platform/misc/utils.ts +++ b/libs/common/src/platform/misc/utils.ts @@ -612,6 +612,55 @@ export class Utils { return path.normalize(decodeURIComponent(denormalizedPath)).replace(/^(\.\.(\/|\\|$))+/, ""); } + /** + * Validates an url checking against invalid patterns + * @param url + * @returns true if invalid patterns found, false if safe + */ + static invalidUrlPatterns(url: string): boolean { + const invalidUrlPatterns = ["..", "%2e", "\\", "%5c"]; + + const decodedUrl = decodeURIComponent(url.toLocaleLowerCase()); + + // Check URL for invalidUrl patterns across entire URL + if (invalidUrlPatterns.some((p) => decodedUrl.includes(p))) { + return true; + } + + // Check for additional invalid patterns inside URL params + if (decodedUrl.includes("?")) { + const hasInvalidParams = this.validateQueryParameters(decodedUrl); + if (hasInvalidParams) { + return true; + } + } + + return false; + } + + /** + * Validates query parameters for additional invalid patterns + * @param url - The URL containing query parameters + * @returns true if invalid patterns found, false if safe + */ + private static validateQueryParameters(url: string): boolean { + try { + let queryString: string; + + if (url.includes("?")) { + queryString = url.split("?")[1]; + } else { + return false; + } + + const paramInvalidPatterns = ["/", "%2f", "#", "%23"]; + + return paramInvalidPatterns.some((p) => queryString.includes(p)); + } catch (error) { + throw new Error(`Error validating query parameters: ${error}`); + } + } + private static isMobile(win: Window) { let mobile = false; ((a) => { diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index 8314e44e75f..633c7eedcc4 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -1588,8 +1588,16 @@ export class ApiService implements ApiServiceAbstraction { ); apiUrl = Utils.isNullOrWhitespace(apiUrl) ? env.getApiUrl() : apiUrl; - // Prevent directory traversal from malicious paths const pathParts = path.split("?"); + // Check for path traversal patterns from any URL. + const fullUrlPath = apiUrl + pathParts[0] + (pathParts.length > 1 ? `?${pathParts[1]}` : ""); + + const isInvalidUrl = Utils.invalidUrlPatterns(fullUrlPath); + if (isInvalidUrl) { + throw new Error("The request URL contains dangerous patterns."); + } + + // Prevent directory traversal from malicious paths const requestUrl = apiUrl + Utils.normalizePath(pathParts[0]) + (pathParts.length > 1 ? `?${pathParts[1]}` : ""); From 02ef4e72de615ca6fff7319398e69e244bb3a9be Mon Sep 17 00:00:00 2001 From: Github Actions <actions@github.com> Date: Tue, 18 Nov 2025 17:54:35 +0000 Subject: [PATCH 46/75] Bumped Desktop client to 2025.11.2 --- apps/desktop/package.json | 2 +- apps/desktop/src/package-lock.json | 4 ++-- apps/desktop/src/package.json | 2 +- package-lock.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 4639a0b636d..2633f3d5909 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@bitwarden/desktop", "description": "A secure and free password manager for all of your devices.", - "version": "2025.11.1", + "version": "2025.11.2", "keywords": [ "bitwarden", "password", diff --git a/apps/desktop/src/package-lock.json b/apps/desktop/src/package-lock.json index c11c8f08cd0..0a4204bf233 100644 --- a/apps/desktop/src/package-lock.json +++ b/apps/desktop/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bitwarden/desktop", - "version": "2025.11.1", + "version": "2025.11.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@bitwarden/desktop", - "version": "2025.11.1", + "version": "2025.11.2", "license": "GPL-3.0", "dependencies": { "@bitwarden/desktop-napi": "file:../desktop_native/napi" diff --git a/apps/desktop/src/package.json b/apps/desktop/src/package.json index d4800009de9..765bc771b9e 100644 --- a/apps/desktop/src/package.json +++ b/apps/desktop/src/package.json @@ -2,7 +2,7 @@ "name": "@bitwarden/desktop", "productName": "Bitwarden", "description": "A secure and free password manager for all of your devices.", - "version": "2025.11.1", + "version": "2025.11.2", "author": "Bitwarden Inc. <hello@bitwarden.com> (https://bitwarden.com)", "homepage": "https://bitwarden.com", "license": "GPL-3.0", diff --git a/package-lock.json b/package-lock.json index b7b625fdde2..67d3b687862 100644 --- a/package-lock.json +++ b/package-lock.json @@ -280,7 +280,7 @@ }, "apps/desktop": { "name": "@bitwarden/desktop", - "version": "2025.11.1", + "version": "2025.11.2", "hasInstallScript": true, "license": "GPL-3.0" }, From b952e6ea445314323f9342dcee96e3c9c7576fcc Mon Sep 17 00:00:00 2001 From: Will Martin <contact@willmartian.com> Date: Tue, 18 Nov 2025 13:08:21 -0500 Subject: [PATCH 47/75] [PM-28071] add prod test domain for phishing detection (#17450) --- .../dirt/phishing-detection/services/phishing-data.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/browser/src/dirt/phishing-detection/services/phishing-data.service.ts b/apps/browser/src/dirt/phishing-detection/services/phishing-data.service.ts index cb76a1cc354..6e1bf07c647 100644 --- a/apps/browser/src/dirt/phishing-detection/services/phishing-data.service.ts +++ b/apps/browser/src/dirt/phishing-detection/services/phishing-data.service.ts @@ -58,6 +58,7 @@ export class PhishingDataService { new Set( (state?.domains?.filter((line) => line.trim().length > 0) ?? []).concat( this._testDomains, + "phishing.testcategory.com", // Included for QA to test in prod ), ), ), From df03664827c3f77c3d0eff8b7c6cd2efd83721b8 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik <jprusik@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:49:12 -0500 Subject: [PATCH 48/75] [PM-27915] Add additional global styling collision defenses for pseudo-elements (#17340) * add additional global styling collision defenses for pseudo-elements * move internal stylesheet into closed shadow root --- .../autofill-inline-menu-content.service.ts | 26 ---------- .../autofill-inline-menu-iframe-element.ts | 49 +++++++++++++++++++ 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts index b550ae203d5..be93e863275 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts @@ -240,8 +240,6 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte this.buttonElement = globalThis.document.createElement(customElementName); this.buttonElement.setAttribute("popover", "manual"); - - this.createInternalStyleNode(this.buttonElement); } /** @@ -270,30 +268,6 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte this.listElement = globalThis.document.createElement(customElementName); this.listElement.setAttribute("popover", "manual"); - - this.createInternalStyleNode(this.listElement); - } - - /** - * Builds and prepends an internal stylesheet to the container node with rules - * to prevent targeting by the host's global styling rules. This should only be - * used for pseudo elements such as `::backdrop` or `::before`. All other - * styles should be applied inline upon the parent container itself. - */ - private createInternalStyleNode(parent: HTMLElement) { - const css = document.createTextNode(` - ${parent.tagName}::backdrop { - background: none !important; - pointer-events: none !important; - } - ${parent.tagName}::before, ${parent.tagName}::after { - content:"" !important; - } - `); - const style = globalThis.document.createElement("style"); - style.setAttribute("type", "text/css"); - style.appendChild(css); - parent.prepend(style); } /** diff --git a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe-element.ts b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe-element.ts index 2fea65a7f01..3e2b364b17b 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe-element.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe-element.ts @@ -8,7 +8,10 @@ export class AutofillInlineMenuIframeElement { iframeTitle: string, ariaAlert?: string, ) { + const style = this.createInternalStyleNode(); const shadow: ShadowRoot = element.attachShadow({ mode: "closed" }); + shadow.prepend(style); + const autofillInlineMenuIframeService = new AutofillInlineMenuIframeService( shadow, portName, @@ -18,4 +21,50 @@ export class AutofillInlineMenuIframeElement { ); autofillInlineMenuIframeService.initMenuIframe(); } + + /** + * Builds and prepends an internal stylesheet to the container node with rules + * to prevent targeting by the host's global styling rules. This should only be + * used for pseudo elements such as `::backdrop` or `::before`. All other + * styles should be applied inline upon the parent container itself for improved + * specificity priority. + */ + private createInternalStyleNode() { + const css = document.createTextNode(` + :host::backdrop, + :host::before, + :host::after { + all: initial !important; + backdrop-filter: none !important; + filter: none !important; + inset: auto !important; + touch-action: auto !important; + user-select: text !important; + display: none !important; + position: relative !important; + top: auto !important; + right: auto !important; + bottom: auto !important; + left: auto !important; + transform: none !important; + transform-origin: 50% 50% !important; + opacity: 1 !important; + mix-blend-mode: normal !important; + isolation: isolate !important; + z-index: 0 !important; + background: none !important; + background-color: transparent !important; + background-image: none !important; + width: 0 !important; + height: 0 !important; + content: "" !important; + pointer-events: all !important; + } + `); + const style = globalThis.document.createElement("style"); + style.setAttribute("type", "text/css"); + style.appendChild(css); + + return style; + } } From bf8976ca66af75f6132b962b6b43b6734442e5bf Mon Sep 17 00:00:00 2001 From: gitclonebrian <235774926+gitclonebrian@users.noreply.github.com> Date: Tue, 18 Nov 2025 15:27:21 -0500 Subject: [PATCH 49/75] [BRE-1333] Added permissions to token generation (#17471) --- .github/workflows/crowdin-pull.yml | 2 ++ .github/workflows/sdk-breaking-change-check.yml | 2 ++ .github/workflows/version-auto-bump.yml | 1 + 3 files changed, 5 insertions(+) diff --git a/.github/workflows/crowdin-pull.yml b/.github/workflows/crowdin-pull.yml index 19532493071..311737a2c0e 100644 --- a/.github/workflows/crowdin-pull.yml +++ b/.github/workflows/crowdin-pull.yml @@ -54,6 +54,8 @@ jobs: with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} + permission-contents: write # for creating, committing to, and pushing new branches + permission-pull-requests: write # for generating pull requests - name: Checkout repo uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 diff --git a/.github/workflows/sdk-breaking-change-check.yml b/.github/workflows/sdk-breaking-change-check.yml index 759f2292d2a..1b9653417f2 100644 --- a/.github/workflows/sdk-breaking-change-check.yml +++ b/.github/workflows/sdk-breaking-change-check.yml @@ -58,6 +58,8 @@ jobs: with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} + permission-actions: read # for reading and downloading the artifacts for a workflow run + - name: Log out from Azure uses: bitwarden/gh-actions/azure-logout@main diff --git a/.github/workflows/version-auto-bump.yml b/.github/workflows/version-auto-bump.yml index 9ff252d2fe8..d807dd046d3 100644 --- a/.github/workflows/version-auto-bump.yml +++ b/.github/workflows/version-auto-bump.yml @@ -36,6 +36,7 @@ jobs: with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} + permission-contents: write # for committing and pushing to the current branch - name: Check out target ref uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 From fde1e26ad9d26bca7fd925fb863c4fea32012b6f Mon Sep 17 00:00:00 2001 From: Kyle Denney <4227399+kdenney@users.noreply.github.com> Date: Tue, 18 Nov 2025 15:24:36 -0600 Subject: [PATCH 50/75] [PM-28370] fix defect for self-hosted metadata (#17464) --- .../src/services/jslib-services.module.ts | 2 +- .../billing-api.service.abstraction.ts | 4 +++ .../billing/services/billing-api.service.ts | 14 ++++++++++ .../organization-metadata.service.spec.ts | 28 ++++++++++++++++++- .../organization-metadata.service.ts | 6 +++- 5 files changed, 51 insertions(+), 3 deletions(-) diff --git a/libs/angular/src/services/jslib-services.module.ts b/libs/angular/src/services/jslib-services.module.ts index 9dbc6679963..b1215654cfd 100644 --- a/libs/angular/src/services/jslib-services.module.ts +++ b/libs/angular/src/services/jslib-services.module.ts @@ -1451,7 +1451,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: OrganizationMetadataServiceAbstraction, useClass: DefaultOrganizationMetadataService, - deps: [BillingApiServiceAbstraction, ConfigService], + deps: [BillingApiServiceAbstraction, ConfigService, PlatformUtilsServiceAbstraction], }), safeProvider({ provide: BillingAccountProfileStateService, diff --git a/libs/common/src/billing/abstractions/billing-api.service.abstraction.ts b/libs/common/src/billing/abstractions/billing-api.service.abstraction.ts index ef01c98ecb5..dcb395ef85c 100644 --- a/libs/common/src/billing/abstractions/billing-api.service.abstraction.ts +++ b/libs/common/src/billing/abstractions/billing-api.service.abstraction.ts @@ -25,6 +25,10 @@ export abstract class BillingApiServiceAbstraction { organizationId: OrganizationId, ): Promise<OrganizationBillingMetadataResponse>; + abstract getOrganizationBillingMetadataVNextSelfHost( + organizationId: OrganizationId, + ): Promise<OrganizationBillingMetadataResponse>; + abstract getPlans(): Promise<ListResponse<PlanResponse>>; abstract getPremiumPlan(): Promise<PremiumPlanResponse>; diff --git a/libs/common/src/billing/services/billing-api.service.ts b/libs/common/src/billing/services/billing-api.service.ts index 673d4a9784e..ae6913e545c 100644 --- a/libs/common/src/billing/services/billing-api.service.ts +++ b/libs/common/src/billing/services/billing-api.service.ts @@ -62,6 +62,20 @@ export class BillingApiService implements BillingApiServiceAbstraction { return new OrganizationBillingMetadataResponse(r); } + async getOrganizationBillingMetadataVNextSelfHost( + organizationId: OrganizationId, + ): Promise<OrganizationBillingMetadataResponse> { + const r = await this.apiService.send( + "GET", + "/organizations/" + organizationId + "/billing/vnext/self-host/metadata", + null, + true, + true, + ); + + return new OrganizationBillingMetadataResponse(r); + } + async getPlans(): Promise<ListResponse<PlanResponse>> { const r = await this.apiService.send("GET", "/plans", null, true, true); return new ListResponse(r, PlanResponse); diff --git a/libs/common/src/billing/services/organization/organization-metadata.service.spec.ts b/libs/common/src/billing/services/organization/organization-metadata.service.spec.ts index c67f4aed175..a2b012eb161 100644 --- a/libs/common/src/billing/services/organization/organization-metadata.service.spec.ts +++ b/libs/common/src/billing/services/organization/organization-metadata.service.spec.ts @@ -4,6 +4,7 @@ import { BehaviorSubject, firstValueFrom } from "rxjs"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { OrganizationBillingMetadataResponse } from "@bitwarden/common/billing/models/response/organization-billing-metadata.response"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { newGuid } from "@bitwarden/guid"; import { FeatureFlag } from "../../../enums/feature-flag.enum"; @@ -15,6 +16,7 @@ describe("DefaultOrganizationMetadataService", () => { let service: DefaultOrganizationMetadataService; let billingApiService: jest.Mocked<BillingApiServiceAbstraction>; let configService: jest.Mocked<ConfigService>; + let platformUtilsService: jest.Mocked<PlatformUtilsService>; let featureFlagSubject: BehaviorSubject<boolean>; const mockOrganizationId = newGuid() as OrganizationId; @@ -33,11 +35,17 @@ describe("DefaultOrganizationMetadataService", () => { beforeEach(() => { billingApiService = mock<BillingApiServiceAbstraction>(); configService = mock<ConfigService>(); + platformUtilsService = mock<PlatformUtilsService>(); featureFlagSubject = new BehaviorSubject<boolean>(false); configService.getFeatureFlag$.mockReturnValue(featureFlagSubject.asObservable()); + platformUtilsService.isSelfHost.mockReturnValue(false); - service = new DefaultOrganizationMetadataService(billingApiService, configService); + service = new DefaultOrganizationMetadataService( + billingApiService, + configService, + platformUtilsService, + ); }); afterEach(() => { @@ -142,6 +150,24 @@ describe("DefaultOrganizationMetadataService", () => { expect(result3).toEqual(mockResponse1); expect(result4).toEqual(mockResponse2); }); + + it("calls getOrganizationBillingMetadataVNextSelfHost when feature flag is on and isSelfHost is true", async () => { + platformUtilsService.isSelfHost.mockReturnValue(true); + const mockResponse = createMockMetadataResponse(true, 25); + billingApiService.getOrganizationBillingMetadataVNextSelfHost.mockResolvedValue( + mockResponse, + ); + + const result = await firstValueFrom(service.getOrganizationMetadata$(mockOrganizationId)); + + expect(platformUtilsService.isSelfHost).toHaveBeenCalled(); + expect(billingApiService.getOrganizationBillingMetadataVNextSelfHost).toHaveBeenCalledWith( + mockOrganizationId, + ); + expect(billingApiService.getOrganizationBillingMetadataVNext).not.toHaveBeenCalled(); + expect(billingApiService.getOrganizationBillingMetadata).not.toHaveBeenCalled(); + expect(result).toEqual(mockResponse); + }); }); describe("shareReplay behavior", () => { diff --git a/libs/common/src/billing/services/organization/organization-metadata.service.ts b/libs/common/src/billing/services/organization/organization-metadata.service.ts index fe96f0d984c..5ce87262c4b 100644 --- a/libs/common/src/billing/services/organization/organization-metadata.service.ts +++ b/libs/common/src/billing/services/organization/organization-metadata.service.ts @@ -1,6 +1,7 @@ import { BehaviorSubject, combineLatest, from, Observable, shareReplay, switchMap } from "rxjs"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { FeatureFlag } from "../../../enums/feature-flag.enum"; import { ConfigService } from "../../../platform/abstractions/config/config.service"; @@ -17,6 +18,7 @@ export class DefaultOrganizationMetadataService implements OrganizationMetadataS constructor( private billingApiService: BillingApiServiceAbstraction, private configService: ConfigService, + private platformUtilsService: PlatformUtilsService, ) {} private refreshMetadataTrigger = new BehaviorSubject<void>(undefined); @@ -67,7 +69,9 @@ export class DefaultOrganizationMetadataService implements OrganizationMetadataS featureFlagEnabled: boolean, ): Promise<OrganizationBillingMetadataResponse> { return featureFlagEnabled - ? await this.billingApiService.getOrganizationBillingMetadataVNext(organizationId) + ? this.platformUtilsService.isSelfHost() + ? await this.billingApiService.getOrganizationBillingMetadataVNextSelfHost(organizationId) + : await this.billingApiService.getOrganizationBillingMetadataVNext(organizationId) : await this.billingApiService.getOrganizationBillingMetadata(organizationId); } } From 84ce21643dc3789f36b773012768e854a08d7530 Mon Sep 17 00:00:00 2001 From: Alex <55413326+AlexRubik@users.noreply.github.com> Date: Tue, 18 Nov 2025 17:27:40 -0500 Subject: [PATCH 51/75] text and style (#17439) --- .../access-intelligence/activity/all-activity.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 ffc67028b77..c9b930b3b50 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 @@ -80,11 +80,11 @@ @else { <li class="tw-col-span-1"> <dirt-activity-card - [title]="'applicationsNeedingReview' | i18n" + [title]="'reviewNewApplications' | i18n" [cardMetrics]="'newApplicationsWithCount' | i18n: newApplicationsCount" [metricDescription]="'newApplicationsDescription' | i18n" [iconClass]="'bwi-exclamation-triangle'" - [iconColorClass]="'tw-text-muted'" + [iconColorClass]="'tw-text-warning'" [buttonText]="'reviewNow' | i18n" [buttonType]="'primary'" (buttonClick)="onReviewNewApplications()" From 64bfbf274aa4e33447445f5dcc32311b16119b01 Mon Sep 17 00:00:00 2001 From: Github Actions <actions@github.com> Date: Wed, 19 Nov 2025 00:18:10 +0000 Subject: [PATCH 52/75] Bumped client version(s) --- apps/browser/package.json | 2 +- apps/browser/src/manifest.json | 2 +- apps/browser/src/manifest.v3.json | 2 +- package-lock.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/browser/package.json b/apps/browser/package.json index 82d2ad7ab7a..72e112e62f7 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -1,6 +1,6 @@ { "name": "@bitwarden/browser", - "version": "2025.11.0", + "version": "2025.11.1", "scripts": { "build": "npm run build:chrome", "build:bit": "npm run build:bit:chrome", diff --git a/apps/browser/src/manifest.json b/apps/browser/src/manifest.json index d44a3d2a2e7..3d8f648daca 100644 --- a/apps/browser/src/manifest.json +++ b/apps/browser/src/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "__MSG_extName__", "short_name": "Bitwarden", - "version": "2025.11.0", + "version": "2025.11.1", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index b6381201c7d..73403d2321e 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -3,7 +3,7 @@ "minimum_chrome_version": "102.0", "name": "__MSG_extName__", "short_name": "Bitwarden", - "version": "2025.11.0", + "version": "2025.11.1", "description": "__MSG_extDesc__", "default_locale": "en", "author": "Bitwarden Inc.", diff --git a/package-lock.json b/package-lock.json index 67d3b687862..44e46a5d41b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -194,7 +194,7 @@ }, "apps/browser": { "name": "@bitwarden/browser", - "version": "2025.11.0" + "version": "2025.11.1" }, "apps/cli": { "name": "@bitwarden/cli", From e32309260cf85ac2c3fc7530aa2d4866d862be08 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 05:39:11 -0600 Subject: [PATCH 53/75] [deps] Platform: Update @types/node-forge to v1.3.14 (#17490) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 44e46a5d41b..ccfad167c86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -114,7 +114,7 @@ "@types/lunr": "2.3.7", "@types/node": "22.19.1", "@types/node-fetch": "2.6.4", - "@types/node-forge": "1.3.11", + "@types/node-forge": "1.3.14", "@types/papaparse": "5.5.0", "@types/proper-lockfile": "4.1.4", "@types/retry": "0.12.5", @@ -14463,9 +14463,9 @@ } }, "node_modules/@types/node-forge": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", - "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.14.tgz", + "integrity": "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==", "license": "MIT", "dependencies": { "@types/node": "*" diff --git a/package.json b/package.json index 52a7d1c60a2..757b7fec628 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "@types/lunr": "2.3.7", "@types/node": "22.19.1", "@types/node-fetch": "2.6.4", - "@types/node-forge": "1.3.11", + "@types/node-forge": "1.3.14", "@types/papaparse": "5.5.0", "@types/proper-lockfile": "4.1.4", "@types/retry": "0.12.5", From 58727e884302feb8b835b7aca2b2b019870f870f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 05:39:33 -0600 Subject: [PATCH 54/75] [deps] Platform: Update @types/chrome to v0.1.28 (#17489) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index ccfad167c86..674dcabf122 100644 --- a/package-lock.json +++ b/package-lock.json @@ -100,7 +100,7 @@ "@storybook/theming": "8.6.12", "@storybook/web-components-webpack5": "8.6.12", "@tailwindcss/container-queries": "0.1.1", - "@types/chrome": "0.1.27", + "@types/chrome": "0.1.28", "@types/firefox-webext-browser": "120.0.4", "@types/inquirer": "8.2.10", "@types/jest": "29.5.14", @@ -13957,9 +13957,9 @@ } }, "node_modules/@types/chrome": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.1.27.tgz", - "integrity": "sha512-pkkCb0Ft8X+Igi751POzT+YqchSxUCtB6s4Gs6ttgSj8qzJga/qlJMgSW1mKxuQTW4i0sTqQbqVtzXDS5AU+4A==", + "version": "0.1.28", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.1.28.tgz", + "integrity": "sha512-wANMmVt9H8UJeRsk4vlk5IVTTUIdk0J6CJC2ER60fGHTJOFVMuXpGhCqs6fUGw3m9pF1eXEvi+6ejlQZrtGA4A==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 757b7fec628..8322d065eba 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@storybook/theming": "8.6.12", "@storybook/web-components-webpack5": "8.6.12", "@tailwindcss/container-queries": "0.1.1", - "@types/chrome": "0.1.27", + "@types/chrome": "0.1.28", "@types/firefox-webext-browser": "120.0.4", "@types/inquirer": "8.2.10", "@types/jest": "29.5.14", From 90ca6bf2cd769934ca778fb5a124939e9897823c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 12:00:49 +0000 Subject: [PATCH 55/75] [deps]: Update codecov/test-results-action action to v1.1.1 (#17493) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 71f8e7c9155..ed967e63b5a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -71,7 +71,7 @@ jobs: fail-on-error: true - name: Upload results to codecov.io - uses: codecov/test-results-action@f2dba722c67b86c6caa034178c6e4d35335f6706 # v1.1.0 + uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1 - name: Upload test coverage uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 From db16c201b879c8aa4c1a52a79cc2aa229a808cb2 Mon Sep 17 00:00:00 2001 From: neuronull <9162534+neuronull@users.noreply.github.com> Date: Wed, 19 Nov 2025 08:07:57 -0700 Subject: [PATCH 56/75] Align Desktop Native's Rust CI checks with SDK (#17261) * clean crate deps * update lint workflow * add rustfmt.toml * apply rust fmt * missed one * fix lint of lint lol * more deps platform fixes * fix macos_provider * some more deps clean * more cleanup * add --all-targets * remove another unused dep * generate index.d.ts * fix whitespace * fix split comment in biometric * formatting comment in biometric_v2 * apply fmt --- .github/workflows/lint.yml | 24 ++- apps/desktop/desktop_native/Cargo.lock | 200 ------------------ apps/desktop/desktop_native/Cargo.toml | 2 - .../autotype/src/windows/type_input.rs | 15 +- .../autotype/src/windows/window_title.rs | 16 +- .../src/windows/crypto.rs | 3 +- .../src/windows/impersonate.rs | 3 +- .../src/windows/log.rs | 6 +- .../src/windows/main.rs | 10 +- .../chromium_importer/Cargo.toml | 17 +- .../chromium_importer/src/chromium/mod.rs | 20 +- .../src/chromium/platform/linux.rs | 10 +- .../src/chromium/platform/macos.rs | 7 +- .../src/chromium/platform/windows/abe.rs | 6 +- .../src/chromium/platform/windows/mod.rs | 16 +- .../chromium/platform/windows/signature.rs | 3 +- .../chromium_importer/src/metadata.rs | 2 +- .../chromium_importer/src/util.rs | 43 ++-- apps/desktop/desktop_native/core/Cargo.toml | 29 +-- .../desktop_native/core/src/biometric/mod.rs | 10 +- .../desktop_native/core/src/biometric/unix.rs | 10 +- .../core/src/biometric/windows.rs | 7 +- .../core/src/biometric_v2/linux.rs | 27 +-- .../core/src/biometric_v2/mod.rs | 7 +- .../core/src/biometric_v2/windows.rs | 69 +++--- .../core/src/biometric_v2/windows_focus.rs | 32 +-- .../desktop_native/core/src/crypto/crypto.rs | 6 +- apps/desktop/desktop_native/core/src/error.rs | 11 - .../desktop_native/core/src/ipc/mod.rs | 8 +- .../desktop_native/core/src/ipc/server.rs | 19 +- .../desktop_native/core/src/password/macos.rs | 3 +- .../desktop_native/core/src/password/unix.rs | 6 +- .../core/src/password/windows.rs | 3 +- .../core/src/process_isolation/linux.rs | 8 +- .../core/src/secure_memory/dpapi.rs | 5 +- .../secure_memory/encrypted_memory_store.rs | 4 +- .../src/secure_memory/secure_key/crypto.rs | 6 +- .../src/secure_memory/secure_key/dpapi.rs | 7 +- .../src/secure_memory/secure_key/keyctl.rs | 13 +- .../secure_memory/secure_key/memfd_secret.rs | 11 +- .../src/secure_memory/secure_key/mlock.rs | 7 +- .../core/src/secure_memory/secure_key/mod.rs | 27 ++- .../desktop_native/core/src/ssh_agent/mod.rs | 8 +- .../ssh_agent/named_pipe_listener_stream.rs | 5 +- .../peercred_unix_listener_stream.rs | 12 +- .../core/src/ssh_agent/peerinfo/models.rs | 7 +- .../desktop_native/core/src/ssh_agent/unix.rs | 3 +- .../core/src/ssh_agent/windows.rs | 1 + .../desktop_native/macos_provider/Cargo.toml | 7 +- apps/desktop/desktop_native/napi/Cargo.toml | 4 - apps/desktop/desktop_native/napi/index.d.ts | 23 +- apps/desktop/desktop_native/napi/src/lib.rs | 37 ++-- apps/desktop/desktop_native/objc/Cargo.toml | 8 +- .../process_isolation/Cargo.toml | 2 +- .../process_isolation/src/lib.rs | 3 +- apps/desktop/desktop_native/proxy/Cargo.toml | 1 - apps/desktop/desktop_native/proxy/src/main.rs | 10 +- apps/desktop/desktop_native/rustfmt.toml | 7 + .../windows_plugin_authenticator/src/lib.rs | 11 +- 59 files changed, 382 insertions(+), 505 deletions(-) create mode 100644 apps/desktop/desktop_native/rustfmt.toml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c14abd7cd86..67186905390 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -98,12 +98,27 @@ jobs: with: persist-credentials: false + - name: Install Rust + uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # stable + with: + toolchain: stable + components: rustfmt, clippy + + - name: Install Rust nightly + uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # stable + with: + toolchain: nightly + components: rustfmt + - name: Check Rust version run: rustup --version + - name: Cache cargo registry + uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2.7.7 + - name: Run cargo fmt working-directory: ./apps/desktop/desktop_native - run: cargo fmt --check + run: cargo +nightly fmt --check - name: Run Clippy working-directory: ./apps/desktop/desktop_native @@ -118,6 +133,13 @@ jobs: working-directory: ./apps/desktop/desktop_native run: cargo sort --workspace --check + - name: Install cargo-udeps + run: cargo install cargo-udeps --version 0.1.57 --locked + + - name: Cargo udeps + working-directory: ./apps/desktop/desktop_native + run: cargo +nightly udeps --workspace --all-features --all-targets + - name: Install cargo-deny uses: taiki-e/install-action@81ee1d48d9194cdcab880cbdc7d36e87d39874cb # v2.62.45 with: diff --git a/apps/desktop/desktop_native/Cargo.lock b/apps/desktop/desktop_native/Cargo.lock index 4da82144305..e8cc9385bb2 100644 --- a/apps/desktop/desktop_native/Cargo.lock +++ b/apps/desktop/desktop_native/Cargo.lock @@ -684,17 +684,6 @@ dependencies = [ "error-code", ] -[[package]] -name = "codespan-reporting" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" -dependencies = [ - "serde", - "termcolor", - "unicode-width", -] - [[package]] name = "colorchoice" version = "1.0.3" @@ -841,65 +830,6 @@ dependencies = [ "syn", ] -[[package]] -name = "cxx" -version = "1.0.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a71ea7f29c73f7ffa64c50b83c9fe4d3a6d4be89a86b009eb80d5a6d3429d741" -dependencies = [ - "cc", - "cxxbridge-cmd", - "cxxbridge-flags", - "cxxbridge-macro", - "foldhash", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a8232661d66dcf713394726157d3cfe0a89bfc85f52d6e9f9bbc2306797fe7" -dependencies = [ - "cc", - "codespan-reporting", - "proc-macro2", - "quote", - "scratch", - "syn", -] - -[[package]] -name = "cxxbridge-cmd" -version = "1.0.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f44296c8693e9ea226a48f6a122727f77aa9e9e338380cb021accaeeb7ee279" -dependencies = [ - "clap", - "codespan-reporting", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f69c181c176981ae44ba9876e2ea41ce8e574c296b38d06925ce9214fb8e4" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8faff5d4467e0709448187df29ccbf3b0982cc426ee444a193f87b11afb565a8" -dependencies = [ - "proc-macro2", - "quote", - "rustversion", - "syn", -] - [[package]] name = "der" version = "0.7.10" @@ -921,27 +851,21 @@ dependencies = [ "ashpd", "base64", "bitwarden-russh", - "byteorder", "bytes", "cbc", "chacha20poly1305", "core-foundation", "desktop_objc", "dirs", - "ed25519", "futures", "homedir", "interprocess", - "keytar", "libc", "linux-keyutils", "memsec", "oo7", "pin-project", - "pkcs8", "rand 0.9.1", - "rsa", - "russh-cryptovec", "scopeguard", "secmem-proc", "security-framework", @@ -949,12 +873,10 @@ dependencies = [ "serde", "serde_json", "sha2", - "ssh-encoding", "ssh-key", "sysinfo", "thiserror 2.0.12", "tokio", - "tokio-stream", "tokio-util", "tracing", "typenum", @@ -972,18 +894,14 @@ version = "0.0.0" dependencies = [ "anyhow", "autotype", - "base64", "chromium_importer", "desktop_core", - "hex", "napi", "napi-build", "napi-derive", "serde", "serde_json", "tokio", - "tokio-stream", - "tokio-util", "tracing", "tracing-subscriber", "windows-registry", @@ -996,9 +914,7 @@ version = "0.0.0" dependencies = [ "anyhow", "cc", - "core-foundation", "glob", - "thiserror 2.0.12", "tokio", "tracing", ] @@ -1007,7 +923,6 @@ dependencies = [ name = "desktop_proxy" version = "0.0.0" dependencies = [ - "anyhow", "desktop_core", "embed_plist", "futures", @@ -1740,27 +1655,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" -[[package]] -name = "keytar" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d361c55fba09829ac620b040f5425bf239b1030c3d6820a84acac8da867dca4d" -dependencies = [ - "keytar-sys", -] - -[[package]] -name = "keytar-sys" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe908c6896705a1cb516cd6a5d956c63f08d95ace81b93253a98cd93e1e6a65a" -dependencies = [ - "cc", - "cxx", - "cxx-build", - "pkg-config", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -1813,15 +1707,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "link-cplusplus" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212" -dependencies = [ - "cc", -] - [[package]] name = "linux-keyutils" version = "0.2.4" @@ -1875,7 +1760,6 @@ dependencies = [ "serde", "serde_json", "tokio", - "tokio-util", "tracing", "tracing-oslog", "tracing-subscriber", @@ -2521,21 +2405,6 @@ dependencies = [ "spki", ] -[[package]] -name = "pkcs5" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" -dependencies = [ - "aes", - "cbc", - "der", - "pbkdf2", - "scrypt", - "sha2", - "spki", -] - [[package]] name = "pkcs8" version = "0.10.2" @@ -2543,8 +2412,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", - "pkcs5", - "rand_core 0.6.4", "spki", ] @@ -2923,27 +2790,12 @@ dependencies = [ "rustix 1.0.7", ] -[[package]] -name = "rustversion" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" - [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - [[package]] name = "scc" version = "2.4.0" @@ -2959,12 +2811,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "scratch" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52" - [[package]] name = "scroll" version = "0.12.0" @@ -2985,17 +2831,6 @@ dependencies = [ "syn", ] -[[package]] -name = "scrypt" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" -dependencies = [ - "pbkdf2", - "salsa20", - "sha2", -] - [[package]] name = "sdd" version = "3.0.10" @@ -3370,15 +3205,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - [[package]] name = "termtree" version = "0.5.1" @@ -3483,17 +3309,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-stream" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-util" version = "0.7.13" @@ -3693,12 +3508,6 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" - [[package]] name = "uniffi" version = "0.28.3" @@ -4029,15 +3838,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" -dependencies = [ - "windows-sys 0.59.0", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/apps/desktop/desktop_native/Cargo.toml b/apps/desktop/desktop_native/Cargo.toml index ccf7c1f3796..d7afd44e9cd 100644 --- a/apps/desktop/desktop_native/Cargo.toml +++ b/apps/desktop/desktop_native/Cargo.toml @@ -39,7 +39,6 @@ futures = "=0.3.31" hex = "=0.4.3" homedir = "=0.3.4" interprocess = "=2.2.1" -keytar = "=0.1.6" libc = "=0.2.172" linux-keyutils = "=0.2.4" memsec = "=0.7.0" @@ -64,7 +63,6 @@ ssh-key = { version = "=0.6.7", default-features = false } sysinfo = "=0.35.0" thiserror = "=2.0.12" 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 = [ diff --git a/apps/desktop/desktop_native/autotype/src/windows/type_input.rs b/apps/desktop/desktop_native/autotype/src/windows/type_input.rs index b757cf7752f..10f30f5ee4f 100644 --- a/apps/desktop/desktop_native/autotype/src/windows/type_input.rs +++ b/apps/desktop/desktop_native/autotype/src/windows/type_input.rs @@ -33,7 +33,8 @@ impl InputOperations for Win32InputOperations { /// Attempts to type the input text wherever the user's cursor is. /// /// `input` must be a vector of utf-16 encoded characters to insert. -/// `keyboard_shortcut` must be a vector of Strings, where valid shortcut keys: Control, Alt, Super, Shift, letters a - Z +/// `keyboard_shortcut` must be a vector of Strings, where valid shortcut keys: Control, Alt, Super, +/// Shift, letters a - Z /// /// https://learn.microsoft.com/en-in/windows/win32/api/winuser/nf-winuser-sendinput pub(super) fn type_input(input: Vec<u16>, keyboard_shortcut: Vec<String>) -> Result<()> { @@ -234,16 +235,16 @@ where #[cfg(test)] mod tests { - //! For the mocking of the traits that are static methods, we need to use the `serial_test` crate - //! in order to mock those, since the mock expectations set have to be global in absence of a `self`. - //! More info: https://docs.rs/mockall/latest/mockall/#static-methods + //! For the mocking of the traits that are static methods, we need to use the `serial_test` + //! crate in order to mock those, since the mock expectations set have to be global in + //! absence of a `self`. More info: https://docs.rs/mockall/latest/mockall/#static-methods - use super::*; - - use crate::windowing::MockErrorOperations; use serial_test::serial; use windows::Win32::Foundation::WIN32_ERROR; + use super::*; + use crate::windowing::MockErrorOperations; + #[test] fn get_alphabetic_hot_key_succeeds() { for c in ('a'..='z').chain('A'..='Z') { diff --git a/apps/desktop/desktop_native/autotype/src/windows/window_title.rs b/apps/desktop/desktop_native/autotype/src/windows/window_title.rs index 58f06eb54c1..d56a811ab5c 100644 --- a/apps/desktop/desktop_native/autotype/src/windows/window_title.rs +++ b/apps/desktop/desktop_native/autotype/src/windows/window_title.rs @@ -127,8 +127,8 @@ where /// /// # Errors /// -/// - If the actual window title length (what the win32 API declares was written into the -/// buffer), is length zero and GetLastError() != 0 , return the GetLastError() message. +/// - If the actual window title length (what the win32 API declares was written into the buffer), +/// is length zero and GetLastError() != 0 , return the GetLastError() message. fn get_window_title<H, E>(window_handle: &H, expected_title_length: usize) -> Result<String> where H: WindowHandleOperations, @@ -169,17 +169,17 @@ where #[cfg(test)] mod tests { - //! For the mocking of the traits that are static methods, we need to use the `serial_test` crate - //! in order to mock those, since the mock expectations set have to be global in absence of a `self`. - //! More info: https://docs.rs/mockall/latest/mockall/#static-methods + //! For the mocking of the traits that are static methods, we need to use the `serial_test` + //! crate in order to mock those, since the mock expectations set have to be global in + //! absence of a `self`. More info: https://docs.rs/mockall/latest/mockall/#static-methods - use super::*; - - use crate::windowing::MockErrorOperations; use mockall::predicate; use serial_test::serial; use windows::Win32::Foundation::WIN32_ERROR; + use super::*; + use crate::windowing::MockErrorOperations; + #[test] #[serial] fn get_window_title_length_can_be_zero() { diff --git a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/crypto.rs b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/crypto.rs index 094dbf94a67..c335a4b296a 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/crypto.rs +++ b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/crypto.rs @@ -95,7 +95,8 @@ pub(crate) fn decode_abe_key_blob(blob_data: &[u8]) -> Result<Vec<u8>> { let content_offset = content_len_offset + 4; let content = get_safe(blob_data, content_offset, content_len)?; - // When the size is exactly 32 bytes, it's a plain key. It's used in unbranded Chromium builds, Brave, possibly Edge + // When the size is exactly 32 bytes, it's a plain key. It's used in unbranded Chromium builds, + // Brave, possibly Edge if content_len == 32 { return Ok(content.to_vec()); } diff --git a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/impersonate.rs b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/impersonate.rs index 5a5109b9d32..22006b8db14 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/impersonate.rs +++ b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/impersonate.rs @@ -30,7 +30,8 @@ pub(crate) fn start_impersonating() -> Result<HANDLE> { // Need to enable SE_DEBUG_PRIVILEGE to enumerate and open SYSTEM processes enable_debug_privilege()?; - // Find a SYSTEM process and get its token. Not every SYSTEM process allows token duplication, so try several. + // Find a SYSTEM process and get its token. Not every SYSTEM process allows token duplication, + // so try several. let (token, pid, name) = find_system_process_with_token(get_system_pid_list())?; // Impersonate the SYSTEM process diff --git a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/log.rs b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/log.rs index 7ee34a4160e..aa00a2f61b7 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/log.rs +++ b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/log.rs @@ -1,13 +1,13 @@ +use chromium_importer::config::{ENABLE_DEVELOPER_LOGGING, LOG_FILENAME}; use tracing::{error, level_filters::LevelFilter}; use tracing_subscriber::{ fmt, layer::SubscriberExt as _, util::SubscriberInitExt as _, EnvFilter, Layer as _, }; -use chromium_importer::config::{ENABLE_DEVELOPER_LOGGING, LOG_FILENAME}; - pub(crate) fn init_logging() { if ENABLE_DEVELOPER_LOGGING { - // We only log to a file. It's impossible to see stdout/stderr when this exe is launched from ShellExecuteW. + // We only log to a file. It's impossible to see stdout/stderr when this exe is launched + // from ShellExecuteW. match std::fs::File::create(LOG_FILENAME) { Ok(file) => { let file_filter = EnvFilter::builder() diff --git a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/main.rs b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/main.rs index e178a8accf7..560135b8ce4 100644 --- a/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/main.rs +++ b/apps/desktop/desktop_native/bitwarden_chromium_import_helper/src/windows/main.rs @@ -1,12 +1,14 @@ -use anyhow::{anyhow, Result}; -use clap::Parser; -use scopeguard::defer; use std::{ ffi::OsString, os::windows::{ffi::OsStringExt as _, io::AsRawHandle}, path::PathBuf, time::Duration, }; + +use anyhow::{anyhow, Result}; +use chromium_importer::chromium::{verify_signature, ADMIN_TO_USER_PIPE_NAME}; +use clap::Parser; +use scopeguard::defer; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, net::windows::named_pipe::{ClientOptions, NamedPipeClient}, @@ -25,8 +27,6 @@ use windows::Win32::{ UI::Shell::IsUserAnAdmin, }; -use chromium_importer::chromium::{verify_signature, ADMIN_TO_USER_PIPE_NAME}; - use super::{ crypto::{ decode_abe_key_blob, decode_base64, decrypt_with_dpapi_as_system, diff --git a/apps/desktop/desktop_native/chromium_importer/Cargo.toml b/apps/desktop/desktop_native/chromium_importer/Cargo.toml index 933b0a8dac3..4b02079bfdb 100644 --- a/apps/desktop/desktop_native/chromium_importer/Cargo.toml +++ b/apps/desktop/desktop_native/chromium_importer/Cargo.toml @@ -7,35 +7,38 @@ publish = { workspace = true } [dependencies] aes = { workspace = true } -aes-gcm = { workspace = true } anyhow = { workspace = true } async-trait = "=0.1.88" -base64 = { workspace = true } -cbc = { workspace = true, features = ["alloc"] } dirs = { workspace = true } hex = { workspace = true } -pbkdf2 = "=0.12.2" rand = { workspace = true } rusqlite = { version = "=0.37.0", features = ["bundled"] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } -sha1 = "=0.10.6" -tokio = { workspace = true, features = ["full"] } -tracing = { workspace = true } [target.'cfg(target_os = "macos")'.dependencies] +cbc = { workspace = true, features = ["alloc"] } +pbkdf2 = "=0.12.2" security-framework = { workspace = true } +sha1 = "=0.10.6" [target.'cfg(target_os = "windows")'.dependencies] +aes-gcm = { workspace = true } +base64 = { workspace = true } windows = { workspace = true, features = [ "Win32_Security_Cryptography", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging", ] } verifysign = "=0.2.4" +tokio = { workspace = true, features = ["full"] } +tracing = { workspace = true } [target.'cfg(target_os = "linux")'.dependencies] +cbc = { workspace = true, features = ["alloc"] } oo7 = { workspace = true } +pbkdf2 = "=0.12.2" +sha1 = "=0.10.6" [lints] workspace = true diff --git a/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs index 369e63e0ad1..e57b40b5778 100644 --- a/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/mod.rs @@ -1,6 +1,8 @@ -use std::collections::HashMap; -use std::path::{Path, PathBuf}; -use std::sync::LazyLock; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, + sync::LazyLock, +}; use anyhow::{anyhow, Result}; use async_trait::async_trait; @@ -10,11 +12,10 @@ use rusqlite::{params, Connection}; mod platform; +pub(crate) use platform::SUPPORTED_BROWSERS as PLATFORM_SUPPORTED_BROWSERS; #[cfg(target_os = "windows")] pub use platform::*; -pub(crate) use platform::SUPPORTED_BROWSERS as PLATFORM_SUPPORTED_BROWSERS; - // // Public API // @@ -87,14 +88,15 @@ pub async fn import_logins( let local_logins = get_logins(&data_dir, profile_id, "Login Data") .map_err(|e| anyhow!("Failed to query logins: {}", e))?; - // This is not available in all browsers, but there's no harm in trying. If the file doesn't exist we just get an empty vector. + // This is not available in all browsers, but there's no harm in trying. If the file doesn't + // exist we just get an empty vector. let account_logins = get_logins(&data_dir, profile_id, "Login Data For Account") .map_err(|e| anyhow!("Failed to query logins: {}", e))?; // TODO: Do we need a better merge strategy? Maybe ignore duplicates at least? - // TODO: Should we also ignore an error from one of the two imports? If one is successful and the other fails, - // should we still return the successful ones? At the moment it doesn't fail for a missing file, only when - // something goes really wrong. + // TODO: Should we also ignore an error from one of the two imports? If one is successful and + // the other fails, should we still return the successful ones? At the moment it + // doesn't fail for a missing file, only when something goes really wrong. let all_logins = local_logins .into_iter() .chain(account_logins.into_iter()) diff --git a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/linux.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/linux.rs index 227dffdcca7..14e38797640 100644 --- a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/linux.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/linux.rs @@ -4,15 +4,17 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use oo7::XDG_SCHEMA_ATTRIBUTE; -use crate::chromium::{BrowserConfig, CryptoService, LocalState}; - -use crate::util; +use crate::{ + chromium::{BrowserConfig, CryptoService, LocalState}, + util, +}; // // Public API // -// TODO: It's possible that there might be multiple possible data directories, depending on the installation method (e.g., snap, flatpak, etc.). +// TODO: It's possible that there might be multiple possible data directories, depending on the +// installation method (e.g., snap, flatpak, etc.). pub(crate) const SUPPORTED_BROWSERS: &[BrowserConfig] = &[ BrowserConfig { name: "Chrome", diff --git a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/macos.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/macos.rs index c0e770c161b..5d0b4f0c75c 100644 --- a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/macos.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/macos.rs @@ -2,9 +2,10 @@ use anyhow::{anyhow, Result}; use async_trait::async_trait; use security_framework::passwords::get_generic_password; -use crate::chromium::{BrowserConfig, CryptoService, LocalState}; - -use crate::util; +use crate::{ + chromium::{BrowserConfig, CryptoService, LocalState}, + util, +}; // // Public API diff --git a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/abe.rs b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/abe.rs index 943727690f2..a76f7b95e5c 100644 --- a/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/abe.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/chromium/platform/windows/abe.rs @@ -1,6 +1,6 @@ -use super::abe_config; -use anyhow::{anyhow, Result}; use std::{ffi::OsStr, os::windows::ffi::OsStrExt}; + +use anyhow::{anyhow, Result}; use tokio::{ io::{self, AsyncReadExt, AsyncWriteExt}, net::windows::named_pipe::{NamedPipeServer, ServerOptions}, @@ -14,6 +14,8 @@ use windows::{ Win32::UI::{Shell::ShellExecuteW, WindowsAndMessaging::SW_HIDE}, }; +use super::abe_config; + const WAIT_FOR_ADMIN_MESSAGE_TIMEOUT_SECS: u64 = 30; fn start_tokio_named_pipe_server<F>( 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 867104d9bfd..9cc89ed2161 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 @@ -1,11 +1,14 @@ +use std::path::{Path, PathBuf}; + use aes_gcm::{aead::Aead, Aes256Gcm, Key, KeyInit, Nonce}; use anyhow::{anyhow, Result}; use async_trait::async_trait; use base64::{engine::general_purpose::STANDARD as BASE64_STANDARD, Engine as _}; -use std::path::{Path, PathBuf}; -use crate::chromium::{BrowserConfig, CryptoService, LocalState}; -use crate::util; +use crate::{ + chromium::{BrowserConfig, CryptoService, LocalState}, + util, +}; mod abe; mod abe_config; mod crypto; @@ -95,7 +98,8 @@ impl CryptoService for WindowsCryptoService { let (version, no_prefix) = util::split_encrypted_string_and_validate(encrypted, &["v10", "v20"])?; - // v10 is already stripped; Windows Chrome uses AES-GCM: [12 bytes IV][ciphertext][16 bytes auth tag] + // v10 is already stripped; Windows Chrome uses AES-GCM: [12 bytes IV][ciphertext][16 bytes + // auth tag] const IV_SIZE: usize = 12; const TAG_SIZE: usize = 16; const MIN_LENGTH: usize = IV_SIZE + TAG_SIZE; @@ -242,8 +246,8 @@ fn get_dist_admin_exe_path(current_exe_full_path: &Path) -> Result<PathBuf> { Ok(admin_exe) } -// Try to find bitwarden_chromium_import_helper.exe in debug build folders. This might not cover all the cases. -// Tested on `npm run electron` from apps/desktop and apps/desktop/desktop_native. +// Try to find bitwarden_chromium_import_helper.exe in debug build folders. This might not cover all +// the cases. Tested on `npm run electron` from apps/desktop and apps/desktop/desktop_native. fn get_debug_admin_exe_path() -> Result<PathBuf> { let current_dir = std::env::current_dir()?; let folder_name = current_dir 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 index d5d6c5d6d15..97cf57935b2 100644 --- 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 @@ -1,5 +1,6 @@ -use anyhow::{anyhow, Result}; use std::path::Path; + +use anyhow::{anyhow, Result}; use tracing::{debug, info}; use verifysign::CodeSignVerifier; diff --git a/apps/desktop/desktop_native/chromium_importer/src/metadata.rs b/apps/desktop/desktop_native/chromium_importer/src/metadata.rs index bfd7f184621..114c9f8df84 100644 --- a/apps/desktop/desktop_native/chromium_importer/src/metadata.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/metadata.rs @@ -59,9 +59,9 @@ pub fn get_supported_importers<T: InstalledBrowserRetriever>( // Tests are cfg-gated based upon OS, and must be compiled/run on each OS for full coverage #[cfg(test)] mod tests { - use super::*; use std::collections::HashSet; + use super::*; use crate::chromium::{InstalledBrowserRetriever, SUPPORTED_BROWSER_MAP}; pub struct MockInstalledBrowserRetriever {} diff --git a/apps/desktop/desktop_native/chromium_importer/src/util.rs b/apps/desktop/desktop_native/chromium_importer/src/util.rs index f346d7e6dd0..2dbc6ed005b 100644 --- a/apps/desktop/desktop_native/chromium_importer/src/util.rs +++ b/apps/desktop/desktop_native/chromium_importer/src/util.rs @@ -32,7 +32,7 @@ pub(crate) fn split_encrypted_string_and_validate<'a>( } /// Decrypt using AES-128 in CBC mode. -#[cfg(any(target_os = "linux", target_os = "macos", test))] +#[cfg(any(target_os = "linux", target_os = "macos"))] pub(crate) fn decrypt_aes_128_cbc(key: &[u8], iv: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>> { use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit}; @@ -41,7 +41,8 @@ pub(crate) fn decrypt_aes_128_cbc(key: &[u8], iv: &[u8], ciphertext: &[u8]) -> R .map_err(|e| anyhow!("Failed to decrypt: {}", e)) } -/// Derives a PBKDF2 key from the static "saltysalt" salt with the given password and iteration count. +/// Derives a PBKDF2 key from the static "saltysalt" salt with the given password and iteration +/// count. #[cfg(any(target_os = "linux", target_os = "macos"))] pub(crate) fn derive_saltysalt(password: &[u8], iterations: u32) -> Result<Vec<u8>> { use pbkdf2::{hmac::Hmac, pbkdf2}; @@ -55,27 +56,9 @@ pub(crate) fn derive_saltysalt(password: &[u8], iterations: u32) -> Result<Vec<u #[cfg(test)] mod tests { - use aes::cipher::{ - block_padding::Pkcs7, - generic_array::{sequence::GenericSequence, GenericArray}, - ArrayLength, BlockEncryptMut, KeyIvInit, - }; - - const LENGTH16: usize = 16; const LENGTH10: usize = 10; const LENGTH0: usize = 0; - fn generate_vec(length: usize, offset: u8, increment: u8) -> Vec<u8> { - (0..length).map(|i| offset + i as u8 * increment).collect() - } - - fn generate_generic_array<N: ArrayLength<u8>>( - offset: u8, - increment: u8, - ) -> GenericArray<u8, N> { - GenericArray::generate(|i| offset + i as u8 * increment) - } - fn run_split_encrypted_string_test<'a, const N: usize>( successfully_split: bool, plaintext_to_encrypt: &'a str, @@ -144,8 +127,28 @@ mod tests { run_split_encrypted_string_and_validate_test(false, "v10EncryptMe!", &[]); } + #[cfg(any(target_os = "linux", target_os = "macos"))] #[test] fn test_decrypt_aes_128_cbc() { + use aes::cipher::{ + block_padding::Pkcs7, + generic_array::{sequence::GenericSequence, GenericArray}, + ArrayLength, BlockEncryptMut, KeyIvInit, + }; + + const LENGTH16: usize = 16; + + fn generate_generic_array<N: ArrayLength<u8>>( + offset: u8, + increment: u8, + ) -> GenericArray<u8, N> { + GenericArray::generate(|i| offset + i as u8 * increment) + } + + fn generate_vec(length: usize, offset: u8, increment: u8) -> Vec<u8> { + (0..length).map(|i| offset + i as u8 * increment).collect() + } + let offset = 0; let increment = 1; diff --git a/apps/desktop/desktop_native/core/Cargo.toml b/apps/desktop/desktop_native/core/Cargo.toml index f6c9d669df6..dc9246f55c6 100644 --- a/apps/desktop/desktop_native/core/Cargo.toml +++ b/apps/desktop/desktop_native/core/Cargo.toml @@ -23,27 +23,15 @@ anyhow = { workspace = true } arboard = { workspace = true, features = ["wayland-data-control"] } base64 = { workspace = true } bitwarden-russh = { workspace = true } -byteorder = { workspace = true } bytes = { workspace = true } cbc = { workspace = true, features = ["alloc"] } chacha20poly1305 = { workspace = true } dirs = { workspace = true } -ed25519 = { workspace = true, features = ["pkcs8"] } futures = { workspace = true } -homedir = { workspace = true } interprocess = { workspace = true, features = ["tokio"] } memsec = { workspace = true, features = ["alloc_ext"] } -pin-project = { workspace = true } -pkcs8 = { workspace = true, features = ["alloc", "encryption", "pem"] } rand = { workspace = true } -rsa = { workspace = true } -russh-cryptovec = { workspace = true } -scopeguard = { workspace = true } -secmem-proc = { workspace = true } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } sha2 = { workspace = true } -ssh-encoding = { workspace = true } ssh-key = { workspace = true, features = [ "encryption", "ed25519", @@ -53,13 +41,17 @@ ssh-key = { workspace = true, features = [ sysinfo = { workspace = true, features = ["windows"] } thiserror = { workspace = true } tokio = { workspace = true, features = ["io-util", "sync", "macros", "net"] } -tokio-stream = { workspace = true, features = ["net"] } tokio-util = { workspace = true, features = ["codec"] } tracing = { workspace = true } typenum = { workspace = true } zeroizing-alloc = { workspace = true } [target.'cfg(windows)'.dependencies] +pin-project = { workspace = true } +scopeguard = { workspace = true } +secmem-proc = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } widestring = { workspace = true, optional = true } windows = { workspace = true, features = [ "Foundation", @@ -76,21 +68,20 @@ windows = { workspace = true, features = [ ], optional = true } windows-future = { workspace = true } -[target.'cfg(windows)'.dev-dependencies] -keytar = { workspace = true } - [target.'cfg(target_os = "macos")'.dependencies] core-foundation = { workspace = true, optional = true } +homedir = { workspace = true } +secmem-proc = { workspace = true } security-framework = { workspace = true, optional = true } security-framework-sys = { workspace = true, optional = true } desktop_objc = { path = "../objc" } [target.'cfg(target_os = "linux")'.dependencies] -oo7 = { workspace = true } +ashpd = { workspace = true } +homedir = { workspace = true } libc = { workspace = true } linux-keyutils = { workspace = true } -ashpd = { workspace = true } - +oo7 = { workspace = true } zbus = { workspace = true, optional = true } zbus_polkit = { workspace = true, optional = true } diff --git a/apps/desktop/desktop_native/core/src/biometric/mod.rs b/apps/desktop/desktop_native/core/src/biometric/mod.rs index e4d51f5da9a..937c67ff30a 100644 --- a/apps/desktop/desktop_native/core/src/biometric/mod.rs +++ b/apps/desktop/desktop_native/core/src/biometric/mod.rs @@ -86,11 +86,15 @@ impl KeyMaterial { #[cfg(test)] mod tests { - use crate::biometric::{decrypt, encrypt, KeyMaterial}; - use crate::crypto::CipherString; - use base64::{engine::general_purpose::STANDARD as base64_engine, Engine}; use std::str::FromStr; + use base64::{engine::general_purpose::STANDARD as base64_engine, Engine}; + + use crate::{ + biometric::{decrypt, encrypt, KeyMaterial}, + crypto::CipherString, + }; + fn key_material() -> KeyMaterial { KeyMaterial { os_key_part_b64: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=".to_owned(), diff --git a/apps/desktop/desktop_native/core/src/biometric/unix.rs b/apps/desktop/desktop_native/core/src/biometric/unix.rs index 0f6ff8f33dc..3f4f10a1fcf 100644 --- a/apps/desktop/desktop_native/core/src/biometric/unix.rs +++ b/apps/desktop/desktop_native/core/src/biometric/unix.rs @@ -1,18 +1,18 @@ use std::str::FromStr; -use anyhow::Result; +use anyhow::{anyhow, Result}; use base64::Engine; use rand::RngCore; use sha2::{Digest, Sha256}; use tracing::error; - -use crate::biometric::{base64_engine, KeyMaterial, OsDerivedKey}; use zbus::Connection; use zbus_polkit::policykit1::*; use super::{decrypt, encrypt}; -use crate::crypto::CipherString; -use anyhow::anyhow; +use crate::{ + biometric::{base64_engine, KeyMaterial, OsDerivedKey}, + crypto::CipherString, +}; /// The Unix implementation of the biometric trait. pub struct Biometric {} diff --git a/apps/desktop/desktop_native/core/src/biometric/windows.rs b/apps/desktop/desktop_native/core/src/biometric/windows.rs index 8013c21bf9a..f72282d9284 100644 --- a/apps/desktop/desktop_native/core/src/biometric/windows.rs +++ b/apps/desktop/desktop_native/core/src/biometric/windows.rs @@ -16,13 +16,12 @@ use windows::{ }; use windows_future::IAsyncOperation; +use super::{decrypt, encrypt, windows_focus::set_focus}; use crate::{ biometric::{KeyMaterial, OsDerivedKey}, crypto::CipherString, }; -use super::{decrypt, encrypt, windows_focus::set_focus}; - /// The Windows OS implementation of the biometric trait. pub struct Biometric {} @@ -61,7 +60,8 @@ impl super::BiometricTrait for Biometric { match ucv_available { UserConsentVerifierAvailability::Available => Ok(true), - UserConsentVerifierAvailability::DeviceBusy => Ok(true), // TODO: Look into removing this and making the check more ad-hoc + // TODO: look into removing this and making the check more ad-hoc + UserConsentVerifierAvailability::DeviceBusy => Ok(true), _ => Ok(false), } } @@ -133,7 +133,6 @@ fn random_challenge() -> [u8; 16] { #[cfg(test)] mod tests { use super::*; - use crate::biometric::BiometricTrait; #[test] diff --git a/apps/desktop/desktop_native/core/src/biometric_v2/linux.rs b/apps/desktop/desktop_native/core/src/biometric_v2/linux.rs index 44cba4a9e5b..ff2abc0686b 100644 --- a/apps/desktop/desktop_native/core/src/biometric_v2/linux.rs +++ b/apps/desktop/desktop_native/core/src/biometric_v2/linux.rs @@ -1,17 +1,19 @@ //! 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. +//! 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. +//! 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 std::sync::Arc; use anyhow::{anyhow, Result}; -use std::sync::Arc; use tokio::sync::Mutex; use tracing::{debug, warn}; use zbus::Connection; @@ -20,8 +22,8 @@ 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 + // The userkeys that are held in memory MUST be protected from memory dumping attacks, to + // ensure locked vaults cannot be unlocked secure_memory: Arc<Mutex<crate::secure_memory::encrypted_memory_store::EncryptedMemoryStore>>, } @@ -88,8 +90,9 @@ impl super::BiometricTrait for BiometricLockSystem { } } -/// 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. +/// 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<bool> { debug!("[Polkit] Authenticating / performing UV"); 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 669267b7829..55aee27dd33 100644 --- a/apps/desktop/desktop_native/core/src/biometric_v2/mod.rs +++ b/apps/desktop/desktop_native/core/src/biometric_v2/mod.rs @@ -17,8 +17,8 @@ pub trait BiometricTrait: Send + Sync { async fn authenticate(&self, hwnd: Vec<u8>, message: String) -> Result<bool>; /// Check if biometric authentication is available async fn authenticate_available(&self) -> Result<bool>; - /// Enroll a key for persistent unlock. If the implementation does not support persistent enrollment, - /// this function should do nothing. + /// Enroll a key for persistent unlock. If the implementation does not support persistent + /// enrollment, this function should do nothing. async fn enroll_persistent(&self, user_id: &str, key: &[u8]) -> Result<()>; /// Clear the persistent and ephemeral keys async fn unenroll(&self, user_id: &str) -> Result<()>; @@ -28,6 +28,7 @@ pub trait BiometricTrait: Send + Sync { async fn provide_key(&self, user_id: &str, key: &[u8]); /// Perform biometric unlock and return the key async fn unlock(&self, user_id: &str, hwnd: Vec<u8>) -> Result<Vec<u8>>; - /// Check if biometric unlock is available based on whether a key is present and whether authentication is possible + /// Check if biometric unlock is available based on whether a key is present and whether + /// authentication is possible async fn unlock_available(&self, user_id: &str) -> Result<bool>; } diff --git a/apps/desktop/desktop_native/core/src/biometric_v2/windows.rs b/apps/desktop/desktop_native/core/src/biometric_v2/windows.rs index 043c2453cd0..32d2eb7e6e6 100644 --- a/apps/desktop/desktop_native/core/src/biometric_v2/windows.rs +++ b/apps/desktop/desktop_native/core/src/biometric_v2/windows.rs @@ -2,38 +2,40 @@ //! //! There are two paths implemented here. //! The former via UV + ephemerally (but protected) keys. This only works after first unlock. -//! The latter via a signing API, that deterministically signs a challenge, from which a windows hello key is derived. This key -//! is used to encrypt the protected key. +//! The latter via a signing API, that deterministically signs a challenge, from which a windows +//! hello key is derived. This key is used to encrypt the protected key. //! //! # Security -//! The security goal is that a locked vault - a running app - cannot be unlocked when the device (user-space) -//! is compromised in this state. +//! The security goal is that a locked vault - a running app - cannot be unlocked when the device +//! (user-space) is compromised in this state. //! //! ## UV path -//! When first unlocking the app, the app sends the user-key to this module, which holds it in secure memory, -//! protected by DPAPI. This makes it inaccessible to other processes, unless they compromise the system administrator, or kernel. -//! While the app is running this key is held in memory, even if locked. When unlocking, the app will prompt the user via +//! When first unlocking the app, the app sends the user-key to this module, which holds it in +//! secure memory, protected by DPAPI. This makes it inaccessible to other processes, unless they +//! compromise the system administrator, or kernel. While the app is running this key is held in +//! memory, even if locked. When unlocking, the app will prompt the user via //! `windows_hello_authenticate` to get a yes/no decision on whether to release the key to the app. -//! Note: Further process isolation is needed here so that code cannot be injected into the running process, which may -//! circumvent DPAPI. +//! Note: Further process isolation is needed here so that code cannot be injected into the running +//! process, which may circumvent DPAPI. //! //! ## Sign path -//! In this scenario, when enrolling, the app sends the user-key to this module, which derives the windows hello key -//! with the Windows Hello prompt. This is done by signing a per-user challenge, which produces a deterministic -//! signature which is hashed to obtain a key. This key is used to encrypt and persist the vault unlock key (user key). +//! In this scenario, when enrolling, the app sends the user-key to this module, which derives the +//! windows hello key with the Windows Hello prompt. This is done by signing a per-user challenge, +//! which produces a deterministic signature which is hashed to obtain a key. This key is used to +//! encrypt and persist the vault unlock key (user key). //! -//! Since the keychain can be accessed by all user-space processes, the challenge is known to all userspace processes. -//! Therefore, to circumvent the security measure, the attacker would need to create a fake Windows-Hello prompt, and -//! get the user to confirm it. +//! Since the keychain can be accessed by all user-space processes, the challenge is known to all +//! userspace processes. Therefore, to circumvent the security measure, the attacker would need to +//! create a fake Windows-Hello prompt, and get the user to confirm it. use std::sync::{atomic::AtomicBool, Arc}; -use tracing::{debug, warn}; use aes::cipher::KeyInit; use anyhow::{anyhow, Result}; use chacha20poly1305::{aead::Aead, XChaCha20Poly1305, XNonce}; use sha2::{Digest, Sha256}; use tokio::sync::Mutex; +use tracing::{debug, warn}; use windows::{ core::{factory, h, Interface, HSTRING}, Security::{ @@ -74,8 +76,8 @@ struct WindowsHelloKeychainEntry { /// The Windows OS implementation of the biometric trait. pub struct BiometricLockSystem { - // The userkeys that are held in memory MUST be protected from memory dumping attacks, to ensure - // locked vaults cannot be unlocked + // The userkeys that are held in memory MUST be protected from memory dumping attacks, to + // ensure locked vaults cannot be unlocked secure_memory: Arc<Mutex<crate::secure_memory::dpapi::DpapiSecretKVStore>>, } @@ -114,12 +116,14 @@ impl super::BiometricTrait for BiometricLockSystem { } async fn enroll_persistent(&self, user_id: &str, key: &[u8]) -> Result<()> { - // Enrollment works by first generating a random challenge unique to the user / enrollment. Then, - // with the challenge and a Windows-Hello prompt, the "windows hello key" is derived. The windows - // hello key is used to encrypt the key to store with XChaCha20Poly1305. The bundle of nonce, - // challenge and wrapped-key are stored to the keychain + // Enrollment works by first generating a random challenge unique to the user / enrollment. + // Then, with the challenge and a Windows-Hello prompt, the "windows hello key" is + // derived. The windows hello key is used to encrypt the key to store with + // XChaCha20Poly1305. The bundle of nonce, challenge and wrapped-key are stored to + // the keychain - // Each enrollment (per user) has a unique challenge, so that the windows-hello key is unique + // Each enrollment (per user) has a unique challenge, so that the windows-hello key is + // unique let challenge: [u8; CHALLENGE_LENGTH] = rand::random(); // This key is unique to the challenge @@ -155,8 +159,8 @@ impl super::BiometricTrait for BiometricLockSystem { }); let mut secure_memory = self.secure_memory.lock().await; - // If the key is held ephemerally, always use UV API. Only use signing API if the key is not held - // ephemerally but the keychain holds it persistently. + // If the key is held ephemerally, always use UV API. Only use signing API if the key is not + // held ephemerally but the keychain holds it persistently. if secure_memory.has(user_id) { if windows_hello_authenticate("Unlock your vault".to_string()).await? { secure_memory @@ -175,7 +179,8 @@ impl super::BiometricTrait for BiometricLockSystem { &keychain_entry.wrapped_key, &keychain_entry.nonce, )?; - // The first unlock already sets the key for subsequent unlocks. The key may again be set externally after unlock finishes. + // The first unlock already sets the key for subsequent unlocks. The key may again be + // set externally after unlock finishes. secure_memory.put(user_id.to_string(), &decrypted_key.clone()); Ok(decrypted_key) } @@ -231,8 +236,8 @@ async fn windows_hello_authenticate_with_crypto( ) -> Result<[u8; XCHACHA20POLY1305_KEY_LENGTH]> { debug!("[Windows Hello] Authenticating to sign challenge"); - // Ugly hack: We need to focus the window via window focusing APIs until Microsoft releases a new API. - // This is unreliable, and if it does not work, the operation may fail + // Ugly hack: We need to focus the window via window focusing APIs until Microsoft releases a + // new API. This is unreliable, and if it does not work, the operation may fail let stop_focusing = Arc::new(AtomicBool::new(false)); let stop_focusing_clone = stop_focusing.clone(); let _ = std::thread::spawn(move || loop { @@ -243,8 +248,8 @@ async fn windows_hello_authenticate_with_crypto( break; } }); - // Only stop focusing once this function exits. The focus MUST run both during the initial creation - // with RequestCreateAsync, and also with the subsequent use with RequestSignAsync. + // Only stop focusing once this function exits. The focus MUST run both during the initial + // creation with RequestCreateAsync, and also with the subsequent use with RequestSignAsync. let _guard = scopeguard::guard((), |_| { stop_focusing.store(true, std::sync::atomic::Ordering::Relaxed); }); @@ -283,8 +288,8 @@ async fn windows_hello_authenticate_with_crypto( let signature_buffer = signature.Result()?; let signature_value = unsafe { as_mut_bytes(&signature_buffer)? }; - // The signature is deterministic based on the challenge and keychain key. Thus, it can be hashed to a key. - // It is unclear what entropy this key provides. + // The signature is deterministic based on the challenge and keychain key. Thus, it can be + // hashed to a key. It is unclear what entropy this key provides. let windows_hello_key = Sha256::digest(signature_value).into(); Ok(windows_hello_key) } diff --git a/apps/desktop/desktop_native/core/src/biometric_v2/windows_focus.rs b/apps/desktop/desktop_native/core/src/biometric_v2/windows_focus.rs index f3ffb6e4ebe..bf303c88e01 100644 --- a/apps/desktop/desktop_native/core/src/biometric_v2/windows_focus.rs +++ b/apps/desktop/desktop_native/core/src/biometric_v2/windows_focus.rs @@ -34,23 +34,25 @@ pub fn focus_security_prompt() { /// Sets focus to a window using a few unstable methods fn set_focus(hwnd: HWND) { unsafe { - // Windows REALLY does not like apps stealing focus, even if it is for fixing Windows-Hello bugs. - // The windows hello signing prompt NEEDS to be focused instantly, or it will error, but it does - // not focus itself. + // Windows REALLY does not like apps stealing focus, even if it is for fixing Windows-Hello + // bugs. The windows hello signing prompt NEEDS to be focused instantly, or it will + // error, but it does not focus itself. // This function implements forced focusing of windows using a few hacks. // The conditions to successfully foreground a window are: // All of the following conditions are true: - // The calling process belongs to a desktop application, not a UWP app or a Windows Store app designed for Windows 8 or 8.1. - // The foreground process has not disabled calls to SetForegroundWindow by a previous call to the LockSetForegroundWindow function. - // The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo). - // No menus are active. + // - The calling process belongs to a desktop application, not a UWP app or a Windows + // Store app designed for Windows 8 or 8.1. + // - The foreground process has not disabled calls to SetForegroundWindow by a previous + // call to the LockSetForegroundWindow function. + // - The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in + // SystemParametersInfo). No menus are active. // Additionally, at least one of the following conditions is true: - // The calling process is the foreground process. - // The calling process was started by the foreground process. - // There is currently no foreground window, and thus no foreground process. - // The calling process received the last input event. - // Either the foreground process or the calling process is being debugged. + // - The calling process is the foreground process. + // - The calling process was started by the foreground process. + // - There is currently no foreground window, and thus no foreground process. + // - The calling process received the last input event. + // - Either the foreground process or the calling process is being debugged. // Update the foreground lock timeout temporarily let mut old_timeout = 0; @@ -75,7 +77,8 @@ fn set_focus(hwnd: HWND) { ); }); - // Attach to the foreground thread once attached, we can foreground, even if in the background + // Attach to the foreground thread once attached, we can foreground, even if in the + // background let dw_current_thread = GetCurrentThreadId(); let dw_fg_thread = GetWindowThreadProcessId(GetForegroundWindow(), None); @@ -91,7 +94,8 @@ fn set_focus(hwnd: HWND) { } } -/// When restoring focus to the application window, we need a less aggressive method so the electron window doesn't get frozen. +/// When restoring focus to the application window, we need a less aggressive method so the electron +/// window doesn't get frozen. pub(crate) fn restore_focus(hwnd: HWND) { unsafe { let _ = SetForegroundWindow(hwnd); diff --git a/apps/desktop/desktop_native/core/src/crypto/crypto.rs b/apps/desktop/desktop_native/core/src/crypto/crypto.rs index d9e2aec3046..7991c87ca28 100644 --- a/apps/desktop/desktop_native/core/src/crypto/crypto.rs +++ b/apps/desktop/desktop_native/core/src/crypto/crypto.rs @@ -5,9 +5,8 @@ use aes::cipher::{ BlockEncryptMut, KeyIvInit, }; -use crate::error::{CryptoError, Result}; - use super::CipherString; +use crate::error::{CryptoError, Result}; pub fn decrypt_aes256(iv: &[u8; 16], data: &[u8], key: GenericArray<u8, U32>) -> Result<Vec<u8>> { let iv = GenericArray::from_slice(iv); @@ -16,7 +15,8 @@ pub fn decrypt_aes256(iv: &[u8; 16], data: &[u8], key: GenericArray<u8, U32>) -> .decrypt_padded_mut::<Pkcs7>(&mut data) .map_err(|_| CryptoError::KeyDecrypt)?; - // Data is decrypted in place and returns a subslice of the original Vec, to avoid cloning it, we truncate to the subslice length + // Data is decrypted in place and returns a subslice of the original Vec, to avoid cloning it, + // we truncate to the subslice length let decrypted_len = decrypted_key_slice.len(); data.truncate(decrypted_len); diff --git a/apps/desktop/desktop_native/core/src/error.rs b/apps/desktop/desktop_native/core/src/error.rs index d70d8624018..c8d3ec02332 100644 --- a/apps/desktop/desktop_native/core/src/error.rs +++ b/apps/desktop/desktop_native/core/src/error.rs @@ -35,15 +35,4 @@ pub enum KdfParamError { InvalidParams(String), } -// Ensure that the error messages implement Send and Sync -#[cfg(test)] -const _: () = { - fn assert_send<T: Send>() {} - fn assert_sync<T: Sync>() {} - fn assert_all() { - assert_send::<Error>(); - assert_sync::<Error>(); - } -}; - pub type Result<T, E = Error> = std::result::Result<T, E>; diff --git a/apps/desktop/desktop_native/core/src/ipc/mod.rs b/apps/desktop/desktop_native/core/src/ipc/mod.rs index 5d4cc9e27f7..f806e395d10 100644 --- a/apps/desktop/desktop_native/core/src/ipc/mod.rs +++ b/apps/desktop/desktop_native/core/src/ipc/mod.rs @@ -49,7 +49,8 @@ pub fn path(name: &str) -> std::path::PathBuf { #[cfg(target_os = "macos")] { // When running in an unsandboxed environment, path is: /Users/<user>/ - // While running sandboxed, it's different: /Users/<user>/Library/Containers/com.bitwarden.desktop/Data + // While running sandboxed, it's different: + // /Users/<user>/Library/Containers/com.bitwarden.desktop/Data let mut home = dirs::home_dir().unwrap(); // Check if the app is sandboxed by looking for the Containers directory @@ -59,8 +60,9 @@ pub fn path(name: &str) -> std::path::PathBuf { // If the app is sanboxed, we need to use the App Group directory if let Some(position) = containers_position { - // We want to use App Groups in /Users/<user>/Library/Group Containers/LTZ2PFU5D6.com.bitwarden.desktop, - // so we need to remove all the components after the user. We can use the previous position to do this. + // We want to use App Groups in /Users/<user>/Library/Group + // Containers/LTZ2PFU5D6.com.bitwarden.desktop, so we need to remove all the + // components after the user. We can use the previous position to do this. while home.components().count() > position - 1 { home.pop(); } diff --git a/apps/desktop/desktop_native/core/src/ipc/server.rs b/apps/desktop/desktop_native/core/src/ipc/server.rs index 2762a832ac6..a65638303f1 100644 --- a/apps/desktop/desktop_native/core/src/ipc/server.rs +++ b/apps/desktop/desktop_native/core/src/ipc/server.rs @@ -3,9 +3,8 @@ use std::{ path::{Path, PathBuf}, }; -use futures::{SinkExt, StreamExt, TryFutureExt}; - use anyhow::Result; +use futures::{SinkExt, StreamExt, TryFutureExt}; use interprocess::local_socket::{tokio::prelude::*, GenericFilePath, ListenerOptions}; use tokio::{ io::{AsyncRead, AsyncWrite}, @@ -42,14 +41,17 @@ impl Server { /// /// # Parameters /// - /// - `name`: The endpoint name to listen on. This name uniquely identifies the IPC connection and must be the same for both the server and client. - /// - `client_to_server_send`: This [`mpsc::Sender<Message>`] will receive all the [`Message`]'s that the clients send to this server. + /// - `name`: The endpoint name to listen on. This name uniquely identifies the IPC connection + /// and must be the same for both the server and client. + /// - `client_to_server_send`: This [`mpsc::Sender<Message>`] will receive all the [`Message`]'s + /// that the clients send to this server. pub fn start( path: &Path, client_to_server_send: mpsc::Sender<Message>, ) -> Result<Self, Box<dyn Error>> { - // If the unix socket file already exists, we get an error when trying to bind to it. So we remove it first. - // Any processes that were using the old socket should remain connected to it but any new connections will use the new socket. + // If the unix socket file already exists, we get an error when trying to bind to it. So we + // remove it first. Any processes that were using the old socket should remain + // connected to it but any new connections will use the new socket. if !cfg!(windows) { let _ = std::fs::remove_file(path); } @@ -58,8 +60,9 @@ impl Server { let opts = ListenerOptions::new().name(name); let listener = opts.create_tokio()?; - // This broadcast channel is used for sending messages to all connected clients, and so the sender - // will be stored in the server while the receiver will be cloned and passed to each client handler. + // This broadcast channel is used for sending messages to all connected clients, and so the + // sender will be stored in the server while the receiver will be cloned and passed + // to each client handler. let (server_to_clients_send, server_to_clients_recv) = broadcast::channel::<String>(MESSAGE_CHANNEL_BUFFER); diff --git a/apps/desktop/desktop_native/core/src/password/macos.rs b/apps/desktop/desktop_native/core/src/password/macos.rs index 4f3a16ba4be..72d8ebeb425 100644 --- a/apps/desktop/desktop_native/core/src/password/macos.rs +++ b/apps/desktop/desktop_native/core/src/password/macos.rs @@ -1,9 +1,10 @@ -use crate::password::PASSWORD_NOT_FOUND; use anyhow::Result; use security_framework::passwords::{ delete_generic_password, get_generic_password, set_generic_password, }; +use crate::password::PASSWORD_NOT_FOUND; + #[allow(clippy::unused_async)] pub async fn get_password(service: &str, account: &str) -> Result<String> { let password = get_generic_password(service, account).map_err(convert_error)?; diff --git a/apps/desktop/desktop_native/core/src/password/unix.rs b/apps/desktop/desktop_native/core/src/password/unix.rs index b7595dca287..57b71adefed 100644 --- a/apps/desktop/desktop_native/core/src/password/unix.rs +++ b/apps/desktop/desktop_native/core/src/password/unix.rs @@ -1,9 +1,11 @@ -use crate::password::PASSWORD_NOT_FOUND; +use std::collections::HashMap; + use anyhow::{anyhow, Result}; use oo7::dbus::{self}; -use std::collections::HashMap; use tracing::info; +use crate::password::PASSWORD_NOT_FOUND; + pub async fn get_password(service: &str, account: &str) -> Result<String> { match get_password_new(service, account).await { Ok(res) => Ok(res), diff --git a/apps/desktop/desktop_native/core/src/password/windows.rs b/apps/desktop/desktop_native/core/src/password/windows.rs index ad09019f014..645620b444e 100644 --- a/apps/desktop/desktop_native/core/src/password/windows.rs +++ b/apps/desktop/desktop_native/core/src/password/windows.rs @@ -1,4 +1,3 @@ -use crate::password::PASSWORD_NOT_FOUND; use anyhow::{anyhow, Result}; use widestring::{U16CString, U16String}; use windows::{ @@ -12,6 +11,8 @@ use windows::{ }, }; +use crate::password::PASSWORD_NOT_FOUND; + const CRED_FLAGS_NONE: u32 = 0; #[allow(clippy::unused_async)] diff --git a/apps/desktop/desktop_native/core/src/process_isolation/linux.rs b/apps/desktop/desktop_native/core/src/process_isolation/linux.rs index bad348c93e2..263cc10b716 100644 --- a/apps/desktop/desktop_native/core/src/process_isolation/linux.rs +++ b/apps/desktop/desktop_native/core/src/process_isolation/linux.rs @@ -4,15 +4,15 @@ use libc::c_uint; use libc::{self, c_int}; use tracing::info; -// RLIMIT_CORE is the maximum size of a core dump file. Setting both to 0 disables core dumps, on crashes -// https://github.com/torvalds/linux/blob/1613e604df0cd359cf2a7fbd9be7a0bcfacfabd0/include/uapi/asm-generic/resource.h#L20 +// RLIMIT_CORE is the maximum size of a core dump file. Setting both to 0 disables core dumps, on +// crashes https://github.com/torvalds/linux/blob/1613e604df0cd359cf2a7fbd9be7a0bcfacfabd0/include/uapi/asm-generic/resource.h#L20 #[cfg(target_env = "musl")] const RLIMIT_CORE: c_int = 4; #[cfg(target_env = "gnu")] const RLIMIT_CORE: c_uint = 4; -// PR_SET_DUMPABLE makes it so no other running process (root or same user) can dump the memory of this process -// or attach a debugger to it. +// PR_SET_DUMPABLE makes it so no other running process (root or same user) can dump the memory of +// this process or attach a debugger to it. // https://github.com/torvalds/linux/blob/a38297e3fb012ddfa7ce0321a7e5a8daeb1872b6/include/uapi/linux/prctl.h#L14 const PR_SET_DUMPABLE: c_int = 4; diff --git a/apps/desktop/desktop_native/core/src/secure_memory/dpapi.rs b/apps/desktop/desktop_native/core/src/secure_memory/dpapi.rs index 3ff8a6d3d83..8d8e10d92c4 100644 --- a/apps/desktop/desktop_native/core/src/secure_memory/dpapi.rs +++ b/apps/desktop/desktop_native/core/src/secure_memory/dpapi.rs @@ -29,8 +29,9 @@ impl SecureMemoryStore for DpapiSecretKVStore { fn put(&mut self, key: String, value: &[u8]) { let length_header_len = std::mem::size_of::<usize>(); - // The allocated data has to be a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE, so we pad it and write the length in front - // We are storing LENGTH|DATA|00..00, where LENGTH is the length of DATA, the total length is a multiple + // The allocated data has to be a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE, so we pad it + // and write the length in front We are storing LENGTH|DATA|00..00, where LENGTH is + // the length of DATA, the total length is a multiple // of CRYPTPROTECTMEMORY_BLOCK_SIZE, and the padding is filled with zeros. let data_len = value.len(); diff --git a/apps/desktop/desktop_native/core/src/secure_memory/encrypted_memory_store.rs b/apps/desktop/desktop_native/core/src/secure_memory/encrypted_memory_store.rs index a8952d8f55a..d116e564bc8 100644 --- a/apps/desktop/desktop_native/core/src/secure_memory/encrypted_memory_store.rs +++ b/apps/desktop/desktop_native/core/src/secure_memory/encrypted_memory_store.rs @@ -10,8 +10,8 @@ use crate::secure_memory::{ /// allows circumventing length and amount limitations on platform specific secure memory APIs since /// only a single short item needs to be protected. /// -/// The key is briefly in process memory during encryption and decryption, in memory that is protected -/// from swapping to disk via mlock, and then zeroed out immediately after use. +/// The key is briefly in process memory during encryption and decryption, in memory that is +/// protected from swapping to disk via mlock, and then zeroed out immediately after use. #[allow(unused)] pub(crate) struct EncryptedMemoryStore { map: std::collections::HashMap<String, EncryptedMemory>, diff --git a/apps/desktop/desktop_native/core/src/secure_memory/secure_key/crypto.rs b/apps/desktop/desktop_native/core/src/secure_memory/secure_key/crypto.rs index 1ee6c4cdf40..7e2917ade6d 100644 --- a/apps/desktop/desktop_native/core/src/secure_memory/secure_key/crypto.rs +++ b/apps/desktop/desktop_native/core/src/secure_memory/secure_key/crypto.rs @@ -6,9 +6,9 @@ use rand::{rng, Rng}; pub(super) const KEY_SIZE: usize = 32; pub(super) const NONCE_SIZE: usize = 24; -/// The encryption performed here is xchacha-poly1305. Any tampering with the key or the ciphertexts will result -/// in a decryption failure and panic. The key's memory contents are protected from being swapped to disk -/// via mlock. +/// The encryption performed here is xchacha-poly1305. Any tampering with the key or the ciphertexts +/// will result in a decryption failure and panic. The key's memory contents are protected from +/// being swapped to disk via mlock. pub(super) struct MemoryEncryptionKey(NonNull<[u8]>); /// An encrypted memory blob that must be decrypted using the same key that it was encrypted with. diff --git a/apps/desktop/desktop_native/core/src/secure_memory/secure_key/dpapi.rs b/apps/desktop/desktop_native/core/src/secure_memory/secure_key/dpapi.rs index 0975b542877..52b75d94a09 100644 --- a/apps/desktop/desktop_native/core/src/secure_memory/secure_key/dpapi.rs +++ b/apps/desktop/desktop_native/core/src/secure_memory/secure_key/dpapi.rs @@ -1,10 +1,13 @@ -use super::crypto::{MemoryEncryptionKey, KEY_SIZE}; -use super::SecureKeyContainer; use windows::Win32::Security::Cryptography::{ CryptProtectMemory, CryptUnprotectMemory, CRYPTPROTECTMEMORY_BLOCK_SIZE, CRYPTPROTECTMEMORY_SAME_PROCESS, }; +use super::{ + crypto::{MemoryEncryptionKey, KEY_SIZE}, + SecureKeyContainer, +}; + /// https://learn.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptprotectdata /// The DPAPI store encrypts data using the Windows Data Protection API (DPAPI). The key is bound /// to the current process, and cannot be decrypted by other user-mode processes. diff --git a/apps/desktop/desktop_native/core/src/secure_memory/secure_key/keyctl.rs b/apps/desktop/desktop_native/core/src/secure_memory/secure_key/keyctl.rs index a738d964671..29c62759740 100644 --- a/apps/desktop/desktop_native/core/src/secure_memory/secure_key/keyctl.rs +++ b/apps/desktop/desktop_native/core/src/secure_memory/secure_key/keyctl.rs @@ -1,9 +1,8 @@ -use crate::secure_memory::secure_key::crypto::MemoryEncryptionKey; - -use super::crypto::KEY_SIZE; -use super::SecureKeyContainer; use linux_keyutils::{KeyRing, KeyRingIdentifier}; +use super::{crypto::KEY_SIZE, SecureKeyContainer}; +use crate::secure_memory::secure_key::crypto::MemoryEncryptionKey; + /// The keys are bound to the process keyring. const KEY_RING_IDENTIFIER: KeyRingIdentifier = KeyRingIdentifier::Process; /// This is an atomic global counter used to help generate unique key IDs @@ -26,9 +25,9 @@ pub(super) struct KeyctlSecureKeyContainer { id: String, } -// SAFETY: The key id is fully owned by this struct and not exposed or cloned, and cleaned up on drop. -// Further, since we use `KeyRingIdentifier::Process` and not `KeyRingIdentifier::Thread`, the key -// is accessible across threads within the same process bound. +// SAFETY: The key id is fully owned by this struct and not exposed or cloned, and cleaned up on +// drop. Further, since we use `KeyRingIdentifier::Process` and not `KeyRingIdentifier::Thread`, the +// key is accessible across threads within the same process bound. unsafe impl Send for KeyctlSecureKeyContainer {} // SAFETY: The container is non-mutable and thus safe to share between threads. unsafe impl Sync for KeyctlSecureKeyContainer {} diff --git a/apps/desktop/desktop_native/core/src/secure_memory/secure_key/memfd_secret.rs b/apps/desktop/desktop_native/core/src/secure_memory/secure_key/memfd_secret.rs index 4e6a2c4d7ac..e9f96db3148 100644 --- a/apps/desktop/desktop_native/core/src/secure_memory/secure_key/memfd_secret.rs +++ b/apps/desktop/desktop_native/core/src/secure_memory/secure_key/memfd_secret.rs @@ -1,8 +1,9 @@ use std::{ptr::NonNull, sync::LazyLock}; -use super::crypto::MemoryEncryptionKey; -use super::crypto::KEY_SIZE; -use super::SecureKeyContainer; +use super::{ + crypto::{MemoryEncryptionKey, KEY_SIZE}, + SecureKeyContainer, +}; /// https://man.archlinux.org/man/memfd_secret.2.en /// The memfd_secret store protects the data using the `memfd_secret` syscall. The @@ -15,8 +16,8 @@ pub(super) struct MemfdSecretSecureKeyContainer { // SAFETY: The pointers in this struct are allocated by `memfd_secret`, and we have full ownership. // They are never exposed outside or cloned, and are cleaned up by drop. unsafe impl Send for MemfdSecretSecureKeyContainer {} -// SAFETY: The container is non-mutable and thus safe to share between threads. Further, memfd-secret -// is accessible across threads within the same process bound. +// SAFETY: The container is non-mutable and thus safe to share between threads. Further, +// memfd-secret is accessible across threads within the same process bound. unsafe impl Sync for MemfdSecretSecureKeyContainer {} impl SecureKeyContainer for MemfdSecretSecureKeyContainer { diff --git a/apps/desktop/desktop_native/core/src/secure_memory/secure_key/mlock.rs b/apps/desktop/desktop_native/core/src/secure_memory/secure_key/mlock.rs index db21cd7fedc..961988c1d40 100644 --- a/apps/desktop/desktop_native/core/src/secure_memory/secure_key/mlock.rs +++ b/apps/desktop/desktop_native/core/src/secure_memory/secure_key/mlock.rs @@ -1,8 +1,9 @@ use std::ptr::NonNull; -use super::crypto::MemoryEncryptionKey; -use super::crypto::KEY_SIZE; -use super::SecureKeyContainer; +use super::{ + crypto::{MemoryEncryptionKey, KEY_SIZE}, + SecureKeyContainer, +}; /// A SecureKeyContainer that uses mlock to prevent the memory from being swapped to disk. /// This does not provide as strong protections as other methods, but is always supported. diff --git a/apps/desktop/desktop_native/core/src/secure_memory/secure_key/mod.rs b/apps/desktop/desktop_native/core/src/secure_memory/secure_key/mod.rs index 6c3b53117a5..26e72f7d581 100644 --- a/apps/desktop/desktop_native/core/src/secure_memory/secure_key/mod.rs +++ b/apps/desktop/desktop_native/core/src/secure_memory/secure_key/mod.rs @@ -1,9 +1,12 @@ -//! This module provides hardened storage for single cryptographic keys. These are meant for encrypting large amounts of memory. -//! Some platforms restrict how many keys can be protected by their APIs, which necessitates this layer of indirection. This significantly -//! reduces the complexity of each platform specific implementation, since all that's needed is implementing protecting a single fixed sized key -//! instead of protecting many arbitrarily sized secrets. This significantly lowers the effort to maintain each implementation. +//! This module provides hardened storage for single cryptographic keys. These are meant for +//! encrypting large amounts of memory. Some platforms restrict how many keys can be protected by +//! their APIs, which necessitates this layer of indirection. This significantly reduces the +//! complexity of each platform specific implementation, since all that's needed is implementing +//! protecting a single fixed sized key instead of protecting many arbitrarily sized secrets. This +//! significantly lowers the effort to maintain each implementation. //! -//! The implementations include DPAPI on Windows, `keyctl` on Linux, and `memfd_secret` on Linux, and a fallback implementation using mlock. +//! The implementations include DPAPI on Windows, `keyctl` on Linux, and `memfd_secret` on Linux, +//! and a fallback implementation using mlock. use tracing::info; @@ -20,12 +23,13 @@ pub use crypto::EncryptedMemory; use crate::secure_memory::secure_key::crypto::DecryptionError; -/// An ephemeral key that is protected using a platform mechanism. It is generated on construction freshly, and can be used -/// to encrypt and decrypt segments of memory. Since the key is ephemeral, persistent data cannot be encrypted with this key. -/// On Linux and Windows, in most cases the protection mechanisms prevent memory dumps/debuggers from reading the key. +/// An ephemeral key that is protected using a platform mechanism. It is generated on construction +/// freshly, and can be used to encrypt and decrypt segments of memory. Since the key is ephemeral, +/// persistent data cannot be encrypted with this key. On Linux and Windows, in most cases the +/// protection mechanisms prevent memory dumps/debuggers from reading the key. /// -/// Note: This can be circumvented if code can be injected into the process and is only effective in combination with the -/// memory isolation provided in `process_isolation`. +/// Note: This can be circumvented if code can be injected into the process and is only effective in +/// combination with the memory isolation provided in `process_isolation`. /// - https://github.com/zer1t0/keydump #[allow(unused)] pub(crate) struct SecureMemoryEncryptionKey(CrossPlatformSecureKeyContainer); @@ -55,7 +59,8 @@ impl SecureMemoryEncryptionKey { /// from memory attacks. #[allow(unused)] trait SecureKeyContainer: Sync + Send { - /// Returns the key as a byte slice. This slice does not have additional memory protections applied. + /// Returns the key as a byte slice. This slice does not have additional memory protections + /// applied. fn as_key(&self) -> crypto::MemoryEncryptionKey; /// Creates a new SecureKeyContainer from the provided key. fn from_key(key: crypto::MemoryEncryptionKey) -> Self; diff --git a/apps/desktop/desktop_native/core/src/ssh_agent/mod.rs b/apps/desktop/desktop_native/core/src/ssh_agent/mod.rs index 61cb8fc187d..8ba64618ffa 100644 --- a/apps/desktop/desktop_native/core/src/ssh_agent/mod.rs +++ b/apps/desktop/desktop_native/core/src/ssh_agent/mod.rs @@ -7,13 +7,12 @@ use std::{ }; use base64::{engine::general_purpose::STANDARD, Engine as _}; -use tokio::sync::Mutex; -use tokio_util::sync::CancellationToken; - use bitwarden_russh::{ session_bind::SessionBindResult, ssh_agent::{self, SshKey}, }; +use tokio::sync::Mutex; +use tokio_util::sync::CancellationToken; use tracing::{error, info}; #[cfg_attr(target_os = "windows", path = "windows.rs")] @@ -34,7 +33,8 @@ pub struct BitwardenDesktopAgent { show_ui_request_tx: tokio::sync::mpsc::Sender<SshAgentUIRequest>, get_ui_response_rx: Arc<Mutex<tokio::sync::broadcast::Receiver<(u32, bool)>>>, request_id: Arc<AtomicU32>, - /// before first unlock, or after account switching, listing keys should require an unlock to get a list of public keys + /// before first unlock, or after account switching, listing keys should require an unlock to + /// get a list of public keys needs_unlock: Arc<AtomicBool>, is_running: Arc<AtomicBool>, } diff --git a/apps/desktop/desktop_native/core/src/ssh_agent/named_pipe_listener_stream.rs b/apps/desktop/desktop_native/core/src/ssh_agent/named_pipe_listener_stream.rs index cb10e873a33..38b2193faf5 100644 --- a/apps/desktop/desktop_native/core/src/ssh_agent/named_pipe_listener_stream.rs +++ b/apps/desktop/desktop_native/core/src/ssh_agent/named_pipe_listener_stream.rs @@ -1,7 +1,6 @@ -use futures::Stream; -use std::os::windows::prelude::AsRawHandle as _; use std::{ io, + os::windows::prelude::AsRawHandle as _, pin::Pin, sync::{ atomic::{AtomicBool, Ordering}, @@ -9,6 +8,8 @@ use std::{ }, task::{Context, Poll}, }; + +use futures::Stream; use tokio::{ net::windows::named_pipe::{NamedPipeServer, ServerOptions}, select, diff --git a/apps/desktop/desktop_native/core/src/ssh_agent/peercred_unix_listener_stream.rs b/apps/desktop/desktop_native/core/src/ssh_agent/peercred_unix_listener_stream.rs index 77eec5e35c7..5b6b1d8f36b 100644 --- a/apps/desktop/desktop_native/core/src/ssh_agent/peercred_unix_listener_stream.rs +++ b/apps/desktop/desktop_native/core/src/ssh_agent/peercred_unix_listener_stream.rs @@ -1,11 +1,13 @@ +use std::{ + io, + pin::Pin, + task::{Context, Poll}, +}; + use futures::Stream; -use std::io; -use std::pin::Pin; -use std::task::{Context, Poll}; use tokio::net::{UnixListener, UnixStream}; -use super::peerinfo; -use super::peerinfo::models::PeerInfo; +use super::{peerinfo, peerinfo::models::PeerInfo}; #[derive(Debug)] pub struct PeercredUnixListenerStream { diff --git a/apps/desktop/desktop_native/core/src/ssh_agent/peerinfo/models.rs b/apps/desktop/desktop_native/core/src/ssh_agent/peerinfo/models.rs index fad535cb80e..74b909f5ce7 100644 --- a/apps/desktop/desktop_native/core/src/ssh_agent/peerinfo/models.rs +++ b/apps/desktop/desktop_native/core/src/ssh_agent/peerinfo/models.rs @@ -1,9 +1,10 @@ use std::sync::{atomic::AtomicBool, Arc, Mutex}; /** -* Peerinfo represents the information of a peer process connecting over a socket. -* This can be later extended to include more information (icon, app name) for the corresponding application. -*/ + * Peerinfo represents the information of a peer process connecting over a socket. + * This can be later extended to include more information (icon, app name) for the corresponding + * application. + */ #[derive(Debug, Clone)] pub struct PeerInfo { uid: u32, diff --git a/apps/desktop/desktop_native/core/src/ssh_agent/unix.rs b/apps/desktop/desktop_native/core/src/ssh_agent/unix.rs index a45c2f6c0bf..8623df13776 100644 --- a/apps/desktop/desktop_native/core/src/ssh_agent/unix.rs +++ b/apps/desktop/desktop_native/core/src/ssh_agent/unix.rs @@ -6,9 +6,8 @@ use homedir::my_home; use tokio::{net::UnixListener, sync::Mutex}; use tracing::{error, info}; -use crate::ssh_agent::peercred_unix_listener_stream::PeercredUnixListenerStream; - use super::{BitwardenDesktopAgent, SshAgentUIRequest}; +use crate::ssh_agent::peercred_unix_listener_stream::PeercredUnixListenerStream; /// User can override the default socket path with this env var const ENV_BITWARDEN_SSH_AUTH_SOCK: &str = "BITWARDEN_SSH_AUTH_SOCK"; diff --git a/apps/desktop/desktop_native/core/src/ssh_agent/windows.rs b/apps/desktop/desktop_native/core/src/ssh_agent/windows.rs index 662a4658ede..2012dab2d77 100644 --- a/apps/desktop/desktop_native/core/src/ssh_agent/windows.rs +++ b/apps/desktop/desktop_native/core/src/ssh_agent/windows.rs @@ -2,6 +2,7 @@ use bitwarden_russh::ssh_agent; pub mod named_pipe_listener_stream; use std::sync::Arc; + use tokio::sync::Mutex; use super::{BitwardenDesktopAgent, SshAgentUIRequest}; diff --git a/apps/desktop/desktop_native/macos_provider/Cargo.toml b/apps/desktop/desktop_native/macos_provider/Cargo.toml index ea44f3d9a27..50f1834851d 100644 --- a/apps/desktop/desktop_native/macos_provider/Cargo.toml +++ b/apps/desktop/desktop_native/macos_provider/Cargo.toml @@ -14,17 +14,16 @@ crate-type = ["staticlib", "cdylib"] bench = false [dependencies] +uniffi = { workspace = true, features = ["cli"] } + +[target.'cfg(target_os = "macos")'.dependencies] desktop_core = { path = "../core" } futures = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } tokio = { workspace = true, features = ["sync"] } -tokio-util = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } -uniffi = { workspace = true, features = ["cli"] } - -[target.'cfg(target_os = "macos")'.dependencies] tracing-oslog = "0.3.0" [build-dependencies] diff --git a/apps/desktop/desktop_native/napi/Cargo.toml b/apps/desktop/desktop_native/napi/Cargo.toml index 4198baa4b5a..b5847a602d5 100644 --- a/apps/desktop/desktop_native/napi/Cargo.toml +++ b/apps/desktop/desktop_native/napi/Cargo.toml @@ -16,17 +16,13 @@ manual_test = [] [dependencies] anyhow = { workspace = true } autotype = { path = "../autotype" } -base64 = { workspace = true } chromium_importer = { path = "../chromium_importer" } desktop_core = { path = "../core" } -hex = { workspace = true } napi = { workspace = true, features = ["async"] } napi-derive = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } tokio = { workspace = true } -tokio-stream = { workspace = true } -tokio-util = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } diff --git a/apps/desktop/desktop_native/napi/index.d.ts b/apps/desktop/desktop_native/napi/index.d.ts index 0a8beb8c427..01bfa65d571 100644 --- a/apps/desktop/desktop_native/napi/index.d.ts +++ b/apps/desktop/desktop_native/napi/index.d.ts @@ -11,7 +11,10 @@ export declare namespace passwords { * Throws {@link Error} with message {@link PASSWORD_NOT_FOUND} if the password does not exist. */ export function getPassword(service: string, account: string): Promise<string> - /** Save the password to the keychain. Adds an entry if none exists otherwise updates the existing entry. */ + /** + * Save the password to the keychain. Adds an entry if none exists otherwise updates the + * existing entry. + */ export function setPassword(service: string, account: string, password: string): Promise<void> /** * Delete the stored password from the keychain. @@ -35,7 +38,8 @@ export declare namespace biometrics { * base64 encoded key and the base64 encoded challenge used to create it * separated by a `|` character. * - * If the iv is provided, it will be used as the challenge. Otherwise a random challenge will be generated. + * If the iv is provided, it will be used as the challenge. Otherwise a random challenge will + * be generated. * * `format!("<key_base64>|<iv_base64>")` */ @@ -119,8 +123,9 @@ export declare namespace ipc { /** * Create and start the IPC server without blocking. * - * @param name The endpoint name to listen on. This name uniquely identifies the IPC connection and must be the same for both the server and client. - * @param callback This function will be called whenever a message is received from a client. + * @param name The endpoint name to listen on. This name uniquely identifies the IPC + * connection and must be the same for both the server and client. @param callback + * This function will be called whenever a message is received from a client. */ static listen(name: string, callback: (error: null | Error, message: IpcMessage) => void): Promise<IpcServer> /** Return the path to the IPC server. */ @@ -130,8 +135,9 @@ export declare namespace ipc { /** * Send a message over the IPC server to all the connected clients * - * @return The number of clients that the message was sent to. Note that the number of messages - * actually received may be less, as some clients could disconnect before receiving the message. + * @return The number of clients that the message was sent to. Note that the number of + * messages actually received may be less, as some clients could disconnect before + * receiving the message. */ send(message: string): number } @@ -194,8 +200,9 @@ export declare namespace autofill { /** * Create and start the IPC server without blocking. * - * @param name The endpoint name to listen on. This name uniquely identifies the IPC connection and must be the same for both the server and client. - * @param callback This function will be called whenever a message is received from a client. + * @param name The endpoint name to listen on. This name uniquely identifies the IPC + * connection and must be the same for both the server and client. @param callback + * This function will be called whenever a message is received from a client. */ static listen(name: string, registrationCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyRegistrationRequest) => void, assertionCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionRequest) => void, assertionWithoutUserInterfaceCallback: (error: null | Error, clientId: number, sequenceNumber: number, message: PasskeyAssertionWithoutUserInterfaceRequest) => void): Promise<IpcServer> /** Return the path to the IPC server. */ diff --git a/apps/desktop/desktop_native/napi/src/lib.rs b/apps/desktop/desktop_native/napi/src/lib.rs index 01d60ff5f56..c34e7574f68 100644 --- a/apps/desktop/desktop_native/napi/src/lib.rs +++ b/apps/desktop/desktop_native/napi/src/lib.rs @@ -19,7 +19,8 @@ pub mod passwords { .map_err(|e| napi::Error::from_reason(e.to_string())) } - /// Save the password to the keychain. Adds an entry if none exists otherwise updates the existing entry. + /// Save the password to the keychain. Adds an entry if none exists otherwise updates the + /// existing entry. #[napi] pub async fn set_password( service: String, @@ -107,7 +108,8 @@ pub mod biometrics { /// base64 encoded key and the base64 encoded challenge used to create it /// separated by a `|` character. /// - /// If the iv is provided, it will be used as the challenge. Otherwise a random challenge will be generated. + /// If the iv is provided, it will be used as the challenge. Otherwise a random challenge will + /// be generated. /// /// `format!("<key_base64>|<iv_base64>")` #[allow(clippy::unused_async)] // FIXME: Remove unused async! @@ -556,8 +558,9 @@ pub mod ipc { impl IpcServer { /// Create and start the IPC server without blocking. /// - /// @param name The endpoint name to listen on. This name uniquely identifies the IPC connection and must be the same for both the server and client. - /// @param callback This function will be called whenever a message is received from a client. + /// @param name The endpoint name to listen on. This name uniquely identifies the IPC + /// connection and must be the same for both the server and client. @param callback + /// This function will be called whenever a message is received from a client. #[allow(clippy::unused_async)] // FIXME: Remove unused async! #[napi(factory)] pub async fn listen( @@ -598,8 +601,9 @@ pub mod ipc { /// Send a message over the IPC server to all the connected clients /// - /// @return The number of clients that the message was sent to. Note that the number of messages - /// actually received may be less, as some clients could disconnect before receiving the message. + /// @return The number of clients that the message was sent to. Note that the number of + /// messages actually received may be less, as some clients could disconnect before + /// receiving the message. #[napi] pub fn send(&self, message: String) -> napi::Result<u32> { self.server @@ -743,8 +747,9 @@ pub mod autofill { impl IpcServer { /// Create and start the IPC server without blocking. /// - /// @param name The endpoint name to listen on. This name uniquely identifies the IPC connection and must be the same for both the server and client. - /// @param callback This function will be called whenever a message is received from a client. + /// @param name The endpoint name to listen on. This name uniquely identifies the IPC + /// connection and must be the same for both the server and client. @param callback + /// This function will be called whenever a message is received from a client. #[allow(clippy::unused_async)] // FIXME: Remove unused async! #[napi(factory)] pub async fn listen( @@ -946,18 +951,21 @@ pub mod logging { //! //! # Example //! - //! [Elec] 14:34:03.517 › [NAPI] [INFO] desktop_core::ssh_agent::platform_ssh_agent: Starting SSH Agent server {socket=/Users/foo/.bitwarden-ssh-agent.sock} + //! [Elec] 14:34:03.517 › [NAPI] [INFO] desktop_core::ssh_agent::platform_ssh_agent: Starting + //! SSH Agent server {socket=/Users/foo/.bitwarden-ssh-agent.sock} - use std::fmt::Write; - use std::sync::OnceLock; + use std::{fmt::Write, sync::OnceLock}; use napi::threadsafe_function::{ ErrorStrategy::CalleeHandled, ThreadsafeFunction, ThreadsafeFunctionCallMode, }; use tracing::Level; - use tracing_subscriber::fmt::format::{DefaultVisitor, Writer}; use tracing_subscriber::{ - filter::EnvFilter, layer::SubscriberExt, util::SubscriberInitExt, Layer, + filter::EnvFilter, + fmt::format::{DefaultVisitor, Writer}, + layer::SubscriberExt, + util::SubscriberInitExt, + Layer, }; struct JsLogger(OnceLock<ThreadsafeFunction<(LogLevel, String), CalleeHandled>>); @@ -1069,6 +1077,8 @@ pub mod logging { #[napi] pub mod chromium_importer { + use std::collections::HashMap; + use chromium_importer::{ chromium::{ DefaultInstalledBrowserRetriever, LoginImportResult as _LoginImportResult, @@ -1076,7 +1086,6 @@ pub mod chromium_importer { }, metadata::NativeImporterMetadata as _NativeImporterMetadata, }; - use std::collections::HashMap; #[napi(object)] pub struct ProfileInfo { diff --git a/apps/desktop/desktop_native/objc/Cargo.toml b/apps/desktop/desktop_native/objc/Cargo.toml index fc8910bddd3..c161b8226ba 100644 --- a/apps/desktop/desktop_native/objc/Cargo.toml +++ b/apps/desktop/desktop_native/objc/Cargo.toml @@ -8,16 +8,12 @@ publish = { workspace = true } [features] default = [] -[dependencies] +[target.'cfg(target_os = "macos")'.dependencies] anyhow = { workspace = true } -thiserror = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } -[target.'cfg(target_os = "macos")'.dependencies] -core-foundation = "=0.10.1" - -[build-dependencies] +[target.'cfg(target_os = "macos")'.build-dependencies] cc = "=1.2.4" glob = "=0.3.2" diff --git a/apps/desktop/desktop_native/process_isolation/Cargo.toml b/apps/desktop/desktop_native/process_isolation/Cargo.toml index 170832c2fde..d8c6c7a618c 100644 --- a/apps/desktop/desktop_native/process_isolation/Cargo.toml +++ b/apps/desktop/desktop_native/process_isolation/Cargo.toml @@ -8,7 +8,7 @@ publish = { workspace = true } [lib] crate-type = ["cdylib"] -[dependencies] +[target.'cfg(target_os = "linux")'.dependencies] ctor = { workspace = true } desktop_core = { path = "../core" } libc = { workspace = true } diff --git a/apps/desktop/desktop_native/process_isolation/src/lib.rs b/apps/desktop/desktop_native/process_isolation/src/lib.rs index 850ffac841e..55c5d7fafae 100644 --- a/apps/desktop/desktop_native/process_isolation/src/lib.rs +++ b/apps/desktop/desktop_native/process_isolation/src/lib.rs @@ -5,8 +5,9 @@ //! On Linux, this is PR_SET_DUMPABLE to prevent debuggers from attaching, the env //! from being read and the memory from being stolen. -use desktop_core::process_isolation; use std::{ffi::c_char, sync::LazyLock}; + +use desktop_core::process_isolation; use tracing::info; static ORIGINAL_UNSETENV: LazyLock<unsafe extern "C" fn(*const c_char) -> i32> = diff --git a/apps/desktop/desktop_native/proxy/Cargo.toml b/apps/desktop/desktop_native/proxy/Cargo.toml index c672f57543d..25682fe2aa3 100644 --- a/apps/desktop/desktop_native/proxy/Cargo.toml +++ b/apps/desktop/desktop_native/proxy/Cargo.toml @@ -6,7 +6,6 @@ version = { workspace = true } publish = { workspace = true } [dependencies] -anyhow = { workspace = true } desktop_core = { path = "../core" } futures = { workspace = true } tokio = { workspace = true, features = ["io-std", "io-util", "macros", "rt"] } diff --git a/apps/desktop/desktop_native/proxy/src/main.rs b/apps/desktop/desktop_native/proxy/src/main.rs index c2c525b865a..21957d8ba32 100644 --- a/apps/desktop/desktop_native/proxy/src/main.rs +++ b/apps/desktop/desktop_native/proxy/src/main.rs @@ -60,7 +60,6 @@ fn init_logging(log_path: &Path, console_level: LevelFilter, file_level: LevelFi /// a stable communication channel between the proxy and the running desktop application. /// /// Browser extension <-[native messaging]-> proxy <-[ipc]-> desktop -/// // FIXME: Remove unwraps! They panic and terminate the whole application. #[allow(clippy::unwrap_used)] #[tokio::main(flavor = "current_thread")] @@ -83,8 +82,10 @@ async fn main() { // Different browsers send different arguments when the app starts: // // Firefox: - // - The complete path to the app manifest. (in the form `/Users/<user>/Library/.../Mozilla/NativeMessagingHosts/com.8bit.bitwarden.json`) - // - (in Firefox 55+) the ID (as given in the manifest.json) of the add-on that started it (in the form `{[UUID]}`). + // - The complete path to the app manifest. (in the form + // `/Users/<user>/Library/.../Mozilla/NativeMessagingHosts/com.8bit.bitwarden.json`) + // - (in Firefox 55+) the ID (as given in the manifest.json) of the add-on that started it (in + // the form `{[UUID]}`). // // Chrome on Windows: // - Origin of the extension that started it (in the form `chrome-extension://[ID]`). @@ -96,7 +97,8 @@ async fn main() { let args: Vec<_> = std::env::args().skip(1).collect(); info!(?args, "Process args"); - // Setup two channels, one for sending messages to the desktop application (`out`) and one for receiving messages from the desktop application (`in`) + // Setup two channels, one for sending messages to the desktop application (`out`) and one for + // receiving messages from the desktop application (`in`) let (in_send, in_recv) = tokio::sync::mpsc::channel(MESSAGE_CHANNEL_BUFFER); let (out_send, mut out_recv) = tokio::sync::mpsc::channel(MESSAGE_CHANNEL_BUFFER); diff --git a/apps/desktop/desktop_native/rustfmt.toml b/apps/desktop/desktop_native/rustfmt.toml new file mode 100644 index 00000000000..bb3baeccd76 --- /dev/null +++ b/apps/desktop/desktop_native/rustfmt.toml @@ -0,0 +1,7 @@ +# Wrap comments and increase the width of comments to 100 +comment_width = 100 +wrap_comments = true + +# Sort and group imports +group_imports = "StdExternalCrate" +imports_granularity = "Crate" diff --git a/apps/desktop/desktop_native/windows_plugin_authenticator/src/lib.rs b/apps/desktop/desktop_native/windows_plugin_authenticator/src/lib.rs index 2e4f453d8f0..893fdf765fc 100644 --- a/apps/desktop/desktop_native/windows_plugin_authenticator/src/lib.rs +++ b/apps/desktop/desktop_native/windows_plugin_authenticator/src/lib.rs @@ -2,11 +2,12 @@ #![allow(non_snake_case)] #![allow(non_camel_case_types)] -use std::ffi::c_uchar; -use std::ptr; -use windows::Win32::Foundation::*; -use windows::Win32::System::Com::*; -use windows::Win32::System::LibraryLoader::*; +use std::{ffi::c_uchar, ptr}; + +use windows::Win32::{ + Foundation::*, + System::{Com::*, LibraryLoader::*}, +}; use windows_core::*; mod pluginauthenticator; From 0912d1abe8f280be48f6c28cb932d6407bb22341 Mon Sep 17 00:00:00 2001 From: "bw-ghapp[bot]" <178206702+bw-ghapp[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 18:31:02 +0100 Subject: [PATCH 57/75] Autosync the updated translations (#17462) Co-authored-by: bitwarden-devops-bot <106330231+bitwarden-devops-bot@users.noreply.github.com> --- apps/web/src/locales/af/messages.json | 72 ++++++------- apps/web/src/locales/ar/messages.json | 72 ++++++------- apps/web/src/locales/az/messages.json | 82 +++++++-------- apps/web/src/locales/be/messages.json | 72 ++++++------- apps/web/src/locales/bg/messages.json | 78 +++++++------- apps/web/src/locales/bn/messages.json | 72 ++++++------- apps/web/src/locales/bs/messages.json | 72 ++++++------- apps/web/src/locales/ca/messages.json | 72 ++++++------- apps/web/src/locales/cs/messages.json | 70 ++++++------- apps/web/src/locales/cy/messages.json | 72 ++++++------- apps/web/src/locales/da/messages.json | 72 ++++++------- apps/web/src/locales/de/messages.json | 84 +++++++-------- apps/web/src/locales/el/messages.json | 72 ++++++------- apps/web/src/locales/en_GB/messages.json | 72 ++++++------- apps/web/src/locales/en_IN/messages.json | 72 ++++++------- apps/web/src/locales/eo/messages.json | 72 ++++++------- apps/web/src/locales/es/messages.json | 72 ++++++------- apps/web/src/locales/et/messages.json | 72 ++++++------- apps/web/src/locales/eu/messages.json | 72 ++++++------- apps/web/src/locales/fa/messages.json | 72 ++++++------- apps/web/src/locales/fi/messages.json | 72 ++++++------- apps/web/src/locales/fil/messages.json | 72 ++++++------- apps/web/src/locales/fr/messages.json | 78 +++++++------- apps/web/src/locales/gl/messages.json | 72 ++++++------- apps/web/src/locales/he/messages.json | 128 +++++++++++------------ apps/web/src/locales/hi/messages.json | 72 ++++++------- apps/web/src/locales/hr/messages.json | 74 ++++++------- apps/web/src/locales/hu/messages.json | 74 ++++++------- apps/web/src/locales/id/messages.json | 74 ++++++------- apps/web/src/locales/it/messages.json | 72 ++++++------- apps/web/src/locales/ja/messages.json | 72 ++++++------- apps/web/src/locales/ka/messages.json | 72 ++++++------- apps/web/src/locales/km/messages.json | 72 ++++++------- apps/web/src/locales/kn/messages.json | 72 ++++++------- apps/web/src/locales/ko/messages.json | 72 ++++++------- apps/web/src/locales/lv/messages.json | 74 ++++++------- apps/web/src/locales/ml/messages.json | 72 ++++++------- apps/web/src/locales/mr/messages.json | 72 ++++++------- apps/web/src/locales/my/messages.json | 72 ++++++------- apps/web/src/locales/nb/messages.json | 72 ++++++------- apps/web/src/locales/ne/messages.json | 72 ++++++------- apps/web/src/locales/nl/messages.json | 74 ++++++------- apps/web/src/locales/nn/messages.json | 72 ++++++------- apps/web/src/locales/or/messages.json | 72 ++++++------- apps/web/src/locales/pl/messages.json | 72 ++++++------- apps/web/src/locales/pt_BR/messages.json | 72 ++++++------- apps/web/src/locales/pt_PT/messages.json | 76 +++++++------- apps/web/src/locales/ro/messages.json | 72 ++++++------- apps/web/src/locales/ru/messages.json | 72 ++++++------- apps/web/src/locales/si/messages.json | 72 ++++++------- apps/web/src/locales/sk/messages.json | 102 +++++++++--------- apps/web/src/locales/sl/messages.json | 72 ++++++------- apps/web/src/locales/sr_CS/messages.json | 72 ++++++------- apps/web/src/locales/sr_CY/messages.json | 74 ++++++------- apps/web/src/locales/sv/messages.json | 80 +++++++------- apps/web/src/locales/ta/messages.json | 72 ++++++------- apps/web/src/locales/te/messages.json | 72 ++++++------- apps/web/src/locales/th/messages.json | 72 ++++++------- apps/web/src/locales/tr/messages.json | 74 ++++++------- apps/web/src/locales/uk/messages.json | 72 ++++++------- apps/web/src/locales/vi/messages.json | 74 ++++++------- apps/web/src/locales/zh_CN/messages.json | 104 +++++++++--------- apps/web/src/locales/zh_TW/messages.json | 78 +++++++------- 63 files changed, 2171 insertions(+), 2549 deletions(-) diff --git a/apps/web/src/locales/af/messages.json b/apps/web/src/locales/af/messages.json index 684e107cd57..2fee7410e93 100644 --- a/apps/web/src/locales/af/messages.json +++ b/apps/web/src/locales/af/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/ar/messages.json b/apps/web/src/locales/ar/messages.json index fb42b2b5fa2..7dbfa670741 100644 --- a/apps/web/src/locales/ar/messages.json +++ b/apps/web/src/locales/ar/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "الأعضاء المعرضون للخطر" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "يقوم هؤلاء الأعضاء بتسجيل الدخول إلى التطبيقات باستخدام كلمات مرور ضعيفة أو مكشوفة أو مُعادة الاستخدام." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "يقوم هؤلاء الأعضاء بتسجيل الدخول إلى التطبيقات باستخدام كلمات مرور ضعيفة أو مكشوفة أو مُعادة الاستخدام." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/az/messages.json b/apps/web/src/locales/az/messages.json index 8dcfa862f03..9ff3fc46040 100644 --- a/apps/web/src/locales/az/messages.json +++ b/apps/web/src/locales/az/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "İrəliləyişi izləmək üçün üzvlərə tapşırıqlar təyin edin" }, - "onceYouReviewApps": { - "message": "Tətbiqləri incələyib kritik olaraq işarələdikdən sonra, riskli elementləri həll etməsi üçün üzvlərə tapşırıqlar verə və irəliləyişi burada izləyə bilərsiniz" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Xatırlatma göndər" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "$ORG NAME$ üçün heç bir tətbiq tapılmadı", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Kimlik məlumatı təhlükəsizlik risklərini monitorinq etməyə başlamaq üçün təşkilatınızın giriş verilərini daxilə köçürün. Daxilə köçürdükdən sonra əldə edəcəkləriniz:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Riskləri prioritetləşdirmə" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Ən vacib tətbiqlərə fokuslanma" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Riskləri azaltma bələdçisi" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Risk altındakı üzvlərə, riskli kimlik məlumatlarını dəyişmək üçün rəhbər tapşırıqlar təyin edin" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { - "message": "İrəliləyişin monitorinqi" + "feature3Title": { + "message": "Monitor progress" }, - "benefit3Description": { - "message": "Təhlükəsizlik təkmilləşdirmələrini göstərmək üçün zamanla dəyişiklikləri izləyin" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Tətbiqləri görmək üçün ilk hesabatınızı çalışdırın" + "noReportsRunTitle": { + "message": "Generate report" }, - "noReportRunDescription": { - "message": "Təşkilatınızın tətbiqlərini təhlil etmək və diqqət edilməli riskli parolları müəyyənləşdirmək üçün risk təhlili hesabatını yaradın. İlk hesabatınızı çalışdırdıqda:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "Heç bir tətbiqi kritik olaraq işarələməmisiniz" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Riskli üzvlər" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Kritik tətbiqlər üçün risk altındakı elementlərə erişimi olan üzvlər" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Riskli parollara sahib üzvlər" }, - "membersWillReceiveNotification": { - "message": "Üzvlər, riskli girişləri həll etmək üçün brauzer uzantısı üzərindən bildiriş alacaqlar." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ üzv risk altındadır", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Bu üzvlər, tətbiqlərə zəif, ifşa olunmuş və ya təkrar istifadə olunan parollarla giriş edir." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "Tətbiqlərə zəif, ifşa olunmuş və ya təkrar istifadə olunan parollarla giriş edən üzv yoxdur." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Kritik tətbiqləri prioritetləşdir" }, - "selectCriticalApplicationsDescription": { - "message": "Təşkilatınız üçün ən kritik sayılan tətbiqləri seçin, daha sonra riskləri həll etmələri üçün üzvlərə təhlükəsizlik tapşırıqları təyin edin." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Yeni tətbiqlər incələ" }, - "reviewNewApplicationsDescription": { - "message": "Admin konsolunda saxlanılmış və zəif, ifşa olunmuş və ya təkrar istifadə olunmuş parollara sahib yeni tətbiqlər üçün riskli elementləri vurğulamışıq." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Bir tətbiqi kritik olaraq işarələmək üçün ulduz ikonuna klikləyin" @@ -3251,7 +3245,7 @@ "message": "Növbəti ödəniş" }, "nextChargeHeader": { - "message": "Next Charge" + "message": "Növbəti ödəniş" }, "plan": { "message": "Plan" @@ -3260,7 +3254,7 @@ "message": "Təfsilatlar" }, "discount": { - "message": "discount" + "message": "endirim" }, "downloadLicense": { "message": "Lisenziyanı endir" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Tapşırıq təyin et" }, - "assignTasksToMembers": { - "message": "Üzvlərə rəhbər həll üçün tapşırıqlar təyin edin" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Kolleksiyalara təyin et" @@ -12159,9 +12153,9 @@ } }, "confirmNoSelectedCriticalApplicationsTitle": { - "message": "No critical applications are selected" + "message": "Heç bir kritik tətbiq seçilməyib" }, "confirmNoSelectedCriticalApplicationsDesc": { - "message": "Are you sure you want to continue?" + "message": "Davam etmək istədiyinizə əminsiniz?" } } diff --git a/apps/web/src/locales/be/messages.json b/apps/web/src/locales/be/messages.json index 9fd22d0ccd0..e562c39ea7f 100644 --- a/apps/web/src/locales/be/messages.json +++ b/apps/web/src/locales/be/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Удзельнікі ў зоне рызыкі" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Гэтыя ўдзельнікі ўваходзяць у праграму з ненадзейнымі, скампраметаванымі або паўторна выкарыстанымі паролямі." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/bg/messages.json b/apps/web/src/locales/bg/messages.json index 6f17d4b264b..1f897b5d74e 100644 --- a/apps/web/src/locales/bg/messages.json +++ b/apps/web/src/locales/bg/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Назначете задачи на членовете, за да следите напредъка" }, - "onceYouReviewApps": { - "message": "След като прегледате приложенията и отбележите някои като важни, можете да зададете задачи на членовете, така че те да се занимаят с нещата в риск, а Вие да следите напредъка тук" + "onceYouReviewApplications": { + "message": "След като прегледате приложенията и ги отбележите като важни, можете да зададете задачи на членовете си, за да сменят паролите си." }, "sendReminders": { "message": "Изпращане на напомняния" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "Няма намерени приложения за $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "Няма намерени данни" }, - "noApplicationsInOrgDescription": { - "message": "Внесете данните за вписване на организацията си, за да започнете да следите рисковете по сигурността им. След внасянето ще можете:" + "noDataInOrgDescription": { + "message": "Внесете данните за вписване на организацията си, за да започнете да използвате Анализа на достъпа. След като направите това, ще можете да:" }, - "benefit1Title": { - "message": "Да приоритизирате рисковете" + "feature1Title": { + "message": "Отбелязвате приложения като важни" }, - "benefit1Description": { - "message": "Фокусирайте се върху приложенията, които са най-важни" + "feature1Description": { + "message": "Това ще Ви помогне да отстранявате рисковете първо в най-важните си приложения." }, - "benefit2Title": { - "message": "Да давате насоки за подобряване" + "feature2Title": { + "message": "Помагате на членовете да подобряват сигурността си" }, - "benefit2Description": { - "message": "Давайте задачи на членовете в риск, така че те да не забравят да променят данните си" + "feature2Description": { + "message": "Задавайте на членовете в риск задачи по сигурността, които да ги насочват в промяната на данните им." }, - "benefit3Title": { - "message": "Да следите напредъка" + "feature3Title": { + "message": "Следите напредъка" }, - "benefit3Description": { - "message": "Следете промените във времето, за да виждате как сигурността се подобрява" + "feature3Description": { + "message": "Следете промените във времето, за да виждате как сигурността се подобрява." }, - "noReportRunTitle": { - "message": "Създайте първия си доклад, за да видите приложенията" + "noReportsRunTitle": { + "message": "Създаване на доклад" }, - "noReportRunDescription": { - "message": "Създайте доклад с подробности за рисковете, за да анализирате приложенията в организацията си и да установите кои пароли са в риск и имат нужда от внимание. Създаването на първия доклад ще:" + "noReportsRunDescription": { + "message": "Всичко е готово и можете да започнете да създавате доклади. След като го направите, ще можете да:" }, "noCriticalApplicationsTitle": { "message": "Не сте отбелязали нито едно приложение като важно" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Членове в риск" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Членове с достъп до елементи в риск за важни приложения" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "Тези членове имат достъп до рисковите елементи за важните приложения." }, "membersWithAtRiskPasswords": { "message": "Членове с пароли в риск" }, - "membersWillReceiveNotification": { - "message": "Членовете ще получат известие, за да отстранят проблема с данните си за вписване в риск, чрез добавката за браузъра." + "membersWillReceiveSecurityTask": { + "message": "Членовете на организацията ще получат задача за промяна на рисковите пароли. Те ще получат известие в разширението за браузър на Битуорден." }, "membersAtRiskCount": { "message": "$COUNT$ членове в риск", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Тези членове се вписват в приложенията със слаби, преизползвани или разобличени пароли." + "atRiskMemberDescription": { + "message": "Тези членове се вписват във важните приложения със слаби, преизползвани или разкрити пароли." }, "atRiskMembersDescriptionNone": { "message": "Няма членове, които се вписват в приложенията със слаби, преизползвани или разобличени пароли." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Даване на приоритет на важните приложения" }, - "selectCriticalApplicationsDescription": { - "message": "Изберете кои приложения са най-важни за организацията Ви, а след това раздайте задачи на членовете, за да отстраните рисковете." + "selectCriticalAppsDescription": { + "message": "Изберете кои приложения са най-важни за организацията Ви. След това ще можете да зададете задачи на членовете, за да отстраните рисковете." }, "reviewNewApplications": { "message": "Преглед на новите приложения" }, - "reviewNewApplicationsDescription": { - "message": "Отбелязахме елементите в риск за новите приложения съхранявани в Административната конзола, които имат слаби, преизползвани или разкрити пароли." + "reviewNewAppsDescription": { + "message": "Прегледайте новите приложения с рискови елементи и ги отбележете, ако искате да ги следите внимателно като важни. След това ще можете да задавате задачи на членовете, за да отстраните рисковете." }, "clickIconToMarkAppAsCritical": { "message": "Щракнете върху иконката със звезда, за да отбележите приложение като важно" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Назначаване на задачи" }, - "assignTasksToMembers": { - "message": "Задавайте задачи на членовете, за да отстраняват проблемите" + "assignSecurityTasksToMembers": { + "message": "Изпращане на известия за промяна на пароли" }, "assignToCollections": { "message": "Свързване с колекции" @@ -12159,9 +12153,9 @@ } }, "confirmNoSelectedCriticalApplicationsTitle": { - "message": "No critical applications are selected" + "message": "Няма избрани важни приложения" }, "confirmNoSelectedCriticalApplicationsDesc": { - "message": "Are you sure you want to continue?" + "message": "Наистина ли искате да продължите?" } } diff --git a/apps/web/src/locales/bn/messages.json b/apps/web/src/locales/bn/messages.json index 4baf480b517..67fddc7d6e0 100644 --- a/apps/web/src/locales/bn/messages.json +++ b/apps/web/src/locales/bn/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/bs/messages.json b/apps/web/src/locales/bs/messages.json index f9b5481cd23..cae2a248d0a 100644 --- a/apps/web/src/locales/bs/messages.json +++ b/apps/web/src/locales/bs/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/ca/messages.json b/apps/web/src/locales/ca/messages.json index 2994137855e..77204835dbe 100644 --- a/apps/web/src/locales/ca/messages.json +++ b/apps/web/src/locales/ca/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assigna a col·leccions" diff --git a/apps/web/src/locales/cs/messages.json b/apps/web/src/locales/cs/messages.json index ed82a38c390..ccb2172dbf6 100644 --- a/apps/web/src/locales/cs/messages.json +++ b/apps/web/src/locales/cs/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Přiřadit úkoly členů ke sledování průběhu" }, - "onceYouReviewApps": { - "message": "Jakmile zkontrolujete aplikace a označíte je jako kritické, můžete přidělit členům úkoly k řešení ohrožených položek a sledovat průběh zde" + "onceYouReviewApplications": { + "message": "Jakmile zkontrolujete aplikace a označíte je jako kritické, přiřaďte svým členům úkoly ke změně jejich hesel." }, "sendReminders": { "message": "Odeslat připomenutí" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "Pro $ORG NAME$ nebyly nalezeny žádné aplikace", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "Nebyla nalezena žádná data" }, - "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:" + "noDataInOrgDescription": { + "message": "Importujte přihlašovací údaje Vaší organizace a začněte s Access Intelligence. Jakmile to uděláte, budete moci:" }, - "benefit1Title": { - "message": "Upřednostnit rizika" + "feature1Title": { + "message": "Označit aplikace jako kritické" }, - "benefit1Description": { - "message": "Zaměřit se na aplikace, na které záleží nejvíce" + "feature1Description": { + "message": "To Vám pomůže nejprve odebrat rizika pro Vaše nejdůležitější aplikace." }, - "benefit2Title": { - "message": "Průvodce nápravou" + "feature2Title": { + "message": "Pomoci členům zlepšit jejich zabezpečení" }, - "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ů" + "feature2Description": { + "message": "Přiřaďte ohroženým členům řízené bezpečnostní úkoly k aktualizaci přihlašovacích údajů." }, - "benefit3Title": { + "feature3Title": { "message": "Sledovat průběh" }, - "benefit3Description": { + "feature3Description": { "message": "Sleduje změny v průběhu času pro zobrazení zlepšení zabezpečení." }, - "noReportRunTitle": { - "message": "Spustit první hlášení pro zobrazení aplikací" + "noReportsRunTitle": { + "message": "Vygenerovat přehled" }, - "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:" + "noReportsRunDescription": { + "message": "Jste připraveni začít generovat přehledy. Jakmile je vytvoříte, budete moci:" }, "noCriticalApplicationsTitle": { "message": "Neoznačili jste žádné aplikace jako kritické" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Ohrožení členové" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Členové s přístupem k rizikovým položkám pro kritické aplikace" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "Tito členové mají přístup k zranitelným položkám pro kritické aplikace." }, "membersWithAtRiskPasswords": { "message": "Členové s ohroženými hesly" }, - "membersWillReceiveNotification": { - "message": "Členové obdrží oznámení, aby vyřešili ohrožená přihlášení prostřednictvím rozšíření prohlížeče." + "membersWillReceiveSecurityTask": { + "message": "Členům Vaší organizace bude přiřazen úkol ke změně zranitelných hesel. Obdrželi oznámení v rámci jejich rozšíření prohlížeče Bitwarden." }, "membersAtRiskCount": { "message": "$COUNT$ členů v ohrožení", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Tito členové se přihlašují do aplikací se slabými, odhalenými nebo znovu použitými hesly." + "atRiskMemberDescription": { + "message": "Tito členové se přihlašují do kritických aplikací s odhalenými nebo znovu použitými hesly." }, "atRiskMembersDescriptionNone": { "message": "Žádní členové se nepřihlašují do aplikací se slabými, odhalenými nebo znovu použitými hesly." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Upřednostnit kritické aplikace" }, - "selectCriticalApplicationsDescription": { - "message": "Vybere, které aplikace jsou pro Vaši organizaci nejkritičtější, a pro řešení rizik přiřaďte členům bezpečnostní úkoly." + "selectCriticalAppsDescription": { + "message": "Vyberte, které aplikace jsou pro Vaši organizaci nejkritičtější. Potom budete moci přidělit členům bezpečnostní úkoly pro odebrání rizik." }, "reviewNewApplications": { "message": "Zkontrolovat nové aplikace" }, - "reviewNewApplicationsDescription": { - "message": "Zvýraznili jsme rizikové položky pro nové aplikace uložené v administrátorské konzoli, které mají slabá, exponovaná nebo znovu použitá hesla." + "reviewNewAppsDescription": { + "message": "Zkontrolujte nové aplikace s citlivými položkami a označte ty, které chcete pečlivě sledovat jako kritické. Potom budete moci přidělit členům bezpečnostní úkoly, abyste odebrali rizika." }, "clickIconToMarkAppAsCritical": { "message": "Klepnutím na ikonu hvězdičky označte aplikaci jako kritickou" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Přiřadit úkoly" }, - "assignTasksToMembers": { - "message": "Přiřadit úkoly členům k řízenému řešení" + "assignSecurityTasksToMembers": { + "message": "Odeslat oznámení pro změnu hesla" }, "assignToCollections": { "message": "Přiřadit ke sbírkám" diff --git a/apps/web/src/locales/cy/messages.json b/apps/web/src/locales/cy/messages.json index 8c62a789fbd..c140204619e 100644 --- a/apps/web/src/locales/cy/messages.json +++ b/apps/web/src/locales/cy/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/da/messages.json b/apps/web/src/locales/da/messages.json index 7ddd60a2b75..cc2a58aa27d 100644 --- a/apps/web/src/locales/da/messages.json +++ b/apps/web/src/locales/da/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Udsatte medlemmer" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Disse medlemmer logger ind på programmer med svage, udsatte eller genbrugte adgangskoder." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Tildel til samlinger" diff --git a/apps/web/src/locales/de/messages.json b/apps/web/src/locales/de/messages.json index 162eefe9923..073126da446 100644 --- a/apps/web/src/locales/de/messages.json +++ b/apps/web/src/locales/de/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Mitglieder Aufgaben zuweisen, um den Fortschritt zu überwachen" }, - "onceYouReviewApps": { - "message": "Sobald du die Anwendungen überprüft und als kritisch markiert hast, kannst du Mitgliedern Aufgaben zuweisen, um gefährdete Einträge zu beheben und den Fortschritt hier überwachen" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Erinnerungen senden" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "Keine Anwendungen für $ORG NAME$ gefunden", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Importiere die Zugangsdaten deiner Organisation, um mit der Überwachung von Sicherheitsrisiken für Zugangsdaten zu starten. Nach dem Import kannst du:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Risiken priorisieren" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Auf Anwendungen konzentrieren, die am wichtigsten sind" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Anleitung zur Behebung" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Gefährdeten Mitgliedern gezielte Aufgaben zuweisen, um gefährdete Zugangsdaten zu erneuern" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Fortschritt überwachen" }, - "benefit3Description": { - "message": "Änderungen im Laufe der Zeit verfolgen, um Sicherheitsverbesserungen anzuzeigen" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Erstelle deinen ersten Bericht, um Anwendungen zu sehen" + "noReportsRunTitle": { + "message": "Generate report" }, - "noReportRunDescription": { - "message": "Erstelle einen Risikoanalysebericht, um die Anwendungen deines Unternehmens zu analysieren und gefährdete Passwörter zu identifizieren, die Aufmerksamkeit erfordern. Die Erstellung deines ersten Berichts wird:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "Du hast keine Anwendung als kritisch markiert" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Gefährdete Mitglieder" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Mitglieder mit Zugriff auf gefährdete Einträge für kritische Anwendungen" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Mitglieder mit gefährdeten Passwörtern" }, - "membersWillReceiveNotification": { - "message": "Mitglieder erhalten über die Browser-Erweiterung eine Benachrichtigung, um gefährdete Zugangsdaten zu ändern." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ gefährdete Mitglieder", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Diese Mitglieder melden sich bei Anwendungen mit schwachen, kompromittierten oder wiederverwendeten Passwörtern an." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "Dies sind keine Mitglieder, die sich in Anwendungen mit schwachen, kompromittierten oder wiederverwendeten Passwörtern anmelden." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Kritische Anwendungen priorisieren" }, - "selectCriticalApplicationsDescription": { - "message": "Wähle die für deine Organisation kritischsten Anwendungen aus und weise den Mitgliedern Sicherheitsaufgaben zu, um Risiken zu beseitigen." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Neue Anwendungen überprüfen" }, - "reviewNewApplicationsDescription": { - "message": "Wir haben gefährdete Einträge für neue Anwendungen hervorgehoben, die in der Administrator-Konsole gespeichert sind und schwache, kompromittierte oder wiederverwendete Passwörter beinhalten." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Klicke auf das Sternsymbol, um eine App als kritisch zu markieren" @@ -3251,16 +3245,16 @@ "message": "Nächste Abbuchung" }, "nextChargeHeader": { - "message": "Next Charge" + "message": "Nächste Abbuchung" }, "plan": { - "message": "Plan" + "message": "Tarif" }, "details": { "message": "Details" }, "discount": { - "message": "discount" + "message": "Rabatt" }, "downloadLicense": { "message": "Lizenz herunterladen" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Aufgaben zuweisen" }, - "assignTasksToMembers": { - "message": "Mitgliedern Aufgaben für eine gezielte Lösung zuweisen" + "assignSecurityTasksToMembers": { + "message": "Sende Benachrichtigungen, um Passwörter zu ändern" }, "assignToCollections": { "message": "Sammlungen zuweisen" @@ -10369,7 +10363,7 @@ "message": "Index" }, "selectAPlan": { - "message": "Ein Abo auswählen" + "message": "Einen Tarif auswählen" }, "thirtyFivePercentDiscount": { "message": "35% Rabatt" @@ -12159,9 +12153,9 @@ } }, "confirmNoSelectedCriticalApplicationsTitle": { - "message": "No critical applications are selected" + "message": "Es sind keine kritischen Anwendungen ausgewählt" }, "confirmNoSelectedCriticalApplicationsDesc": { - "message": "Are you sure you want to continue?" + "message": "Bist du sicher, dass du fortfahren möchtest?" } } diff --git a/apps/web/src/locales/el/messages.json b/apps/web/src/locales/el/messages.json index fb066120434..606d10fa600 100644 --- a/apps/web/src/locales/el/messages.json +++ b/apps/web/src/locales/el/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Ανάθεση σε συλλογές" diff --git a/apps/web/src/locales/en_GB/messages.json b/apps/web/src/locales/en_GB/messages.json index 2270d95056c..2f40b6fa13d 100644 --- a/apps/web/src/locales/en_GB/messages.json +++ b/apps/web/src/locales/en_GB/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organisation's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritise risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritise critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organisation, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/en_IN/messages.json b/apps/web/src/locales/en_IN/messages.json index 99ebcf22413..180b90cf878 100644 --- a/apps/web/src/locales/en_IN/messages.json +++ b/apps/web/src/locales/en_IN/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organisation's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritise risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritise critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organisation, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/eo/messages.json b/apps/web/src/locales/eo/messages.json index b1acc78487d..94fd92f45c9 100644 --- a/apps/web/src/locales/eo/messages.json +++ b/apps/web/src/locales/eo/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/es/messages.json b/apps/web/src/locales/es/messages.json index 80767d520e8..be77b075923 100644 --- a/apps/web/src/locales/es/messages.json +++ b/apps/web/src/locales/es/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Asignar a colecciones" diff --git a/apps/web/src/locales/et/messages.json b/apps/web/src/locales/et/messages.json index 0e47bce83ff..2dddfda1bb6 100644 --- a/apps/web/src/locales/et/messages.json +++ b/apps/web/src/locales/et/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/eu/messages.json b/apps/web/src/locales/eu/messages.json index 95281e381ca..785ba6e2e0d 100644 --- a/apps/web/src/locales/eu/messages.json +++ b/apps/web/src/locales/eu/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/fa/messages.json b/apps/web/src/locales/fa/messages.json index aec55daad51..71da2a1b8ba 100644 --- a/apps/web/src/locales/fa/messages.json +++ b/apps/web/src/locales/fa/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "اعضای در معرض خطر" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "این اعضا با کلمات عبور ضعیف، افشا شده یا تکراری وارد برنامه‌ها می‌شوند." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "هیچ عضوی با کلمات عبور ضعیف، افشا شده یا تکراری وارد برنامه‌ها نمی‌شود." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "اختصاص به مجموعه‌ها" diff --git a/apps/web/src/locales/fi/messages.json b/apps/web/src/locales/fi/messages.json index 8a1c7550d42..b735280c312 100644 --- a/apps/web/src/locales/fi/messages.json +++ b/apps/web/src/locales/fi/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Riskialttiit jäsenet" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Määritä kokoelmiin" diff --git a/apps/web/src/locales/fil/messages.json b/apps/web/src/locales/fil/messages.json index 6b0d5e3e8a5..f91cf0d9bf0 100644 --- a/apps/web/src/locales/fil/messages.json +++ b/apps/web/src/locales/fil/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/fr/messages.json b/apps/web/src/locales/fr/messages.json index 8d51d685d79..d97fca90d57 100644 --- a/apps/web/src/locales/fr/messages.json +++ b/apps/web/src/locales/fr/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Affecter des tâches aux membres pour surveiller la progression" }, - "onceYouReviewApps": { - "message": "Une fois que vous examinez les applications et marquez comme critiques, vous pouvez assigner des tâches aux membres pour résoudre les éléments à risque et suivre les progrès ici" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Envoyer des rappels" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "Aucune application trouvée pour $ORG NOM$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "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 :" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioriser les risques" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Vous concentrer sur les applications qui comptent le plus" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guider la remédiation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assignez aux membres à risque des tâches guidées pour faire pivoter les identifiants à risque." + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { - "message": "Suivre la progression" + "feature3Title": { + "message": "Monitor progress" }, - "benefit3Description": { - "message": "Suivre les changements au fil du temps pour afficher les améliorations de sécurité" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Exécutez votre premier rapport pour voir les applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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 :" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "Vous n'avez marqué aucune application comme critique" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Membres à risque" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Les membres ayant accès aux éléments à risque pour les applications critiques" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Membres avec des mots de passe à risque" }, - "membersWillReceiveNotification": { - "message": "Les membres recevront une notification pour résoudre les identifiants à risque via l'extension du navigateur." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ membres à risque", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Ces membres se connectent à des applications avec des mots de passe faibles, exposés ou réutilisés." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "Aucun membre ne se connecte dans des applications avec des mots de passe faibles, exposés ou réutilisés." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioriser les applications critiques" }, - "selectCriticalApplicationsDescription": { - "message": "Sélectionnez les applications les plus critiques pour votre organisation, puis attribuez des tâches de sécurité aux membres pour résoudre les risques." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Examiner les nouvelles applications" }, - "reviewNewApplicationsDescription": { - "message": "Nous avons mis en évidence des éléments à risque pour les nouvelles applications stockées dans la console Admin qui ont des mots de passe faibles, exposés ou réutilisés." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Cliquez sur l'icône étoile pour marquer une application comme critique" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assigner des tâches" }, - "assignTasksToMembers": { - "message": "Assigner des tâches aux membres pour une résolution guidée" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assigner aux collections" @@ -12159,9 +12153,9 @@ } }, "confirmNoSelectedCriticalApplicationsTitle": { - "message": "No critical applications are selected" + "message": "Aucune application critique n'est sélectionnée" }, "confirmNoSelectedCriticalApplicationsDesc": { - "message": "Are you sure you want to continue?" + "message": "Êtes-vous sûr de vouloir continuer ?" } } diff --git a/apps/web/src/locales/gl/messages.json b/apps/web/src/locales/gl/messages.json index e5c8ce9b49f..c9fc10749d7 100644 --- a/apps/web/src/locales/gl/messages.json +++ b/apps/web/src/locales/gl/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/he/messages.json b/apps/web/src/locales/he/messages.json index b60e76f6a75..5eb5c7969ee 100644 --- a/apps/web/src/locales/he/messages.json +++ b/apps/web/src/locales/he/messages.json @@ -21,13 +21,13 @@ "message": "סיכון סיסמה" }, "noEditPermissions": { - "message": "You don't have permission to edit this item" + "message": "אין לך הרשאות לערוך את הפריט הזה" }, "reviewAtRiskPasswords": { "message": "סקור סיסמאות בסיכון (חלשות, חשופות, או משומשות) בין יישומים. בחר את היישומים הכי קריטיים שלך על מנת לתעדף פעולות אבטחה עבור המשתמשים שלך כדי לטפל בסיסמאות בסיכון." }, "reviewAtRiskLoginsPrompt": { - "message": "Review at-risk logins" + "message": "סקור כניסות בסיכון" }, "dataLastUpdated": { "message": "הנתונים עודכנו לאחרונה: $DATE$", @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "הקצה משימות לחברים כדי לנטר התקדמות" }, - "onceYouReviewApps": { - "message": "לאחר שתסקור יישומים ותסמן אותם כקריטיים, באפשרותך להקצות משימות לחברים כדי לפתור פריטים בסיכון ולנטר התקדמות כאן" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "שלח תזכורות" @@ -131,7 +131,7 @@ } }, "criticalApplicationsMarked": { - "message": "critical applications marked" + "message": "יישומים קריטיים סומנו" }, "countOfCriticalApplications": { "message": "$COUNT$ יישומים קריטיים", @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "לא נמצאו יישומים עבור $ORG NAME", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "ייבא את נתוני הכניסה של הארגון שלך כדי להתחיל בניטור סיכוני אבטחה של אישורים. לאחר הייבוא תוכל:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "לתעדף סיכונים" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "התמקדות ביישומים החשובים ביותר" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "להדריך תיקון" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "הקצה משימות מודרכות לחברים בסיכון כדי להחליף אישורים בסיכון" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { - "message": "לנטר התקדמות" + "feature3Title": { + "message": "Monitor progress" }, - "benefit3Description": { - "message": "עקוב אחר שינויים לאורך זמן כדי להציג שיפורי אבטחה" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "הרץ את הדוח הראשון שלך כדי לראות יישומים" + "noReportsRunTitle": { + "message": "Generate report" }, - "noReportRunDescription": { - "message": "ייצר דוח תובנות סיכון כדי לנתח את היישומים של הארגון שלך ולזהות סיסמאות בסיכון שצריכות תשומת לב. הרצת הדוח הראשון שלך תגרום ל:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "לא סימנת אף יישום כקריטי" @@ -242,7 +236,7 @@ "message": "יישומים המסומנים כקריטיים" }, "criticalApplicationsMarkedSuccess": { - "message": "$COUNT$ applications marked as critical", + "message": "$COUNT$ יישומים סומנו כקריטיים", "placeholders": { "count": { "content": "$1", @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "חברים בסיכון" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "חברים עם גישה לפריטים בסיכון עבור יישומים קריטיים" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { - "message": "Members with at-risk passwords" + "message": "חברים עם סיסמאות בסיכון" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ חברים בסיכון", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "חברים אלה נכנסו אל יישומים עם סיסמאות חלשות, חשופות, או משומשות." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "אין חברים שנכנסו אל יישומים עם סיסמאות חלשות, חשופות, או משומשות." @@ -350,7 +344,7 @@ "message": "יישומים צריכים סקירה" }, "newApplicationsCardTitle": { - "message": "Review new applications" + "message": "סקור יישומים חדשים" }, "newApplicationsWithCount": { "message": "$COUNT$ יישומים חדשים", @@ -368,7 +362,7 @@ "message": "סקור עכשיו" }, "allCaughtUp": { - "message": "All caught up!" + "message": "הכל טופל!" }, "noNewApplicationsToReviewAtThisTime": { "message": "No new applications to review at this time" @@ -386,19 +380,19 @@ "message": "Review applications to secure the items most critical to your organization's security" }, "reviewApplications": { - "message": "Review applications" + "message": "סקור יישומים" }, "prioritizeCriticalApplications": { "message": "תעדוף יישומים קריטיים" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { - "message": "Review new applications" + "message": "סקור יישומים חדשים" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -881,7 +875,7 @@ "message": "מועדפים" }, "taskSummary": { - "message": "Task summary" + "message": "סיכום משימה" }, "types": { "message": "סוגים" @@ -3251,16 +3245,16 @@ "message": "החיוב הבא" }, "nextChargeHeader": { - "message": "Next Charge" + "message": "החיוב הבא" }, "plan": { - "message": "Plan" + "message": "תוכנית" }, "details": { "message": "פרטים" }, "discount": { - "message": "discount" + "message": "הנחה" }, "downloadLicense": { "message": "הורד רישיון" @@ -5856,14 +5850,14 @@ "message": "איך להפעיל אישור משתמש אוטומטי" }, "autoConfirmExtension1": { - "message": "Open your Bitwarden extension" + "message": "פתח את הרחבת Bitwarden שלך" }, "autoConfirmExtension2": { - "message": "Select", + "message": "בחר", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtension3": { - "message": " Turn on", + "message": " הפעל", "description": "This is a fragment of a larger sencence. The whole sentence will read: 'Select Turn on'" }, "autoConfirmExtensionOpened": { @@ -5954,13 +5948,13 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "uriMatchDetectionPolicy": { - "message": "Default URI match detection" + "message": "ברירת מחדל לזיהוי התאמת URI" }, "uriMatchDetectionPolicyDesc": { "message": "Determine when logins are suggested for autofill. Admins and owners are exempt from this policy." }, "uriMatchDetectionOptionsLabel": { - "message": "Default URI match detection" + "message": "ברירת מחדל לזיהוי התאמת URI" }, "invalidUriMatchDefaultPolicySetting": { "message": "Please select a valid URI match detection option.", @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "הקצה משימות" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "הקצה לאוספים" @@ -12125,28 +12119,28 @@ "message": "התחל ניסיון משפחות בחינם" }, "unlockMethodNeededToChangeTimeoutActionDesc": { - "message": "Set up an unlock method to change your vault timeout action." + "message": "הגדר שיטת ביטול נעילה כדי לשנות את פעולת פסק הזמן לכספת שלך." }, "vaultTimeoutPolicyAffectingOptions": { - "message": "Enterprise policy requirements have been applied to your timeout options" + "message": "דרישות מדיניות ארגונית הוחלו על אפשרויות פסק הזמן שלך" }, "vaultTimeoutTooLarge": { - "message": "Your vault timeout exceeds the restrictions set by your organization." + "message": "פסק הזמן לכספת שלך חורג מהמגבלות שנקבעו על ידי הארגון שלך." }, "neverLockWarning": { - "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." + "message": "האם אתה בטוח שברצונך להשתמש באפשרות \"אף פעם לא\"? במצב זה הסיסמה לכספת שלך תשמר על המכשיר שלך. אם תשתמש באפשרות זו עליך לוודא כי המכשיר מאובטח כראוי." }, "sessionTimeoutSettingsAction": { - "message": "Timeout action" + "message": "פעולת פסק זמן" }, "sessionTimeoutHeader": { - "message": "Session timeout" + "message": "פסק זמן להפעלה" }, "appearance": { - "message": "Appearance" + "message": "מראה" }, "vaultTimeoutPolicyMaximumError": { - "message": "Timeout exceeds the restriction set by your organization: $HOURS$ hour(s) and $MINUTES$ minute(s) maximum", + "message": "פסק זמן חורג את ההגבלה שהוגדרה על ידי הארגון שלך: $HOURS$ שעות ו־$MINUTES$ דקות לכל היותר", "placeholders": { "hours": { "content": "$1", @@ -12159,7 +12153,7 @@ } }, "confirmNoSelectedCriticalApplicationsTitle": { - "message": "No critical applications are selected" + "message": "לא סומנו יישומים קריטים" }, "confirmNoSelectedCriticalApplicationsDesc": { "message": "Are you sure you want to continue?" diff --git a/apps/web/src/locales/hi/messages.json b/apps/web/src/locales/hi/messages.json index 01cf40c3c0d..766c6c3e8bd 100644 --- a/apps/web/src/locales/hi/messages.json +++ b/apps/web/src/locales/hi/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/hr/messages.json b/apps/web/src/locales/hr/messages.json index e0f0ce0b28b..a0dbd290ab3 100644 --- a/apps/web/src/locales/hr/messages.json +++ b/apps/web/src/locales/hr/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Dodijeli članovima zadatke za praćenje napretka" }, - "onceYouReviewApps": { - "message": "Nakon što pregledaš prijave i označiš kritične, možeš dodijeliti zadatke članovima za rješavanje rizičnih stavki i ovdje pratiti napredak" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Pošalji podsjetnike" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "Nisu nađene aplikacije u $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Uvezi podatke za prijavu svoje organizacije za početak praćenja sigurnosnih rizika vjerodajnica. Nakon uvoza možeš:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritizirajte rizike" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Usredotoči se na najvažnije aplikacije" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Vodič za sanaciju" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Dodijeli vođene zadatke rizičnim članovima za rotaciju rizičnih vjerodajnica" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { - "message": "Prati napredak" + "feature3Title": { + "message": "Monitor progress" }, - "benefit3Description": { - "message": "Prati promjene tijekom vremena za prikaz sigurnosnih poboljšanja" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Za pregled aplikacija, pokreni svoje prvo izvješće" + "noReportsRunTitle": { + "message": "Generate report" }, - "noReportRunDescription": { - "message": "Generiraj izvješće o uvidima u rizike za analizu aplikacije svoje organizacije i identifikaciju lozinki koje su u riziku i kojima je potrebna pozornost. Pokretanje tvojeg prvog izvješća će:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "Niti jedna aplikacija nije označena kao kritična" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Rizični korisnici" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Članovi koji imaju pristup stavkama za aplikacije označene kao kritične" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Članovi s rizičnim lozinkama" }, - "membersWillReceiveNotification": { - "message": "Članovi će dobiti obavijest o rješavanju rizičnih prijava putem proširenja preglednika." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "Rizičnih članova: $COUNT$", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Ovi članovi se prijavljuju u aplikacije slabim, izloženim ili ponovno korištenim lozinkama." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "Nema članova koji se prijavljuju slabim, izloženim ili ponovno korištenim lozinkama." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Daj prioritet kritičnim aplikacijama" }, - "selectCriticalApplicationsDescription": { - "message": "Odaberi koje su aplikacije najvažnije za tvoju organizaciju, a zatim dodijeli sigurnosne zadatke članovima kako bi se riješili rizici." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Pregledaj nove prijave" }, - "reviewNewApplicationsDescription": { - "message": "Istaknuli smo rizične stavke za nove aplikacije pohranjene u administratorskoj konzoli koje imaju slabe, otkrivene ili ponovno korištene lozinke." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Klikni ikonu zvjezdice za označavanje aplikacije kao kritične" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Dodijeli zadatke" }, - "assignTasksToMembers": { - "message": "Dodijeli zadatke članovima za vođeno rješavanje" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Dodijeli zbirkama" diff --git a/apps/web/src/locales/hu/messages.json b/apps/web/src/locales/hu/messages.json index d9bbd09b0f6..df292a29c02 100644 --- a/apps/web/src/locales/hu/messages.json +++ b/apps/web/src/locales/hu/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Rendeljünk a tagokhoz feladatokat az előrehaladás monitorozására." }, - "onceYouReviewApps": { - "message": "Miután az alkalmazásokat áttekintettük és kritikusként jelöltük meg azokat, feladatokat rendelhetünk a tagokhoz a kockázatos elemek megoldására és itt nyomon követhetjük a feldolgozást." + "onceYouReviewApplications": { + "message": "Az alkalmazások áttekintése és azok kritikusként megjelölése után rendeljünk feladatokat a tagoknak a jelszavak megváltoztatásához." }, "sendReminders": { "message": "Emlékeztető küldés" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "Nem találhatók alkalmazások: $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "Nem található adat." }, - "noApplicationsInOrgDescription": { - "message": "A szervezet bejelentkezési adatainak importálása a hitelesítő adatok biztonsági kockázat figyelésének megkezdéséhez. Az importálás után a következőkhöz jutunk el:" + "noDataInOrgDescription": { + "message": "Importáljuk a szervezet bejelentkezési adatait az Access Intelligence használatának megkezdéséhez. Ezután a következőket tehetjük:" }, - "benefit1Title": { - "message": "Kockázatok rangsorolása" + "feature1Title": { + "message": "Alkalmazások megjelölése kritikusként" }, - "benefit1Description": { - "message": "Összpontosítás a legfontosabb alkalmazásokra" + "feature1Description": { + "message": "Ez először is segít eltávolítani a legfontosabb alkalmazások kockázatait." }, - "benefit2Title": { - "message": "Kármentési útmutató" + "feature2Title": { + "message": "Segítsünk a tagoknak biztonságuk javításában." }, - "benefit2Description": { - "message": "Irányított feladatok hozzárendelése a veszélyeztetett tagokhoz a veszélyeztetett jogosultságok rotálásához." + "feature2Description": { + "message": "Rendeljünk a veszélyeztetett tagoknak irányított biztonsági feladatokat a hitelesítő adatok frissítéséhez." }, - "benefit3Title": { - "message": "Folyamatok nyomonkövetése" + "feature3Title": { + "message": "Folyamat nyomonkövetése" }, - "benefit3Description": { - "message": "Kövessük az idő múlásával bekövetkező változásokat a biztonsági fejlesztések megjelenítéséhez." + "feature3Description": { + "message": "Kövessük nyomon az időbeli változásokat a biztonsági fejlesztések megjelenítéséhez." }, - "noReportRunTitle": { - "message": "Futtassuk le az első jelentést az alkalmazások megtekintéséhez" + "noReportsRunTitle": { + "message": "Jelentés generálása" }, - "noReportRunDescription": { - "message": "Készítsünk kockázati betekintés jelentést a szervezet alkalmazásainak elemzéséhez és azonosítsuk azokat a veszélyeztetett jelszavakat, amelyekre figyelmet kell fordítani. Az első jelentés futtatása:" + "noReportsRunDescription": { + "message": "Készen állunk a jelentések generálására. A generálás után a következőket teheti meg:" }, "noCriticalApplicationsTitle": { "message": "Egyetlen alkalmazás sem lett megjelölve kritikusként." @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Veszélyes tagok" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "Ezek a tagok hozzáférhetnek a kritikus alkalmazásoknál a sebezhető elemekhez." }, "membersWithAtRiskPasswords": { "message": "Kockázatos jelszavú tagok" }, - "membersWillReceiveNotification": { - "message": "A tagok értesítést kapnak a kockázatos bejelentkezések megoldásáról a böngésző bővítményen keresztül." + "membersWillReceiveSecurityTask": { + "message": "A szervezet tagjaira feladat hárul a sebezhető jelszavak megváltoztatására. Értesítést kapnak a Bitwarden böngésző bővítményen belül." }, "membersAtRiskCount": { "message": "$COUNT$ kockázatos tag", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Ezek a tagok gyenge, nyílt vagy újrafelhasznált jelszavakkal jelentkeznek be az alkalmazásokba." + "atRiskMemberDescription": { + "message": "Ezek a tagok gyenge, kitett vagy újrafelhasznált jelszavakkal jelentkeznek be az alkalmazásokba." }, "atRiskMembersDescriptionNone": { "message": "Ezek nem olyan tagok, akik gyenge, kitett vagy újrafelhasznált jelszavakkal jelentkeznek be az alkalmazásokba." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Kritikus alkalmazások rangsorolása" }, - "selectCriticalApplicationsDescription": { - "message": "Válasszuk ki, hogy mely alkalmazások a legkritikusabbak a szervezet számára, majd rendeljünk biztonsági feladatokat a tagokhoz a kockázatok megoldása érdekében." + "selectCriticalAppsDescription": { + "message": "Válasszuk ki, hogy mely alkalmazások a legkritikusabbak a szervezet számára. Ezután rendeljünk biztonsági feladatokat a tagokhoz a kockázatok eltávolítása érdekében." }, "reviewNewApplications": { "message": "Új alkalmazások felülvizsgálata" }, - "reviewNewApplicationsDescription": { - "message": "Kiemelésre került az Admin konzolban tárolt új alkalmazások veszélyeztetett elemei, amelyek gyenge, kitett vagy újrafelhasznált jelszavakkal rendelkeznek." + "reviewNewAppsDescription": { + "message": "Tekintsük át a sebezhető elemeket tartalmazó új alkalmazásokat és jelöljük meg kritikusnak azokat, amelyeket szorosan figyelemmel szeretnénk kísérni. Ezután biztonsági feladatokat rendelhetünk a tagokhoz a kockázatok eltávolítása érdekében." }, "clickIconToMarkAppAsCritical": { "message": "Kattintsunk a csillag ikonra egy alkalmazás kritikusként megjelöléséhez." @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Feladatok hozzárendelése" }, - "assignTasksToMembers": { - "message": "Feladatok hozzárendelése a tagokhoz irányított megoldáshoz." + "assignSecurityTasksToMembers": { + "message": "Értesítések küldése a jelszavak megváltoztatásához" }, "assignToCollections": { "message": "Hozzárendelés gyűjteményekhez" diff --git a/apps/web/src/locales/id/messages.json b/apps/web/src/locales/id/messages.json index ad9d2405adb..388ca3ba9cf 100644 --- a/apps/web/src/locales/id/messages.json +++ b/apps/web/src/locales/id/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Tetapkan tugas ke anggota untuk memantau progres" }, - "onceYouReviewApps": { - "message": "Setelah meninjau aplikasi dan menandainya sebagai penting, Anda dapat menetapkan tugas kepada anggota untuk menyelesaikan item berisiko dan memantau kemajuan di sini" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Kirim pengingat" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "Tidak ada aplikasi ditemukan untuk $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Impor data login organisasi Anda untuk mulai memantau risiko keamanan kredensial. Setelah diimpor, Anda akan mendapatkan:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritaskan risiko" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Fokus pada aplikasi yang paling penting" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { - "message": "Memantau kemajuan" + "feature3Title": { + "message": "Monitor progress" }, - "benefit3Description": { - "message": "Lacak perubahan dari waktu ke waktu untuk menunjukkan peningkatan keamanan" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Jalankan laporan pertama Anda untuk melihat aplikasi" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "Anda belum menandai aplikasi apa pun sebagai penting" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Anggota berisiko" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Anggota dengan akses ke item berisiko untuk apl penting" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ anggota berisiko", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Anggota ini masuk ke aplikasi dengan sandi yang lemah, terekspos, atau sama." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "Anggota ini masuk ke aplikasi dengan sandi yang lemah, terekspos, atau sama." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritaskan aplikasi penting" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/it/messages.json b/apps/web/src/locales/it/messages.json index fa3146996d7..b60e8f4584a 100644 --- a/apps/web/src/locales/it/messages.json +++ b/apps/web/src/locales/it/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assegna compiti ai membri per monitorarne i progressi" }, - "onceYouReviewApps": { - "message": "Dopo aver esaminato le applicazioni e contrassegnato quelle critiche, puoi assegnare attività ai membri per risolvere gli elementi a rischio e monitorare qui lo stato di avanzamento" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Invia promemoria" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "Nessuna applicazione trovata per $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Importa i dati di accesso della tua organizzazione per iniziare a monitorare i rischi per la sicurezza delle credenziali. Una volta importati, potrai:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Priorità dei rischi" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Concentrati sulle applicazioni più importanti" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "Non hai contrassegnato nessuna applicazione come critica" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Membri a rischio" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Membri con accesso agli elementi a rischio per applicazioni critiche" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ membri a rischio", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Questi membri accedono ad applicazioni con password deboli, esposte, o riutilizzate." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "Non ci sono utenti connessi con password deboli, esposte o riutilizzate." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Priorità alle applicazioni critiche" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assegna attività" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assegna alle raccolte" diff --git a/apps/web/src/locales/ja/messages.json b/apps/web/src/locales/ja/messages.json index a70b821a1fc..eed4085320d 100644 --- a/apps/web/src/locales/ja/messages.json +++ b/apps/web/src/locales/ja/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "リスクがあるメンバー" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "これらのメンバーは、脆弱な、または流出したか再利用されたパスワードでアプリにログインしています。" + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "コレクションに割り当てる" diff --git a/apps/web/src/locales/ka/messages.json b/apps/web/src/locales/ka/messages.json index 973dcc2951b..b64d882bb03 100644 --- a/apps/web/src/locales/ka/messages.json +++ b/apps/web/src/locales/ka/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/km/messages.json b/apps/web/src/locales/km/messages.json index 47df4826851..9f57ed8c6ca 100644 --- a/apps/web/src/locales/km/messages.json +++ b/apps/web/src/locales/km/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/kn/messages.json b/apps/web/src/locales/kn/messages.json index 17aaa51f261..41017933852 100644 --- a/apps/web/src/locales/kn/messages.json +++ b/apps/web/src/locales/kn/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/ko/messages.json b/apps/web/src/locales/ko/messages.json index f831e76b1eb..971a2ee59ae 100644 --- a/apps/web/src/locales/ko/messages.json +++ b/apps/web/src/locales/ko/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/lv/messages.json b/apps/web/src/locales/lv/messages.json index b35e704eb21..ba978730477 100644 --- a/apps/web/src/locales/lv/messages.json +++ b/apps/web/src/locales/lv/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Piešķirt dalībniekiem uzdevumus, lai pārraudzītu virzību" }, - "onceYouReviewApps": { - "message": "Tiklīdz lietotnes būs pārskatītas un atzīmētas kā būtiskas, dalībniekiem varēs piešķirt uzdevumus atrisināt riskam pakļautos vienumus un šeit pārskatīt virzību" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Nosūtīt atgādinājumus" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "$ORG NAME$ netika atrasta neviena lietotne", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Ievieto savas apvienības pieteikšanās datus, lai uzsāktu to drošības risku uzraudzīšanu. Tiklīdz dati ir ievietoti, Tu vari:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Noteikt risku svarīgumu" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Pievērst uzmanību visbūtiskākajām lietotnēm" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Virzīt nepilnību novēršanu" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Piešķirt riskam pakļautajiem dalībniekiem uzdevumus riskam pakļauto pieteikšanās datu nomaiņai" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { - "message": "Pārraudzīt virzību" + "feature3Title": { + "message": "Monitor progress" }, - "benefit3Description": { - "message": "Sekot izmaiņām, lai uzrādītu drošības uzlabojumus" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Izveidot savu pirmo atskaiti, lai redzētu lietotnes" + "noReportsRunTitle": { + "message": "Generate report" }, - "noReportRunDescription": { - "message": "Izveidot riska ieskatu atskaiti, lai izvērtētu savas apvienības lietotnes un noteiktu riskam pakļautas paroles, kurēm jāpievērš uzmanība. Pirmās atskaites izveidošana:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "Neviena lietotne nav atzīmēta kā kritiska" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Riskam pakļautie dalībnieki" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Dalībnieki ar piekluvi riskam pakļautajiem vienumiem būtiskajām lietotnēm" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Dalībnieki ar riskam pakļautām parolēm" }, - "membersWillReceiveNotification": { - "message": "Dalībnieki pārlūka paplašinājumā saņems paziņojumu, ka jānomaina riskam pakļautās paroles." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ dalībnieki ir pakļauti riskam", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Šie dalībnieki piesakās lietotnēs ar vājām, atklātām vai atkārtoti izmantotām parolēm." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "Nav dalībnieku, kas piesakās lietotnēs ar vājām, atklātām vai atkārtoti izmantotām parolēm." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Paaugstināt būtisko lietotņu svarīgumu" }, - "selectCriticalApplicationsDescription": { - "message": "Jāatlasa, kuras lietontes apvienībai ir visbūtiskākās, tad jāpiešķir drošības uzdevumi dalībniekiem risku novēršanai." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Pārskatīt jaunās lietotnes" }, - "reviewNewApplicationsDescription": { - "message": "Mēs izcēlām riskam pakļautos vienumus jaunām lietotnēm, kas tiek glabātas pārvaldības konsolē un kurās ir vājas, atklātas vai atkārtoti izmantotas paroles." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Jāklikšķina uz zvaigznes, lai atzīmētu lietotni kā būtisku" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Piešķirt uzdevumus" }, - "assignTasksToMembers": { - "message": "Piešķirt uzdevumus dalībniekiem risinājumam ar norādēm" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Piešķirt krājumiem" diff --git a/apps/web/src/locales/ml/messages.json b/apps/web/src/locales/ml/messages.json index 624121be5d9..14f5b921ce7 100644 --- a/apps/web/src/locales/ml/messages.json +++ b/apps/web/src/locales/ml/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/mr/messages.json b/apps/web/src/locales/mr/messages.json index 612ff8b8765..a7a1e405ed7 100644 --- a/apps/web/src/locales/mr/messages.json +++ b/apps/web/src/locales/mr/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/my/messages.json b/apps/web/src/locales/my/messages.json index 47df4826851..9f57ed8c6ca 100644 --- a/apps/web/src/locales/my/messages.json +++ b/apps/web/src/locales/my/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/nb/messages.json b/apps/web/src/locales/nb/messages.json index b2fb413be8c..345d875875c 100644 --- a/apps/web/src/locales/nb/messages.json +++ b/apps/web/src/locales/nb/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Legg til i samlinger" diff --git a/apps/web/src/locales/ne/messages.json b/apps/web/src/locales/ne/messages.json index 8bd88ef4a57..8387c568e94 100644 --- a/apps/web/src/locales/ne/messages.json +++ b/apps/web/src/locales/ne/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/nl/messages.json b/apps/web/src/locales/nl/messages.json index 8ac8cf24ca5..35d61fc7ce3 100644 --- a/apps/web/src/locales/nl/messages.json +++ b/apps/web/src/locales/nl/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Leden taken toewijzen om de voortgang te controleren" }, - "onceYouReviewApps": { - "message": "Zodra je applicaties beoordeelt en ze als kritiek markeert, kun je leden taken toewijzen om risico-items op te lossen en de voortgang hier te controleren" + "onceYouReviewApplications": { + "message": "Zodra je applicaties beoordeelt en als belangrijk markeert, wijs je taken toe aan je leden om hun wachtwoorden te wijzigen." }, "sendReminders": { "message": "Herinneringen versturen" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "Geen gegevens gevonden" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Importeer de inloggegevens van je organisatie om aan de slag te gaan met Access Intelligence. Zodra je dat doet, kun je:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Applicaties als belangrijk markeren" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "Hiermee kun je eerst risico's voor je belangrijkste applicaties weghalen." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help leden hun beveiliging te verbeteren" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Risicovolle leden begeleide beveiligingstaken toewijzien om inloggegevens bij te werken." }, - "benefit3Title": { - "message": "Monitor progress" + "feature3Title": { + "message": "Voortgang monitoren" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Bijhouden van wijzigingen over tijd voor het weergeven van beveiligingsverbeteringen." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Rapport genereren" }, - "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:" + "noReportsRunDescription": { + "message": "Je bent klaar om rapporten te genereren. Zodra je rapporten maakt, kun je:" }, "noCriticalApplicationsTitle": { "message": "Je hebt nog geen applicaties als belangrijk aangewezen" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Leden in gevaar" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Leden met toegang tot risico-items voor belangrijke applicaties" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "Deze leden hebben toegang tot kwetsbare items voor belangrijke applicaties." }, "membersWithAtRiskPasswords": { "message": "Leden met risicovolle wachtwoorden" }, - "membersWillReceiveNotification": { - "message": "Leden ontvangen een melding via de browserextensie om risico-logins op te lossen." + "membersWillReceiveSecurityTask": { + "message": "Leden van jew organisatie krijgen een taak om kwetsbare wachtwoorden te wijzigen. Ze ontvangen een melding binnen hun Bitwarden-browserextensie." }, "membersAtRiskCount": { "message": "$COUNT$ leden lopen risico", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Deze leden loggen in op toepassingen met zwakke, blootgestelde of hergebruikte wachtwoorden." + "atRiskMemberDescription": { + "message": "Deze leden loggen in op applicaties met zwakke, blootgestelde of hergebruikte wachtwoorden." }, "atRiskMembersDescriptionNone": { "message": "Deze niet-leden loggen in op toepassingen met zwakke, blootgestelde of hergebruikte wachtwoorden." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Belangrijke applicaties prioriteren" }, - "selectCriticalApplicationsDescription": { - "message": "Selecteer welke toepassingen het meest belangrijk zijn voor je organisatie en wijs de leden dan beveiligingstaken toe om risico's op te lossen." + "selectCriticalAppsDescription": { + "message": "Selecteer welke toepassingen het meest belangrijk zijn voor je organisatie. Vervolgens kun je leden beveiligingstaken toewijzen om risico's weg te halen." }, "reviewNewApplications": { "message": "Nieuwe toepassingen beoordelen" }, - "reviewNewApplicationsDescription": { - "message": "We hebben items met risico gemarkeerd voor nieuwe toepassingen die zijn opgeslagen in Admin-console die zwakke, onthulde of hergebruikte wachtwoorden hebben." + "reviewNewAppsDescription": { + "message": "Bekijk nieuwe applicaties met kwetsbare items en markeer degenen die je wilt monitoren als belangrijk. Vervolgens kun je beveiligingstaken toewijzen aan leden om risico's weg te halen." }, "clickIconToMarkAppAsCritical": { "message": "Klik op het sterpictogram om een app als belangrijk te markeren" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Taken toewijzen" }, - "assignTasksToMembers": { - "message": "Taken aan leden toewijzen voor een begeleide oplossing" + "assignSecurityTasksToMembers": { + "message": "Meldingen verzenden om wachtwoorden te wijzigen" }, "assignToCollections": { "message": "Toewijzen aan collecties" diff --git a/apps/web/src/locales/nn/messages.json b/apps/web/src/locales/nn/messages.json index b6ec66bf695..4979a3eb72a 100644 --- a/apps/web/src/locales/nn/messages.json +++ b/apps/web/src/locales/nn/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/or/messages.json b/apps/web/src/locales/or/messages.json index 47df4826851..9f57ed8c6ca 100644 --- a/apps/web/src/locales/or/messages.json +++ b/apps/web/src/locales/or/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/pl/messages.json b/apps/web/src/locales/pl/messages.json index 9750a2a7df7..48f25c451ae 100644 --- a/apps/web/src/locales/pl/messages.json +++ b/apps/web/src/locales/pl/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Zagrożeni użytkownicy" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Ci członkowie logują się do aplikacji ze słabymi, ujawnionymi lub ponownie używanymi hasłami." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "Ci członkowie nie logują się do aplikacji ze słabymi, ujawnionymi lub ponownie używanymi hasłami." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Przypisz do kolekcji" diff --git a/apps/web/src/locales/pt_BR/messages.json b/apps/web/src/locales/pt_BR/messages.json index 4ef989dcd75..4520e190b53 100644 --- a/apps/web/src/locales/pt_BR/messages.json +++ b/apps/web/src/locales/pt_BR/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Atribuir tarefas a membros para monitorar progresso" }, - "onceYouReviewApps": { - "message": "Ao revisar aplicativos e marcá-los como críticos, você pode atribuir traferas a membros para resolver itens em risco e monitorar o progresso aqui" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Enviar lembretes" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "Você não marcou nenhum aplicativo como crítico" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Membros em risco" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Membros com acesso a itens em risco de aplicativos críticos" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ membros em risco", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Esses membros estão conectando-se em aplicativos com senhas fracas, expostas, ou reutilizadas." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "Não há nenhum membro se conectando em aplicativos com senhas fracas, expostas, ou reutilizadas." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Priorizar aplicativos críticos" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Atribuir à coleções" diff --git a/apps/web/src/locales/pt_PT/messages.json b/apps/web/src/locales/pt_PT/messages.json index 45b511a857f..4831f14d694 100644 --- a/apps/web/src/locales/pt_PT/messages.json +++ b/apps/web/src/locales/pt_PT/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Atribuir tarefas aos membros para monitorizar o progresso" }, - "onceYouReviewApps": { - "message": "Depois de analisar as aplicações e marcá-las como críticas, pode atribuir tarefas aos membros para resolver itens em risco e monitorizar o progresso aqui" + "onceYouReviewApplications": { + "message": "Depois de rever as aplicações e marcá-las como críticas, atribua tarefas aos seus membros para que alterem as respetivas palavras-passe." }, "sendReminders": { "message": "Enviar lembretes" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "Não foram encontradas aplicações para $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "Não foram encontrados dados" }, - "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á:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Priorizar os riscos" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Com foco nas aplicações mais importantes" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Remediação de guias" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Atribuir tarefas orientadas aos membros em risco para alterar as credenciais em risco" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { - "message": "Monitorizar o progresso" + "feature3Title": { + "message": "Monitor progress" }, - "benefit3Description": { - "message": "Acompanhe as alterações ao longo do tempo para mostrar as melhorias na segurança" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Execute o seu primeiro relatório para ver as aplicações" + "noReportsRunTitle": { + "message": "Generate report" }, - "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á:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "Não marcou nenhuma aplicação como crítica" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Membros em risco" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Membros com acesso a itens em risco para aplicações críticas" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Membros com palavras-passe em risco" }, - "membersWillReceiveNotification": { - "message": "Os membros receberão uma notificação para resolver as credenciais em risco através da extensão do navegador." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ membros em risco", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Estes membros estão a iniciar sessão em aplicações com palavras-passe fracas, expostas ou reutilizadas." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "Estes não são membros que iniciam sessão em aplicações com palavras-passe fracas, expostas ou reutilizadas." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Dê prioridade a aplicações críticas" }, - "selectCriticalApplicationsDescription": { - "message": "Selecione quais aplicações são mais críticas para a sua organização e, em seguida, atribua tarefas de segurança aos membros para resolver os riscos." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Rever novas aplicações" }, - "reviewNewApplicationsDescription": { - "message": "Destacámos os itens em risco para novas aplicações armazenadas na consola de administração que têm palavras-passe fracas, expostas ou reutilizadas." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Clique no ícone de estrela para marcar uma app como crítica" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Atribuir tarefas" }, - "assignTasksToMembers": { - "message": "Atribuir tarefas aos membros para resolução guiada" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Atribuir às coleções" @@ -12083,7 +12077,7 @@ "message": "Tem o plano gratuito do Bitwarden" }, "upgradeCompleteSecurity": { - "message": "Atualize para segurança total" + "message": "Atualize para obter segurança total" }, "viewbusinessplans": { "message": "Ver planos empresariais" diff --git a/apps/web/src/locales/ro/messages.json b/apps/web/src/locales/ro/messages.json index e683e0a5f10..0c3eaa3ea08 100644 --- a/apps/web/src/locales/ro/messages.json +++ b/apps/web/src/locales/ro/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/ru/messages.json b/apps/web/src/locales/ru/messages.json index 9b5a9399491..205e953c0ed 100644 --- a/apps/web/src/locales/ru/messages.json +++ b/apps/web/src/locales/ru/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Назначайте членам команды задачи по мониторингу прогресса" }, - "onceYouReviewApps": { - "message": "Как только вы оцените приложения и пометите их как критичные, вы можете назначить задачи участникам касающиеся элементов, подверженных риску, и следить за прогрессом здесь" + "onceYouReviewApplications": { + "message": "После того как вы просмотрите приложения и отметите их как критичные, назначьте своим пользователям задачи по смене паролей." }, "sendReminders": { "message": "Отправить напоминания" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "Не найдено приложений для $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "Данные не найдены" }, - "noApplicationsInOrgDescription": { - "message": "Импортируйте логины вашей организации, чтобы начать мониторинг угроз безопасности учетных данных. После импорта вы получите доступ к:" + "noDataInOrgDescription": { + "message": "Импортируйте данные логинов вашей организации, чтобы начать работу с Access Intelligence. Как только вы это сделаете, вы сможете:" }, - "benefit1Title": { - "message": "Приоритет рисков" + "feature1Title": { + "message": "Пометить приложения как критичные" }, - "benefit1Description": { - "message": "Сосредоточьтесь на наиболее важных приложениях" + "feature1Description": { + "message": "Это поможет вам в первую очередь устранить риски для ваших наиболее важных приложений." }, - "benefit2Title": { - "message": "Руководство по восстановлению" + "feature2Title": { + "message": "Помогите пользователями повысить безопасность" }, - "benefit2Description": { - "message": "Назначайте участникам, подверженным риску, управляемые задания для ротации учетных данных" + "feature2Description": { + "message": "Назначайте участникам, находящимся в группе риска, управляемые задачи по обеспечению безопасности для обновления учетных данных." }, - "benefit3Title": { + "feature3Title": { "message": "Отслеживать прогресс" }, - "benefit3Description": { - "message": "Отслеживать изменения с течением времени, чтобы показать улучшения безопасности" + "feature3Description": { + "message": "Отслеживать изменения с течением времени, чтобы показать улучшения безопасности." }, - "noReportRunTitle": { - "message": "Запустите ваш первый отчет для просмотра приложений" + "noReportsRunTitle": { + "message": "Создать отчет" }, - "noReportRunDescription": { - "message": "Создайте аналитический отчет о рисках для анализа приложений вашей организации и выявления паролей, подверженных риску, которые требуют внимания. Запуск первого отчета позволит:" + "noReportsRunDescription": { + "message": "Вы готовы приступить к созданию отчетов. После создания вы сможете:" }, "noCriticalApplicationsTitle": { "message": "Вы не отметили ни одного приложения в качестве критичного" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Участники, подверженные риску" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Участники, имеющие доступ к элементам, подверженным риску, для критичных приложений" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "Эти пользователи имеют доступ к уязвимым элементам критичных приложений." }, "membersWithAtRiskPasswords": { "message": "Пользователи, пароли которых подверженны риску" }, - "membersWillReceiveNotification": { - "message": "Пользователи получат уведомление о разрешении подверженных риску логинов с помощью расширения браузера." + "membersWillReceiveSecurityTask": { + "message": "Сотрудникам вашей организации будет поручено изменить уязвимые пароли. Они получат уведомление в своем расширении Bitwarden для браузера." }, "membersAtRiskCount": { "message": "$COUNT$ участников, подверженных риску", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Эти пользователи входят в приложения со слабыми, скомпрометированными или повторно используемыми паролями." + "atRiskMemberDescription": { + "message": "Эти пользователи входят в критичные приложения со слабыми, скомпрометированными или повторно используемыми паролями." }, "atRiskMembersDescriptionNone": { "message": "Нет пользователей, входящих в приложения со слабыми, скомпрометированными или повторно используемыми паролями." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Приоритет критичных приложений" }, - "selectCriticalApplicationsDescription": { - "message": "Выберите, какие приложения наиболее критичны для вашей организации, а затем назначьте пользователям задачи по обеспечению безопасности для устранения рисков." + "selectCriticalAppsDescription": { + "message": "Выберите, какие приложения наиболее критичны для вашей организации. Затем вы сможете назначить пользователям задачи по обеспечению безопасности по устранению рисков." }, "reviewNewApplications": { "message": "Обзор новых приложений" }, - "reviewNewApplicationsDescription": { - "message": "Мы выделили элементы, подверженные риску, для новых приложений, хранящихся в консоли администратора, которые имеют слабые, незащищенные или повторно используемые пароли." + "reviewNewAppsDescription": { + "message": "Просмотрите новые приложения, содержащие уязвимые элементы, и отметьте те, за которыми вы хотели бы внимательно следить, как критичные. После этого вы сможете назначать пользователям задачи по обеспечению безопасности для устранения рисков." }, "clickIconToMarkAppAsCritical": { "message": "Нажмите на значок звездочки, чтобы отметить приложение как критичное" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Назначить задачи" }, - "assignTasksToMembers": { - "message": "Назначайте задачи пользователям для управляемого решения" + "assignSecurityTasksToMembers": { + "message": "Отправляйте уведомления о смене паролей" }, "assignToCollections": { "message": "Назначить коллекциям" diff --git a/apps/web/src/locales/si/messages.json b/apps/web/src/locales/si/messages.json index 0fdc35fb8dc..aeb1a4b4e0e 100644 --- a/apps/web/src/locales/si/messages.json +++ b/apps/web/src/locales/si/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/sk/messages.json b/apps/web/src/locales/sk/messages.json index fdcaa412db9..30afb3afcc2 100644 --- a/apps/web/src/locales/sk/messages.json +++ b/apps/web/src/locales/sk/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Pre sledovanie progresu, priraďte členom úlohy" }, - "onceYouReviewApps": { - "message": "Keď skontrolujete a označíte kritické aplikácie, môžete na tomto mieste prideliť členom úlohy pre ohrozené položky a sledovať progres" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Poslať upomienky" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "Neboli nájdené žiadne aplikácie v $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Pre začatie monitorovania bezpečnostných problémov importujte prihlasovacie údaje vašej organizácie. Po importe budete mať k dispozícii:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritizácia bezpečnostných problémov" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Zamerajte sa na najdôležitejšie aplikácie" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Sprievodca nápravou" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Prideľte ohrozeným členom úlohy pre obnovu ohrozených prihlasovacích údajov" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { - "message": "Monitorovanie progresu" + "feature3Title": { + "message": "Monitor progress" }, - "benefit3Description": { - "message": "Zobrazte zlepšenie zabezpečenia sledovaním zmien v priebehu času" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Pre prehľad aplikácií generujte váš prvý report" + "noReportsRunTitle": { + "message": "Generate report" }, - "noReportRunDescription": { - "message": "Generujte prehľad rizík pre analýzu aplikácií vo vašej organizácii a identifikujte ohrozene heslá ktoré vyžadujú vašu pozornosť. Spustením vášho prvého reportu:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "Neoznačili ste žiadne aplikácie ako kritické" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Ohrozených členov" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Členovia s prístupom k ohrozeným položkám kritických aplikácii" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Členovia s ohrozenými heslami" }, - "membersWillReceiveNotification": { - "message": "Členovia dostanú notifikáciu aby vyriešili problémy s ohrozeným heslom prostredníctvom rozšírenia pre prehliadače." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ ohrozených členov", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Títo členovia sa prihlasujú do aplikácií so slabým, uniknutým alebo viacnásobne použitým heslom." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -368,13 +362,13 @@ "message": "Skontrolovať teraz" }, "allCaughtUp": { - "message": "All caught up!" + "message": "Všetko hotové!" }, "noNewApplicationsToReviewAtThisTime": { - "message": "No new applications to review at this time" + "message": "Žiadne nové aplikácie na kontrolu" }, "organizationHasItemsSavedForApplications": { - "message": "Your organization has items saved for $COUNT$ applications", + "message": "Vaša organizácia ma uložené položky pre $COUNT$ aplikácii", "placeholders": { "count": { "content": "$1", @@ -383,22 +377,22 @@ } }, "reviewApplicationsToSecureItems": { - "message": "Review applications to secure the items most critical to your organization's security" + "message": "Pre zabezpečenie položiek najkritickejších pre bezpečnosť vašej organizácie, skontrolujte aplikácie" }, "reviewApplications": { - "message": "Review applications" + "message": "Skontrolovať aplikácie" }, "prioritizeCriticalApplications": { "message": "Uprednostniť kritické aplikácie" }, - "selectCriticalApplicationsDescription": { - "message": "Vyberte ktoré aplikácie sú pre vašu organizáciu najkritickejšie, potom prideľte členom bezpečnostné úlohy pre vyriešenie ohrozených hesiel." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Aplikáciu označíte za kritickú kliknutím na ikonu hviezdičky" @@ -3251,16 +3245,16 @@ "message": "Ďalšia platba" }, "nextChargeHeader": { - "message": "Next Charge" + "message": "Ďalšia platba" }, "plan": { - "message": "Plan" + "message": "Plán" }, "details": { "message": "Podrobnosti" }, "discount": { - "message": "discount" + "message": "zľava" }, "downloadLicense": { "message": "Stiahnuť licenciu" @@ -5954,16 +5948,16 @@ "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." }, "uriMatchDetectionPolicy": { - "message": "Default URI match detection" + "message": "Predvolené zisťovanie zhody URI" }, "uriMatchDetectionPolicyDesc": { - "message": "Determine when logins are suggested for autofill. Admins and owners are exempt from this policy." + "message": "Určite kedy budú ponúknuté položky pre automatické vypĺňanie. Správcovia a majitelia majú pre toto pravidlo výnimku." }, "uriMatchDetectionOptionsLabel": { - "message": "Default URI match detection" + "message": "Predvolené zisťovanie zhody URI" }, "invalidUriMatchDefaultPolicySetting": { - "message": "Please select a valid URI match detection option.", + "message": "Prosím vyberte platnú predvoľbu zisťovania zhody URI.", "description": "Error message displayed when a user attempts to save URI match detection policy settings with an invalid selection." }, "modifiedPolicyId": { @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Priradiť úlohy" }, - "assignTasksToMembers": { - "message": "Pre riadené riešenie problémov prideľte úlohy členom" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Prideliť k zbierkam" @@ -12159,9 +12153,9 @@ } }, "confirmNoSelectedCriticalApplicationsTitle": { - "message": "No critical applications are selected" + "message": "Nie sú vybrané žiadne kritické aplikácie" }, "confirmNoSelectedCriticalApplicationsDesc": { - "message": "Are you sure you want to continue?" + "message": "Ste si istí, že chcete pokračovať?" } } diff --git a/apps/web/src/locales/sl/messages.json b/apps/web/src/locales/sl/messages.json index 709baa2e36c..2f14c42dd95 100644 --- a/apps/web/src/locales/sl/messages.json +++ b/apps/web/src/locales/sl/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/sr_CS/messages.json b/apps/web/src/locales/sr_CS/messages.json index 876669827c8..6201f3a7e4e 100644 --- a/apps/web/src/locales/sr_CS/messages.json +++ b/apps/web/src/locales/sr_CS/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/sr_CY/messages.json b/apps/web/src/locales/sr_CY/messages.json index dd41032a031..1d0ae7b0897 100644 --- a/apps/web/src/locales/sr_CY/messages.json +++ b/apps/web/src/locales/sr_CY/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Доделите задатке чланова да прате напредак" }, - "onceYouReviewApps": { - "message": "Када прегледате апликације и означите их као критичне, можете доделити задатке члановима за решавање ризичних ставки и пратити напредак овде" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Пошаљи подсетнике" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "Није пронађена ниједна апликација за $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Увезите податке за пријаву своје организације да бисте почели да надгледате безбедносне ризике акредитива. Када увезете, добијате:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Одредите приоритете ризика" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Фокусирајте се на апликације које су најважније" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Водич за санацију" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Доделите вођене задатке члановима изложеним ризику да ротирају акредитиве у ризику" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { - "message": "Праћење напретка" + "feature3Title": { + "message": "Monitor progress" }, - "benefit3Description": { - "message": "Пратите промене током времена да бисте показали безбедносна побољшања" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Покрените свој први извештај да бисте видели апликације" + "noReportsRunTitle": { + "message": "Generate report" }, - "noReportRunDescription": { - "message": "Направите извештај о увиду у ризик да бисте анализирали апликације ваше организације и идентификовали ризичне лозинке на које треба обратити пажњу. Покретање вашег првог извештаја ће:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "Нисте означили ниједну апликацију као критичну" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Чланови под ризиком" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Чланови са приступом за угрожене ставке критичних апликација" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Чланови са ризичним лозинкама" }, - "membersWillReceiveNotification": { - "message": "Чланови ће добити обавештење за решавање ризичних пријава путем екстензије претраживача." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "Угрожени чланови: $COUNT$", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Ови чланови се пријављују у апликације са слабим, откривеним или поново коришћеним лозинкама." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "Нема чланова пријављена у апликације са слабим, откривеним или поново коришћеним лозинкама." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Дајте приоритет критичним апликацијама" }, - "selectCriticalApplicationsDescription": { - "message": "Изаберите које су апликације најкритичније за вашу организацију, а затим доделите безбедносне задатке члановима да бисте решили ризике." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Прегледајте нове апликације" }, - "reviewNewApplicationsDescription": { - "message": "Истакли смо ризичне ставке за нове апликације ускладиштене у Админ конзоли које имају слабе, откривене или поново коришћене лозинке." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Кликните на икону звездице да бисте означили апликацију као критичну" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Додели задатке" }, - "assignTasksToMembers": { - "message": "Доделите задатке члановима за вођено решавање" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Додели колекцијама" diff --git a/apps/web/src/locales/sv/messages.json b/apps/web/src/locales/sv/messages.json index ca0547d7d95..3ebe0499428 100644 --- a/apps/web/src/locales/sv/messages.json +++ b/apps/web/src/locales/sv/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Tilldela medlemmar uppgifter för att övervaka framsteg" }, - "onceYouReviewApps": { - "message": "När du har granskat applikationer och markerat dem som kritiska kan du tilldela uppgifter till medlemmar för att åtgärda riskutsatta objekt och övervaka framsteg här" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Skicka påminnelser" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "Inga applikationer hittades för $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "Ingen data hittades" }, - "noApplicationsInOrgDescription": { - "message": "Importera din organisations inloggningsdata för att börja övervaka säkerhetsrisker för autentiseringsuppgifter. När de har importerats kan du:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritera risker" + "feature1Title": { + "message": "Markera applikationer som kritiska" }, - "benefit1Description": { - "message": "Fokusera på applikationer som betyder mest" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide åtgärder" + "feature2Title": { + "message": "Hjälp medlemmar att förbättra sin säkerhet" }, - "benefit2Description": { - "message": "Tilldela riskutsatta medlemmar vägledda uppgifter för att byta ut riskutsatta autentiseringsuppgifter" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Övervaka framsteg" }, - "benefit3Description": { - "message": "Spåra förändringar över tid för att visa säkerhetsförbättringar" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Kör din första rapport för att se applikationer" + "noReportsRunTitle": { + "message": "Generera rapport" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "Du har inte markerat några applikationer som kritiska" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Riskutsatta medlemmar" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ medlemmar i riskzonen", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Dessa medlemmar loggar in i applikationer med svaga, exponerade eller återanvända lösenord." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "Det finns inga medlemmar som loggar in i applikationer med svaga, exponerade eller återanvända lösenord." @@ -386,19 +380,19 @@ "message": "Review applications to secure the items most critical to your organization's security" }, "reviewApplications": { - "message": "Review applications" + "message": "Granska applikationer" }, "prioritizeCriticalApplications": { "message": "Prioritera kritiska applikationer" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Granska nya applikationer" }, - "reviewNewApplicationsDescription": { - "message": "Vi har markerat objekt i riskzonen för nya applikationer som lagras i administratörskonsolen och som har svaga, exponerade eller återanvända lösenord." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -3251,7 +3245,7 @@ "message": "Nästa debitering" }, "nextChargeHeader": { - "message": "Next Charge" + "message": "Nästa betalning" }, "plan": { "message": "Plan" @@ -3260,7 +3254,7 @@ "message": "Detaljer" }, "discount": { - "message": "discount" + "message": "rabatt" }, "downloadLicense": { "message": "Hämta licens" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Tilldela uppgifter" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Skicka aviseringar om att ändra lösenord" }, "assignToCollections": { "message": "Tilldela till samlingar" @@ -12162,6 +12156,6 @@ "message": "No critical applications are selected" }, "confirmNoSelectedCriticalApplicationsDesc": { - "message": "Are you sure you want to continue?" + "message": "Är du säker på att du vill fortsätta?" } } diff --git a/apps/web/src/locales/ta/messages.json b/apps/web/src/locales/ta/messages.json index bea4f3365bc..64ce03adb11 100644 --- a/apps/web/src/locales/ta/messages.json +++ b/apps/web/src/locales/ta/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "ஆபத்தில் உள்ள உறுப்பினர்கள்" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "இந்த உறுப்பினர்கள் பலவீனமான, அம்பலப்படுத்தப்பட்ட அல்லது மீண்டும் பயன்படுத்தப்பட்ட கடவுச்சொற்களுடன் பயன்பாடுகளில் உள்நுழைகிறார்கள்." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "பலவீனமான, அம்பலப்படுத்தப்பட்ட அல்லது மீண்டும் பயன்படுத்தப்பட்ட கடவுச்சொற்களுடன் பயன்பாடுகளில் உள்நுழையும் உறுப்பினர்கள் யாரும் இல்லை." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "சேகரிப்புகளுக்கு ஒதுக்கு" diff --git a/apps/web/src/locales/te/messages.json b/apps/web/src/locales/te/messages.json index 47df4826851..9f57ed8c6ca 100644 --- a/apps/web/src/locales/te/messages.json +++ b/apps/web/src/locales/te/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/th/messages.json b/apps/web/src/locales/th/messages.json index 3e4797eb03f..30a75f4d4da 100644 --- a/apps/web/src/locales/th/messages.json +++ b/apps/web/src/locales/th/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "You haven’t marked any applications as critical" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "At-risk members" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "These members are logging into applications with weak, exposed, or reused passwords." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "These are no members logging into applications with weak, exposed, or reused passwords." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Assign to collections" diff --git a/apps/web/src/locales/tr/messages.json b/apps/web/src/locales/tr/messages.json index bb52d98e1e4..448172f3c43 100644 --- a/apps/web/src/locales/tr/messages.json +++ b/apps/web/src/locales/tr/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "$ORG NAME$ kuruluşu için hiçbir uygulama bulunmadı", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "Hiçbir uygulamayı kritik olarak işaretlemediniz" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Riskli üyeler" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Kritik uygulamalar için risk altındaki kayıtlara erişimi olan üyeler" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ üye risk altında", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Bu üyeler uygulamalara zayıf, ele geçirilmiş veya tekrar kullanılan parolalarla giriş yapmaktadır." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "Zayıf, ele geçirilmiş veya tekrar kullanılan parolayla uygulamalara giriş yapan üye yok." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Koleksiyonlara ata" @@ -12162,6 +12156,6 @@ "message": "No critical applications are selected" }, "confirmNoSelectedCriticalApplicationsDesc": { - "message": "Are you sure you want to continue?" + "message": "Devam etmek istediğinizden emin misiniz?" } } diff --git a/apps/web/src/locales/uk/messages.json b/apps/web/src/locales/uk/messages.json index 6fa971b98a5..f1f7d6530b7 100644 --- a/apps/web/src/locales/uk/messages.json +++ b/apps/web/src/locales/uk/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Assign members tasks to monitor progress" }, - "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" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Send reminders" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "No applications found for $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Import your organization's login data to start monitoring credential security risks. Once imported you get to:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Prioritize risks" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Focus on applications that matter the most" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Guide remediation" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Assign at-risk members guided tasks to rotate at-risk credentials" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { + "feature3Title": { "message": "Monitor progress" }, - "benefit3Description": { - "message": "Track changes over time to show security improvements" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Run your first report to see applications" + "noReportsRunTitle": { + "message": "Generate report" }, - "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:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "Ви не відмітили жодного додатку в якості критичного" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Ризиковані учасники" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Members with access to at-risk items for critical applications" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Members with at-risk passwords" }, - "membersWillReceiveNotification": { - "message": "Members will receive a notification to resolve at-risk logins through the browser extension." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ members at-risk", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Ці учасники використовують у програмах слабкі, викриті, або повторювані паролі." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "Немає учасників, які використовують у програмах слабкі, викриті, або повторювані паролі." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Prioritize critical applications" }, - "selectCriticalApplicationsDescription": { - "message": "Select which applications are most critical to your organization, then assign security tasks to members to resolve risks." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Click the star icon to mark an app as critical" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Assign tasks" }, - "assignTasksToMembers": { - "message": "Assign tasks to members for guided resolution" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Призначити до збірок" diff --git a/apps/web/src/locales/vi/messages.json b/apps/web/src/locales/vi/messages.json index 6e6ba52daad..f05004b46da 100644 --- a/apps/web/src/locales/vi/messages.json +++ b/apps/web/src/locales/vi/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "Giao tác vụ cho thành viên để theo dõi tiến trình" }, - "onceYouReviewApps": { - "message": "Sau khi bạn xem xét các ứng dụng và đánh dấu chúng là nghiêm trọng, bạn có thể giao nhiệm vụ cho các thành viên để xử lý các mục có rủi ro và theo dõi tiến độ tại đây" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "Gửi lời nhắc" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "Không tìm thấy ứng dụng nào cho $ORG NAME$", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "Nhập dữ liệu đăng nhập của tổ chức bạn để bắt đầu giám sát các rủi ro bảo mật thông tin xác thực. Sau khi nhập, bạn có thể:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "Ưu tiên các rủi ro" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "Tập trung vào các ứng dụng quan trọng nhất" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "Hướng dẫn khắc phục" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "Giao nhiệm vụ được hướng dẫn cho các thành viên có rủi ro để xoay vòng thông tin xác thực có rủi ro" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { - "message": "Theo dõi tiến độ" + "feature3Title": { + "message": "Monitor progress" }, - "benefit3Description": { - "message": "Theo dõi thay đổi theo thời gian để hiển thị cải tiến bảo mật" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "Chạy báo cáo đầu tiên của bạn để xem các ứng dụng" + "noReportsRunTitle": { + "message": "Generate report" }, - "noReportRunDescription": { - "message": "Tạo báo cáo thông tin chi tiết về rủi ro để phân tích các ứng dụng của tổ chức bạn và xác định các mật khẩu có rủi ro cần chú ý. Khi chạy báo cáo đầu tiên, bạn sẽ:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "Bạn chưa đánh dấu ứng dụng nào là quan trọng" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "Các thành viên có rủi ro" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "Thành viên có quyền truy cập vào các mục có nguy cơ dành cho các ứng dụng quan trọng" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "Thành viên có mật khẩu rủi ro" }, - "membersWillReceiveNotification": { - "message": "Các thành viên sẽ nhận được thông báo để giải quyết các đăng nhập có rủi ro thông qua tiện ích mở rộng trình duyệt." + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ thành viên gặp rủi ro", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "Các thành viên này đang đăng nhập vào các ứng dụng bằng mật khẩu yếu, dễ bị lộ hoặc được sử dụng lại." + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "Không có thành viên nào đăng nhập vào ứng dụng bằng mật khẩu yếu, dễ bị lộ hoặc được sử dụng lại." @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "Ưu tiên các ứng dụng quan trọng" }, - "selectCriticalApplicationsDescription": { - "message": "Chọn những ứng dụng quan trọng nhất đối với tổ chức của bạn, sau đó giao nhiệm vụ bảo mật cho các thành viên để giải quyết rủi ro." + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "Review new applications" }, - "reviewNewApplicationsDescription": { - "message": "We've highlighted at-risk items for new applications stored in Admin console that have weak, exposed, or reused passwords." + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "Nhấp vào biểu tượng ngôi sao để đánh dấu một ứng dụng là quan trọng" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "Giao tác vụ" }, - "assignTasksToMembers": { - "message": "Giao nhiệm vụ cho các thành viên để hướng dẫn giải quyết" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "Gán vào bộ sưu tập" diff --git a/apps/web/src/locales/zh_CN/messages.json b/apps/web/src/locales/zh_CN/messages.json index cce4544ac7b..05534b3a8b2 100644 --- a/apps/web/src/locales/zh_CN/messages.json +++ b/apps/web/src/locales/zh_CN/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "分配成员任务以监测进度" }, - "onceYouReviewApps": { - "message": "审查新应用程序并将其标记为关键后,您可以在这里将任务分配给成员以解决存在风险的项目并监测进度" + "onceYouReviewApplications": { + "message": "审查应用程序并将其标记为关键后,将任务分配给您的成员以更改他们的密码。" }, "sendReminders": { "message": "发送提醒" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "未找到与 $ORG NAME$ 相关的应用程序", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "未找到任何数据" }, - "noApplicationsInOrgDescription": { - "message": "导入您组织的登录数据,以开始监测凭据安全风险。导入后您将能够:" + "noDataInOrgDescription": { + "message": "导入您组织的登录数据以开始使用 Access Intelligence。导入完成后,您将能够:" }, - "benefit1Title": { - "message": "优先处理风险" + "feature1Title": { + "message": "将应用程序标记为关键" }, - "benefit1Description": { - "message": "重点关注最重要的应用程序" + "feature1Description": { + "message": "这将帮助您优先消除您最重要的应用程序的风险。" }, - "benefit2Title": { - "message": "指导补救措施" + "feature2Title": { + "message": "帮助成员提升他们的安全性" }, - "benefit2Description": { - "message": "为存在风险的成员分配引导式任务,以轮换存在风险的凭据" + "feature2Description": { + "message": "为存在风险的成员分配引导式安全任务,以更新凭证。" }, - "benefit3Title": { + "feature3Title": { "message": "监测进度" }, - "benefit3Description": { - "message": "追踪变化趋势,展示安全改进成效" + "feature3Description": { + "message": "追踪一段时间内的变化,以展示安全性的改进。" }, - "noReportRunTitle": { - "message": "运行您的第一份报告以查看应用程序" + "noReportsRunTitle": { + "message": "生成报告" }, - "noReportRunDescription": { - "message": "生成风险洞察报告,以分析您组织的应用程序并识别需要关注的风险密码。运行您的第一份报告将:" + "noReportsRunDescription": { + "message": "您已准备好开始生成报告。生成报告后,您将能够:" }, "noCriticalApplicationsTitle": { "message": "您还没有将任何应用程序标记为关键" @@ -224,7 +218,7 @@ "message": "选择关键应用程序" }, "markAppAsCritical": { - "message": "标记应用程序为关键" + "message": "将 App 标记为关键" }, "markAsCritical": { "message": "标记为关键" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "存在风险的成员" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "对关键应用程序中存在风险的项目具有访问权限的成员" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "这些成员拥有关键应用程序中易受攻击项目的访问权限。" }, "membersWithAtRiskPasswords": { "message": "密码存在风险的成员" }, - "membersWillReceiveNotification": { - "message": "成员将通过浏览器扩展收到通知,以解决存在风险的登录。" + "membersWillReceiveSecurityTask": { + "message": "您的组织成员将被分配更改易受攻击的密码的任务。他们将在 Bitwarden 浏览器扩展中收到通知。" }, "membersAtRiskCount": { "message": "$COUNT$ 个成员存在风险", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "这些成员正登录到具有弱的、暴露的或重复使用的密码的应用程序。" + "atRiskMemberDescription": { + "message": "这些成员正在登录到具有弱的、暴露的或重复使用的密码的关键应用程序。" }, "atRiskMembersDescriptionNone": { "message": "没有成员登录到具有弱的、暴露的或重复使用的密码的应用程序。" @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "优先处理关键应用程序" }, - "selectCriticalApplicationsDescription": { - "message": "选择对您的组织最关键的应用程序,然后将安全任务分配给成员以解决风险。" + "selectCriticalAppsDescription": { + "message": "选择对您的组织最关键的应用程序。然后,您可以将安全任务分配给成员以消除风险。" }, "reviewNewApplications": { "message": "审查新应用程序" }, - "reviewNewApplicationsDescription": { - "message": "我们突出显示了存储在管理控制台中的新应用程序的风险项目,这些项目使用了弱、暴露或重复使用的密码。" + "reviewNewAppsDescription": { + "message": "审查具有易受攻击项目的新应用程序然后将需要密切监测的应用程序标记为关键。然后,您可以将安全任务分配给成员以消除风险。" }, "clickIconToMarkAppAsCritical": { "message": "点击星形图标以将 App 标记为关键" @@ -2543,23 +2537,23 @@ "message": "恢复访问权限" }, "premium": { - "message": "高级会员", + "message": "高级版", "description": "Premium membership" }, "premiumMembership": { "message": "高级会员" }, "premiumRequired": { - "message": "需要高级会员" + "message": "需要高级版" }, "premiumRequiredDesc": { "message": "使用此功能需要高级会员资格。" }, "youHavePremiumAccess": { - "message": "您拥有高级访问权限" + "message": "您拥有高级版访问权限" }, "alreadyPremiumFromOrg": { - "message": "由于您是拥有高级会员功能的组织的成员,您已经拥有此功能。" + "message": "由于您是组织的成员,您已经拥有高级版功能访问权限。" }, "manage": { "message": "管理" @@ -3056,7 +3050,7 @@ "message": "您账户的信用额度可用于进行消费。任何可用的信用额度将用于自动抵扣此账户的账单。" }, "goPremium": { - "message": "升级高级会员", + "message": "升级高级版", "description": "Another way of saying \"Get a Premium membership\"" }, "premiumUpdated": { @@ -3084,7 +3078,7 @@ "message": "优先客户支持。" }, "premiumSignUpFuture": { - "message": "未来的更多高级功能。敬请期待!" + "message": "未来的更多高级版功能。敬请期待!" }, "premiumPrice": { "message": "所有功能仅需 $PRICE$ /年!", @@ -3096,7 +3090,7 @@ } }, "premiumPriceWithFamilyPlan": { - "message": "升级高级会员仅需 $PRICE$ /年,或成为具有 $FAMILYPLANUSERCOUNT$ 位用户以及无限制的家庭共享的高级账户,通过 ", + "message": "升级高级版仅需 $PRICE$ /年,或成为具有 $FAMILYPLANUSERCOUNT$ 位用户以及无限制的家庭共享的高级账户,通过 ", "placeholders": { "price": { "content": "$1", @@ -3115,10 +3109,10 @@ "message": "附加项目" }, "premiumAccess": { - "message": "高级会员" + "message": "高级版访问权限" }, "premiumAccessDesc": { - "message": "您可以为您的组织所有成员添加高级访问权限,只需 $PRICE$ /$INTERVAL$ 。", + "message": "您可以为您的组织所有成员添加高级版访问权限,只需 $PRICE$ /$INTERVAL$ 。", "placeholders": { "price": { "content": "$1", @@ -3559,7 +3553,7 @@ "message": "本地托管(可选)" }, "usersGetPremium": { - "message": "用户拥有高级会员功能权限" + "message": "用户拥有高级版功能访问权限" }, "controlAccessWithGroups": { "message": "使用群组控制用户访问权限" @@ -6915,7 +6909,7 @@ "message": "Bitwarden 家庭方案包含" }, "sponsoredFamiliesPremiumAccess": { - "message": "最多 6 个用户的高级访问权限" + "message": "最多 6 个高级版访问权限的用户" }, "sponsoredFamiliesSharedCollectionsForFamilyMembers": { "message": "为家庭成员提供共享集合" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "分配任务" }, - "assignTasksToMembers": { - "message": "将任务分配给成员以引导式解决" + "assignSecurityTasksToMembers": { + "message": "发送通知以更改密码" }, "assignToCollections": { "message": "分配到集合" @@ -10877,7 +10871,7 @@ "message": "保护您的家庭登录" }, "accessToPremiumFeatures": { - "message": "访问高级会员功能" + "message": "高级版功能访问权限" }, "additionalStorageGbMessage": { "message": "GB 附加存储" @@ -10916,7 +10910,7 @@ "message": "RSA 4096-Bit" }, "premiumAccounts": { - "message": "6 个高级账户" + "message": "6 个高级版账户" }, "unlimitedSharing": { "message": "不限数量的共享" @@ -12159,9 +12153,9 @@ } }, "confirmNoSelectedCriticalApplicationsTitle": { - "message": "No critical applications are selected" + "message": "未选择任何关键应用程序" }, "confirmNoSelectedCriticalApplicationsDesc": { - "message": "Are you sure you want to continue?" + "message": "确定要继续吗?" } } diff --git a/apps/web/src/locales/zh_TW/messages.json b/apps/web/src/locales/zh_TW/messages.json index 784027a735a..3036366816a 100644 --- a/apps/web/src/locales/zh_TW/messages.json +++ b/apps/web/src/locales/zh_TW/messages.json @@ -93,8 +93,8 @@ "assignMembersTasksToMonitorProgress": { "message": "指派成員任務以監控進度" }, - "onceYouReviewApps": { - "message": "在您檢視應用程式並將其標記為關鍵後,您可以指派任務給成員,以解決有風險的項目,並在此監控進度" + "onceYouReviewApplications": { + "message": "Once you review applications and mark them as critical, assign tasks to your members to change their passwords." }, "sendReminders": { "message": "傳送提醒" @@ -178,41 +178,35 @@ } } }, - "noApplicationsInOrgTitle": { - "message": "未找到 $ORG NAME$ 的應用程式", - "placeholders": { - "org name": { - "content": "$1", - "example": "Company Name" - } - } + "noDataInOrgTitle": { + "message": "No data found" }, - "noApplicationsInOrgDescription": { - "message": "匯入您組織的登入資料以開始監控憑證安全風險。匯入後您可以:" + "noDataInOrgDescription": { + "message": "Import your organization's login data to get started with Access Intelligence. Once you do that, you'll be able to:" }, - "benefit1Title": { - "message": "風險優先排序" + "feature1Title": { + "message": "Mark applications as critical" }, - "benefit1Description": { - "message": "專注於最重要的應用程式" + "feature1Description": { + "message": "This will help you remove risks to your most important applications first." }, - "benefit2Title": { - "message": "指導補救措施" + "feature2Title": { + "message": "Help members improve their security" }, - "benefit2Description": { - "message": "指派有風險的成員執行指導任務以輪換有風險的憑證" + "feature2Description": { + "message": "Assign at-risk members guided security tasks to update credentials." }, - "benefit3Title": { - "message": "監控進展" + "feature3Title": { + "message": "Monitor progress" }, - "benefit3Description": { - "message": "追蹤隨時間變化的狀況以顯示安全性改善" + "feature3Description": { + "message": "Track changes over time to show security improvements." }, - "noReportRunTitle": { - "message": "執行您的第一份報告以查看應用程式" + "noReportsRunTitle": { + "message": "Generate report" }, - "noReportRunDescription": { - "message": "產生風險洞察報告以分析組織的應用程式並找出需要注意的高風險密碼。執行第一份報告將會:" + "noReportsRunDescription": { + "message": "You’re ready to start generating reports. Once you generate, you’ll be able to:" }, "noCriticalApplicationsTitle": { "message": "您尚未將任何應用程式標記為關鍵" @@ -271,14 +265,14 @@ "atRiskMembers": { "message": "具有風險的成員" }, - "membersWithAccessToAtRiskItemsForCriticalApps": { - "message": "擁有關鍵應用程式中有風險項目存取權的成員" + "membersWithAccessToAtRiskItemsForCriticalApplications": { + "message": "These members have access to vulnerable items for critical applications." }, "membersWithAtRiskPasswords": { "message": "使用有風險密碼的成員" }, - "membersWillReceiveNotification": { - "message": "成員會透過瀏覽器擴充套件接收到有風險登入的通知。" + "membersWillReceiveSecurityTask": { + "message": "Members of your organization will be assigned a task to change vulnerable passwords. They’ll receive a notification within their Bitwarden browser extension." }, "membersAtRiskCount": { "message": "$COUNT$ 位有風險的成員", @@ -307,8 +301,8 @@ } } }, - "atRiskMembersDescription": { - "message": "這些成員正在使用弱密碼、外洩密碼或重複密碼登入應用程式。" + "atRiskMemberDescription": { + "message": "These members are logging into critical applications with weak, exposed, or reused passwords." }, "atRiskMembersDescriptionNone": { "message": "目前沒有成員使用弱密碼、外洩密碼或重複密碼登入應用程式。" @@ -391,14 +385,14 @@ "prioritizeCriticalApplications": { "message": "優先處理關鍵應用程式" }, - "selectCriticalApplicationsDescription": { - "message": "選擇對組織最關鍵的應用程式,然後將安全任務指派給成員以供解決。" + "selectCriticalAppsDescription": { + "message": "Select which applications are most critical to your organization. Then, you’ll be able to assign security tasks to members to remove risks." }, "reviewNewApplications": { "message": "審查新應用程式" }, - "reviewNewApplicationsDescription": { - "message": "我們已在管理主控台中標示出新應用程式中密碼薄弱、已外洩或重複使用的高風險項目。" + "reviewNewAppsDescription": { + "message": "Review new applications with vulnerable items and mark those you’d like to monitor closely as critical. Then, you’ll be able to assign security tasks to members to remove risks." }, "clickIconToMarkAppAsCritical": { "message": "點擊星形圖示以將應用程式標記為關鍵" @@ -9856,8 +9850,8 @@ "assignTasks": { "message": "指派任務" }, - "assignTasksToMembers": { - "message": "指派任務給成員並引導解決" + "assignSecurityTasksToMembers": { + "message": "Send notifications to change passwords" }, "assignToCollections": { "message": "指派至集合" @@ -12159,9 +12153,9 @@ } }, "confirmNoSelectedCriticalApplicationsTitle": { - "message": "No critical applications are selected" + "message": "未選取任何關鍵應用程式" }, "confirmNoSelectedCriticalApplicationsDesc": { - "message": "Are you sure you want to continue?" + "message": "您確定要繼續嗎?" } } From 28dc244fd3bc272fd2e36c8727df76b27f75ede4 Mon Sep 17 00:00:00 2001 From: Brandon Treston <btreston@bitwarden.com> Date: Wed, 19 Nov 2025 13:32:50 -0500 Subject: [PATCH 58/75] fix error in console (#17468) --- .../group-name-badge.component.html | 2 +- .../group-badge/group-name-badge.component.ts | 39 +++++++++---------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/collections/group-badge/group-name-badge.component.html b/apps/web/src/app/admin-console/organizations/collections/group-badge/group-name-badge.component.html index 9ddc9897a31..a8021e82c39 100644 --- a/apps/web/src/app/admin-console/organizations/collections/group-badge/group-name-badge.component.html +++ b/apps/web/src/app/admin-console/organizations/collections/group-badge/group-name-badge.component.html @@ -1 +1 @@ -<bit-badge-list [items]="groupNames" [maxItems]="3" variant="secondary"></bit-badge-list> +<bit-badge-list [items]="groupNames()" [maxItems]="3" variant="secondary"></bit-badge-list> diff --git a/apps/web/src/app/admin-console/organizations/collections/group-badge/group-name-badge.component.ts b/apps/web/src/app/admin-console/organizations/collections/group-badge/group-name-badge.component.ts index 8a58f5b92d7..3c1d0d2b691 100644 --- a/apps/web/src/app/admin-console/organizations/collections/group-badge/group-name-badge.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/group-badge/group-name-badge.component.ts @@ -1,36 +1,33 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, Input, OnChanges } from "@angular/core"; +import { ChangeDetectionStrategy, Component, computed, input } from "@angular/core"; import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { GroupView } from "../../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-group-badge", templateUrl: "group-name-badge.component.html", standalone: false, + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class GroupNameBadgeComponent implements OnChanges { - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input() selectedGroups: SelectionReadOnlyRequest[]; - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input() allGroups: GroupView[]; +export class GroupNameBadgeComponent { + readonly selectedGroups = input<SelectionReadOnlyRequest[]>([]); + readonly allGroups = input<GroupView[]>([]); - protected groupNames: string[] = []; + protected readonly groupNames = computed(() => { + const allGroups = this.allGroups(); + if (!allGroups) { + return []; + } + + return this.selectedGroups() + .map((g) => { + return allGroups.find((o) => o.id === g.id)?.name; + }) + .filter((name): name is string => name !== undefined) + .sort(this.i18nService.collator.compare); + }); constructor(private i18nService: I18nService) {} - - ngOnChanges() { - this.groupNames = this.selectedGroups - .map((g) => { - return this.allGroups.find((o) => o.id === g.id)?.name; - }) - .sort(this.i18nService.collator.compare); - } } From 9ec05a96b9f14331f8c2dfde5513115cf238940f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 18:44:01 +0000 Subject: [PATCH 59/75] [deps]: Update GitHub Artifact Actions (#17305) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel James Smith <2670567+djsmith85@users.noreply.github.com> --- .github/workflows/build-browser.yml | 8 +-- .github/workflows/build-cli.yml | 12 ++--- .github/workflows/build-desktop.yml | 84 ++++++++++++++--------------- .github/workflows/build-web.yml | 2 +- .github/workflows/test.yml | 8 +-- 5 files changed, 57 insertions(+), 57 deletions(-) diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index 83e6c2d696e..dc71d33d605 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -193,7 +193,7 @@ jobs: zip -r browser-source.zip browser-source - name: Upload browser source - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: ${{matrix.license_type.archive_name_prefix}}browser-source-${{ env._BUILD_NUMBER }}.zip path: browser-source.zip @@ -268,7 +268,7 @@ jobs: npm --version - name: Download browser source - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: name: ${{matrix.license_type.source_archive_name_prefix}}browser-source-${{ env._BUILD_NUMBER }}.zip @@ -332,7 +332,7 @@ jobs: working-directory: browser-source/apps/browser - name: Upload extension artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: ${{ matrix.license_type.artifact_prefix }}${{ matrix.browser.artifact_name }}-${{ env._BUILD_NUMBER }}.zip path: browser-source/apps/browser/dist/${{matrix.license_type.archive_name_prefix}}${{ matrix.browser.archive_name }} @@ -506,7 +506,7 @@ jobs: ls -la - name: Upload Safari artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: ${{matrix.license_type.archive_name_prefix}}dist-safari-${{ env._BUILD_NUMBER }}.zip path: apps/browser/dist/${{matrix.license_type.archive_name_prefix}}dist-safari.zip diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index 414c043b89e..babd00a323f 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -268,7 +268,7 @@ jobs: fi - name: Upload unix zip asset - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}${{ matrix.os.target_suffix }}-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}${{ matrix.os.target_suffix }}-${{ env._PACKAGE_VERSION }}.zip @@ -482,7 +482,7 @@ jobs: } - name: Upload windows zip asset - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bw${{ matrix.license_type.artifact_prefix }}-windows-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-windows-${{ env._PACKAGE_VERSION }}.zip @@ -490,7 +490,7 @@ jobs: - name: Upload Chocolatey asset if: matrix.license_type.build_prefix == 'bit' - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg path: apps/cli/dist/chocolatey/bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg @@ -503,7 +503,7 @@ jobs: - name: Upload NPM Build Directory asset if: matrix.license_type.build_prefix == 'bit' - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip path: apps/cli/bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip @@ -535,7 +535,7 @@ jobs: echo "BW Package Version: $_PACKAGE_VERSION" - name: Get bw linux cli - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: name: bw-linux-${{ env._PACKAGE_VERSION }}.zip path: apps/cli/dist/snap @@ -572,7 +572,7 @@ jobs: run: sudo snap remove bw - name: Upload snap asset - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bw_${{ env._PACKAGE_VERSION }}_amd64.snap path: apps/cli/dist/snap/bw_${{ env._PACKAGE_VERSION }}_amd64.snap diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 27a7bfe4124..1877374a525 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -257,35 +257,35 @@ jobs: run: npm run dist:lin - name: Upload .deb artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb if-no-files-found: error - name: Upload .rpm artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm if-no-files-found: error - name: Upload .snap artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap path: apps/desktop/dist/bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap if-no-files-found: error - name: Upload .AppImage artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: ${{ needs.setup.outputs.release_channel }}-linux.yml path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-linux.yml @@ -298,7 +298,7 @@ jobs: sudo npm run pack:lin:flatpak - name: Upload flatpak artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: com.bitwarden.desktop.flatpak path: apps/desktop/dist/com.bitwarden.desktop.flatpak @@ -426,14 +426,14 @@ jobs: run: npm run dist:lin:arm64 - name: Upload .snap artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden_${{ env._PACKAGE_VERSION }}_arm64.snap path: apps/desktop/dist/bitwarden_${{ env._PACKAGE_VERSION }}_arm64.snap if-no-files-found: error - name: Upload tar.gz artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden_${{ env._PACKAGE_VERSION }}_arm64.tar.gz path: apps/desktop/dist/bitwarden_desktop_arm64.tar.gz @@ -446,7 +446,7 @@ jobs: sudo npm run pack:lin:flatpak - name: Upload flatpak artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: com.bitwarden.desktop-arm64.flatpak path: apps/desktop/dist/com.bitwarden.desktop.flatpak @@ -617,7 +617,7 @@ jobs: -NewName bitwarden-$env:_PACKAGE_VERSION-arm64.nsis.7z - name: Upload portable exe artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe @@ -625,7 +625,7 @@ jobs: - name: Upload installer exe artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Installer-${{ env._PACKAGE_VERSION }}..exe path: apps/desktop/dist/nsis-web/Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe @@ -633,7 +633,7 @@ jobs: - name: Upload appx ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx @@ -641,7 +641,7 @@ jobs: - name: Upload store appx ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx @@ -649,7 +649,7 @@ jobs: - name: Upload NSIS ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z @@ -657,7 +657,7 @@ jobs: - name: Upload appx x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx @@ -665,7 +665,7 @@ jobs: - name: Upload store appx x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx @@ -673,7 +673,7 @@ jobs: - name: Upload NSIS x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z @@ -681,7 +681,7 @@ jobs: - name: Upload appx ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx @@ -689,7 +689,7 @@ jobs: - name: Upload store appx ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx @@ -697,7 +697,7 @@ jobs: - name: Upload NSIS ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z @@ -705,7 +705,7 @@ jobs: - name: Upload nupkg artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden.${{ env._PACKAGE_VERSION }}.nupkg path: apps/desktop/dist/chocolatey/bitwarden.${{ env._PACKAGE_VERSION }}.nupkg @@ -713,7 +713,7 @@ jobs: - name: Upload auto-update artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: ${{ needs.setup.outputs.release_channel }}.yml path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release_channel }}.yml @@ -868,7 +868,7 @@ jobs: -NewName latest-beta.yml - name: Upload portable exe artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-Portable-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/Bitwarden-Beta-Portable-${{ env._PACKAGE_VERSION }}.exe @@ -876,7 +876,7 @@ jobs: - name: Upload installer exe artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-Installer-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/nsis-web/Bitwarden-Beta-Installer-${{ env._PACKAGE_VERSION }}.exe @@ -884,7 +884,7 @@ jobs: - name: Upload appx ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32.appx @@ -892,7 +892,7 @@ jobs: - name: Upload store appx ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32-store.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-ia32-store.appx @@ -900,7 +900,7 @@ jobs: - name: Upload NSIS ia32 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-beta-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-beta-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z @@ -908,7 +908,7 @@ jobs: - name: Upload appx x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64.appx @@ -916,7 +916,7 @@ jobs: - name: Upload store appx x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64-store.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-x64-store.appx @@ -924,7 +924,7 @@ jobs: - name: Upload NSIS x64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-beta-${{ env._PACKAGE_VERSION }}-x64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-beta-${{ env._PACKAGE_VERSION }}-x64.nsis.7z @@ -932,7 +932,7 @@ jobs: - name: Upload appx ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64.appx @@ -940,7 +940,7 @@ jobs: - name: Upload store appx ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64-store.appx path: apps/desktop/dist/Bitwarden-Beta-${{ env._PACKAGE_VERSION }}-arm64-store.appx @@ -948,7 +948,7 @@ jobs: - name: Upload NSIS ARM64 artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: bitwarden-beta-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z path: apps/desktop/dist/nsis-web/bitwarden-beta-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z @@ -956,7 +956,7 @@ jobs: - name: Upload auto-update artifact if: ${{ needs.setup.outputs.has_secrets == 'true' }} - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: latest-beta.yml path: apps/desktop/dist/nsis-web/latest-beta.yml @@ -1408,7 +1408,7 @@ jobs: run: npm run build - name: Download Browser artifact - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: path: ${{ github.workspace }}/browser-build-artifacts @@ -1441,28 +1441,28 @@ jobs: run: npm run pack:mac - name: Upload .zip artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip if-no-files-found: error - name: Upload .dmg artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg if-no-files-found: error - name: Upload .dmg blockmap artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap if-no-files-found: error - name: Upload auto-update artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: ${{ needs.setup.outputs.release_channel }}-mac.yml path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-mac.yml @@ -1688,7 +1688,7 @@ jobs: run: npm run build - name: Download Browser artifact - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: path: ${{ github.workspace }}/browser-build-artifacts @@ -1731,14 +1731,14 @@ jobs: $buildInfo | ConvertTo-Json | Set-Content -Path dist/macos-build-number.json - name: Upload MacOS App Store build number artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: macos-build-number.json path: apps/desktop/dist/macos-build-number.json if-no-files-found: error - name: Upload .pkg artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg path: apps/desktop/dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml index 89dd684c5f0..caf806af9f0 100644 --- a/.github/workflows/build-web.yml +++ b/.github/workflows/build-web.yml @@ -307,7 +307,7 @@ jobs: zip -r web-$_VERSION-${{ matrix.artifact_name }}.zip build - name: Upload ${{ matrix.artifact_name }} artifact - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip path: apps/web/web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ed967e63b5a..f471826355f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -74,7 +74,7 @@ jobs: uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1 - name: Upload test coverage - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: jest-coverage path: ./coverage/lcov.info @@ -160,7 +160,7 @@ jobs: run: cargo llvm-cov --all-features --lcov --output-path lcov.info --workspace --no-cfg-coverage - name: Upload test coverage - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: rust-coverage path: ./apps/desktop/desktop_native/lcov.info @@ -178,13 +178,13 @@ jobs: persist-credentials: false - name: Download jest coverage - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: name: jest-coverage path: ./ - name: Download rust coverage - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 with: name: rust-coverage path: ./apps/desktop/desktop_native From de42cf303fd3a567463e474996a9917b57594805 Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Wed, 19 Nov 2025 12:57:00 -0600 Subject: [PATCH 60/75] [PM-27925] Refactor `StripeService` to allow more than one instance (#17467) * Refactor StripeService to allow more than one instance per scope * Fix linting issue * Claude's feedback --- .../settings/create-organization.component.ts | 15 +- .../enter-payment-method.component.ts | 154 ++-- .../billing/services/stripe.service.spec.ts | 797 ++++++++++++++++++ .../app/billing/services/stripe.service.ts | 259 ++++-- 4 files changed, 1088 insertions(+), 137 deletions(-) create mode 100644 apps/web/src/app/billing/services/stripe.service.spec.ts diff --git a/apps/web/src/app/admin-console/settings/create-organization.component.ts b/apps/web/src/app/admin-console/settings/create-organization.component.ts index 45ce89c0e3d..78398fd8897 100644 --- a/apps/web/src/app/admin-console/settings/create-organization.component.ts +++ b/apps/web/src/app/admin-console/settings/create-organization.component.ts @@ -1,8 +1,8 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Component, OnInit } from "@angular/core"; -import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { Component, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; +import { Subject, takeUntil } from "rxjs"; import { first } from "rxjs/operators"; import { PlanType, ProductTierType, ProductType } from "@bitwarden/common/billing/enums"; @@ -19,7 +19,7 @@ import { SharedModule } from "../../shared"; templateUrl: "create-organization.component.html", imports: [SharedModule, OrganizationPlansComponent, HeaderModule], }) -export class CreateOrganizationComponent implements OnInit { +export class CreateOrganizationComponent implements OnInit, OnDestroy { protected secretsManager = false; protected plan: PlanType = PlanType.Free; protected productTier: ProductTierType = ProductTierType.Free; @@ -29,6 +29,8 @@ export class CreateOrganizationComponent implements OnInit { private configService: ConfigService, ) {} + private destroy$ = new Subject<void>(); + async ngOnInit(): Promise<void> { const milestone3FeatureEnabled = await this.configService.getFeatureFlag( FeatureFlag.PM26462_Milestone_3, @@ -37,7 +39,7 @@ export class CreateOrganizationComponent implements OnInit { ? PlanType.FamiliesAnnually : PlanType.FamiliesAnnually2025; - this.route.queryParams.pipe(first(), takeUntilDestroyed()).subscribe((qParams) => { + this.route.queryParams.pipe(first(), takeUntil(this.destroy$)).subscribe((qParams) => { if (qParams.plan === "families" || qParams.productTier == ProductTierType.Families) { this.plan = familyPlan; this.productTier = ProductTierType.Families; @@ -61,4 +63,9 @@ export class CreateOrganizationComponent implements OnInit { this.secretsManager = qParams.product == ProductType.SecretsManager; }); } + + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } } diff --git a/apps/web/src/app/billing/payment/components/enter-payment-method.component.ts b/apps/web/src/app/billing/payment/components/enter-payment-method.component.ts index 5448f03aa56..9e7b870579d 100644 --- a/apps/web/src/app/billing/payment/components/enter-payment-method.component.ts +++ b/apps/web/src/app/billing/payment/components/enter-payment-method.component.ts @@ -1,9 +1,10 @@ -import { Component, Input, OnInit } from "@angular/core"; +import { ChangeDetectionStrategy, Component, input, OnDestroy, OnInit } from "@angular/core"; import { FormControl, FormGroup, Validators } from "@angular/forms"; import { map, Observable, of, startWith, Subject, takeUntil } from "rxjs"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { Utils } from "@bitwarden/common/platform/misc/utils"; import { PopoverModule, ToastService } from "@bitwarden/components"; import { SharedModule } from "../../../shared"; @@ -34,18 +35,17 @@ type PaymentMethodFormGroup = FormGroup<{ }>; }>; -// 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-enter-payment-method", + changeDetection: ChangeDetectionStrategy.OnPush, template: ` - @let showBillingDetails = includeBillingAddress && selected !== "payPal"; - <form [formGroup]="group"> + @let showBillingDetails = includeBillingAddress() && selected !== "payPal"; + <form [formGroup]="group()"> @if (showBillingDetails) { <h5 bitTypography="h5">{{ "paymentMethod" | i18n }}</h5> } <div class="tw-mb-4 tw-text-lg"> - <bit-radio-group [formControl]="group.controls.type"> + <bit-radio-group [formControl]="group().controls.type"> <bit-radio-button id="card-payment-method" [value]="'card'"> <bit-label> <i class="bwi bwi-fw bwi-credit-card" aria-hidden="true"></i> @@ -60,7 +60,7 @@ type PaymentMethodFormGroup = FormGroup<{ </bit-label> </bit-radio-button> } - @if (showPayPal) { + @if (showPayPal()) { <bit-radio-button id="paypal-payment-method" [value]="'payPal'"> <bit-label> <i class="bwi bwi-fw bwi-paypal" aria-hidden="true"></i> @@ -68,7 +68,7 @@ type PaymentMethodFormGroup = FormGroup<{ </bit-label> </bit-radio-button> } - @if (showAccountCredit) { + @if (showAccountCredit()) { <bit-radio-button id="credit-payment-method" [value]="'accountCredit'"> <bit-label> <i class="bwi bwi-fw bwi-dollar" aria-hidden="true"></i> @@ -82,10 +82,10 @@ type PaymentMethodFormGroup = FormGroup<{ @case ("card") { <div class="tw-grid tw-grid-cols-2 tw-gap-4 tw-mb-4"> <div class="tw-col-span-1"> - <app-payment-label for="stripe-card-number" required> + <app-payment-label [for]="'stripe-card-number-' + instanceId" required> {{ "cardNumberLabel" | i18n }} </app-payment-label> - <div id="stripe-card-number" class="tw-stripe-form-control"></div> + <div [id]="'stripe-card-number-' + instanceId" class="tw-stripe-form-control"></div> </div> <div class="tw-col-span-1 tw-flex tw-items-end"> <img @@ -95,13 +95,13 @@ type PaymentMethodFormGroup = FormGroup<{ /> </div> <div class="tw-col-span-1"> - <app-payment-label for="stripe-card-expiry" required> + <app-payment-label [for]="'stripe-card-expiry-' + instanceId" required> {{ "expiration" | i18n }} </app-payment-label> - <div id="stripe-card-expiry" class="tw-stripe-form-control"></div> + <div [id]="'stripe-card-expiry-' + instanceId" class="tw-stripe-form-control"></div> </div> <div class="tw-col-span-1"> - <app-payment-label for="stripe-card-cvc" required> + <app-payment-label [for]="'stripe-card-cvc-' + instanceId" required> {{ "securityCodeSlashCVV" | i18n }} <button [bitPopoverTriggerFor]="cardSecurityCodePopover" @@ -115,7 +115,7 @@ type PaymentMethodFormGroup = FormGroup<{ <p class="tw-mb-0">{{ "cardSecurityCodeDescription" | i18n }}</p> </bit-popover> </app-payment-label> - <div id="stripe-card-cvc" class="tw-stripe-form-control"></div> + <div [id]="'stripe-card-cvc-' + instanceId" class="tw-stripe-form-control"></div> </div> </div> } @@ -131,7 +131,7 @@ type PaymentMethodFormGroup = FormGroup<{ bitInput id="routingNumber" type="text" - [formControl]="group.controls.bankAccount.controls.routingNumber" + [formControl]="group().controls.bankAccount.controls.routingNumber" required /> </bit-form-field> @@ -141,7 +141,7 @@ type PaymentMethodFormGroup = FormGroup<{ bitInput id="accountNumber" type="text" - [formControl]="group.controls.bankAccount.controls.accountNumber" + [formControl]="group().controls.bankAccount.controls.accountNumber" required /> </bit-form-field> @@ -151,7 +151,7 @@ type PaymentMethodFormGroup = FormGroup<{ id="accountHolderName" bitInput type="text" - [formControl]="group.controls.bankAccount.controls.accountHolderName" + [formControl]="group().controls.bankAccount.controls.accountHolderName" required /> </bit-form-field> @@ -159,7 +159,7 @@ type PaymentMethodFormGroup = FormGroup<{ <bit-label>{{ "bankAccountType" | i18n }}</bit-label> <bit-select id="accountHolderType" - [formControl]="group.controls.bankAccount.controls.accountHolderType" + [formControl]="group().controls.bankAccount.controls.accountHolderType" required > <bit-option [value]="''" label="-- {{ 'select' | i18n }} --"></bit-option> @@ -186,7 +186,7 @@ type PaymentMethodFormGroup = FormGroup<{ } @case ("accountCredit") { <ng-container> - @if (hasEnoughAccountCredit) { + @if (hasEnoughAccountCredit()) { <bit-callout type="info"> {{ "makeSureEnoughCredit" | i18n }} </bit-callout> @@ -204,7 +204,7 @@ type PaymentMethodFormGroup = FormGroup<{ <div class="tw-col-span-6"> <bit-form-field [disableMargin]="true"> <bit-label>{{ "country" | i18n }}</bit-label> - <bit-select [formControl]="group.controls.billingAddress.controls.country"> + <bit-select [formControl]="group().controls.billingAddress.controls.country"> @for (selectableCountry of selectableCountries; track selectableCountry.value) { <bit-option [value]="selectableCountry.value" @@ -221,7 +221,7 @@ type PaymentMethodFormGroup = FormGroup<{ <input bitInput type="text" - [formControl]="group.controls.billingAddress.controls.postalCode" + [formControl]="group().controls.billingAddress.controls.postalCode" autocomplete="postal-code" /> </bit-form-field> @@ -233,26 +233,15 @@ type PaymentMethodFormGroup = FormGroup<{ standalone: true, imports: [BillingServicesModule, PaymentLabelComponent, PopoverModule, SharedModule], }) -export class EnterPaymentMethodComponent implements OnInit { - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input({ required: true }) group!: PaymentMethodFormGroup; +export class EnterPaymentMethodComponent implements OnInit, OnDestroy { + protected readonly instanceId = Utils.newGuid(); - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input() private showBankAccount = true; - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input() showPayPal = true; - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input() showAccountCredit = false; - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input() hasEnoughAccountCredit = true; - // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals - // eslint-disable-next-line @angular-eslint/prefer-signals - @Input() includeBillingAddress = false; + readonly group = input.required<PaymentMethodFormGroup>(); + protected readonly showBankAccount = input(true); + readonly showPayPal = input(true); + readonly showAccountCredit = input(false); + readonly hasEnoughAccountCredit = input(true); + readonly includeBillingAddress = input(false); protected showBankAccount$!: Observable<boolean>; protected selectableCountries = selectableCountries; @@ -269,57 +258,62 @@ export class EnterPaymentMethodComponent implements OnInit { ngOnInit() { this.stripeService.loadStripe( + this.instanceId, { - cardNumber: "#stripe-card-number", - cardExpiry: "#stripe-card-expiry", - cardCvc: "#stripe-card-cvc", + cardNumber: `#stripe-card-number-${this.instanceId}`, + cardExpiry: `#stripe-card-expiry-${this.instanceId}`, + cardCvc: `#stripe-card-cvc-${this.instanceId}`, }, true, ); - if (this.showPayPal) { + if (this.showPayPal()) { this.braintreeService.loadBraintree("#braintree-container", false); } - if (!this.includeBillingAddress) { - this.showBankAccount$ = of(this.showBankAccount); - this.group.controls.billingAddress.disable(); + if (!this.includeBillingAddress()) { + this.showBankAccount$ = of(this.showBankAccount()); + this.group().controls.billingAddress.disable(); } else { - this.group.controls.billingAddress.patchValue({ + this.group().controls.billingAddress.patchValue({ country: "US", }); - this.showBankAccount$ = this.group.controls.billingAddress.controls.country.valueChanges.pipe( - startWith(this.group.controls.billingAddress.controls.country.value), - map((country) => this.showBankAccount && country === "US"), - ); + this.showBankAccount$ = + this.group().controls.billingAddress.controls.country.valueChanges.pipe( + startWith(this.group().controls.billingAddress.controls.country.value), + map((country) => this.showBankAccount() && country === "US"), + ); } - this.group.controls.type.valueChanges - .pipe(startWith(this.group.controls.type.value), takeUntil(this.destroy$)) + this.group() + .controls.type.valueChanges.pipe( + startWith(this.group().controls.type.value), + takeUntil(this.destroy$), + ) .subscribe((selected) => { if (selected === "bankAccount") { - this.group.controls.bankAccount.enable(); - if (this.includeBillingAddress) { - this.group.controls.billingAddress.enable(); + this.group().controls.bankAccount.enable(); + if (this.includeBillingAddress()) { + this.group().controls.billingAddress.enable(); } } else { switch (selected) { case "card": { - this.stripeService.mountElements(); - if (this.includeBillingAddress) { - this.group.controls.billingAddress.enable(); + this.stripeService.mountElements(this.instanceId); + if (this.includeBillingAddress()) { + this.group().controls.billingAddress.enable(); } break; } case "payPal": { this.braintreeService.createDropin(); - if (this.includeBillingAddress) { - this.group.controls.billingAddress.disable(); + if (this.includeBillingAddress()) { + this.group().controls.billingAddress.disable(); } break; } } - this.group.controls.bankAccount.disable(); + this.group().controls.bankAccount.disable(); } }); @@ -330,22 +324,28 @@ export class EnterPaymentMethodComponent implements OnInit { }); } + ngOnDestroy() { + this.stripeService.unloadStripe(this.instanceId); + this.destroy$.next(); + this.destroy$.complete(); + } + select = (paymentMethod: PaymentMethodOption) => - this.group.controls.type.patchValue(paymentMethod); + this.group().controls.type.patchValue(paymentMethod); tokenize = async (): Promise<TokenizedPaymentMethod | null> => { const exchange = async (paymentMethod: TokenizablePaymentMethod) => { switch (paymentMethod) { case "bankAccount": { - this.group.controls.bankAccount.markAllAsTouched(); - if (!this.group.controls.bankAccount.valid) { + this.group().controls.bankAccount.markAllAsTouched(); + if (!this.group().controls.bankAccount.valid) { throw new Error("Attempted to tokenize invalid bank account information."); } - const bankAccount = this.group.controls.bankAccount.getRawValue(); + const bankAccount = this.group().controls.bankAccount.getRawValue(); const clientSecret = await this.stripeService.createSetupIntent("bankAccount"); - const billingDetails = this.group.controls.billingAddress.enabled - ? this.group.controls.billingAddress.getRawValue() + const billingDetails = this.group().controls.billingAddress.enabled + ? this.group().controls.billingAddress.getRawValue() : undefined; return await this.stripeService.setupBankAccountPaymentMethod( clientSecret, @@ -355,10 +355,14 @@ export class EnterPaymentMethodComponent implements OnInit { } case "card": { const clientSecret = await this.stripeService.createSetupIntent("card"); - const billingDetails = this.group.controls.billingAddress.enabled - ? this.group.controls.billingAddress.getRawValue() + const billingDetails = this.group().controls.billingAddress.enabled + ? this.group().controls.billingAddress.getRawValue() : undefined; - return this.stripeService.setupCardPaymentMethod(clientSecret, billingDetails); + return this.stripeService.setupCardPaymentMethod( + this.instanceId, + clientSecret, + billingDetails, + ); } case "payPal": { return this.braintreeService.requestPaymentMethod(); @@ -410,15 +414,15 @@ export class EnterPaymentMethodComponent implements OnInit { validate = (): boolean => { if (this.selected === "bankAccount") { - this.group.controls.bankAccount.markAllAsTouched(); - return this.group.controls.bankAccount.valid; + this.group().controls.bankAccount.markAllAsTouched(); + return this.group().controls.bankAccount.valid; } return true; }; get selected(): PaymentMethodOption { - return this.group.value.type!; + return this.group().value.type!; } static getFormGroup = (): PaymentMethodFormGroup => diff --git a/apps/web/src/app/billing/services/stripe.service.spec.ts b/apps/web/src/app/billing/services/stripe.service.spec.ts new file mode 100644 index 00000000000..983aeb266ae --- /dev/null +++ b/apps/web/src/app/billing/services/stripe.service.spec.ts @@ -0,0 +1,797 @@ +import { mock, MockProxy } from "jest-mock-extended"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { BankAccount } from "@bitwarden/common/billing/models/domain"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; + +import { StripeService } from "./stripe.service"; + +// Extend Window interface to include Stripe +declare global { + interface Window { + Stripe: any; + } +} + +describe("StripeService", () => { + let service: StripeService; + let apiService: MockProxy<ApiService>; + let logService: MockProxy<LogService>; + + // Stripe SDK mocks + let mockStripeInstance: any; + let mockElements: any; + let mockCardNumber: any; + let mockCardExpiry: any; + let mockCardCvc: any; + + // DOM mocks + let mockScript: HTMLScriptElement; + let mockIframe: HTMLIFrameElement; + + beforeEach(() => { + jest.useFakeTimers(); + + // Setup service dependency mocks + apiService = mock<ApiService>(); + logService = mock<LogService>(); + + // Setup Stripe element mocks + mockCardNumber = { + mount: jest.fn(), + unmount: jest.fn(), + }; + mockCardExpiry = { + mount: jest.fn(), + unmount: jest.fn(), + }; + mockCardCvc = { + mount: jest.fn(), + unmount: jest.fn(), + }; + + // Setup Stripe Elements mock + mockElements = { + create: jest.fn((type: string) => { + switch (type) { + case "cardNumber": + return mockCardNumber; + case "cardExpiry": + return mockCardExpiry; + case "cardCvc": + return mockCardCvc; + default: + return null; + } + }), + getElement: jest.fn((type: string) => { + switch (type) { + case "cardNumber": + return mockCardNumber; + case "cardExpiry": + return mockCardExpiry; + case "cardCvc": + return mockCardCvc; + default: + return null; + } + }), + }; + + // Setup Stripe instance mock + mockStripeInstance = { + elements: jest.fn(() => mockElements), + confirmCardSetup: jest.fn(), + confirmUsBankAccountSetup: jest.fn(), + }; + + // Setup window.Stripe mock + window.Stripe = jest.fn(() => mockStripeInstance); + + // Setup DOM mocks + mockScript = { + id: "", + src: "", + onload: null, + onerror: null, + } as any; + + mockIframe = { + src: "https://js.stripe.com/v3/", + remove: jest.fn(), + } as any; + + jest.spyOn(window.document, "createElement").mockReturnValue(mockScript); + jest.spyOn(window.document, "getElementById").mockReturnValue(null); + jest.spyOn(window.document.head, "appendChild").mockReturnValue(mockScript); + jest.spyOn(window.document.head, "removeChild").mockImplementation(() => mockScript); + jest.spyOn(window.document, "querySelectorAll").mockReturnValue([mockIframe] as any); + + // Mock getComputedStyle + jest.spyOn(window, "getComputedStyle").mockReturnValue({ + getPropertyValue: (prop: string) => { + const props: Record<string, string> = { + "--color-text-main": "0, 0, 0", + "--color-text-muted": "128, 128, 128", + "--color-danger-600": "220, 38, 38", + }; + return props[prop] || ""; + }, + } as any); + + // Create service instance + service = new StripeService(apiService, logService); + }); + + afterEach(() => { + jest.clearAllTimers(); + jest.useRealTimers(); + jest.restoreAllMocks(); + }); + + // Helper function to trigger script load + const triggerScriptLoad = () => { + if (mockScript.onload) { + mockScript.onload(new Event("load")); + } + }; + + // Helper function to advance timers and flush promises + const advanceTimersAndFlush = async (ms: number) => { + jest.advanceTimersByTime(ms); + await Promise.resolve(); + }; + + describe("createSetupIntent", () => { + it("should call API with correct path for card payment", async () => { + apiService.send.mockResolvedValue("client_secret_card_123"); + + const result = await service.createSetupIntent("card"); + + expect(apiService.send).toHaveBeenCalledWith("POST", "/setup-intent/card", null, true, true); + expect(result).toBe("client_secret_card_123"); + }); + + it("should call API with correct path for bank account payment", async () => { + apiService.send.mockResolvedValue("client_secret_bank_456"); + + const result = await service.createSetupIntent("bankAccount"); + + expect(apiService.send).toHaveBeenCalledWith( + "POST", + "/setup-intent/bank-account", + null, + true, + true, + ); + expect(result).toBe("client_secret_bank_456"); + }); + + it("should return client secret from API response", async () => { + const expectedSecret = "seti_1234567890_secret_abcdefg"; + apiService.send.mockResolvedValue(expectedSecret); + + const result = await service.createSetupIntent("card"); + + expect(result).toBe(expectedSecret); + }); + + it("should propagate API errors", async () => { + const error = new Error("API error"); + apiService.send.mockRejectedValue(error); + + await expect(service.createSetupIntent("card")).rejects.toThrow("API error"); + }); + }); + + describe("loadStripe - initial load", () => { + const instanceId = "test-instance-1"; + const elementIds = { + cardNumber: "#card-number", + cardExpiry: "#card-expiry", + cardCvc: "#card-cvc", + }; + + it("should create script element with correct attributes", () => { + service.loadStripe(instanceId, elementIds, false); + + expect(window.document.createElement).toHaveBeenCalledWith("script"); + expect(mockScript.id).toBe("stripe-script"); + expect(mockScript.src).toBe("https://js.stripe.com/v3?advancedFraudSignals=false"); + }); + + it("should append script to document head", () => { + service.loadStripe(instanceId, elementIds, false); + + expect(window.document.head.appendChild).toHaveBeenCalledWith(mockScript); + }); + + it("should initialize Stripe client on script load", async () => { + service.loadStripe(instanceId, elementIds, false); + + triggerScriptLoad(); + await advanceTimersAndFlush(0); + + expect(window.Stripe).toHaveBeenCalledWith(process.env.STRIPE_KEY); + }); + + it("should create Elements instance and store in Map", async () => { + service.loadStripe(instanceId, elementIds, false); + + triggerScriptLoad(); + await advanceTimersAndFlush(50); + + expect(mockStripeInstance.elements).toHaveBeenCalled(); + expect(service["instances"].size).toBe(1); + expect(service["instances"].get(instanceId)).toBeDefined(); + }); + + it("should increment instanceCount", async () => { + service.loadStripe(instanceId, elementIds, false); + + triggerScriptLoad(); + await advanceTimersAndFlush(50); + + expect(service["instanceCount"]).toBe(1); + }); + }); + + describe("loadStripe - already loaded", () => { + const instanceId1 = "instance-1"; + const instanceId2 = "instance-2"; + const elementIds = { + cardNumber: "#card-number", + cardExpiry: "#card-expiry", + cardCvc: "#card-cvc", + }; + + beforeEach(async () => { + // Load first instance to initialize Stripe + service.loadStripe(instanceId1, elementIds, false); + triggerScriptLoad(); + await advanceTimersAndFlush(100); + }); + + it("should not create new script if already loaded", () => { + jest.clearAllMocks(); + + service.loadStripe(instanceId2, elementIds, false); + + expect(window.document.createElement).not.toHaveBeenCalled(); + expect(window.document.head.appendChild).not.toHaveBeenCalled(); + }); + + it("should immediately initialize instance when script loaded", async () => { + service.loadStripe(instanceId2, elementIds, false); + await advanceTimersAndFlush(50); + + expect(service["instances"].size).toBe(2); + expect(service["instances"].get(instanceId2)).toBeDefined(); + }); + + it("should increment instanceCount correctly", async () => { + expect(service["instanceCount"]).toBe(1); + + service.loadStripe(instanceId2, elementIds, false); + await advanceTimersAndFlush(50); + + expect(service["instanceCount"]).toBe(2); + }); + }); + + describe("loadStripe - concurrent calls", () => { + const elementIds = { + cardNumber: "#card-number", + cardExpiry: "#card-expiry", + cardCvc: "#card-cvc", + }; + + it("should handle multiple loadStripe calls sequentially", async () => { + // Test practical scenario: load instances one after another + service.loadStripe("instance-1", elementIds, false); + triggerScriptLoad(); + await advanceTimersAndFlush(100); + + service.loadStripe("instance-2", elementIds, false); + await advanceTimersAndFlush(100); + + service.loadStripe("instance-3", elementIds, false); + await advanceTimersAndFlush(100); + + // All instances should be initialized + expect(service["instances"].size).toBe(3); + expect(service["instanceCount"]).toBe(3); + expect(service["instances"].get("instance-1")).toBeDefined(); + expect(service["instances"].get("instance-2")).toBeDefined(); + expect(service["instances"].get("instance-3")).toBeDefined(); + }); + + it("should share Stripe client across instances", async () => { + // Load first instance + service.loadStripe("instance-1", elementIds, false); + triggerScriptLoad(); + await advanceTimersAndFlush(100); + + const stripeClientAfterFirst = service["stripe"]; + expect(stripeClientAfterFirst).toBeDefined(); + + // Load second instance + service.loadStripe("instance-2", elementIds, false); + await advanceTimersAndFlush(100); + + // Should reuse the same Stripe client + expect(service["stripe"]).toBe(stripeClientAfterFirst); + expect(service["instances"].size).toBe(2); + }); + }); + + describe("mountElements - success path", () => { + const instanceId = "mount-test-instance"; + const elementIds = { + cardNumber: "#card-number-mount", + cardExpiry: "#card-expiry-mount", + cardCvc: "#card-cvc-mount", + }; + + beforeEach(async () => { + service.loadStripe(instanceId, elementIds, false); + triggerScriptLoad(); + await advanceTimersAndFlush(100); + }); + + it("should mount all three card elements to DOM", async () => { + service.mountElements(instanceId); + await advanceTimersAndFlush(100); + + expect(mockCardNumber.mount).toHaveBeenCalledWith("#card-number-mount"); + expect(mockCardExpiry.mount).toHaveBeenCalledWith("#card-expiry-mount"); + expect(mockCardCvc.mount).toHaveBeenCalledWith("#card-cvc-mount"); + }); + + it("should use correct element IDs from instance", async () => { + const customIds = { + cardNumber: "#custom-card", + cardExpiry: "#custom-expiry", + cardCvc: "#custom-cvc", + }; + + service.loadStripe("custom-instance", customIds, false); + await advanceTimersAndFlush(100); + + service.mountElements("custom-instance"); + await advanceTimersAndFlush(100); + + expect(mockCardNumber.mount).toHaveBeenCalledWith("#custom-card"); + expect(mockCardExpiry.mount).toHaveBeenCalledWith("#custom-expiry"); + expect(mockCardCvc.mount).toHaveBeenCalledWith("#custom-cvc"); + }); + + it("should handle autoMount flag correctly", async () => { + const autoMountId = "auto-mount-instance"; + jest.clearAllMocks(); + + service.loadStripe(autoMountId, elementIds, true); + triggerScriptLoad(); + await advanceTimersAndFlush(150); + + // Should auto-mount without explicit call + expect(mockCardNumber.mount).toHaveBeenCalled(); + expect(mockCardExpiry.mount).toHaveBeenCalled(); + expect(mockCardCvc.mount).toHaveBeenCalled(); + }); + }); + + describe("mountElements - retry logic", () => { + const elementIds = { + cardNumber: "#card-number", + cardExpiry: "#card-expiry", + cardCvc: "#card-cvc", + }; + + it("should retry if instance not found", async () => { + service.mountElements("non-existent-instance"); + await advanceTimersAndFlush(100); + + expect(logService.warning).toHaveBeenCalledWith( + expect.stringContaining("Stripe instance non-existent-instance not found"), + ); + }); + + it("should log error after 10 failed attempts", async () => { + service.mountElements("non-existent-instance"); + + for (let i = 0; i < 10; i++) { + await advanceTimersAndFlush(100); + } + + expect(logService.error).toHaveBeenCalledWith( + expect.stringContaining("not found after 10 attempts"), + ); + }); + + it("should retry if elements not ready", async () => { + const instanceId = "retry-elements-instance"; + service.loadStripe(instanceId, elementIds, false); + triggerScriptLoad(); + await advanceTimersAndFlush(100); + + // Make elements temporarily unavailable + mockElements.getElement.mockReturnValueOnce(null); + mockElements.getElement.mockReturnValueOnce(null); + mockElements.getElement.mockReturnValueOnce(null); + + service.mountElements(instanceId); + await advanceTimersAndFlush(100); + + expect(logService.warning).toHaveBeenCalledWith( + expect.stringContaining("Some Stripe card elements"), + ); + }); + }); + + describe("setupCardPaymentMethod", () => { + const instanceId = "card-setup-instance"; + const clientSecret = "seti_card_secret_123"; + const elementIds = { + cardNumber: "#card-number", + cardExpiry: "#card-expiry", + cardCvc: "#card-cvc", + }; + + beforeEach(async () => { + service.loadStripe(instanceId, elementIds, false); + triggerScriptLoad(); + await advanceTimersAndFlush(100); + }); + + it("should call Stripe confirmCardSetup with correct parameters", async () => { + mockStripeInstance.confirmCardSetup.mockResolvedValue({ + setupIntent: { status: "succeeded", payment_method: "pm_card_123" }, + }); + + await service.setupCardPaymentMethod(instanceId, clientSecret); + + expect(mockStripeInstance.confirmCardSetup).toHaveBeenCalledWith(clientSecret, { + payment_method: { + card: mockCardNumber, + }, + }); + }); + + it("should include billing details when provided", async () => { + mockStripeInstance.confirmCardSetup.mockResolvedValue({ + setupIntent: { status: "succeeded", payment_method: "pm_card_123" }, + }); + + const billingDetails = { country: "US", postalCode: "12345" }; + await service.setupCardPaymentMethod(instanceId, clientSecret, billingDetails); + + expect(mockStripeInstance.confirmCardSetup).toHaveBeenCalledWith(clientSecret, { + payment_method: { + card: mockCardNumber, + billing_details: { + address: { + country: "US", + postal_code: "12345", + }, + }, + }, + }); + }); + + it("should throw error if instance not found", async () => { + await expect(service.setupCardPaymentMethod("non-existent", clientSecret)).rejects.toThrow( + "Payment method initialization failed. Please try again.", + ); + expect(logService.error).toHaveBeenCalledWith( + expect.stringContaining("Stripe instance non-existent not found"), + ); + }); + + it("should throw error if setup fails", async () => { + const error = { message: "Card declined" }; + mockStripeInstance.confirmCardSetup.mockResolvedValue({ error }); + + await expect(service.setupCardPaymentMethod(instanceId, clientSecret)).rejects.toEqual(error); + expect(logService.error).toHaveBeenCalledWith(error); + }); + + it("should throw error if status is not succeeded", async () => { + const error = { message: "Invalid status" }; + mockStripeInstance.confirmCardSetup.mockResolvedValue({ + setupIntent: { status: "requires_action" }, + error, + }); + + await expect(service.setupCardPaymentMethod(instanceId, clientSecret)).rejects.toEqual(error); + }); + + it("should return payment method ID on success", async () => { + mockStripeInstance.confirmCardSetup.mockResolvedValue({ + setupIntent: { status: "succeeded", payment_method: "pm_card_success_123" }, + }); + + const result = await service.setupCardPaymentMethod(instanceId, clientSecret); + + expect(result).toBe("pm_card_success_123"); + }); + }); + + describe("setupBankAccountPaymentMethod", () => { + const clientSecret = "seti_bank_secret_456"; + const bankAccount: BankAccount = { + accountHolderName: "John Doe", + routingNumber: "110000000", + accountNumber: "000123456789", + accountHolderType: "individual", + }; + + beforeEach(async () => { + // Initialize Stripe instance for bank account tests + service.loadStripe( + "bank-test-instance", + { + cardNumber: "#card", + cardExpiry: "#expiry", + cardCvc: "#cvc", + }, + false, + ); + triggerScriptLoad(); + await advanceTimersAndFlush(100); + }); + + it("should call Stripe confirmUsBankAccountSetup with bank details", async () => { + mockStripeInstance.confirmUsBankAccountSetup.mockResolvedValue({ + setupIntent: { status: "requires_action", payment_method: "pm_bank_123" }, + }); + + await service.setupBankAccountPaymentMethod(clientSecret, bankAccount); + + expect(mockStripeInstance.confirmUsBankAccountSetup).toHaveBeenCalledWith(clientSecret, { + payment_method: { + us_bank_account: { + routing_number: "110000000", + account_number: "000123456789", + account_holder_type: "individual", + }, + billing_details: { + name: "John Doe", + }, + }, + }); + }); + + it("should include billing address when provided", async () => { + mockStripeInstance.confirmUsBankAccountSetup.mockResolvedValue({ + setupIntent: { status: "requires_action", payment_method: "pm_bank_123" }, + }); + + const billingDetails = { country: "US", postalCode: "90210" }; + await service.setupBankAccountPaymentMethod(clientSecret, bankAccount, billingDetails); + + expect(mockStripeInstance.confirmUsBankAccountSetup).toHaveBeenCalledWith(clientSecret, { + payment_method: { + us_bank_account: { + routing_number: "110000000", + account_number: "000123456789", + account_holder_type: "individual", + }, + billing_details: { + name: "John Doe", + address: { + country: "US", + postal_code: "90210", + }, + }, + }, + }); + }); + + it("should omit billing address when not provided", async () => { + mockStripeInstance.confirmUsBankAccountSetup.mockResolvedValue({ + setupIntent: { status: "requires_action", payment_method: "pm_bank_123" }, + }); + + await service.setupBankAccountPaymentMethod(clientSecret, bankAccount); + + const call = mockStripeInstance.confirmUsBankAccountSetup.mock.calls[0][1]; + expect(call.payment_method.billing_details.address).toBeUndefined(); + }); + + it("should validate status is requires_action", async () => { + const error = { message: "Invalid status" }; + mockStripeInstance.confirmUsBankAccountSetup.mockResolvedValue({ + setupIntent: { status: "succeeded" }, + error, + }); + + await expect( + service.setupBankAccountPaymentMethod(clientSecret, bankAccount), + ).rejects.toEqual(error); + }); + + it("should return payment method ID on success", async () => { + mockStripeInstance.confirmUsBankAccountSetup.mockResolvedValue({ + setupIntent: { status: "requires_action", payment_method: "pm_bank_success_456" }, + }); + + const result = await service.setupBankAccountPaymentMethod(clientSecret, bankAccount); + + expect(result).toBe("pm_bank_success_456"); + }); + }); + + describe("unloadStripe - single instance", () => { + const instanceId = "unload-test-instance"; + const elementIds = { + cardNumber: "#card-number", + cardExpiry: "#card-expiry", + cardCvc: "#card-cvc", + }; + + beforeEach(async () => { + service.loadStripe(instanceId, elementIds, false); + triggerScriptLoad(); + await advanceTimersAndFlush(100); + }); + + it("should unmount all card elements", () => { + service.unloadStripe(instanceId); + + expect(mockCardNumber.unmount).toHaveBeenCalled(); + expect(mockCardExpiry.unmount).toHaveBeenCalled(); + expect(mockCardCvc.unmount).toHaveBeenCalled(); + }); + + it("should remove instance from Map", () => { + expect(service["instances"].has(instanceId)).toBe(true); + + service.unloadStripe(instanceId); + + expect(service["instances"].has(instanceId)).toBe(false); + }); + + it("should decrement instanceCount", () => { + expect(service["instanceCount"]).toBe(1); + + service.unloadStripe(instanceId); + + expect(service["instanceCount"]).toBe(0); + }); + + it("should remove script when last instance unloaded", () => { + jest.spyOn(window.document, "getElementById").mockReturnValue(mockScript); + + service.unloadStripe(instanceId); + + expect(window.document.head.removeChild).toHaveBeenCalledWith(mockScript); + }); + + it("should remove Stripe iframes after cleanup delay", async () => { + service.unloadStripe(instanceId); + + await advanceTimersAndFlush(500); + + expect(window.document.querySelectorAll).toHaveBeenCalledWith("iframe"); + expect(mockIframe.remove).toHaveBeenCalled(); + }); + }); + + describe("unloadStripe - multiple instances", () => { + const elementIds = { + cardNumber: "#card-number", + cardExpiry: "#card-expiry", + cardCvc: "#card-cvc", + }; + + beforeEach(async () => { + // Load first instance + service.loadStripe("instance-1", elementIds, false); + triggerScriptLoad(); + await advanceTimersAndFlush(100); + + // Load second instance (script already loaded) + service.loadStripe("instance-2", elementIds, false); + await advanceTimersAndFlush(100); + }); + + it("should not remove script when other instances exist", () => { + expect(service["instanceCount"]).toBe(2); + + service.unloadStripe("instance-1"); + + expect(service["instanceCount"]).toBe(1); + expect(window.document.head.removeChild).not.toHaveBeenCalled(); + }); + + it("should only cleanup specific instance", () => { + service.unloadStripe("instance-1"); + + expect(service["instances"].has("instance-1")).toBe(false); + expect(service["instances"].has("instance-2")).toBe(true); + }); + + it("should handle reference counting correctly", () => { + expect(service["instanceCount"]).toBe(2); + + service.unloadStripe("instance-1"); + expect(service["instanceCount"]).toBe(1); + + service.unloadStripe("instance-2"); + expect(service["instanceCount"]).toBe(0); + }); + }); + + describe("unloadStripe - edge cases", () => { + it("should handle unload of non-existent instance gracefully", () => { + expect(() => service.unloadStripe("non-existent")).not.toThrow(); + expect(service["instanceCount"]).toBe(0); + }); + + it("should handle duplicate unload calls", async () => { + const instanceId = "duplicate-unload"; + const elementIds = { + cardNumber: "#card-number", + cardExpiry: "#card-expiry", + cardCvc: "#card-cvc", + }; + + service.loadStripe(instanceId, elementIds, false); + triggerScriptLoad(); + await advanceTimersAndFlush(100); + + service.unloadStripe(instanceId); + expect(service["instanceCount"]).toBe(0); + + service.unloadStripe(instanceId); + expect(service["instanceCount"]).toBe(0); // Should not go negative + }); + + it("should catch and log element unmount errors", async () => { + const instanceId = "error-unmount"; + const elementIds = { + cardNumber: "#card-number", + cardExpiry: "#card-expiry", + cardCvc: "#card-cvc", + }; + + service.loadStripe(instanceId, elementIds, false); + triggerScriptLoad(); + await advanceTimersAndFlush(100); + + const unmountError = new Error("Unmount failed"); + mockCardNumber.unmount.mockImplementation(() => { + throw unmountError; + }); + + service.unloadStripe(instanceId); + + expect(logService.error).toHaveBeenCalledWith( + expect.stringContaining("Error unmounting Stripe elements"), + unmountError, + ); + }); + }); + + describe("element styling", () => { + it("should apply correct CSS custom properties", () => { + const options = service["getElementOptions"]("cardNumber"); + + expect(options.style.base.color).toBe("rgb(0, 0, 0)"); + expect(options.style.base["::placeholder"].color).toBe("rgb(128, 128, 128)"); + expect(options.style.invalid.color).toBe("rgb(0, 0, 0)"); + expect(options.style.invalid.borderColor).toBe("rgb(220, 38, 38)"); + }); + + it("should remove placeholder for cardNumber and cardCvc", () => { + const cardNumberOptions = service["getElementOptions"]("cardNumber"); + const cardCvcOptions = service["getElementOptions"]("cardCvc"); + const cardExpiryOptions = service["getElementOptions"]("cardExpiry"); + + expect(cardNumberOptions.placeholder).toBe(""); + expect(cardCvcOptions.placeholder).toBe(""); + expect(cardExpiryOptions.placeholder).toBeUndefined(); + }); + }); +}); diff --git a/apps/web/src/app/billing/services/stripe.service.ts b/apps/web/src/app/billing/services/stripe.service.ts index a2eb7cd98f2..9aabab9beb0 100644 --- a/apps/web/src/app/billing/services/stripe.service.ts +++ b/apps/web/src/app/billing/services/stripe.service.ts @@ -8,8 +8,6 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { BankAccountPaymentMethod, CardPaymentMethod } from "../payment/types"; -import { BillingServicesModule } from "./billing-services.module"; - type SetupBankAccountRequest = { payment_method: { us_bank_account: { @@ -39,15 +37,21 @@ type SetupCardRequest = { }; }; -@Injectable({ providedIn: BillingServicesModule }) +@Injectable({ providedIn: "root" }) export class StripeService { - private stripe: any; - private elements: any; - private elementIds: { - cardNumber: string; - cardExpiry: string; - cardCvc: string; - }; + // Shared/Global - One Stripe client for entire application + private stripe: any = null; + private stripeScriptLoaded = false; + private instanceCount = 0; + + // Per-Instance - Isolated Elements for each component + private instances = new Map< + string, + { + elements: any; + elementIds: { cardNumber: string; cardExpiry: string; cardCvc: string }; + } + >(); constructor( private apiService: ApiService, @@ -76,53 +80,121 @@ export class StripeService { * Loads [Stripe JS]{@link https://docs.stripe.com/js} in the <head> element of the current page and mounts * Stripe credit card [elements]{@link https://docs.stripe.com/js/elements_object/create} into the HTML elements with the provided element IDS. * We do this to avoid having to load the Stripe JS SDK on every page of the Web Vault given many pages contain sensitive information. + * @param instanceId - Unique identifier for this component instance. * @param elementIds - The ID attributes of the HTML elements used to load the Stripe JS credit card elements. * @param autoMount - A flag indicating whether you want to immediately mount the Stripe credit card elements. */ loadStripe( + instanceId: string, elementIds: { cardNumber: string; cardExpiry: string; cardCvc: string }, autoMount: boolean, ) { - this.elementIds = elementIds; - const script = window.document.createElement("script"); - script.id = "stripe-script"; - script.src = "https://js.stripe.com/v3?advancedFraudSignals=false"; - script.onload = async () => { - const window$ = window as any; - this.stripe = window$.Stripe(process.env.STRIPE_KEY); - this.elements = this.stripe.elements(); - setTimeout(() => { - this.elements.create("cardNumber", this.getElementOptions("cardNumber")); - this.elements.create("cardExpiry", this.getElementOptions("cardExpiry")); - this.elements.create("cardCvc", this.getElementOptions("cardCvc")); - if (autoMount) { - this.mountElements(); - } - }, 50); - }; + // Check if script is already loaded + if (this.stripeScriptLoaded) { + // Script already loaded, initialize this instance immediately + this.initializeInstance(instanceId, elementIds, autoMount); + } else if (!window.document.getElementById("stripe-script")) { + // Script not loaded and not loading, start loading it + const script = window.document.createElement("script"); + script.id = "stripe-script"; + script.src = "https://js.stripe.com/v3?advancedFraudSignals=false"; + script.onload = async () => { + const window$ = window as any; + this.stripe = window$.Stripe(process.env.STRIPE_KEY); + this.stripeScriptLoaded = true; // Mark as loaded after script loads - window.document.head.appendChild(script); + // Initialize this instance after script loads + this.initializeInstance(instanceId, elementIds, autoMount); + }; + window.document.head.appendChild(script); + } else { + // Script is currently loading, wait for it + this.initializeInstance(instanceId, elementIds, autoMount); + } } - mountElements(attempt: number = 1) { - setTimeout(() => { - if (!this.elements) { - this.logService.warning(`Stripe elements are missing, retrying for attempt ${attempt}...`); - this.mountElements(attempt + 1); + private initializeInstance( + instanceId: string, + elementIds: { cardNumber: string; cardExpiry: string; cardCvc: string }, + autoMount: boolean, + attempt: number = 1, + ) { + // Wait for stripe to be available if script just loaded + if (!this.stripe) { + if (attempt < 10) { + this.logService.warning( + `Stripe not yet loaded for instance ${instanceId}, retrying attempt ${attempt}...`, + ); + setTimeout( + () => this.initializeInstance(instanceId, elementIds, autoMount, attempt + 1), + 50, + ); } else { - const cardNumber = this.elements.getElement("cardNumber"); - const cardExpiry = this.elements.getElement("cardExpiry"); - const cardCVC = this.elements.getElement("cardCvc"); + this.logService.error( + `Stripe failed to load for instance ${instanceId} after ${attempt} attempts`, + ); + } + return; + } + + // Create a new Elements instance for this component + const elements = this.stripe.elements(); + + // Store instance data + this.instances.set(instanceId, { elements, elementIds }); + + // Increment instance count now that instance is successfully initialized + this.instanceCount++; + + // Create the card elements + setTimeout(() => { + elements.create("cardNumber", this.getElementOptions("cardNumber")); + elements.create("cardExpiry", this.getElementOptions("cardExpiry")); + elements.create("cardCvc", this.getElementOptions("cardCvc")); + + if (autoMount) { + this.mountElements(instanceId); + } + }, 50); + } + + mountElements(instanceId: string, attempt: number = 1) { + setTimeout(() => { + const instance = this.instances.get(instanceId); + + if (!instance) { + if (attempt < 10) { + this.logService.warning( + `Stripe instance ${instanceId} not found, retrying for attempt ${attempt}...`, + ); + this.mountElements(instanceId, attempt + 1); + } else { + this.logService.error( + `Stripe instance ${instanceId} not found after ${attempt} attempts`, + ); + } + return; + } + + if (!instance.elements) { + this.logService.warning( + `Stripe elements for instance ${instanceId} are missing, retrying for attempt ${attempt}...`, + ); + this.mountElements(instanceId, attempt + 1); + } else { + const cardNumber = instance.elements.getElement("cardNumber"); + const cardExpiry = instance.elements.getElement("cardExpiry"); + const cardCVC = instance.elements.getElement("cardCvc"); if ([cardNumber, cardExpiry, cardCVC].some((element) => !element)) { this.logService.warning( - `Some Stripe card elements are missing, retrying for attempt ${attempt}...`, + `Some Stripe card elements for instance ${instanceId} are missing, retrying for attempt ${attempt}...`, ); - this.mountElements(attempt + 1); + this.mountElements(instanceId, attempt + 1); } else { - cardNumber.mount(this.elementIds.cardNumber); - cardExpiry.mount(this.elementIds.cardExpiry); - cardCVC.mount(this.elementIds.cardCvc); + cardNumber.mount(instance.elementIds.cardNumber); + cardExpiry.mount(instance.elementIds.cardExpiry); + cardCVC.mount(instance.elementIds.cardCvc); } } }, 100); @@ -132,6 +204,9 @@ export class StripeService { * Creates a Stripe [SetupIntent]{@link https://docs.stripe.com/api/setup_intents} and uses the resulting client secret * to invoke the Stripe JS [confirmUsBankAccountSetup]{@link https://docs.stripe.com/js/setup_intents/confirm_us_bank_account_setup} method, * thereby creating and storing a Stripe [PaymentMethod]{@link https://docs.stripe.com/api/payment_methods}. + * @param clientSecret - The client secret from the SetupIntent. + * @param bankAccount - The bank account details. + * @param billingDetails - Optional billing details. * @returns The ID of the newly created PaymentMethod. */ async setupBankAccountPaymentMethod( @@ -171,13 +246,28 @@ export class StripeService { * Creates a Stripe [SetupIntent]{@link https://docs.stripe.com/api/setup_intents} and uses the resulting client secret * to invoke the Stripe JS [confirmCardSetup]{@link https://docs.stripe.com/js/setup_intents/confirm_card_setup} method, * thereby creating and storing a Stripe [PaymentMethod]{@link https://docs.stripe.com/api/payment_methods}. + * @param instanceId - Unique identifier for the component instance. + * @param clientSecret - The client secret from the SetupIntent. + * @param billingDetails - Optional billing details. * @returns The ID of the newly created PaymentMethod. */ async setupCardPaymentMethod( + instanceId: string, clientSecret: string, billingDetails?: { country: string; postalCode: string }, ): Promise<string> { - const cardNumber = this.elements.getElement("cardNumber"); + const instance = this.instances.get(instanceId); + if (!instance) { + const availableInstances = Array.from(this.instances.keys()); + this.logService.error( + `Stripe instance ${instanceId} not found. ` + + `Available instances: [${availableInstances.join(", ")}]. ` + + `This may occur if the component was destroyed during the payment flow.`, + ); + throw new Error("Payment method initialization failed. Please try again."); + } + + const cardNumber = instance.elements.getElement("cardNumber"); const request: SetupCardRequest = { payment_method: { card: cardNumber, @@ -200,24 +290,77 @@ export class StripeService { } /** - * Removes {@link https://docs.stripe.com/js} from the <head> element of the current page as well as all - * Stripe-managed <iframe> elements. + * Removes the Stripe Elements instance for the specified component. + * Only removes the Stripe script and iframes when the last instance is unloaded. + * @param instanceId - Unique identifier for the component instance to unload. */ - unloadStripe() { - const script = window.document.getElementById("stripe-script"); - window.document.head.removeChild(script); - window.setTimeout(() => { - const iFrames = Array.from(window.document.querySelectorAll("iframe")).filter( - (element) => element.src != null && element.src.indexOf("stripe") > -1, - ); - iFrames.forEach((iFrame) => { - try { - window.document.body.removeChild(iFrame); - } catch (error) { - this.logService.error(error); + unloadStripe(instanceId: string) { + const instance = this.instances.get(instanceId); + + // Only proceed if instance was actually initialized + if (!instance) { + return; + } + + // Unmount all elements for this instance + if (instance.elements) { + try { + const cardNumber = instance.elements.getElement("cardNumber"); + const cardExpiry = instance.elements.getElement("cardExpiry"); + const cardCvc = instance.elements.getElement("cardCvc"); + + if (cardNumber) { + cardNumber.unmount(); } - }); - }, 500); + if (cardExpiry) { + cardExpiry.unmount(); + } + if (cardCvc) { + cardCvc.unmount(); + } + } catch (error) { + this.logService.error( + `Error unmounting Stripe elements for instance ${instanceId}:`, + error, + ); + } + } + + // Remove instance from map + this.instances.delete(instanceId); + + // Decrement instance count (only if instance was initialized) + this.instanceCount--; + + // Only remove script and iframes when no instances remain + if (this.instanceCount <= 0) { + if (this.instanceCount < 0) { + this.logService.error( + `Stripe instance count became negative (${this.instanceCount}). This indicates a reference counting bug.`, + ); + } + this.instanceCount = 0; + this.stripeScriptLoaded = false; + this.stripe = null; + + const script = window.document.getElementById("stripe-script"); + if (script) { + window.document.head.removeChild(script); + } + + window.setTimeout(() => { + const iFrames = Array.from(window.document.querySelectorAll("iframe")).filter( + (element) => element.src != null && element.src.indexOf("stripe") > -1, + ); + iFrames.forEach((iFrame) => { + try { + iFrame.remove(); + } catch (error) { + this.logService.error(error); + } + }); + }, 500); + } } private getElementOptions(element: "cardNumber" | "cardExpiry" | "cardCvc"): any { From 9ee4fd0e44420376411e1ebb70882a8cc6984ff3 Mon Sep 17 00:00:00 2001 From: Matt Andreko <mandreko@bitwarden.com> Date: Wed, 19 Nov 2025 14:15:12 -0500 Subject: [PATCH 61/75] Workflow corrections (#17392) Co-authored-by: Amy Galles <9685081+AmyLGalles@users.noreply.github.com> --- .github/workflows/build-desktop.yml | 2 +- .github/workflows/publish-cli.yml | 2 +- .github/workflows/publish-desktop.yml | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 1877374a525..d3566535b65 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -627,7 +627,7 @@ jobs: if: ${{ needs.setup.outputs.has_secrets == 'true' }} uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: - name: Bitwarden-Installer-${{ env._PACKAGE_VERSION }}..exe + name: Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe path: apps/desktop/dist/nsis-web/Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error diff --git a/.github/workflows/publish-cli.yml b/.github/workflows/publish-cli.yml index 08d3f1de503..426947526a4 100644 --- a/.github/workflows/publish-cli.yml +++ b/.github/workflows/publish-cli.yml @@ -206,7 +206,7 @@ jobs: uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - + - name: Get Node version id: retrieve-node-version working-directory: ./ diff --git a/.github/workflows/publish-desktop.yml b/.github/workflows/publish-desktop.yml index f42f9811d77..b17312950e9 100644 --- a/.github/workflows/publish-desktop.yml +++ b/.github/workflows/publish-desktop.yml @@ -336,7 +336,7 @@ jobs: ruby-version: '3.4.7' bundler-cache: false working-directory: apps/desktop - + - name: Install Fastlane working-directory: apps/desktop run: gem install fastlane @@ -377,15 +377,15 @@ jobs: echo "📦 Publishing build $BUILD_NUMBER to Mac App Store" IS_DRY_RUN="false" fi - + echo "📝 Release notes (${#CHANGELOG} chars): ${CHANGELOG:0:100}..." - + # Validate changelog length (App Store limit is 4000 chars) if [ ${#CHANGELOG} -gt 4000 ]; then echo "❌ Release notes too long: ${#CHANGELOG} characters (max 4000)" exit 1 fi - + fastlane publish --verbose \ app_version:"${_PKG_VERSION}" \ build_number:"$BUILD_NUMBER" \ From e44ab1b411a92d1b3734a2b1af06ed62ec8be7bd Mon Sep 17 00:00:00 2001 From: Addison Beck <github@addisonbeck.com> Date: Wed, 19 Nov 2025 14:57:59 -0500 Subject: [PATCH 62/75] fix: enable dynamic URLs for Chrome web accessible resources (#17429) This commit adds use_dynamic_url: true to the extension's web_accessible_resources configuration. When enabled, Chrome generates random session-based GUIDs for extension resource URLs instead of using the predictable static extension ID. This enhances privacy by making extension resource URLs unpredictable and prevents third-party enumeration of installed extensions. The feature is supported in Chrome 102+ and changes resource URLs from chrome-extension://[static-id]/resource to chrome-extension://[random-guid]/resource, with GUIDs regenerating each browser session while maintaining all existing extension functionality. Addresses: https://bitwarden.atlassian.net/browse/PM-28344 --- apps/browser/src/manifest.v3.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/browser/src/manifest.v3.json b/apps/browser/src/manifest.v3.json index 73403d2321e..2b2aa0f117b 100644 --- a/apps/browser/src/manifest.v3.json +++ b/apps/browser/src/manifest.v3.json @@ -164,7 +164,8 @@ "overlay/menu.html", "popup/fonts/*" ], - "matches": ["<all_urls>"] + "matches": ["<all_urls>"], + "use_dynamic_url": true } ], "__firefox__browser_specific_settings": { From 6d1c474fc50165bd316f100d0319629b664ed406 Mon Sep 17 00:00:00 2001 From: Addison Beck <github@addisonbeck.com> Date: Wed, 19 Nov 2025 15:13:41 -0500 Subject: [PATCH 63/75] fix: add world: MAIN to Firefox page script registration (#17466) * chore: update @types/firefox-webext-browser * fix: add world: MAIN to Firefox page script registration * review: add world property to registration type --- .../fido2/background/abstractions/fido2.background.ts | 1 + .../autofill/fido2/background/fido2.background.spec.ts | 1 + .../src/autofill/fido2/background/fido2.background.ts | 1 + package-lock.json | 8 ++++---- package.json | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/browser/src/autofill/fido2/background/abstractions/fido2.background.ts b/apps/browser/src/autofill/fido2/background/abstractions/fido2.background.ts index 6ad069ad56e..b341be28ebb 100644 --- a/apps/browser/src/autofill/fido2/background/abstractions/fido2.background.ts +++ b/apps/browser/src/autofill/fido2/background/abstractions/fido2.background.ts @@ -13,6 +13,7 @@ type SharedFido2ScriptRegistrationOptions = SharedFido2ScriptInjectionDetails & matches: string[]; excludeMatches: string[]; allFrames: true; + world?: "MAIN" | "ISOLATED"; }; type Fido2ExtensionMessage = { diff --git a/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts b/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts index 752851b3d37..adb59b8f845 100644 --- a/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts +++ b/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts @@ -203,6 +203,7 @@ describe("Fido2Background", () => { { file: Fido2ContentScript.PageScriptDelayAppend }, { file: Fido2ContentScript.ContentScript }, ], + world: "MAIN", ...sharedRegistrationOptions, }); }); diff --git a/apps/browser/src/autofill/fido2/background/fido2.background.ts b/apps/browser/src/autofill/fido2/background/fido2.background.ts index 22ee4a1822d..a8b016a14d6 100644 --- a/apps/browser/src/autofill/fido2/background/fido2.background.ts +++ b/apps/browser/src/autofill/fido2/background/fido2.background.ts @@ -176,6 +176,7 @@ export class Fido2Background implements Fido2BackgroundInterface { { file: await this.getFido2PageScriptAppendFileName() }, { file: Fido2ContentScript.ContentScript }, ], + world: "MAIN", ...this.sharedRegistrationOptions, }); } diff --git a/package-lock.json b/package-lock.json index 674dcabf122..b017272cd77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -101,7 +101,7 @@ "@storybook/web-components-webpack5": "8.6.12", "@tailwindcss/container-queries": "0.1.1", "@types/chrome": "0.1.28", - "@types/firefox-webext-browser": "120.0.4", + "@types/firefox-webext-browser": "143.0.0", "@types/inquirer": "8.2.10", "@types/jest": "29.5.14", "@types/jsdom": "21.1.7", @@ -14083,9 +14083,9 @@ "license": "MIT" }, "node_modules/@types/firefox-webext-browser": { - "version": "120.0.4", - "resolved": "https://registry.npmjs.org/@types/firefox-webext-browser/-/firefox-webext-browser-120.0.4.tgz", - "integrity": "sha512-lBrpf08xhiZBigrtdQfUaqX1UauwZ+skbFiL8u2Tdra/rklkKadYmIzTwkNZSWtuZ7OKpFqbE2HHfDoFqvZf6w==", + "version": "143.0.0", + "resolved": "https://registry.npmjs.org/@types/firefox-webext-browser/-/firefox-webext-browser-143.0.0.tgz", + "integrity": "sha512-865dYKMOP0CllFyHmgXV4IQgVL51OSQQCwSoihQ17EwugePKFSAZRc0EI+y7Ly4q7j5KyURlA7LgRpFieO4JOw==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index 8322d065eba..54e2685bbec 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@storybook/web-components-webpack5": "8.6.12", "@tailwindcss/container-queries": "0.1.1", "@types/chrome": "0.1.28", - "@types/firefox-webext-browser": "120.0.4", + "@types/firefox-webext-browser": "143.0.0", "@types/inquirer": "8.2.10", "@types/jest": "29.5.14", "@types/jsdom": "21.1.7", From d86c918e7161df3cfa27f179394a3f6ad7f2373b Mon Sep 17 00:00:00 2001 From: Andy Pixley <3723676+pixman20@users.noreply.github.com> Date: Wed, 19 Nov 2025 16:11:51 -0500 Subject: [PATCH 64/75] [BRE-1303] Providing method for pinning Chrome extension ID for dev (#17432) --- .github/workflows/build-browser.yml | 17 +++++++++++ apps/browser/package.json | 7 ++++- apps/browser/scripts/update-manifest-dev.sh | 34 +++++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100755 apps/browser/scripts/update-manifest-dev.sh diff --git a/.github/workflows/build-browser.yml b/.github/workflows/build-browser.yml index dc71d33d605..772cb25d98d 100644 --- a/.github/workflows/build-browser.yml +++ b/.github/workflows/build-browser.yml @@ -218,6 +218,7 @@ jobs: source_archive_name_prefix: "" archive_name_prefix: "" npm_command_prefix: "dist:" + npm_package_dev_prefix: "package:dev:" readable: "open source license" type: "oss" - build_prefix: "bit-" @@ -225,6 +226,7 @@ jobs: source_archive_name_prefix: "bit-" archive_name_prefix: "bit-" npm_command_prefix: "dist:bit:" + npm_package_dev_prefix: "package:bit:dev:" readable: "commercial license" type: "commercial" browser: @@ -232,6 +234,8 @@ jobs: npm_command_suffix: "chrome" archive_name: "dist-chrome.zip" artifact_name: "dist-chrome-MV3" + artifact_name_dev: "dev-chrome-MV3" + archive_name_dev: "dev-chrome.zip" - name: "edge" npm_command_suffix: "edge" archive_name: "dist-edge.zip" @@ -338,6 +342,19 @@ jobs: path: browser-source/apps/browser/dist/${{matrix.license_type.archive_name_prefix}}${{ matrix.browser.archive_name }} if-no-files-found: error + - name: Package dev extension + if: ${{ matrix.browser.archive_name_dev != '' }} + run: npm run ${{ matrix.license_type.npm_package_dev_prefix }}${{ matrix.browser.npm_command_suffix }} + working-directory: browser-source/apps/browser + + - name: Upload dev extension artifact + if: ${{ matrix.browser.archive_name_dev != '' }} + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: ${{ matrix.license_type.artifact_prefix }}${{ matrix.browser.artifact_name_dev }}-${{ env._BUILD_NUMBER }}.zip + path: browser-source/apps/browser/dist/${{matrix.license_type.archive_name_prefix}}${{ matrix.browser.archive_name_dev }} + if-no-files-found: error + build-safari: name: Build Safari - ${{ matrix.license_type.readable }} diff --git a/apps/browser/package.json b/apps/browser/package.json index 72e112e62f7..a6a88b53db0 100644 --- a/apps/browser/package.json +++ b/apps/browser/package.json @@ -6,6 +6,8 @@ "build:bit": "npm run build:bit:chrome", "build:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 NODE_OPTIONS=\"--max-old-space-size=8192\" webpack", "build:bit:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 NODE_OPTIONS=\"--max-old-space-size=8192\" webpack -c ../../bitwarden_license/bit-browser/webpack.config.js", + "build:dev:chrome": "npm run build:chrome && npm run update:dev:chrome", + "build:bit:dev:chrome": "npm run build:bit:chrome && npm run update:dev:chrome", "build:edge": "cross-env BROWSER=edge MANIFEST_VERSION=3 NODE_OPTIONS=\"--max-old-space-size=8192\" webpack", "build:bit:edge": "cross-env BROWSER=edge MANIFEST_VERSION=3 NODE_OPTIONS=\"--max-old-space-size=8192\" webpack -c ../../bitwarden_license/bit-browser/webpack.config.js", "build:firefox": "cross-env BROWSER=firefox NODE_OPTIONS=\"--max-old-space-size=8192\" webpack", @@ -55,9 +57,12 @@ "dist:bit:opera:mv3": "cross-env MANIFEST_VERSION=3 npm run dist:bit:opera", "dist:safari:mv3": "cross-env MANIFEST_VERSION=3 npm run dist:safari", "dist:bit:safari:mv3": "cross-env MANIFEST_VERSION=3 npm run dist:bit:safari", + "package:dev:chrome": "npm run update:dev:chrome && ./scripts/compress.sh dev-chrome.zip", + "package:bit:dev:chrome": "npm run update:dev:chrome && ./scripts/compress.sh bit-dev-chrome.zip", "test": "jest", "test:watch": "jest --watch", "test:watch:all": "jest --watchAll", - "test:clearCache": "jest --clear-cache" + "test:clearCache": "jest --clear-cache", + "update:dev:chrome": "./scripts/update-manifest-dev.sh" } } diff --git a/apps/browser/scripts/update-manifest-dev.sh b/apps/browser/scripts/update-manifest-dev.sh new file mode 100755 index 00000000000..2823d4cb510 --- /dev/null +++ b/apps/browser/scripts/update-manifest-dev.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +#### +# Update the manifest key in the build directory. +#### + +set -e +set -u +set -x +set -o pipefail + +SCRIPT_ROOT="$(dirname "$0")" +BUILD_DIR="$SCRIPT_ROOT/../build" + +# Check if build directory exists +if [ -d "$BUILD_DIR" ]; then + cd "$BUILD_DIR" + + # Update manifest with dev public key + MANIFEST_PATH="./manifest.json" + + # Generated arbitrary public key from Chrome Dev Console to pin side-loaded extension IDs during development + DEV_PUBLIC_KEY='MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuIvjtsAVWZM0i5jFhSZcrmwgaf3KWcxM5F16LNDNeivC1EqJ+H5xNZ5R9UN5ueHA2xyyYAOlxY07OcY6CKTGJRJyefbUhszb66sdx26SV5gVkCois99fKBlsbSbd6und/BJYmoFUWvFCNNVH+OxLMqMQWjMMhM2ItLqTYi7dxRE5qd+7LwQpnGG2vTkm/O7nu8U3CtkfcIAGLsiTd7/iuytcMDnC0qFM5tJyY/5I+9QOhpUJ7Ybj3C18BDWDORhqxutWv+MSw//SgUn2/lPQrnrKq7FIVQL7FxxEPqkv4QwFvaixps1cBbMdJ1Ygit1z5JldoSyNxzCa5vVcJLecMQIDAQAB' + + MANIFEST_PATH_TMP="${MANIFEST_PATH}.tmp" + if jq --arg key "$DEV_PUBLIC_KEY" '.key = $key' "$MANIFEST_PATH" > "$MANIFEST_PATH_TMP"; then + mv "$MANIFEST_PATH_TMP" "$MANIFEST_PATH" + echo "Updated manifest key in $MANIFEST_PATH" + else + echo "ERROR: Failed to update manifest with jq" + rm -f "$MANIFEST_PATH_TMP" + exit 1 + fi +fi From 7c4db701b933b91e36d437a6f199e68b1522c367 Mon Sep 17 00:00:00 2001 From: Jonathan Prusik <jprusik@users.noreply.github.com> Date: Wed, 19 Nov 2025 19:14:05 -0500 Subject: [PATCH 65/75] [PM-27797] Prevent host page manipulation of inline menu popover attribute (#17400) * turn off inline experience if host page aggressively competes for top of top-layer * add alert message for top-layer hijack scenarios * widen the backoff threshold * refactor backoff logic to include popover attribute mutations * improve getPageIsOpaque check * do not attempt inline menu insertion if it has been disabled for security concerns * fix typo * cleanup * add tests --- apps/browser/src/_locales/en/messages.json | 3 + ...tofill-inline-menu-content.service.spec.ts | 490 +++++++++++++++++- .../autofill-inline-menu-content.service.ts | 138 ++++- 3 files changed, 611 insertions(+), 20 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index f793b24a0e9..5cc7c30bfb4 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -2436,6 +2436,9 @@ } } }, + "topLayerHijackWarning": { + "message": "This page is interfering with the Bitwarden experience. The Bitwarden inline menu has been temporarily disabled as a safety measure." + }, "setMasterPassword": { "message": "Set master password" }, diff --git a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.spec.ts index f1a74556b24..b7bd24c537b 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.spec.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.spec.ts @@ -53,13 +53,35 @@ describe("AutofillInlineMenuContentService", () => { }); }); + describe("messageHandlers", () => { + it("returns the extension message handlers", () => { + const handlers = autofillInlineMenuContentService.messageHandlers; + + expect(handlers).toHaveProperty("closeAutofillInlineMenu"); + expect(handlers).toHaveProperty("appendAutofillInlineMenuToDom"); + }); + }); + describe("isElementInlineMenu", () => { - it("returns true if the passed element is the inline menu", () => { + it("returns true if the passed element is the inline menu list", () => { const element = document.createElement("div"); autofillInlineMenuContentService["listElement"] = element; expect(autofillInlineMenuContentService.isElementInlineMenu(element)).toBe(true); }); + + it("returns true if the passed element is the inline menu button", () => { + const element = document.createElement("div"); + autofillInlineMenuContentService["buttonElement"] = element; + + expect(autofillInlineMenuContentService.isElementInlineMenu(element)).toBe(true); + }); + + it("returns false if the passed element is not the inline menu", () => { + const element = document.createElement("div"); + + expect(autofillInlineMenuContentService.isElementInlineMenu(element)).toBe(false); + }); }); describe("extension message handlers", () => { @@ -388,7 +410,7 @@ describe("AutofillInlineMenuContentService", () => { }); it("closes the inline menu if the page body is not sufficiently opaque", async () => { - document.querySelector("html").style.opacity = "0.9"; + document.documentElement.style.opacity = "0.9"; document.body.style.opacity = "0"; await autofillInlineMenuContentService["handlePageMutations"]([mockBodyMutationRecord]); @@ -397,7 +419,7 @@ describe("AutofillInlineMenuContentService", () => { }); it("closes the inline menu if the page html is not sufficiently opaque", async () => { - document.querySelector("html").style.opacity = "0.3"; + document.documentElement.style.opacity = "0.3"; document.body.style.opacity = "0.7"; await autofillInlineMenuContentService["handlePageMutations"]([mockHTMLMutationRecord]); @@ -406,7 +428,7 @@ describe("AutofillInlineMenuContentService", () => { }); it("does not close the inline menu if the page html and body is sufficiently opaque", async () => { - document.querySelector("html").style.opacity = "0.9"; + document.documentElement.style.opacity = "0.9"; document.body.style.opacity = "1"; await autofillInlineMenuContentService["handlePageMutations"]([mockBodyMutationRecord]); await waitForIdleCallback(); @@ -599,5 +621,465 @@ describe("AutofillInlineMenuContentService", () => { overlayElement: AutofillOverlayElement.List, }); }); + + it("clears the persistent last child override timeout", () => { + jest.useFakeTimers(); + const clearTimeoutSpy = jest.spyOn(globalThis, "clearTimeout"); + autofillInlineMenuContentService["handlePersistentLastChildOverrideTimeout"] = setTimeout( + jest.fn(), + 500, + ); + + autofillInlineMenuContentService.destroy(); + + expect(clearTimeoutSpy).toHaveBeenCalled(); + }); + + it("unobserves page attributes", () => { + const disconnectSpy = jest.spyOn( + autofillInlineMenuContentService["htmlMutationObserver"], + "disconnect", + ); + + autofillInlineMenuContentService.destroy(); + + expect(disconnectSpy).toHaveBeenCalled(); + }); + }); + + describe("getOwnedTagNames", () => { + it("returns an empty array when no elements are created", () => { + expect(autofillInlineMenuContentService.getOwnedTagNames()).toEqual([]); + }); + + it("returns the button element tag name", () => { + const buttonElement = document.createElement("div"); + autofillInlineMenuContentService["buttonElement"] = buttonElement; + + const tagNames = autofillInlineMenuContentService.getOwnedTagNames(); + + expect(tagNames).toContain("DIV"); + }); + + it("returns both button and list element tag names", () => { + const buttonElement = document.createElement("div"); + const listElement = document.createElement("span"); + autofillInlineMenuContentService["buttonElement"] = buttonElement; + autofillInlineMenuContentService["listElement"] = listElement; + + const tagNames = autofillInlineMenuContentService.getOwnedTagNames(); + + expect(tagNames).toEqual(["DIV", "SPAN"]); + }); + }); + + describe("getUnownedTopLayerItems", () => { + beforeEach(() => { + document.body.innerHTML = ""; + }); + + it("returns the tag names from button and list elements", () => { + const buttonElement = document.createElement("div"); + buttonElement.setAttribute("popover", "manual"); + autofillInlineMenuContentService["buttonElement"] = buttonElement; + + const listElement = document.createElement("span"); + listElement.setAttribute("popover", "manual"); + autofillInlineMenuContentService["listElement"] = listElement; + + /** Mock querySelectorAll to avoid :modal selector issues in jsdom */ + const querySelectorAllSpy = jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([] as any); + + const items = autofillInlineMenuContentService.getUnownedTopLayerItems(); + + expect(querySelectorAllSpy).toHaveBeenCalled(); + expect(items.length).toBe(0); + }); + + it("calls querySelectorAll with correct selector when includeCandidates is false", () => { + /** Mock querySelectorAll to avoid :modal selector issues in jsdom */ + const querySelectorAllSpy = jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([] as any); + + autofillInlineMenuContentService.getUnownedTopLayerItems(false); + + const calledSelector = querySelectorAllSpy.mock.calls[0][0]; + expect(calledSelector).toContain(":modal"); + expect(calledSelector).toContain(":popover-open"); + }); + + it("includes candidates selector when requested", () => { + /** Mock querySelectorAll to avoid :modal selector issues in jsdom */ + const querySelectorAllSpy = jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([] as any); + + autofillInlineMenuContentService.getUnownedTopLayerItems(true); + + const calledSelector = querySelectorAllSpy.mock.calls[0][0]; + expect(calledSelector).toContain("[popover], dialog"); + }); + }); + + describe("refreshTopLayerPosition", () => { + it("does nothing when inline menu is disabled", () => { + const getUnownedTopLayerItemsSpy = jest.spyOn( + autofillInlineMenuContentService, + "getUnownedTopLayerItems", + ); + + autofillInlineMenuContentService["inlineMenuEnabled"] = false; + const buttonElement = document.createElement("div"); + autofillInlineMenuContentService["buttonElement"] = buttonElement; + + autofillInlineMenuContentService.refreshTopLayerPosition(); + + // Should exit early and not call `getUnownedTopLayerItems` + expect(getUnownedTopLayerItemsSpy).not.toHaveBeenCalled(); + }); + + it("does nothing when no other top layer items exist", () => { + const buttonElement = document.createElement("div"); + autofillInlineMenuContentService["buttonElement"] = buttonElement; + jest + .spyOn(autofillInlineMenuContentService, "getUnownedTopLayerItems") + .mockReturnValue([] as any); + + const getElementsByTagSpy = jest.spyOn(globalThis.document, "getElementsByTagName"); + + autofillInlineMenuContentService.refreshTopLayerPosition(); + + // Should exit early and not get inline elements to refresh + expect(getElementsByTagSpy).not.toHaveBeenCalled(); + }); + + it("refreshes button popover when button is in document", () => { + jest + .spyOn(autofillInlineMenuContentService, "getUnownedTopLayerItems") + .mockReturnValue([document.createElement("div")] as any); + + const buttonElement = document.createElement("div"); + buttonElement.setAttribute("popover", "manual"); + buttonElement.showPopover = jest.fn(); + buttonElement.hidePopover = jest.fn(); + document.body.appendChild(buttonElement); + autofillInlineMenuContentService["buttonElement"] = buttonElement; + + autofillInlineMenuContentService.refreshTopLayerPosition(); + + expect(buttonElement.hidePopover).toHaveBeenCalled(); + expect(buttonElement.showPopover).toHaveBeenCalled(); + }); + + it("refreshes list popover when list is in document", () => { + jest + .spyOn(autofillInlineMenuContentService, "getUnownedTopLayerItems") + .mockReturnValue([document.createElement("div")] as any); + + const listElement = document.createElement("div"); + listElement.setAttribute("popover", "manual"); + listElement.showPopover = jest.fn(); + listElement.hidePopover = jest.fn(); + document.body.appendChild(listElement); + autofillInlineMenuContentService["listElement"] = listElement; + + autofillInlineMenuContentService.refreshTopLayerPosition(); + + expect(listElement.hidePopover).toHaveBeenCalled(); + expect(listElement.showPopover).toHaveBeenCalled(); + }); + }); + + describe("checkAndUpdateRefreshCount", () => { + beforeEach(() => { + jest.useFakeTimers(); + jest.setSystemTime(new Date("2023-01-01T00:00:00.000Z")); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it("does nothing when inline menu is disabled", () => { + autofillInlineMenuContentService["inlineMenuEnabled"] = false; + + autofillInlineMenuContentService["checkAndUpdateRefreshCount"]("topLayer"); + + expect(autofillInlineMenuContentService["refreshCountWithinTimeThreshold"].topLayer).toBe(0); + }); + + it("increments refresh count when within time threshold", () => { + autofillInlineMenuContentService["lastTrackedTimestamp"].topLayer = Date.now() - 1000; + + autofillInlineMenuContentService["checkAndUpdateRefreshCount"]("topLayer"); + + expect(autofillInlineMenuContentService["refreshCountWithinTimeThreshold"].topLayer).toBe(1); + }); + + it("resets count when outside time threshold", () => { + autofillInlineMenuContentService["lastTrackedTimestamp"].topLayer = Date.now() - 6000; + autofillInlineMenuContentService["refreshCountWithinTimeThreshold"].topLayer = 5; + + autofillInlineMenuContentService["checkAndUpdateRefreshCount"]("topLayer"); + + expect(autofillInlineMenuContentService["refreshCountWithinTimeThreshold"].topLayer).toBe(0); + }); + + it("disables inline menu and shows alert when count exceeds threshold", () => { + const alertSpy = jest.spyOn(globalThis.window, "alert").mockImplementation(); + const checkPageRisksSpy = jest.spyOn( + autofillInlineMenuContentService as any, + "checkPageRisks", + ); + autofillInlineMenuContentService["lastTrackedTimestamp"].topLayer = Date.now() - 1000; + autofillInlineMenuContentService["refreshCountWithinTimeThreshold"].topLayer = 6; + + autofillInlineMenuContentService["checkAndUpdateRefreshCount"]("topLayer"); + + expect(autofillInlineMenuContentService["inlineMenuEnabled"]).toBe(false); + expect(alertSpy).toHaveBeenCalled(); + expect(checkPageRisksSpy).toHaveBeenCalled(); + }); + }); + + describe("refreshPopoverAttribute", () => { + it("calls checkAndUpdateRefreshCount with popoverAttribute type", () => { + const checkSpy = jest.spyOn( + autofillInlineMenuContentService as any, + "checkAndUpdateRefreshCount", + ); + const element = document.createElement("div"); + element.setAttribute("popover", "auto"); + element.showPopover = jest.fn(); + + autofillInlineMenuContentService["refreshPopoverAttribute"](element); + + expect(checkSpy).toHaveBeenCalledWith("popoverAttribute"); + expect(element.getAttribute("popover")).toBe("manual"); + expect(element.showPopover).toHaveBeenCalled(); + }); + }); + + describe("handleInlineMenuElementMutationObserverUpdate - popover attribute", () => { + it("refreshes popover attribute when changed from manual", () => { + const element = document.createElement("div"); + element.setAttribute("popover", "auto"); + element.showPopover = jest.fn(); + const refreshSpy = jest.spyOn( + autofillInlineMenuContentService as any, + "refreshPopoverAttribute", + ); + autofillInlineMenuContentService["buttonElement"] = element; + + const mockMutation = createMutationRecordMock({ + target: element, + type: "attributes", + attributeName: "popover", + }); + + autofillInlineMenuContentService["handleInlineMenuElementMutationObserverUpdate"]([ + mockMutation, + ]); + + expect(refreshSpy).toHaveBeenCalledWith(element); + }); + + it("does not refresh popover attribute when already manual", () => { + const element = document.createElement("div"); + element.setAttribute("popover", "manual"); + const refreshSpy = jest.spyOn( + autofillInlineMenuContentService as any, + "refreshPopoverAttribute", + ); + autofillInlineMenuContentService["buttonElement"] = element; + + const mockMutation = createMutationRecordMock({ + target: element, + type: "attributes", + attributeName: "popover", + }); + + autofillInlineMenuContentService["handleInlineMenuElementMutationObserverUpdate"]([ + mockMutation, + ]); + + expect(refreshSpy).not.toHaveBeenCalled(); + }); + }); + + describe("appendInlineMenuElements when disabled", () => { + beforeEach(() => { + observeContainerMutationsSpy.mockImplementation(); + }); + + it("does not append button when inline menu is disabled", async () => { + autofillInlineMenuContentService["inlineMenuEnabled"] = false; + jest.spyOn(globalThis.document.body, "appendChild"); + + sendMockExtensionMessage({ + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.Button, + }); + await flushPromises(); + + expect(globalThis.document.body.appendChild).not.toHaveBeenCalled(); + }); + + it("does not append list when inline menu is disabled", async () => { + autofillInlineMenuContentService["inlineMenuEnabled"] = false; + jest.spyOn(globalThis.document.body, "appendChild"); + + sendMockExtensionMessage({ + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.List, + }); + await flushPromises(); + + expect(globalThis.document.body.appendChild).not.toHaveBeenCalled(); + }); + }); + + describe("custom element creation for non-Firefox browsers", () => { + beforeEach(() => { + autofillInlineMenuContentService["isFirefoxBrowser"] = false; + observeContainerMutationsSpy.mockImplementation(); + }); + + it("creates a custom element for button in non-Firefox browsers", () => { + const definespy = jest.spyOn(globalThis.customElements, "define"); + + sendMockExtensionMessage({ + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.Button, + }); + + expect(definespy).toHaveBeenCalled(); + expect(autofillInlineMenuContentService["buttonElement"]).toBeDefined(); + expect(autofillInlineMenuContentService["buttonElement"]?.tagName).not.toBe("DIV"); + }); + + it("creates a custom element for list in non-Firefox browsers", () => { + const defineSpy = jest.spyOn(globalThis.customElements, "define"); + + sendMockExtensionMessage({ + command: "appendAutofillInlineMenuToDom", + overlayElement: AutofillOverlayElement.List, + }); + + expect(defineSpy).toHaveBeenCalled(); + expect(autofillInlineMenuContentService["listElement"]).toBeDefined(); + expect(autofillInlineMenuContentService["listElement"]?.tagName).not.toBe("DIV"); + }); + }); + + describe("getPageIsOpaque", () => { + it("returns false when no page elements exist", () => { + jest.spyOn(globalThis.document, "querySelectorAll").mockReturnValue([] as any); + + const result = autofillInlineMenuContentService["getPageIsOpaque"](); + + expect(result).toBe(false); + }); + + it("returns true when all html and body nodes have sufficient opacity", () => { + jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([document.documentElement, document.body] as any); + jest + .spyOn(globalThis.window, "getComputedStyle") + .mockImplementation(() => ({ opacity: "1" }) as CSSStyleDeclaration); + + const result = autofillInlineMenuContentService["getPageIsOpaque"](); + + expect(result).toBe(true); + }); + + it("returns false when html opacity is below threshold", () => { + jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([document.documentElement, document.body] as any); + let callCount = 0; + jest.spyOn(globalThis.window, "getComputedStyle").mockImplementation(() => { + callCount++; + return { opacity: callCount === 1 ? "0.5" : "1" } as CSSStyleDeclaration; + }); + + const result = autofillInlineMenuContentService["getPageIsOpaque"](); + + expect(result).toBe(false); + }); + + it("returns false when body opacity is below threshold", () => { + jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([document.documentElement, document.body] as any); + let callCount = 0; + jest.spyOn(globalThis.window, "getComputedStyle").mockImplementation(() => { + callCount++; + return { opacity: callCount === 1 ? "1" : "0.5" } as CSSStyleDeclaration; + }); + + const result = autofillInlineMenuContentService["getPageIsOpaque"](); + + expect(result).toBe(false); + }); + + it("returns false when opacity of at least one duplicate body is below threshold", () => { + const duplicateBody = document.createElement("body"); + jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([document.documentElement, document.body, duplicateBody] as any); + let callCount = 0; + jest.spyOn(globalThis.window, "getComputedStyle").mockImplementation(() => { + callCount++; + + let opacityValue = "0.5"; + switch (callCount) { + case 1: + opacityValue = "1"; + break; + case 2: + opacityValue = "0.7"; + break; + default: + break; + } + + return { opacity: opacityValue } as CSSStyleDeclaration; + }); + + const result = autofillInlineMenuContentService["getPageIsOpaque"](); + + expect(result).toBe(false); + }); + + it("returns true when opacity is above threshold", () => { + jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([document.documentElement, document.body] as any); + jest + .spyOn(globalThis.window, "getComputedStyle") + .mockImplementation(() => ({ opacity: "0.7" }) as CSSStyleDeclaration); + + const result = autofillInlineMenuContentService["getPageIsOpaque"](); + + expect(result).toBe(true); + }); + + it("returns false when opacity is at threshold", () => { + jest + .spyOn(globalThis.document, "querySelectorAll") + .mockReturnValue([document.documentElement, document.body] as any); + jest + .spyOn(globalThis.window, "getComputedStyle") + .mockImplementation(() => ({ opacity: "0.6" }) as CSSStyleDeclaration); + + const result = autofillInlineMenuContentService["getPageIsOpaque"](); + + expect(result).toBe(false); + }); }); }); diff --git a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts index be93e863275..b61e5e19d53 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/content/autofill-inline-menu-content.service.ts @@ -22,6 +22,19 @@ import { import { AutofillInlineMenuButtonIframe } from "../iframe-content/autofill-inline-menu-button-iframe"; import { AutofillInlineMenuListIframe } from "../iframe-content/autofill-inline-menu-list-iframe"; +const experienceValidationBackoffThresholds = { + topLayer: { + countLimit: 5, + timeSpanLimit: 5000, + }, + popoverAttribute: { + countLimit: 10, + timeSpanLimit: 5000, + }, +}; + +type BackoffCheckType = keyof typeof experienceValidationBackoffThresholds; + export class AutofillInlineMenuContentService implements AutofillInlineMenuContentServiceInterface { private readonly sendExtensionMessage = sendExtensionMessage; private readonly generateRandomCustomElementName = generateRandomCustomElementName; @@ -35,6 +48,19 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte private bodyMutationObserver: MutationObserver; private inlineMenuElementsMutationObserver: MutationObserver; private containerElementMutationObserver: MutationObserver; + private refreshCountWithinTimeThreshold: { [key in BackoffCheckType]: number } = { + topLayer: 0, + popoverAttribute: 0, + }; + private lastTrackedTimestamp = { + topLayer: Date.now(), + popoverAttribute: Date.now(), + }; + /** + * Distinct from preventing inline menu script injection, this is for cases + * where the page is subsequently determined to be risky. + */ + private inlineMenuEnabled = true; private mutationObserverIterations = 0; private mutationObserverIterationsResetTimeout: number | NodeJS.Timeout; private handlePersistentLastChildOverrideTimeout: number | NodeJS.Timeout; @@ -140,6 +166,10 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * Updates the position of both the inline menu button and inline menu list. */ private async appendInlineMenuElements({ overlayElement }: AutofillExtensionMessage) { + if (!this.inlineMenuEnabled) { + return; + } + if (overlayElement === AutofillOverlayElement.Button) { return this.appendButtonElement(); } @@ -151,6 +181,10 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * Updates the position of the inline menu button. */ private async appendButtonElement(): Promise<void> { + if (!this.inlineMenuEnabled) { + return; + } + if (!this.buttonElement) { this.createButtonElement(); this.updateCustomElementDefaultStyles(this.buttonElement); @@ -167,6 +201,10 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * Updates the position of the inline menu list. */ private async appendListElement(): Promise<void> { + if (!this.inlineMenuEnabled) { + return; + } + if (!this.listElement) { this.createListElement(); this.updateCustomElementDefaultStyles(this.listElement); @@ -219,6 +257,10 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * to create the element if it already exists in the DOM. */ private createButtonElement() { + if (!this.inlineMenuEnabled) { + return; + } + if (this.isFirefoxBrowser) { this.buttonElement = globalThis.document.createElement("div"); this.buttonElement.setAttribute("popover", "manual"); @@ -247,6 +289,10 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * to create the element if it already exists in the DOM. */ private createListElement() { + if (!this.inlineMenuEnabled) { + return; + } + if (this.isFirefoxBrowser) { this.listElement = globalThis.document.createElement("div"); this.listElement.setAttribute("popover", "manual"); @@ -381,14 +427,23 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte } const element = record.target as HTMLElement; - if (record.attributeName !== "style") { - this.removeModifiedElementAttributes(element); + if (record.attributeName === "popover" && this.inlineMenuEnabled) { + const attributeValue = element.getAttribute(record.attributeName); + if (attributeValue !== "manual") { + this.refreshPopoverAttribute(element); + } continue; } - element.removeAttribute("style"); - this.updateCustomElementDefaultStyles(element); + if (record.attributeName === "style") { + element.removeAttribute("style"); + this.updateCustomElementDefaultStyles(element); + + continue; + } + + this.removeModifiedElementAttributes(element); } }; @@ -402,7 +457,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte const attributes = Array.from(element.attributes); for (let attributeIndex = 0; attributeIndex < attributes.length; attributeIndex++) { const attribute = attributes[attributeIndex]; - if (attribute.name === "style") { + if (attribute.name === "style" || attribute.name === "popover") { continue; } @@ -432,7 +487,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte private checkPageRisks = async () => { const pageIsOpaque = await this.getPageIsOpaque(); - const risksFound = !pageIsOpaque; + const risksFound = !pageIsOpaque || !this.inlineMenuEnabled; if (risksFound) { this.closeInlineMenu(); @@ -483,7 +538,49 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte return otherTopLayeritems; }; + /** + * Internally track owned injected experience refreshes as a side-effect + * of host page interference. + */ + private checkAndUpdateRefreshCount = (countType: BackoffCheckType) => { + if (!this.inlineMenuEnabled) { + return; + } + + const { countLimit, timeSpanLimit } = experienceValidationBackoffThresholds[countType]; + const now = Date.now(); + const timeSinceLastTrackedRefresh = now - this.lastTrackedTimestamp[countType]; + const currentlyWithinTimeThreshold = timeSinceLastTrackedRefresh <= timeSpanLimit; + const withinCountThreshold = this.refreshCountWithinTimeThreshold[countType] <= countLimit; + + if (currentlyWithinTimeThreshold) { + if (withinCountThreshold) { + this.refreshCountWithinTimeThreshold[countType]++; + } else { + // Set inline menu to be off; page is aggressively trying to take top position of top layer + this.inlineMenuEnabled = false; + void this.checkPageRisks(); + + const warningMessage = chrome.i18n.getMessage("topLayerHijackWarning"); + globalThis.window.alert(warningMessage); + } + } else { + this.lastTrackedTimestamp[countType] = now; + this.refreshCountWithinTimeThreshold[countType] = 0; + } + }; + + private refreshPopoverAttribute = (element: HTMLElement) => { + this.checkAndUpdateRefreshCount("popoverAttribute"); + element.setAttribute("popover", "manual"); + element.showPopover(); + }; + refreshTopLayerPosition = () => { + if (!this.inlineMenuEnabled) { + return; + } + const otherTopLayerItems = this.getUnownedTopLayerItems(); // No need to refresh if there are no other top-layer items @@ -497,6 +594,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte const listInDocument = this.listElement && (globalThis.document.getElementsByTagName(this.listElement.tagName)[0] as HTMLElement); + if (buttonInDocument) { buttonInDocument.hidePopover(); buttonInDocument.showPopover(); @@ -506,6 +604,10 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte listInDocument.hidePopover(); listInDocument.showPopover(); } + + if (buttonInDocument || listInDocument) { + this.checkAndUpdateRefreshCount("topLayer"); + } }; /** @@ -515,24 +617,28 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte * `body` (enforced elsewhere). */ private getPageIsOpaque = () => { - // These are computed style values, so we don't need to worry about non-float values - // for `opacity`, here // @TODO for definitive checks, traverse up the node tree from the inline menu container; // nodes can exist between `html` and `body` - const htmlElement = globalThis.document.querySelector("html"); - const bodyElement = globalThis.document.querySelector("body"); + /** + * `querySelectorAll` for (non-standard) cases where the page has additional copies of + * page nodes that should be unique + */ + const pageElements = globalThis.document.querySelectorAll("html, body"); - if (!htmlElement || !bodyElement) { + if (!pageElements.length) { return false; } - const htmlOpacity = globalThis.window.getComputedStyle(htmlElement)?.opacity || "0"; - const bodyOpacity = globalThis.window.getComputedStyle(bodyElement)?.opacity || "0"; + return [...pageElements].every((element) => { + // These are computed style values, so we don't need to worry about non-float values + // for `opacity`, here + const elementOpacity = globalThis.window.getComputedStyle(element)?.opacity || "0"; - // Any value above this is considered "opaque" for our purposes - const opacityThreshold = 0.6; + // Any value above this is considered "opaque" for our purposes + const opacityThreshold = 0.6; - return parseFloat(htmlOpacity) > opacityThreshold && parseFloat(bodyOpacity) > opacityThreshold; + return parseFloat(elementOpacity) > opacityThreshold; + }); }; /** From 5f27452ac22b2b70faa81892c103561d1db61da6 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann <mail@quexten.com> Date: Thu, 20 Nov 2025 02:41:59 +0100 Subject: [PATCH 66/75] Fix desktop not launching (#17485) --- apps/desktop/src/platform/popup-modal-styles.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/platform/popup-modal-styles.ts b/apps/desktop/src/platform/popup-modal-styles.ts index ae46ebb5c76..5c5619bd463 100644 --- a/apps/desktop/src/platform/popup-modal-styles.ts +++ b/apps/desktop/src/platform/popup-modal-styles.ts @@ -45,11 +45,11 @@ export function applyMainWindowStyles(window: BrowserWindow, existingWindowState // need to guard against null/undefined values if (existingWindowState?.width && existingWindowState?.height) { - window.setSize(existingWindowState.width, existingWindowState.height); + window.setSize(Math.floor(existingWindowState.width), Math.floor(existingWindowState.height)); } if (existingWindowState?.x && existingWindowState?.y) { - window.setPosition(existingWindowState.x, existingWindowState.y); + window.setPosition(Math.floor(existingWindowState.x), Math.floor(existingWindowState.y)); } window.setWindowButtonVisibility?.(true); From 9e6d0cce35196a53c9305820333dc701207d9280 Mon Sep 17 00:00:00 2001 From: rr-bw <102181210+rr-bw@users.noreply.github.com> Date: Wed, 19 Nov 2025 19:00:18 -0800 Subject: [PATCH 67/75] feat(marketing-initiated-premium): Auth [PM-27542] Write fromMarketing value to state (#17470) --- .../registration-finish.component.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts b/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts index 7e7b9131fac..19e7c1feabd 100644 --- a/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts +++ b/libs/auth/src/angular/registration/registration-finish/registration-finish.component.ts @@ -5,6 +5,7 @@ import { Component, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute, Params, Router, RouterModule } from "@angular/router"; import { Subject, firstValueFrom } from "rxjs"; +import { PremiumInterestStateService } from "@bitwarden/angular/billing/services/premium-interest/premium-interest-state.service.abstraction"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options"; import { AccountApiService } from "@bitwarden/common/auth/abstractions/account-api.service"; @@ -31,6 +32,12 @@ import { PasswordInputResult } from "../../input-password/password-input-result" import { RegistrationFinishService } from "./registration-finish.service"; +const MarketingInitiative = Object.freeze({ + Premium: "premium", +} as const); + +type MarketingInitiative = (typeof MarketingInitiative)[keyof typeof MarketingInitiative]; + // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ @@ -46,6 +53,12 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy { submitting = false; email: string; + /** + * Indicates that the user is coming from a marketing page designed to streamline + * users who intend to setup a premium subscription after registration. + */ + premiumInterest = false; + // Note: this token is the email verification token. When it is supplied as a query param, // it either comes from the email verification email or, if email verification is disabled server side // via global settings, it comes directly from the registration-start component directly. @@ -79,6 +92,7 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy { private logService: LogService, private anonLayoutWrapperDataService: AnonLayoutWrapperDataService, private loginSuccessHandlerService: LoginSuccessHandlerService, + private premiumInterestStateService: PremiumInterestStateService, ) {} async ngOnInit() { @@ -126,6 +140,10 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy { this.providerInviteToken = qParams.providerInviteToken; this.providerUserId = qParams.providerUserId; } + + if (qParams.fromMarketing != null && qParams.fromMarketing === MarketingInitiative.Premium) { + this.premiumInterest = true; + } } private async initOrgInviteFlowIfPresent(): Promise<boolean> { @@ -190,6 +208,13 @@ export class RegistrationFinishComponent implements OnInit, OnDestroy { await this.loginSuccessHandlerService.run(authenticationResult.userId); + if (this.premiumInterest) { + await this.premiumInterestStateService.setPremiumInterest( + authenticationResult.userId, + true, + ); + } + await this.router.navigate(["/vault"]); } catch (e) { // If login errors, redirect to login page per product. Don't show error From b00987180d8702ecf7908470a538e83cfcd00ce1 Mon Sep 17 00:00:00 2001 From: Nick Krantz <125900171+nick-livefront@users.noreply.github.com> Date: Thu, 20 Nov 2025 08:26:47 -0600 Subject: [PATCH 68/75] [PM-26688][PM-27710] Delay skeletons from showing + search (#17394) * add custom operator for loading skeleton delays * add `isCipherSearching$` observable to search service * prevent vault skeleton from showing immediately * add skeleton for search + delay to sends * update fade-in-out component selector * add fade-in-out component for generic use * address memory leak by using defer to encapsulate `skeletonShownAt` * add missing provider --- .../popup/send-v2/send-v2.component.html | 4 +- .../tools/popup/send-v2/send-v2.component.ts | 13 ++- .../vault-fade-in-out.component.html | 1 + .../vault-fade-in-out.component.ts | 20 ++++ .../vault-v2/vault-v2.component.html | 82 ++++++++----- .../vault-v2/vault-v2.component.spec.ts | 5 + .../components/vault-v2/vault-v2.component.ts | 22 +++- .../src/vault/abstractions/search.service.ts | 3 + .../src/vault/services/search.service.ts | 15 ++- .../utils/skeleton-loading.operator.spec.ts | 109 ++++++++++++++++++ .../vault/utils/skeleton-loading.operator.ts | 59 ++++++++++ 11 files changed, 295 insertions(+), 38 deletions(-) create mode 100644 apps/browser/src/vault/popup/components/vault-fade-in-out/vault-fade-in-out.component.html create mode 100644 apps/browser/src/vault/popup/components/vault-fade-in-out/vault-fade-in-out.component.ts create mode 100644 libs/common/src/vault/utils/skeleton-loading.operator.spec.ts create mode 100644 libs/common/src/vault/utils/skeleton-loading.operator.ts diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.html b/apps/browser/src/tools/popup/send-v2/send-v2.component.html index 0bcbd47a145..47ecd7564dc 100644 --- a/apps/browser/src/tools/popup/send-v2/send-v2.component.html +++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.html @@ -47,8 +47,8 @@ <app-send-list-items-container [headerText]="title | i18n" [sends]="sends$ | async" /> </ng-container> @if (showSkeletonsLoaders$ | async) { - <vault-fade-in-skeleton> + <vault-fade-in-out-skeleton> <vault-loading-skeleton></vault-loading-skeleton> - </vault-fade-in-skeleton> + </vault-fade-in-out-skeleton> } </popup-page> diff --git a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts index 43a1119deca..e3baba53c42 100644 --- a/apps/browser/src/tools/popup/send-v2/send-v2.component.ts +++ b/apps/browser/src/tools/popup/send-v2/send-v2.component.ts @@ -15,6 +15,8 @@ import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; +import { skeletonLoadingDelay } from "@bitwarden/common/vault/utils/skeleton-loading.operator"; import { ButtonModule, CalloutModule, @@ -95,8 +97,16 @@ export class SendV2Component implements OnDestroy { /** Skeleton Loading State */ protected showSkeletonsLoaders$ = combineLatest([ this.sendsLoading$, + this.searchService.isSendSearching$, this.skeletonFeatureFlag$, - ]).pipe(map(([loading, skeletonsEnabled]) => loading && skeletonsEnabled)); + ]).pipe( + map( + ([loading, cipherSearching, skeletonsEnabled]) => + (loading || cipherSearching) && skeletonsEnabled, + ), + distinctUntilChanged(), + skeletonLoadingDelay(), + ); protected title: string = "allSends"; protected noItemIcon = NoSendsIcon; @@ -110,6 +120,7 @@ export class SendV2Component implements OnDestroy { private policyService: PolicyService, private accountService: AccountService, private configService: ConfigService, + private searchService: SearchService, ) { combineLatest([ this.sendItemsService.emptyList$, diff --git a/apps/browser/src/vault/popup/components/vault-fade-in-out/vault-fade-in-out.component.html b/apps/browser/src/vault/popup/components/vault-fade-in-out/vault-fade-in-out.component.html new file mode 100644 index 00000000000..6dbc7430638 --- /dev/null +++ b/apps/browser/src/vault/popup/components/vault-fade-in-out/vault-fade-in-out.component.html @@ -0,0 +1 @@ +<ng-content></ng-content> diff --git a/apps/browser/src/vault/popup/components/vault-fade-in-out/vault-fade-in-out.component.ts b/apps/browser/src/vault/popup/components/vault-fade-in-out/vault-fade-in-out.component.ts new file mode 100644 index 00000000000..a30a447833b --- /dev/null +++ b/apps/browser/src/vault/popup/components/vault-fade-in-out/vault-fade-in-out.component.ts @@ -0,0 +1,20 @@ +import { animate, style, transition, trigger } from "@angular/animations"; +import { ChangeDetectionStrategy, Component, HostBinding } from "@angular/core"; + +@Component({ + selector: "vault-fade-in-out", + templateUrl: "./vault-fade-in-out.component.html", + animations: [ + trigger("fadeInOut", [ + transition(":enter", [ + style({ opacity: 0 }), + animate("100ms ease-in", style({ opacity: 1 })), + ]), + transition(":leave", [animate("300ms ease-out", style({ opacity: 0 }))]), + ]), + ], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class VaultFadeInOutComponent { + @HostBinding("@fadeInOut") fadeInOut = true; +} diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html index faaa6a40e98..7a5a99c8100 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.html @@ -8,20 +8,32 @@ </ng-container> </popup-header> - <div - *ngIf="vaultState === VaultStateEnum.Empty" - class="tw-flex tw-flex-col tw-h-full tw-justify-center" - > - <bit-no-items [icon]="vaultIcon"> - <ng-container slot="title">{{ "yourVaultIsEmpty" | i18n }}</ng-container> - <ng-container slot="description"> - <p bitTypography="body2" class="tw-mx-6 tw-mt-2">{{ "emptyVaultDescription" | i18n }}</p> - </ng-container> - <a slot="button" bitButton buttonType="secondary" [routerLink]="['/add-cipher']"> - {{ "newLogin" | i18n }} - </a> - </bit-no-items> - </div> + <ng-template #emptyVaultTemplate> + <div + *ngIf="vaultState === VaultStateEnum.Empty" + class="tw-flex tw-flex-col tw-h-full tw-justify-center" + > + <bit-no-items [icon]="vaultIcon"> + <ng-container slot="title">{{ "yourVaultIsEmpty" | i18n }}</ng-container> + <ng-container slot="description"> + <p bitTypography="body2" class="tw-mx-6 tw-mt-2"> + {{ "emptyVaultDescription" | i18n }} + </p> + </ng-container> + <a slot="button" bitButton buttonType="secondary" [routerLink]="['/add-cipher']"> + {{ "newLogin" | i18n }} + </a> + </bit-no-items> + </div> + </ng-template> + + @if (skeletonFeatureFlag$ | async) { + <vault-fade-in-out *ngIf="vaultState === VaultStateEnum.Empty"> + <ng-container *ngTemplateOutlet="emptyVaultTemplate"></ng-container> + </vault-fade-in-out> + } @else { + <ng-container *ngTemplateOutlet="emptyVaultTemplate"></ng-container> + } <blocked-injection-banner *ngIf="vaultState !== VaultStateEnum.Empty" @@ -95,22 +107,32 @@ </div> </div> - <ng-container *ngIf="vaultState === null"> - <app-autofill-vault-list-items></app-autofill-vault-list-items> - <app-vault-list-items-container - [title]="'favorites' | i18n" - [ciphers]="(favoriteCiphers$ | async) || []" - id="favorites" - collapsibleKey="favorites" - ></app-vault-list-items-container> - <app-vault-list-items-container - [title]="'allItems' | i18n" - [ciphers]="(remainingCiphers$ | async) || []" - id="allItems" - disableSectionMargin - collapsibleKey="allItems" - ></app-vault-list-items-container> - </ng-container> + <ng-template #vaultContentTemplate> + <ng-container *ngIf="vaultState === null"> + <app-autofill-vault-list-items></app-autofill-vault-list-items> + <app-vault-list-items-container + [title]="'favorites' | i18n" + [ciphers]="(favoriteCiphers$ | async) || []" + id="favorites" + collapsibleKey="favorites" + ></app-vault-list-items-container> + <app-vault-list-items-container + [title]="'allItems' | i18n" + [ciphers]="(remainingCiphers$ | async) || []" + id="allItems" + disableSectionMargin + collapsibleKey="allItems" + ></app-vault-list-items-container> + </ng-container> + </ng-template> + + @if (skeletonFeatureFlag$ | async) { + <vault-fade-in-out *ngIf="vaultState === null"> + <ng-container *ngTemplateOutlet="vaultContentTemplate"></ng-container> + </vault-fade-in-out> + } @else { + <ng-container *ngTemplateOutlet="vaultContentTemplate"></ng-container> + } </ng-container> @if (showSkeletonsLoaders$ | async) { diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.spec.ts index 563ec5f9709..5563cd3033b 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.spec.ts @@ -23,6 +23,7 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { RestrictedItemTypesService } from "@bitwarden/common/vault/services/restricted-item-types.service"; import { TaskService } from "@bitwarden/common/vault/tasks"; import { DialogService } from "@bitwarden/components"; @@ -259,6 +260,10 @@ describe("VaultV2Component", () => { getFeatureFlag$: (_: string) => of(false), }, }, + { + provide: SearchService, + useValue: { isCipherSearching$: of(false) }, + }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); diff --git a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts index 499e9b76757..471e6e70601 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/vault-v2.component.ts @@ -32,8 +32,10 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { CipherId, CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; +import { skeletonLoadingDelay } from "@bitwarden/common/vault/utils/skeleton-loading.operator"; import { ButtonModule, DialogService, @@ -54,6 +56,7 @@ import { VaultPopupListFiltersService } from "../../services/vault-popup-list-fi import { VaultPopupLoadingService } from "../../services/vault-popup-loading.service"; import { VaultPopupScrollPositionService } from "../../services/vault-popup-scroll-position.service"; import { AtRiskPasswordCalloutComponent } from "../at-risk-callout/at-risk-password-callout.component"; +import { VaultFadeInOutComponent } from "../vault-fade-in-out/vault-fade-in-out.component"; import { VaultFadeInOutSkeletonComponent } from "../vault-fade-in-out-skeleton/vault-fade-in-out-skeleton.component"; import { VaultLoadingSkeletonComponent } from "../vault-loading-skeleton/vault-loading-skeleton.component"; @@ -100,6 +103,7 @@ type VaultState = UnionOfValues<typeof VaultState>; TypographyModule, VaultLoadingSkeletonComponent, VaultFadeInOutSkeletonComponent, + VaultFadeInOutComponent, ], }) export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { @@ -129,7 +133,7 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { }), ); - private skeletonFeatureFlag$ = this.configService.getFeatureFlag$( + protected skeletonFeatureFlag$ = this.configService.getFeatureFlag$( FeatureFlag.VaultLoadingSkeletons, ); @@ -183,9 +187,18 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { map(([loading, skeletonsEnabled]) => loading && !skeletonsEnabled), ); - /** When true, show skeleton loading state */ - protected showSkeletonsLoaders$ = combineLatest([this.loading$, this.skeletonFeatureFlag$]).pipe( - map(([loading, skeletonsEnabled]) => loading && skeletonsEnabled), + /** When true, show skeleton loading state with debouncing to prevent flicker */ + protected showSkeletonsLoaders$ = combineLatest([ + this.loading$, + this.searchService.isCipherSearching$, + this.skeletonFeatureFlag$, + ]).pipe( + map( + ([loading, cipherSearching, skeletonsEnabled]) => + (loading || cipherSearching) && skeletonsEnabled, + ), + distinctUntilChanged(), + skeletonLoadingDelay(), ); protected newItemItemValues$: Observable<NewItemInitialValues> = @@ -228,6 +241,7 @@ export class VaultV2Component implements OnInit, AfterViewInit, OnDestroy { private liveAnnouncer: LiveAnnouncer, private i18nService: I18nService, private configService: ConfigService, + private searchService: SearchService, ) { combineLatest([ this.vaultPopupItemsService.emptyVault$, diff --git a/libs/common/src/vault/abstractions/search.service.ts b/libs/common/src/vault/abstractions/search.service.ts index 233dee9ec75..29575ec3af9 100644 --- a/libs/common/src/vault/abstractions/search.service.ts +++ b/libs/common/src/vault/abstractions/search.service.ts @@ -6,6 +6,9 @@ import { CipherView } from "../models/view/cipher.view"; import { CipherViewLike } from "../utils/cipher-view-like-utils"; export abstract class SearchService { + abstract isCipherSearching$: Observable<boolean>; + abstract isSendSearching$: Observable<boolean>; + abstract indexedEntityId$(userId: UserId): Observable<IndexedEntityId | null>; abstract clearIndex(userId: UserId): Promise<void>; diff --git a/libs/common/src/vault/services/search.service.ts b/libs/common/src/vault/services/search.service.ts index 80fddda42d5..0b34bd3863f 100644 --- a/libs/common/src/vault/services/search.service.ts +++ b/libs/common/src/vault/services/search.service.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import * as lunr from "lunr"; -import { Observable, firstValueFrom, map } from "rxjs"; +import { BehaviorSubject, Observable, firstValueFrom, map } from "rxjs"; import { Jsonify } from "type-fest"; import { perUserCache$ } from "@bitwarden/common/vault/utils/observable-utilities"; @@ -81,6 +81,12 @@ export class SearchService implements SearchServiceAbstraction { private readonly defaultSearchableMinLength: number = 2; private searchableMinLength: number = this.defaultSearchableMinLength; + private _isCipherSearching$ = new BehaviorSubject<boolean>(false); + isCipherSearching$: Observable<boolean> = this._isCipherSearching$.asObservable(); + + private _isSendSearching$ = new BehaviorSubject<boolean>(false); + isSendSearching$: Observable<boolean> = this._isSendSearching$.asObservable(); + constructor( private logService: LogService, private i18nService: I18nService, @@ -223,6 +229,7 @@ export class SearchService implements SearchServiceAbstraction { filter: ((cipher: C) => boolean) | ((cipher: C) => boolean)[] = null, ciphers: C[], ): Promise<C[]> { + this._isCipherSearching$.next(true); const results: C[] = []; const searchStartTime = performance.now(); if (query != null) { @@ -243,6 +250,7 @@ export class SearchService implements SearchServiceAbstraction { } if (!(await this.isSearchable(userId, query))) { + this._isCipherSearching$.next(false); return ciphers; } @@ -258,6 +266,7 @@ export class SearchService implements SearchServiceAbstraction { // Fall back to basic search if index is not available const basicResults = this.searchCiphersBasic(ciphers, query); this.logService.measure(searchStartTime, "Vault", "SearchService", "basic search complete"); + this._isCipherSearching$.next(false); return basicResults; } @@ -293,6 +302,7 @@ export class SearchService implements SearchServiceAbstraction { }); } this.logService.measure(searchStartTime, "Vault", "SearchService", "search complete"); + this._isCipherSearching$.next(false); return results; } @@ -335,8 +345,10 @@ export class SearchService implements SearchServiceAbstraction { } searchSends(sends: SendView[], query: string) { + this._isSendSearching$.next(true); query = SearchService.normalizeSearchQuery(query.trim().toLocaleLowerCase()); if (query === null) { + this._isSendSearching$.next(false); return sends; } const sendsMatched: SendView[] = []; @@ -359,6 +371,7 @@ export class SearchService implements SearchServiceAbstraction { lowPriorityMatched.push(s); } }); + this._isSendSearching$.next(false); return sendsMatched.concat(lowPriorityMatched); } diff --git a/libs/common/src/vault/utils/skeleton-loading.operator.spec.ts b/libs/common/src/vault/utils/skeleton-loading.operator.spec.ts new file mode 100644 index 00000000000..3ba790f64cb --- /dev/null +++ b/libs/common/src/vault/utils/skeleton-loading.operator.spec.ts @@ -0,0 +1,109 @@ +import { BehaviorSubject } from "rxjs"; + +import { skeletonLoadingDelay } from "./skeleton-loading.operator"; + +describe("skeletonLoadingDelay", () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.clearAllTimers(); + jest.useRealTimers(); + }); + + it("returns false immediately when starting with false", () => { + const source$ = new BehaviorSubject<boolean>(false); + const results: boolean[] = []; + + source$.pipe(skeletonLoadingDelay()).subscribe((value) => results.push(value)); + + expect(results).toEqual([false]); + }); + + it("waits 1 second before returning true when starting with true", () => { + const source$ = new BehaviorSubject<boolean>(true); + const results: boolean[] = []; + + source$.pipe(skeletonLoadingDelay()).subscribe((value) => results.push(value)); + + expect(results).toEqual([]); + + jest.advanceTimersByTime(999); + expect(results).toEqual([]); + + jest.advanceTimersByTime(1); + expect(results).toEqual([true]); + }); + + it("cancels if source becomes false before show delay completes", () => { + const source$ = new BehaviorSubject<boolean>(true); + const results: boolean[] = []; + + source$.pipe(skeletonLoadingDelay()).subscribe((value) => results.push(value)); + + jest.advanceTimersByTime(500); + source$.next(false); + + expect(results).toEqual([false]); + + jest.advanceTimersByTime(1000); + expect(results).toEqual([false]); + }); + + it("delays hiding if minimum display time has not elapsed", () => { + const source$ = new BehaviorSubject<boolean>(true); + const results: boolean[] = []; + + source$.pipe(skeletonLoadingDelay()).subscribe((value) => results.push(value)); + + jest.advanceTimersByTime(1000); + expect(results).toEqual([true]); + + source$.next(false); + + expect(results).toEqual([true]); + + jest.advanceTimersByTime(1000); + expect(results).toEqual([true, false]); + }); + + it("handles rapid true->false->true transitions", () => { + const source$ = new BehaviorSubject<boolean>(true); + const results: boolean[] = []; + + source$.pipe(skeletonLoadingDelay()).subscribe((value) => results.push(value)); + + jest.advanceTimersByTime(500); + expect(results).toEqual([]); + + source$.next(false); + expect(results).toEqual([false]); + + source$.next(true); + + jest.advanceTimersByTime(999); + expect(results).toEqual([false]); + + jest.advanceTimersByTime(1); + expect(results).toEqual([false, true]); + }); + + it("allows for custom timings", () => { + const source$ = new BehaviorSubject<boolean>(true); + const results: boolean[] = []; + + source$.pipe(skeletonLoadingDelay(1000, 2000)).subscribe((value) => results.push(value)); + + jest.advanceTimersByTime(1000); + expect(results).toEqual([true]); + + source$.next(false); + + jest.advanceTimersByTime(1999); + expect(results).toEqual([true]); + + jest.advanceTimersByTime(1); + expect(results).toEqual([true, false]); + }); +}); diff --git a/libs/common/src/vault/utils/skeleton-loading.operator.ts b/libs/common/src/vault/utils/skeleton-loading.operator.ts new file mode 100644 index 00000000000..b9ff28f64b5 --- /dev/null +++ b/libs/common/src/vault/utils/skeleton-loading.operator.ts @@ -0,0 +1,59 @@ +import { defer, Observable, of, timer } from "rxjs"; +import { map, switchMap, tap } from "rxjs/operators"; + +/** + * RxJS operator that adds skeleton loading delay behavior. + * + * - Waits 1 second before showing (prevents flashing for quick loads) + * - Ensures skeleton stays visible for at least 1 second once shown regardless of the source observable emissions + * - After the minimum display time, if the source is still true, continues to emit true until the source becomes false + * - False can only be emitted either: + * - Immediately when the source emits false before the skeleton is shown + * - After the minimum display time has passed once the skeleton is shown + */ +export function skeletonLoadingDelay( + showDelay = 1000, + minDisplayTime = 1000, +): (source: Observable<boolean>) => Observable<boolean> { + return (source: Observable<boolean>) => { + return defer(() => { + let skeletonShownAt: number | null = null; + + return source.pipe( + switchMap((shouldShow): Observable<boolean> => { + if (shouldShow) { + if (skeletonShownAt !== null) { + return of(true); // Already shown, continue showing + } + + // Wait for delay, then mark the skeleton as shown and emit true + return timer(showDelay).pipe( + tap(() => { + skeletonShownAt = Date.now(); + }), + map(() => true), + ); + } else { + if (skeletonShownAt === null) { + // Skeleton not shown yet, can emit false immediately + return of(false); + } + + // Skeleton shown, ensure minimum display time has passed + const elapsedTime = Date.now() - skeletonShownAt; + const remainingTime = Math.max(0, minDisplayTime - elapsedTime); + + // Wait for remaining time to ensure minimum display time + return timer(remainingTime).pipe( + tap(() => { + // Reset the shown timestamp + skeletonShownAt = null; + }), + map(() => false), + ); + } + }), + ); + }); + }; +} From d7949ab2f3763be8599c848289fcb00c67f792ba Mon Sep 17 00:00:00 2001 From: Kyle Spearrin <kspearrin@users.noreply.github.com> Date: Thu, 20 Nov 2025 09:42:57 -0500 Subject: [PATCH 69/75] [PM-27766] Add policy for blocking account creation from claimed domains (#17211) * Added policy for blocking account creation for claimed domains. * add feature flag * fix desc * learn more link * fix localization key to learnMore * onpush change detection --- apps/web/src/locales/en/messages.json | 9 ++++++ ...med-domain-account-creation.component.html | 15 +++++++++ ...aimed-domain-account-creation.component.ts | 32 +++++++++++++++++++ .../policies/policy-edit-definitions/index.ts | 1 + .../policies/policy-edit-register.ts | 2 ++ .../admin-console/enums/policy-type.enum.ts | 1 + libs/common/src/enums/feature-flag.enum.ts | 2 ++ 7 files changed, 62 insertions(+) create mode 100644 bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/block-claimed-domain-account-creation.component.html create mode 100644 bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/block-claimed-domain-account-creation.component.ts diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 1b0460e2aa6..59db19aa388 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -12122,6 +12122,15 @@ "startFreeFamiliesTrial": { "message": "Start free Families trial" }, + "blockClaimedDomainAccountCreation": { + "message": "Block account creation for claimed domains" + }, + "blockClaimedDomainAccountCreationDesc": { + "message": "Prevent users from creating accounts outside of your organization using email addresses from claimed domains." + }, + "blockClaimedDomainAccountCreationPrerequisite": { + "message": "A domain must be claimed before activating this policy." + }, "unlockMethodNeededToChangeTimeoutActionDesc": { "message": "Set up an unlock method to change your vault timeout action." }, diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/block-claimed-domain-account-creation.component.html b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/block-claimed-domain-account-creation.component.html new file mode 100644 index 00000000000..17225905995 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/block-claimed-domain-account-creation.component.html @@ -0,0 +1,15 @@ +<bit-callout type="info" title="{{ 'prerequisite' | i18n }}"> + {{ "blockClaimedDomainAccountCreationPrerequisite" | i18n }} + <a + bitLink + href="https://bitwarden.com/help/domain-verification/" + target="_blank" + rel="noreferrer" + >{{ "learnMore" | i18n }}</a + > +</bit-callout> + +<bit-form-control> + <input type="checkbox" id="enabled" bitCheckbox [formControl]="enabled" /> + <bit-label>{{ "turnOn" | i18n }}</bit-label> +</bit-form-control> diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/block-claimed-domain-account-creation.component.ts b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/block-claimed-domain-account-creation.component.ts new file mode 100644 index 00000000000..5e2925aa0bb --- /dev/null +++ b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/block-claimed-domain-account-creation.component.ts @@ -0,0 +1,32 @@ +import { ChangeDetectionStrategy, Component } from "@angular/core"; +import { map, Observable } from "rxjs"; + +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { + BasePolicyEditDefinition, + BasePolicyEditComponent, +} from "@bitwarden/web-vault/app/admin-console/organizations/policies"; +import { SharedModule } from "@bitwarden/web-vault/app/shared"; + +export class BlockClaimedDomainAccountCreationPolicy extends BasePolicyEditDefinition { + name = "blockClaimedDomainAccountCreation"; + description = "blockClaimedDomainAccountCreationDesc"; + type = PolicyType.BlockClaimedDomainAccountCreation; + component = BlockClaimedDomainAccountCreationPolicyComponent; + + override display$(organization: Organization, configService: ConfigService): Observable<boolean> { + return configService + .getFeatureFlag$(FeatureFlag.BlockClaimedDomainAccountCreation) + .pipe(map((enabled) => enabled && organization.useOrganizationDomains)); + } +} + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: "block-claimed-domain-account-creation.component.html", + imports: [SharedModule], +}) +export class BlockClaimedDomainAccountCreationPolicyComponent extends BasePolicyEditComponent {} diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/index.ts b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/index.ts index 52325eae160..b03f3680422 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/index.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/index.ts @@ -1,3 +1,4 @@ export { ActivateAutofillPolicy } from "./activate-autofill.component"; export { AutomaticAppLoginPolicy } from "./automatic-app-login.component"; +export { BlockClaimedDomainAccountCreationPolicy } from "./block-claimed-domain-account-creation.component"; export { DisablePersonalVaultExportPolicy } from "./disable-personal-vault-export.component"; diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-register.ts b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-register.ts index 015b4fc17be..c2a31d936b8 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-register.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-register.ts @@ -9,6 +9,7 @@ import { SessionTimeoutPolicy } from "../../key-management/policies/session-time import { ActivateAutofillPolicy, AutomaticAppLoginPolicy, + BlockClaimedDomainAccountCreationPolicy, DisablePersonalVaultExportPolicy, } from "./policy-edit-definitions"; @@ -23,6 +24,7 @@ const policyEditRegister: BasePolicyEditDefinition[] = [ new FreeFamiliesSponsorshipPolicy(), new ActivateAutofillPolicy(), new AutomaticAppLoginPolicy(), + new BlockClaimedDomainAccountCreationPolicy(), ]; export const bitPolicyEditRegister = ossPolicyEditRegister.concat(policyEditRegister); diff --git a/libs/common/src/admin-console/enums/policy-type.enum.ts b/libs/common/src/admin-console/enums/policy-type.enum.ts index ae0070dda89..af8147c41e4 100644 --- a/libs/common/src/admin-console/enums/policy-type.enum.ts +++ b/libs/common/src/admin-console/enums/policy-type.enum.ts @@ -20,4 +20,5 @@ export enum PolicyType { UriMatchDefaults = 16, // Sets the default URI matching strategy for all users within an organization AutotypeDefaultSetting = 17, // Sets the default autotype setting for desktop app AutoConfirm = 18, // Enables the auto confirmation feature for admins to enable in their client + BlockClaimedDomainAccountCreation = 19, // Prevents users from creating personal accounts using email addresses from verified domains } diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index 7d2d831bfb3..d06a14d242f 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -13,6 +13,7 @@ export enum FeatureFlag { /* Admin Console Team */ CreateDefaultLocation = "pm-19467-create-default-location", AutoConfirm = "pm-19934-auto-confirm-organization-users", + BlockClaimedDomainAccountCreation = "block-claimed-domain-account-creation", /* Auth */ PM22110_DisableAlternateLoginMethods = "pm-22110-disable-alternate-login-methods", @@ -91,6 +92,7 @@ export const DefaultFeatureFlagValue = { /* Admin Console Team */ [FeatureFlag.CreateDefaultLocation]: FALSE, [FeatureFlag.AutoConfirm]: FALSE, + [FeatureFlag.BlockClaimedDomainAccountCreation]: FALSE, /* Autofill */ [FeatureFlag.MacOsNativeCredentialSync]: FALSE, From a5caa194cdf010b509fb9dfa0003f5b46fbf6e1d Mon Sep 17 00:00:00 2001 From: Brandon Treston <btreston@bitwarden.com> Date: Thu, 20 Nov 2025 09:51:40 -0500 Subject: [PATCH 70/75] fix copy (#17504) --- .../vnext-organization-data-ownership.component.html | 2 +- apps/web/src/locales/en/messages.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.html index 0abc40da683..bd2237bc2fd 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.html +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.html @@ -1,5 +1,5 @@ <p> - {{ "organizationDataOwnershipContent" | i18n }} + {{ "organizationDataOwnershipDescContent" | i18n }} <a bitLink href="https://bitwarden.com/resources/credential-lifecycle-management/" diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 59db19aa388..0c87e00b26c 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -5813,9 +5813,9 @@ "message": "Require all items to be owned by an organization, removing the option to store items at the account level.", "description": "This is the policy description shown in the policy list." }, - "organizationDataOwnershipContent": { - "message": "All items will be owned and saved to the organization, enabling organization-wide controls, visibility, and reporting. When turned on, a default collection be available for each member to store items. Learn more about managing the ", - "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'All items will be owned and saved to the organization, enabling organization-wide controls, visibility, and reporting. When turned on, a default collection be available for each member to store items. Learn more about managing the credential lifecycle.'" + "organizationDataOwnershipDescContent": { + "message": "All items will be owned and saved to the organization, enabling organization-wide controls, visibility, and reporting. When turned on, a default collection will be available for each member to store items. Learn more about managing the ", + "description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'All items will be owned and saved to the organization, enabling organization-wide controls, visibility, and reporting. When turned on, a default collection will be available for each member to store items. Learn more about managing the credential lifecycle.'" }, "organizationDataOwnershipContentAnchor": { "message": "credential lifecycle", From e23b2d0c98a024d3ccc1aa25d7bbfb4163662bce Mon Sep 17 00:00:00 2001 From: Jeffrey Holland <124393578+jholland-livefront@users.noreply.github.com> Date: Thu, 20 Nov 2025 16:31:05 +0100 Subject: [PATCH 71/75] Autofill/pm 25597 plex password generation (#16997) * Correctly fill generated passwords and current password on plex.tv * Correctly fill generated passwords and current password on plex.tv * Leave existing forEach * Add tests for changes --- .../background/overlay.background.spec.ts | 6 + .../autofill/background/overlay.background.ts | 2 + .../services/abstractions/autofill.service.ts | 3 + .../autofill-overlay-content.service.ts | 6 + .../services/autofill.service.spec.ts | 209 +++++++++++++++++- .../src/autofill/services/autofill.service.ts | 51 ++++- 6 files changed, 259 insertions(+), 18 deletions(-) diff --git a/apps/browser/src/autofill/background/overlay.background.spec.ts b/apps/browser/src/autofill/background/overlay.background.spec.ts index 80e453e9e83..50fb291b121 100644 --- a/apps/browser/src/autofill/background/overlay.background.spec.ts +++ b/apps/browser/src/autofill/background/overlay.background.spec.ts @@ -3286,6 +3286,9 @@ describe("OverlayBackground", () => { pageDetails: [pageDetailsForTab], fillNewPassword: true, allowTotpAutofill: true, + focusedFieldForm: undefined, + focusedFieldOpid: undefined, + inlineMenuFillType: undefined, }); expect(overlayBackground["inlineMenuCiphers"].entries()).toStrictEqual( new Map([ @@ -3680,6 +3683,9 @@ describe("OverlayBackground", () => { pageDetails: [overlayBackground["pageDetailsForTab"][sender.tab.id].get(sender.frameId)], fillNewPassword: true, allowTotpAutofill: false, + focusedFieldForm: undefined, + focusedFieldOpid: undefined, + inlineMenuFillType: InlineMenuFillTypes.PasswordGeneration, }); }); }); diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index f3278fa6b07..225cbbe66ca 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -1177,6 +1177,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { allowTotpAutofill: true, focusedFieldForm: this.focusedFieldData?.focusedFieldForm, focusedFieldOpid: this.focusedFieldData?.focusedFieldOpid, + inlineMenuFillType: this.focusedFieldData?.inlineMenuFillType, }); if (totpCode) { @@ -1863,6 +1864,7 @@ export class OverlayBackground implements OverlayBackgroundInterface { allowTotpAutofill: false, focusedFieldForm: this.focusedFieldData?.focusedFieldForm, focusedFieldOpid: this.focusedFieldData?.focusedFieldOpid, + inlineMenuFillType: InlineMenuFillTypes.PasswordGeneration, }); globalThis.setTimeout(async () => { diff --git a/apps/browser/src/autofill/services/abstractions/autofill.service.ts b/apps/browser/src/autofill/services/abstractions/autofill.service.ts index 85bf8c16610..05bfbf378a8 100644 --- a/apps/browser/src/autofill/services/abstractions/autofill.service.ts +++ b/apps/browser/src/autofill/services/abstractions/autofill.service.ts @@ -6,6 +6,7 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { AutofillMessageCommand } from "../../enums/autofill-message.enums"; +import { InlineMenuFillType } from "../../enums/autofill-overlay.enum"; import AutofillField from "../../models/autofill-field"; import AutofillForm from "../../models/autofill-form"; import AutofillPageDetails from "../../models/autofill-page-details"; @@ -30,6 +31,7 @@ export interface AutoFillOptions { autoSubmitLogin?: boolean; focusedFieldForm?: string; focusedFieldOpid?: string; + inlineMenuFillType?: InlineMenuFillType; } export interface FormData { @@ -49,6 +51,7 @@ export interface GenerateFillScriptOptions { tabUrl: string; defaultUriMatch: UriMatchStrategySetting; focusedFieldOpid?: string; + inlineMenuFillType?: InlineMenuFillType; } export type CollectPageDetailsResponseMessage = { diff --git a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts index 7854dc8e161..817a7cca43c 100644 --- a/apps/browser/src/autofill/services/autofill-overlay-content.service.ts +++ b/apps/browser/src/autofill/services/autofill-overlay-content.service.ts @@ -1118,6 +1118,12 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ * @param autofillFieldData - Autofill field data captured from the form field element. */ private async setQualifiedLoginFillType(autofillFieldData: AutofillField) { + // Check if this is a current password field in a password change form + if (this.inlineMenuFieldQualificationService.isUpdateCurrentPasswordField(autofillFieldData)) { + autofillFieldData.inlineMenuFillType = InlineMenuFillTypes.CurrentPasswordUpdate; + return; + } + autofillFieldData.inlineMenuFillType = CipherType.Login; autofillFieldData.showPasskeys = autofillFieldData.autoCompleteType.includes("webauthn"); diff --git a/apps/browser/src/autofill/services/autofill.service.spec.ts b/apps/browser/src/autofill/services/autofill.service.spec.ts index b436214f327..13e97766594 100644 --- a/apps/browser/src/autofill/services/autofill.service.spec.ts +++ b/apps/browser/src/autofill/services/autofill.service.spec.ts @@ -44,6 +44,7 @@ import { TotpService } from "@bitwarden/common/vault/services/totp.service"; import { BrowserApi } from "../../platform/browser/browser-api"; import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service"; import { AutofillMessageCommand, AutofillMessageSender } from "../enums/autofill-message.enums"; +import { InlineMenuFillTypes } from "../enums/autofill-overlay.enum"; import { AutofillPort } from "../enums/autofill-port.enum"; import AutofillField from "../models/autofill-field"; import AutofillPageDetails from "../models/autofill-page-details"; @@ -103,6 +104,15 @@ describe("AutofillService", () => { beforeEach(() => { configService = mock<ConfigService>(); configService.getFeatureFlag$.mockImplementation(() => of(false)); + + // Initialize domainSettingsService BEFORE it's used + domainSettingsService = new DefaultDomainSettingsService( + fakeStateProvider, + policyService, + accountService, + ); + domainSettingsService.equivalentDomains$ = of(mockEquivalentDomains); + scriptInjectorService = new BrowserScriptInjectorService( domainSettingsService, platformUtilsService, @@ -141,12 +151,6 @@ describe("AutofillService", () => { userNotificationsSettings, messageListener, ); - domainSettingsService = new DefaultDomainSettingsService( - fakeStateProvider, - policyService, - accountService, - ); - domainSettingsService.equivalentDomains$ = of(mockEquivalentDomains); jest.spyOn(BrowserApi, "tabSendMessage"); }); @@ -2077,6 +2081,193 @@ describe("AutofillService", () => { }); }); + describe("given password generation with inlineMenuFillType", () => { + beforeEach(() => { + pageDetails.forms = undefined; + pageDetails.fields = []; // Clear fields to start fresh + options.inlineMenuFillType = InlineMenuFillTypes.PasswordGeneration; + options.cipher.login.totp = null; // Disable TOTP for these tests + }); + + it("includes all password fields from the same form when filling with password generation", async () => { + const newPasswordField = createAutofillFieldMock({ + opid: "new-password", + type: "password", + form: "validFormId", + elementNumber: 2, + }); + const confirmPasswordField = createAutofillFieldMock({ + opid: "confirm-password", + type: "password", + form: "validFormId", + elementNumber: 3, + }); + pageDetails.fields.push(newPasswordField, confirmPasswordField); + options.focusedFieldOpid = newPasswordField.opid; + + await autofillService["generateLoginFillScript"]( + fillScript, + pageDetails, + filledFields, + options, + ); + + expect(filledFields[newPasswordField.opid]).toBeDefined(); + expect(filledFields[confirmPasswordField.opid]).toBeDefined(); + }); + + it("finds username field for the first password field when generating passwords", async () => { + const newPasswordField = createAutofillFieldMock({ + opid: "new-password", + type: "password", + form: "validFormId", + elementNumber: 2, + }); + pageDetails.fields.push(newPasswordField); + options.focusedFieldOpid = newPasswordField.opid; + jest.spyOn(autofillService as any, "findUsernameField"); + + await autofillService["generateLoginFillScript"]( + fillScript, + pageDetails, + filledFields, + options, + ); + + expect(autofillService["findUsernameField"]).toHaveBeenCalledWith( + pageDetails, + expect.objectContaining({ opid: newPasswordField.opid }), + false, + false, + true, + ); + }); + + it("does not include password fields from different forms", async () => { + const formAPasswordField = createAutofillFieldMock({ + opid: "form-a-password", + type: "password", + form: "formA", + elementNumber: 1, + }); + const formBPasswordField = createAutofillFieldMock({ + opid: "form-b-password", + type: "password", + form: "formB", + elementNumber: 2, + }); + pageDetails.fields = [formAPasswordField, formBPasswordField]; + options.focusedFieldOpid = formAPasswordField.opid; + + await autofillService["generateLoginFillScript"]( + fillScript, + pageDetails, + filledFields, + options, + ); + + expect(filledFields[formAPasswordField.opid]).toBeDefined(); + expect(filledFields[formBPasswordField.opid]).toBeUndefined(); + }); + }); + + describe("given current password update with inlineMenuFillType", () => { + beforeEach(() => { + pageDetails.forms = undefined; + pageDetails.fields = []; // Clear fields to start fresh + options.inlineMenuFillType = InlineMenuFillTypes.CurrentPasswordUpdate; + options.cipher.login.totp = null; // Disable TOTP for these tests + }); + + it("includes all password fields from the same form when updating current password", async () => { + const currentPasswordField = createAutofillFieldMock({ + opid: "current-password", + type: "password", + form: "validFormId", + elementNumber: 1, + }); + const newPasswordField = createAutofillFieldMock({ + opid: "new-password", + type: "password", + form: "validFormId", + elementNumber: 2, + }); + const confirmPasswordField = createAutofillFieldMock({ + opid: "confirm-password", + type: "password", + form: "validFormId", + elementNumber: 3, + }); + pageDetails.fields.push(currentPasswordField, newPasswordField, confirmPasswordField); + options.focusedFieldOpid = currentPasswordField.opid; + + await autofillService["generateLoginFillScript"]( + fillScript, + pageDetails, + filledFields, + options, + ); + + expect(filledFields[currentPasswordField.opid]).toBeDefined(); + expect(filledFields[newPasswordField.opid]).toBeDefined(); + expect(filledFields[confirmPasswordField.opid]).toBeDefined(); + }); + + it("includes all password fields from the same form without TOTP", async () => { + const currentPasswordField = createAutofillFieldMock({ + opid: "current-password", + type: "password", + form: "validFormId", + elementNumber: 1, + }); + const newPasswordField = createAutofillFieldMock({ + opid: "new-password", + type: "password", + form: "validFormId", + elementNumber: 2, + }); + pageDetails.fields.push(currentPasswordField, newPasswordField); + options.focusedFieldOpid = currentPasswordField.opid; + + await autofillService["generateLoginFillScript"]( + fillScript, + pageDetails, + filledFields, + options, + ); + + expect(filledFields[currentPasswordField.opid]).toBeDefined(); + expect(filledFields[newPasswordField.opid]).toBeDefined(); + }); + + it("does not include password fields from different forms during password update", async () => { + const formAPasswordField = createAutofillFieldMock({ + opid: "form-a-password", + type: "password", + form: "formA", + elementNumber: 1, + }); + const formBPasswordField = createAutofillFieldMock({ + opid: "form-b-password", + type: "password", + form: "formB", + elementNumber: 2, + }); + pageDetails.fields = [formAPasswordField, formBPasswordField]; + options.focusedFieldOpid = formAPasswordField.opid; + + await autofillService["generateLoginFillScript"]( + fillScript, + pageDetails, + filledFields, + options, + ); + + expect(filledFields[formAPasswordField.opid]).toBeDefined(); + expect(filledFields[formBPasswordField.opid]).toBeUndefined(); + }); + }); + describe("given a set of page details that does not contain a password field", () => { let emailField: AutofillField; let emailFieldView: FieldView; @@ -3140,12 +3331,16 @@ describe("AutofillService", () => { "example.com", "exampleapp.com", ]); - domainSettingsService.equivalentDomains$ = of([["not-example.com"]]); const pageUrl = "https://subdomain.example.com"; const tabUrl = "https://www.not-example.com"; const generateFillScriptOptions = createGenerateFillScriptOptionsMock({ tabUrl }); generateFillScriptOptions.cipher.login.matchesUri = jest.fn().mockReturnValueOnce(false); + // Mock getUrlEquivalentDomains to return the expected domains + jest + .spyOn(domainSettingsService, "getUrlEquivalentDomains") + .mockReturnValue(of(equivalentDomains)); + const result = await autofillService["inUntrustedIframe"](pageUrl, generateFillScriptOptions); expect(generateFillScriptOptions.cipher.login.matchesUri).toHaveBeenCalledWith( diff --git a/apps/browser/src/autofill/services/autofill.service.ts b/apps/browser/src/autofill/services/autofill.service.ts index fcc8861228b..010f5ea0f27 100644 --- a/apps/browser/src/autofill/services/autofill.service.ts +++ b/apps/browser/src/autofill/services/autofill.service.ts @@ -52,6 +52,7 @@ import { ScriptInjectorService } from "../../platform/services/abstractions/scri // eslint-disable-next-line no-restricted-imports import { openVaultItemPasswordRepromptPopout } from "../../vault/popup/utils/vault-popout-window"; import { AutofillMessageCommand, AutofillMessageSender } from "../enums/autofill-message.enums"; +import { InlineMenuFillTypes } from "../enums/autofill-overlay.enum"; import { AutofillPort } from "../enums/autofill-port.enum"; import AutofillField from "../models/autofill-field"; import AutofillPageDetails from "../models/autofill-page-details"; @@ -452,6 +453,7 @@ export default class AutofillService implements AutofillServiceInterface { tabUrl: tab.url, defaultUriMatch: defaultUriMatch, focusedFieldOpid: options.focusedFieldOpid, + inlineMenuFillType: options.inlineMenuFillType, }); if (!fillScript || !fillScript.script || !fillScript.script.length) { @@ -971,26 +973,53 @@ export default class AutofillService implements AutofillServiceInterface { if (passwordFields.length && !passwords.length) { // in the event that password fields exist but weren't processed within form elements. - // select matching password if focused, otherwise first in prioritized list. for username, use focused field if it matches, otherwise find field before password. - const passwordFieldToUse = focusedField - ? prioritizedPasswordFields.find(passwordMatchesFocused) || prioritizedPasswordFields[0] - : prioritizedPasswordFields[0]; + const isPasswordGeneration = + options.inlineMenuFillType === InlineMenuFillTypes.PasswordGeneration; + const isCurrentPasswordUpdate = + options.inlineMenuFillType === InlineMenuFillTypes.CurrentPasswordUpdate; - if (passwordFieldToUse) { - passwords.push(passwordFieldToUse); + // For password generation or current password update, include all password fields from the same form + // This ensures we have access to all fields regardless of their login/registration classification + if ((isPasswordGeneration || isCurrentPasswordUpdate) && focusedField) { + // Add all password fields from the same form as the focused field + const focusedFieldForm = focusedField.form; - if (login.username && passwordFieldToUse.elementNumber > 0) { - username = getUsernameForPassword(passwordFieldToUse, true); + // Check both login and registration fields to ensure we get all password fields + const allPasswordFields = [...loginPasswordFields, ...registrationPasswordFields]; + allPasswordFields.forEach((passField) => { + if (passField.form === focusedFieldForm) { + passwords.push(passField); + } + }); + } + + // If we didn't add any passwords above (either not password generation/update or no matching fields), + // select matching password if focused, otherwise first in prioritized list. + if (!passwords.length) { + const passwordFieldToUse = focusedField + ? prioritizedPasswordFields.find(passwordMatchesFocused) || prioritizedPasswordFields[0] + : prioritizedPasswordFields[0]; + + if (passwordFieldToUse) { + passwords.push(passwordFieldToUse); + } + } + + // Handle username and TOTP for the first password field + const firstPasswordField = passwords[0]; + if (firstPasswordField) { + if (login.username && firstPasswordField.elementNumber > 0) { + username = getUsernameForPassword(firstPasswordField, true); if (username) { usernames.set(username.opid, username); } } - if (options.allowTotpAutofill && login.totp && passwordFieldToUse.elementNumber > 0) { + if (options.allowTotpAutofill && login.totp && firstPasswordField.elementNumber > 0) { totp = - isFocusedTotpField && passwordMatchesFocused(passwordFieldToUse) + isFocusedTotpField && passwordMatchesFocused(firstPasswordField) ? focusedField - : this.findTotpField(pageDetails, passwordFieldToUse, false, false, true); + : this.findTotpField(pageDetails, firstPasswordField, false, false, true); if (totp) { totps.push(totp); } From 81453ede1bbdcf13bc0033a886995c9c3993b530 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 11:45:21 -0500 Subject: [PATCH 72/75] [deps] Vault: Update koa to v2.16.2 [SECURITY] (#15807) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Matt Andreko <mandreko@bitwarden.com> --- apps/cli/package.json | 2 +- package-lock.json | 10 +++++----- package.json | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/cli/package.json b/apps/cli/package.json index 00686959ef0..fc38440b70f 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -75,7 +75,7 @@ "inquirer": "8.2.6", "jsdom": "26.1.0", "jszip": "3.10.1", - "koa": "2.16.1", + "koa": "2.16.2", "koa-bodyparser": "4.4.1", "koa-json": "2.0.2", "lowdb": "1.0.0", diff --git a/package-lock.json b/package-lock.json index b017272cd77..dbb3fdb7e2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,7 @@ "inquirer": "8.2.6", "jsdom": "26.1.0", "jszip": "3.10.1", - "koa": "2.16.1", + "koa": "2.16.2", "koa-bodyparser": "4.4.1", "koa-json": "2.0.2", "lit": "3.3.0", @@ -213,7 +213,7 @@ "inquirer": "8.2.6", "jsdom": "26.1.0", "jszip": "3.10.1", - "koa": "2.16.1", + "koa": "2.16.2", "koa-bodyparser": "4.4.1", "koa-json": "2.0.2", "lowdb": "1.0.0", @@ -27947,9 +27947,9 @@ } }, "node_modules/koa": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/koa/-/koa-2.16.1.tgz", - "integrity": "sha512-umfX9d3iuSxTQP4pnzLOz0HKnPg0FaUUIKcye2lOiz3KPu1Y3M3xlz76dISdFPQs37P9eJz1wUpcTS6KDPn9fA==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.16.2.tgz", + "integrity": "sha512-+CCssgnrWKx9aI3OeZwroa/ckG4JICxvIFnSiOUyl2Uv+UTI+xIw0FfFrWS7cQFpoePpr9o8csss7KzsTzNL8Q==", "license": "MIT", "dependencies": { "accepts": "^1.3.5", diff --git a/package.json b/package.json index 54e2685bbec..5d23d6b9938 100644 --- a/package.json +++ b/package.json @@ -186,7 +186,7 @@ "inquirer": "8.2.6", "jsdom": "26.1.0", "jszip": "3.10.1", - "koa": "2.16.1", + "koa": "2.16.2", "koa-bodyparser": "4.4.1", "koa-json": "2.0.2", "lit": "3.3.0", From 9afba33f58dd63206d5ebf24f95db1d1624cca17 Mon Sep 17 00:00:00 2001 From: Stephon Brown <sbrown@livefront.com> Date: Thu, 20 Nov 2025 13:38:33 -0500 Subject: [PATCH 73/75] [PM-26044] Update Offboarding Survey for User and Organization (#17472) * feat(billing): update messages to add reasons * feat(billing): update survey with switching reason based on param * fix(billing): revert value of switching reasons * fix(billing): revert removal of tooExpensive message * fix(billing): Add plan type to params and update switching logic * fix(billing): update to include logic * fix(billing): PR feedback --- ...ganization-subscription-cloud.component.ts | 1 + .../shared/offboarding-survey.component.html | 3 +- .../shared/offboarding-survey.component.ts | 90 +++++++++++-------- apps/web/src/locales/en/messages.json | 8 ++ 4 files changed, 66 insertions(+), 36 deletions(-) diff --git a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts index 70e16ad3037..e0c1a12a80f 100644 --- a/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts +++ b/apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts @@ -344,6 +344,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy data: { type: "Organization", id: this.organizationId, + plan: this.sub.plan.type, }, }); diff --git a/apps/web/src/app/billing/shared/offboarding-survey.component.html b/apps/web/src/app/billing/shared/offboarding-survey.component.html index b69565d95fa..50cf71a03d5 100644 --- a/apps/web/src/app/billing/shared/offboarding-survey.component.html +++ b/apps/web/src/app/billing/shared/offboarding-survey.component.html @@ -21,7 +21,8 @@ </bit-label> <textarea rows="4" bitInput formControlName="feedback"></textarea> <bit-hint>{{ - "charactersCurrentAndMaximum" | i18n: formGroup.value.feedback.length : MaxFeedbackLength + "charactersCurrentAndMaximum" + | i18n: formGroup.value.feedback?.length ?? 0 : MaxFeedbackLength }}</bit-hint> </bit-form-field> </div> diff --git a/apps/web/src/app/billing/shared/offboarding-survey.component.ts b/apps/web/src/app/billing/shared/offboarding-survey.component.ts index fe7d724a079..40e1572a3bb 100644 --- a/apps/web/src/app/billing/shared/offboarding-survey.component.ts +++ b/apps/web/src/app/billing/shared/offboarding-survey.component.ts @@ -1,9 +1,10 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Component, Inject } from "@angular/core"; +import { ChangeDetectionStrategy, Component, Inject } from "@angular/core"; import { FormBuilder, Validators } from "@angular/forms"; import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction"; +import { PlanType } from "@bitwarden/common/billing/enums"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { @@ -21,6 +22,7 @@ type UserOffboardingParams = { type OrganizationOffboardingParams = { type: "Organization"; id: string; + plan: PlanType; }; export type OffboardingSurveyDialogParams = UserOffboardingParams | OrganizationOffboardingParams; @@ -46,50 +48,20 @@ export const openOffboardingSurvey = ( dialogConfig, ); -// 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-cancel-subscription-form", templateUrl: "offboarding-survey.component.html", + changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, }) export class OffboardingSurveyComponent { protected ResultType = OffboardingSurveyDialogResultType; protected readonly MaxFeedbackLength = 400; - protected readonly reasons: Reason[] = [ - { - value: null, - text: this.i18nService.t("selectPlaceholder"), - }, - { - value: "missing_features", - text: this.i18nService.t("missingFeatures"), - }, - { - value: "switched_service", - text: this.i18nService.t("movingToAnotherTool"), - }, - { - value: "too_complex", - text: this.i18nService.t("tooDifficultToUse"), - }, - { - value: "unused", - text: this.i18nService.t("notUsingEnough"), - }, - { - value: "too_expensive", - text: this.i18nService.t("tooExpensive"), - }, - { - value: "other", - text: this.i18nService.t("other"), - }, - ]; + protected readonly reasons: Reason[] = []; protected formGroup = this.formBuilder.group({ - reason: [this.reasons[0].value, [Validators.required]], + reason: [null, [Validators.required]], feedback: ["", [Validators.maxLength(this.MaxFeedbackLength)]], }); @@ -101,7 +73,35 @@ export class OffboardingSurveyComponent { private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, private toastService: ToastService, - ) {} + ) { + this.reasons = [ + { + value: null, + text: this.i18nService.t("selectPlaceholder"), + }, + { + value: "missing_features", + text: this.i18nService.t("missingFeatures"), + }, + { + value: "switched_service", + text: this.i18nService.t("movingToAnotherTool"), + }, + { + value: "too_complex", + text: this.i18nService.t("tooDifficultToUse"), + }, + { + value: "unused", + text: this.i18nService.t("notUsingEnough"), + }, + this.getSwitchingReason(), + { + value: "other", + text: this.i18nService.t("other"), + }, + ]; + } submit = async () => { this.formGroup.markAllAsTouched(); @@ -127,4 +127,24 @@ export class OffboardingSurveyComponent { this.dialogRef.close(this.ResultType.Submitted); }; + + private getSwitchingReason(): Reason { + if (this.dialogParams.type === "User") { + return { + value: "too_expensive", + text: this.i18nService.t("switchToFreePlan"), + }; + } + + const isFamilyPlan = [ + PlanType.FamiliesAnnually, + PlanType.FamiliesAnnually2019, + PlanType.FamiliesAnnually2025, + ].includes(this.dialogParams.plan); + + return { + value: "too_expensive", + text: this.i18nService.t(isFamilyPlan ? "switchToFreeOrg" : "tooExpensive"), + }; + } } diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 0c87e00b26c..5cf1bea6fd8 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -9824,6 +9824,14 @@ "message": "Too expensive", "description": "An option for the offboarding survey shown when a user cancels their subscription." }, + "switchToFreePlan": { + "message": "Switching to free plan", + "description": "An option for the offboarding survey shown when a user cancels their subscription." + }, + "switchToFreeOrg": { + "message": "Switching to free organization", + "description": "An option for the offboarding survey shown when a user cancels their subscription." + }, "freeForOneYear": { "message": "Free for 1 year" }, From 43897df9ed783f5a38acdc6c14d45283a48ba968 Mon Sep 17 00:00:00 2001 From: Vijay Oommen <voommen@livefront.com> Date: Thu, 20 Nov 2025 12:52:23 -0600 Subject: [PATCH 74/75] [PM-27287] Items in My Items should show in Inactive 2FA report (#17434) --- .../reports/pages/cipher-report.component.ts | 29 +++++----- ...exposed-passwords-report.component.spec.ts | 1 + ...active-two-factor-report.component.spec.ts | 12 ++-- .../inactive-two-factor-report.component.ts | 10 ++-- .../inactive-two-factor-report.component.ts | 57 ++++++++++--------- 5 files changed, 56 insertions(+), 53 deletions(-) diff --git a/apps/web/src/app/dirt/reports/pages/cipher-report.component.ts b/apps/web/src/app/dirt/reports/pages/cipher-report.component.ts index 69dd360ad31..d098be56663 100644 --- a/apps/web/src/app/dirt/reports/pages/cipher-report.component.ts +++ b/apps/web/src/app/dirt/reports/pages/cipher-report.component.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Directive, OnDestroy } from "@angular/core"; import { BehaviorSubject, @@ -36,7 +34,7 @@ import { import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/services/admin-console-cipher-form-config.service"; @Directive() -export class CipherReportComponent implements OnDestroy { +export abstract class CipherReportComponent implements OnDestroy { isAdminConsoleActive = false; loading = false; @@ -44,16 +42,16 @@ export class CipherReportComponent implements OnDestroy { ciphers: CipherView[] = []; allCiphers: CipherView[] = []; dataSource = new TableDataSource<CipherView>(); - organization: Organization; - organizations: Organization[]; + organization: Organization | undefined = undefined; + organizations: Organization[] = []; organizations$: Observable<Organization[]>; filterStatus: any = [0]; showFilterToggle: boolean = false; vaultMsg: string = "vault"; - currentFilterStatus: number | string; + currentFilterStatus: number | string = 0; protected filterOrgStatus$ = new BehaviorSubject<number | string>(0); - private destroyed$: Subject<void> = new Subject(); + protected destroyed$: Subject<void> = new Subject(); private vaultItemDialogRef?: DialogRef<VaultItemDialogResult> | undefined; constructor( @@ -107,7 +105,7 @@ export class CipherReportComponent implements OnDestroy { if (filterId === 0) { cipherCount = this.allCiphers.length; } else if (filterId === 1) { - cipherCount = this.allCiphers.filter((c) => c.organizationId === null).length; + cipherCount = this.allCiphers.filter((c) => !c.organizationId).length; } else { this.organizations.filter((org: Organization) => { if (org.id === filterId) { @@ -121,9 +119,9 @@ export class CipherReportComponent implements OnDestroy { } async filterOrgToggle(status: any) { - let filter = null; + let filter = (c: CipherView) => true; if (typeof status === "number" && status === 1) { - filter = (c: CipherView) => c.organizationId == null; + filter = (c: CipherView) => !c.organizationId; } else if (typeof status === "string") { const orgId = status as OrganizationId; filter = (c: CipherView) => c.organizationId === orgId; @@ -185,7 +183,7 @@ export class CipherReportComponent implements OnDestroy { cipher: CipherView, activeCollectionId?: CollectionId, ) { - const disableForm = cipher ? !cipher.edit && !this.organization.canEditAllCiphers : false; + const disableForm = cipher ? !cipher.edit && !this.organization?.canEditAllCiphers : false; this.vaultItemDialogRef = VaultItemDialogComponent.open(this.dialogService, { mode, @@ -230,10 +228,11 @@ export class CipherReportComponent implements OnDestroy { let updatedCipher = await this.cipherService.get(cipher.id, activeUserId); if (this.isAdminConsoleActive) { - updatedCipher = await this.adminConsoleCipherFormConfigService.getCipher( - cipher.id as CipherId, - this.organization, - ); + updatedCipher = + (await this.adminConsoleCipherFormConfigService.getCipher( + cipher.id as CipherId, + this.organization!, + )) ?? updatedCipher; } // convert cipher to cipher view model diff --git a/apps/web/src/app/dirt/reports/pages/exposed-passwords-report.component.spec.ts b/apps/web/src/app/dirt/reports/pages/exposed-passwords-report.component.spec.ts index 052e3bc7cfe..560245bdc34 100644 --- a/apps/web/src/app/dirt/reports/pages/exposed-passwords-report.component.spec.ts +++ b/apps/web/src/app/dirt/reports/pages/exposed-passwords-report.component.spec.ts @@ -90,6 +90,7 @@ describe("ExposedPasswordsReportComponent", () => { }); beforeEach(() => { + jest.clearAllMocks(); fixture = TestBed.createComponent(ExposedPasswordsReportComponent); component = fixture.componentInstance; fixture.detectChanges(); 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 80893737ffd..64a851e120e 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 @@ -1,3 +1,4 @@ +import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core"; import { ComponentFixture, TestBed } from "@angular/core/testing"; import { MockProxy, mock } from "jest-mock-extended"; import { of } from "rxjs"; @@ -29,14 +30,13 @@ describe("InactiveTwoFactorReportComponent", () => { const userId = Utils.newGuid() as UserId; const accountService: FakeAccountService = mockAccountServiceWith(userId); - beforeEach(() => { + beforeEach(async () => { let cipherFormConfigServiceMock: MockProxy<CipherFormConfigService>; organizationService = mock<OrganizationService>(); organizationService.organizations$.mockReturnValue(of([])); syncServiceMock = mock<SyncService>(); - // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. - // eslint-disable-next-line @typescript-eslint/no-floating-promises - TestBed.configureTestingModule({ + + await TestBed.configureTestingModule({ declarations: [InactiveTwoFactorReportComponent, I18nPipe], providers: [ { @@ -80,9 +80,7 @@ describe("InactiveTwoFactorReportComponent", () => { useValue: adminConsoleCipherFormConfigServiceMock, }, ], - schemas: [], - // FIXME(PM-18598): Replace unknownElements and unknownProperties with actual imports - errorOnUnknownElements: false, + schemas: [CUSTOM_ELEMENTS_SCHEMA], }).compileComponents(); }); 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 2a8ec12ac6a..9d7de688f3e 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 @@ -1,6 +1,4 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, OnInit } from "@angular/core"; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from "@angular/core"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; @@ -19,9 +17,8 @@ import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/se import { CipherReportComponent } from "./cipher-report.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({ + changeDetection: ChangeDetectionStrategy.OnPush, selector: "app-inactive-two-factor-report", templateUrl: "inactive-two-factor-report.component.html", standalone: false, @@ -42,6 +39,7 @@ export class InactiveTwoFactorReportComponent extends CipherReportComponent impl syncService: SyncService, cipherFormConfigService: CipherFormConfigService, adminConsoleCipherFormConfigService: AdminConsoleCipherFormConfigService, + protected changeDetectorRef: ChangeDetectorRef, ) { super( cipherService, @@ -86,6 +84,7 @@ export class InactiveTwoFactorReportComponent extends CipherReportComponent impl this.filterCiphersByOrg(inactive2faCiphers); this.cipherDocs = docs; + this.changeDetectorRef.markForCheck(); } } @@ -157,6 +156,7 @@ export class InactiveTwoFactorReportComponent extends CipherReportComponent impl } this.services.set(serviceData.domain, serviceData.documentation); } + this.changeDetectorRef.markForCheck(); } /** diff --git a/apps/web/src/app/dirt/reports/pages/organizations/inactive-two-factor-report.component.ts b/apps/web/src/app/dirt/reports/pages/organizations/inactive-two-factor-report.component.ts index fde9c35a6de..17555e617cb 100644 --- a/apps/web/src/app/dirt/reports/pages/organizations/inactive-two-factor-report.component.ts +++ b/apps/web/src/app/dirt/reports/pages/organizations/inactive-two-factor-report.component.ts @@ -1,16 +1,12 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { Component, OnInit } from "@angular/core"; +import { ChangeDetectorRef, Component, OnInit, ChangeDetectionStrategy } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; -import { firstValueFrom, map } from "rxjs"; +import { firstValueFrom, map, takeUntil } from "rxjs"; -import { - getOrganizationById, - OrganizationService, -} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { getById } from "@bitwarden/common/platform/misc"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction"; import { Cipher } from "@bitwarden/common/vault/models/domain/cipher"; @@ -23,9 +19,8 @@ import { RoutedVaultFilterService } from "../../../../vault/individual-vault/vau import { AdminConsoleCipherFormConfigService } from "../../../../vault/org-vault/services/admin-console-cipher-form-config.service"; import { InactiveTwoFactorReportComponent as BaseInactiveTwoFactorReportComponent } from "../inactive-two-factor-report.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({ + changeDetection: ChangeDetectionStrategy.OnPush, selector: "app-inactive-two-factor-report", templateUrl: "../inactive-two-factor-report.component.html", providers: [ @@ -44,7 +39,7 @@ export class InactiveTwoFactorReportComponent implements OnInit { // Contains a list of ciphers, the user running the report, can manage - private manageableCiphers: Cipher[]; + private manageableCiphers: Cipher[] = []; constructor( cipherService: CipherService, @@ -58,6 +53,7 @@ export class InactiveTwoFactorReportComponent syncService: SyncService, cipherFormConfigService: CipherFormConfigService, adminConsoleCipherFormConfigService: AdminConsoleCipherFormConfigService, + protected changeDetectorRef: ChangeDetectorRef, ) { super( cipherService, @@ -70,28 +66,37 @@ export class InactiveTwoFactorReportComponent syncService, cipherFormConfigService, adminConsoleCipherFormConfigService, + changeDetectorRef, ); } async ngOnInit() { this.isAdminConsoleActive = true; - // eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe - this.route.parent.parent.params.subscribe(async (params) => { - const userId = await firstValueFrom( - this.accountService.activeAccount$.pipe(map((a) => a?.id)), - ); - this.organization = await firstValueFrom( - this.organizationService - .organizations$(userId) - .pipe(getOrganizationById(params.organizationId)), - ); - this.manageableCiphers = await this.cipherService.getAll(userId); - await super.ngOnInit(); - }); + + this.route.parent?.parent?.params + ?.pipe(takeUntil(this.destroyed$)) + // eslint-disable-next-line rxjs/no-async-subscribe + .subscribe(async (params) => { + const userId = await firstValueFrom( + this.accountService.activeAccount$.pipe(map((a) => a?.id)), + ); + + if (userId) { + this.organization = await firstValueFrom( + this.organizationService.organizations$(userId).pipe(getById(params.organizationId)), + ); + this.manageableCiphers = await this.cipherService.getAll(userId); + await super.ngOnInit(); + } + this.changeDetectorRef.markForCheck(); + }); } - getAllCiphers(): Promise<CipherView[]> { - return this.cipherService.getAllFromApiForOrganization(this.organization.id); + async getAllCiphers(): Promise<CipherView[]> { + if (this.organization) { + return await this.cipherService.getAllFromApiForOrganization(this.organization.id, true); + } + return []; } protected canManageCipher(c: CipherView): boolean { From 98401ccda151d60230911e6b8852b78306c3db5f Mon Sep 17 00:00:00 2001 From: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:22:48 -0500 Subject: [PATCH 75/75] PM-28506 - TwoFactorSetupYubikey - refactor yubikey form to be rows with 1 field per row to allow remove button to be visible again. (#17519) --- .../two-factor-setup-yubikey.component.html | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.html b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.html index 172646f5d4d..8baf304969f 100644 --- a/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.html +++ b/apps/web/src/app/auth/settings/two-factor/two-factor-setup-yubikey.component.html @@ -25,23 +25,21 @@ <li>{{ "twoFactorYubikeySaveForm" | i18n }}</li> </ol> <hr /> - <div class="tw-grid tw-grid-cols-12 tw-gap-4" formArrayName="formKeys"> - <div class="tw-col-span-6" *ngFor="let k of keys; let i = index"> - <div [formGroupName]="i"> - <bit-label>{{ "yubikeyX" | i18n: (i + 1).toString() }}</bit-label> - <bit-form-field *ngIf="!keys[i].existingKey"> - <input bitInput type="password" formControlName="key" appInputVerbatim /> - </bit-form-field> - <div class="tw-flex tw-justify-between tw-mb-6" *ngIf="keys[i].existingKey"> - <span class="tw-mr-2 tw-self-center">{{ keys[i].existingKey }}</span> - <button - bitIconButton="bwi-minus-circle" - type="button" - buttonType="danger" - (click)="remove(i)" - label="{{ 'remove' | i18n }}" - ></button> - </div> + <div class="tw-flex tw-flex-col tw-mt-4" formArrayName="formKeys"> + <div *ngFor="let k of keys; let i = index" [formGroupName]="i"> + <bit-label>{{ "yubikeyX" | i18n: (i + 1).toString() }}</bit-label> + <bit-form-field *ngIf="!keys[i].existingKey"> + <input bitInput type="password" formControlName="key" appInputVerbatim /> + </bit-form-field> + <div class="tw-flex tw-justify-between tw-mb-4" *ngIf="keys[i].existingKey"> + <span class="tw-mr-2 tw-self-center">{{ keys[i].existingKey }}</span> + <button + bitIconButton="bwi-minus-circle" + type="button" + buttonType="danger" + (click)="remove(i)" + label="{{ 'remove' | i18n }}" + ></button> </div> </div> </div>